2 * Copyright 2010 Maarten Lankhorst for CodeWeavers
3 * Copyright 2011 Andrew Eikum for CodeWeavers
5 * This library is free software; you can redistribute it and/or
6 * modify it under the terms of the GNU Lesser General Public
7 * License as published by the Free Software Foundation; either
8 * version 2.1 of the License, or (at your option) any later version.
10 * This library is distributed in the hope that it will be useful,
11 * but WITHOUT ANY WARRANTY; without even the implied warranty of
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
13 * Lesser General Public License for more details.
15 * You should have received a copy of the GNU Lesser General Public
16 * License along with this library; if not, write to the Free Software
17 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
20 #define NONAMELESSUNION
31 #include "wine/debug.h"
32 #include "wine/unicode.h"
33 #include "wine/list.h"
36 #include "mmdeviceapi.h"
42 #include "endpointvolume.h"
43 #include "audioclient.h"
44 #include "audiopolicy.h"
46 #include <alsa/asoundlib.h>
48 WINE_DEFAULT_DEBUG_CHANNEL(alsa
);
49 WINE_DECLARE_DEBUG_CHANNEL(winediag
);
51 #define NULL_PTR_ERR MAKE_HRESULT(SEVERITY_ERROR, FACILITY_WIN32, RPC_X_NULL_REF_POINTER)
53 static const REFERENCE_TIME DefaultPeriod
= 100000;
54 static const REFERENCE_TIME MinimumPeriod
= 50000;
55 #define EXTRA_SAFE_RT 40000
58 typedef struct ACImpl ACImpl
;
60 typedef struct _AudioSession
{
71 CRITICAL_SECTION lock
;
76 typedef struct _AudioSessionWrapper
{
77 IAudioSessionControl2 IAudioSessionControl2_iface
;
78 IChannelAudioVolume IChannelAudioVolume_iface
;
79 ISimpleAudioVolume ISimpleAudioVolume_iface
;
84 AudioSession
*session
;
85 } AudioSessionWrapper
;
88 IAudioClient IAudioClient_iface
;
89 IAudioRenderClient IAudioRenderClient_iface
;
90 IAudioCaptureClient IAudioCaptureClient_iface
;
91 IAudioClock IAudioClock_iface
;
92 IAudioClock2 IAudioClock2_iface
;
93 IAudioStreamVolume IAudioStreamVolume_iface
;
97 snd_pcm_t
*pcm_handle
;
98 snd_pcm_uframes_t alsa_bufsize_frames
, alsa_period_frames
;
99 snd_pcm_hw_params_t
*hw_params
; /* does not hold state between calls */
100 snd_pcm_format_t alsa_format
;
103 IUnknown
*pUnkFTMarshal
;
108 AUDCLNT_SHAREMODE share
;
114 int alsa_channel_map
[32];
116 BOOL initted
, started
;
117 REFERENCE_TIME mmdev_period_rt
;
118 UINT64 written_frames
, last_pos_frames
;
119 UINT32 bufsize_frames
, held_frames
, tmp_buffer_frames
, mmdev_period_frames
;
120 snd_pcm_uframes_t remapping_buf_frames
;
121 UINT32 lcl_offs_frames
; /* offs into local_buffer where valid data starts */
122 UINT32 wri_offs_frames
; /* where to write fresh data in local_buffer */
123 UINT32 hidden_frames
; /* ALSA reserve to ensure continuous rendering */
126 BYTE
*local_buffer
, *tmp_buffer
, *remapping_buf
;
127 LONG32 getbuf_last
; /* <0 when using tmp_buffer */
129 CRITICAL_SECTION lock
;
131 AudioSession
*session
;
132 AudioSessionWrapper
*session_wrapper
;
137 typedef struct _SessionMgr
{
138 IAudioSessionManager2 IAudioSessionManager2_iface
;
145 static HANDLE g_timer_q
;
147 static CRITICAL_SECTION g_sessions_lock
;
148 static CRITICAL_SECTION_DEBUG g_sessions_lock_debug
=
150 0, 0, &g_sessions_lock
,
151 { &g_sessions_lock_debug
.ProcessLocksList
, &g_sessions_lock_debug
.ProcessLocksList
},
152 0, 0, { (DWORD_PTR
)(__FILE__
": g_sessions_lock") }
154 static CRITICAL_SECTION g_sessions_lock
= { &g_sessions_lock_debug
, -1, 0, 0, 0, 0 };
155 static struct list g_sessions
= LIST_INIT(g_sessions
);
157 static const WCHAR defaultW
[] = {'d','e','f','a','u','l','t',0};
158 static const char defname
[] = "default";
160 static const WCHAR drv_keyW
[] = {'S','o','f','t','w','a','r','e','\\',
161 'W','i','n','e','\\','D','r','i','v','e','r','s','\\',
162 'w','i','n','e','a','l','s','a','.','d','r','v',0};
163 static const WCHAR drv_key_devicesW
[] = {'S','o','f','t','w','a','r','e','\\',
164 'W','i','n','e','\\','D','r','i','v','e','r','s','\\',
165 'w','i','n','e','a','l','s','a','.','d','r','v','\\','d','e','v','i','c','e','s',0};
166 static const WCHAR guidW
[] = {'g','u','i','d',0};
168 static const IAudioClientVtbl AudioClient_Vtbl
;
169 static const IAudioRenderClientVtbl AudioRenderClient_Vtbl
;
170 static const IAudioCaptureClientVtbl AudioCaptureClient_Vtbl
;
171 static const IAudioSessionControl2Vtbl AudioSessionControl2_Vtbl
;
172 static const ISimpleAudioVolumeVtbl SimpleAudioVolume_Vtbl
;
173 static const IAudioClockVtbl AudioClock_Vtbl
;
174 static const IAudioClock2Vtbl AudioClock2_Vtbl
;
175 static const IAudioStreamVolumeVtbl AudioStreamVolume_Vtbl
;
176 static const IChannelAudioVolumeVtbl ChannelAudioVolume_Vtbl
;
177 static const IAudioSessionManager2Vtbl AudioSessionManager2_Vtbl
;
179 static AudioSessionWrapper
*AudioSessionWrapper_Create(ACImpl
*client
);
181 static inline ACImpl
*impl_from_IAudioClient(IAudioClient
*iface
)
183 return CONTAINING_RECORD(iface
, ACImpl
, IAudioClient_iface
);
186 static inline ACImpl
*impl_from_IAudioRenderClient(IAudioRenderClient
*iface
)
188 return CONTAINING_RECORD(iface
, ACImpl
, IAudioRenderClient_iface
);
191 static inline ACImpl
*impl_from_IAudioCaptureClient(IAudioCaptureClient
*iface
)
193 return CONTAINING_RECORD(iface
, ACImpl
, IAudioCaptureClient_iface
);
196 static inline AudioSessionWrapper
*impl_from_IAudioSessionControl2(IAudioSessionControl2
*iface
)
198 return CONTAINING_RECORD(iface
, AudioSessionWrapper
, IAudioSessionControl2_iface
);
201 static inline AudioSessionWrapper
*impl_from_ISimpleAudioVolume(ISimpleAudioVolume
*iface
)
203 return CONTAINING_RECORD(iface
, AudioSessionWrapper
, ISimpleAudioVolume_iface
);
206 static inline AudioSessionWrapper
*impl_from_IChannelAudioVolume(IChannelAudioVolume
*iface
)
208 return CONTAINING_RECORD(iface
, AudioSessionWrapper
, IChannelAudioVolume_iface
);
211 static inline ACImpl
*impl_from_IAudioClock(IAudioClock
*iface
)
213 return CONTAINING_RECORD(iface
, ACImpl
, IAudioClock_iface
);
216 static inline ACImpl
*impl_from_IAudioClock2(IAudioClock2
*iface
)
218 return CONTAINING_RECORD(iface
, ACImpl
, IAudioClock2_iface
);
221 static inline ACImpl
*impl_from_IAudioStreamVolume(IAudioStreamVolume
*iface
)
223 return CONTAINING_RECORD(iface
, ACImpl
, IAudioStreamVolume_iface
);
226 static inline SessionMgr
*impl_from_IAudioSessionManager2(IAudioSessionManager2
*iface
)
228 return CONTAINING_RECORD(iface
, SessionMgr
, IAudioSessionManager2_iface
);
231 BOOL WINAPI
DllMain(HINSTANCE dll
, DWORD reason
, void *reserved
)
235 case DLL_PROCESS_ATTACH
:
236 g_timer_q
= CreateTimerQueue();
241 case DLL_PROCESS_DETACH
:
243 DeleteCriticalSection(&g_sessions_lock
);
249 /* From <dlls/mmdevapi/mmdevapi.h> */
250 enum DriverPriority
{
251 Priority_Unavailable
= 0,
257 int WINAPI
AUDDRV_GetPriority(void)
259 return Priority_Neutral
;
262 static void set_device_guid(EDataFlow flow
, HKEY drv_key
, const WCHAR
*key_name
,
270 lr
= RegCreateKeyExW(HKEY_CURRENT_USER
, drv_key_devicesW
, 0, NULL
, 0, KEY_WRITE
,
271 NULL
, &drv_key
, NULL
);
272 if(lr
!= ERROR_SUCCESS
){
273 ERR("RegCreateKeyEx(drv_key) failed: %u\n", lr
);
279 lr
= RegCreateKeyExW(drv_key
, key_name
, 0, NULL
, 0, KEY_WRITE
,
281 if(lr
!= ERROR_SUCCESS
){
282 ERR("RegCreateKeyEx(%s) failed: %u\n", wine_dbgstr_w(key_name
), lr
);
286 lr
= RegSetValueExW(key
, guidW
, 0, REG_BINARY
, (BYTE
*)guid
,
288 if(lr
!= ERROR_SUCCESS
)
289 ERR("RegSetValueEx(%s\\guid) failed: %u\n", wine_dbgstr_w(key_name
), lr
);
294 RegCloseKey(drv_key
);
297 static void get_device_guid(EDataFlow flow
, const char *device
, GUID
*guid
)
299 HKEY key
= NULL
, dev_key
;
300 DWORD type
, size
= sizeof(*guid
);
308 MultiByteToWideChar(CP_UNIXCP
, 0, device
, -1, key_name
+ 2,
309 (sizeof(key_name
) / sizeof(*key_name
)) - 2);
311 if(RegOpenKeyExW(HKEY_CURRENT_USER
, drv_key_devicesW
, 0, KEY_WRITE
|KEY_READ
, &key
) == ERROR_SUCCESS
){
312 if(RegOpenKeyExW(key
, key_name
, 0, KEY_READ
, &dev_key
) == ERROR_SUCCESS
){
313 if(RegQueryValueExW(dev_key
, guidW
, 0, &type
,
314 (BYTE
*)guid
, &size
) == ERROR_SUCCESS
){
315 if(type
== REG_BINARY
){
316 RegCloseKey(dev_key
);
320 ERR("Invalid type for device %s GUID: %u; ignoring and overwriting\n",
321 wine_dbgstr_w(key_name
), type
);
323 RegCloseKey(dev_key
);
329 set_device_guid(flow
, key
, key_name
, guid
);
335 static BOOL
alsa_try_open(const char *devnode
, snd_pcm_stream_t stream
)
340 TRACE("devnode: %s, stream: %d\n", devnode
, stream
);
342 if((err
= snd_pcm_open(&handle
, devnode
, stream
, SND_PCM_NONBLOCK
)) < 0){
343 WARN("The device \"%s\" failed to open: %d (%s).\n",
344 devnode
, err
, snd_strerror(err
));
348 snd_pcm_close(handle
);
352 static WCHAR
*construct_device_id(EDataFlow flow
, const WCHAR
*chunk1
, const char *chunk2
)
356 DWORD len_wchars
= 0, chunk1_len
, copied
= 0, prefix_len
;
358 static const WCHAR dashW
[] = {' ','-',' ',0};
359 static const size_t dashW_len
= (sizeof(dashW
) / sizeof(*dashW
)) - 1;
360 static const WCHAR outW
[] = {'O','u','t',':',' ',0};
361 static const WCHAR inW
[] = {'I','n',':',' ',0};
365 prefix_len
= (sizeof(outW
) / sizeof(*outW
)) - 1;
366 len_wchars
+= prefix_len
;
369 prefix_len
= (sizeof(inW
) / sizeof(*inW
)) - 1;
370 len_wchars
+= prefix_len
;
373 chunk1_len
= strlenW(chunk1
);
374 len_wchars
+= chunk1_len
;
377 len_wchars
+= dashW_len
;
379 len_wchars
+= MultiByteToWideChar(CP_UNIXCP
, 0, chunk2
, -1, NULL
, 0) - 1;
380 len_wchars
+= 1; /* NULL byte */
382 ret
= HeapAlloc(GetProcessHeap(), 0, len_wchars
* sizeof(WCHAR
));
384 memcpy(ret
, prefix
, prefix_len
* sizeof(WCHAR
));
385 copied
+= prefix_len
;
387 memcpy(ret
+ copied
, chunk1
, chunk1_len
* sizeof(WCHAR
));
388 copied
+= chunk1_len
;
390 if(chunk1
&& chunk2
){
391 memcpy(ret
+ copied
, dashW
, dashW_len
* sizeof(WCHAR
));
395 MultiByteToWideChar(CP_UNIXCP
, 0, chunk2
, -1, ret
+ copied
, len_wchars
- copied
);
399 TRACE("Enumerated device: %s\n", wine_dbgstr_w(ret
));
404 static HRESULT
alsa_get_card_devices(EDataFlow flow
, snd_pcm_stream_t stream
,
405 WCHAR
***ids
, GUID
**guids
, UINT
*num
, snd_ctl_t
*ctl
, int card
,
406 const WCHAR
*cardnameW
)
409 snd_pcm_info_t
*info
;
411 info
= HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY
, snd_pcm_info_sizeof());
413 return E_OUTOFMEMORY
;
415 snd_pcm_info_set_subdevice(info
, 0);
416 snd_pcm_info_set_stream(info
, stream
);
419 for(err
= snd_ctl_pcm_next_device(ctl
, &device
); device
!= -1 && err
>= 0;
420 err
= snd_ctl_pcm_next_device(ctl
, &device
)){
424 snd_pcm_info_set_device(info
, device
);
426 if((err
= snd_ctl_pcm_info(ctl
, info
)) < 0){
428 /* This device doesn't have the right stream direction */
431 WARN("Failed to get info for card %d, device %d: %d (%s)\n",
432 card
, device
, err
, snd_strerror(err
));
436 sprintf(devnode
, "plughw:%d,%d", card
, device
);
437 if(!alsa_try_open(devnode
, stream
))
441 *ids
= HeapReAlloc(GetProcessHeap(), 0, *ids
, sizeof(WCHAR
*) * (*num
+ 1));
442 *guids
= HeapReAlloc(GetProcessHeap(), 0, *guids
, sizeof(GUID
) * (*num
+ 1));
444 *ids
= HeapAlloc(GetProcessHeap(), 0, sizeof(WCHAR
*));
445 *guids
= HeapAlloc(GetProcessHeap(), 0, sizeof(GUID
));
448 devname
= snd_pcm_info_get_name(info
);
450 WARN("Unable to get device name for card %d, device %d\n", card
,
455 (*ids
)[*num
] = construct_device_id(flow
, cardnameW
, devname
);
456 get_device_guid(flow
, devnode
, &(*guids
)[*num
]);
461 HeapFree(GetProcessHeap(), 0, info
);
464 WARN("Got a failure during device enumeration on card %d: %d (%s)\n",
465 card
, err
, snd_strerror(err
));
470 static void get_reg_devices(EDataFlow flow
, snd_pcm_stream_t stream
, WCHAR
***ids
,
471 GUID
**guids
, UINT
*num
)
473 static const WCHAR ALSAOutputDevices
[] = {'A','L','S','A','O','u','t','p','u','t','D','e','v','i','c','e','s',0};
474 static const WCHAR ALSAInputDevices
[] = {'A','L','S','A','I','n','p','u','t','D','e','v','i','c','e','s',0};
476 WCHAR reg_devices
[256];
477 DWORD size
= sizeof(reg_devices
), type
;
478 const WCHAR
*value_name
= (stream
== SND_PCM_STREAM_PLAYBACK
) ? ALSAOutputDevices
: ALSAInputDevices
;
480 /* @@ Wine registry key: HKCU\Software\Wine\Drivers\winealsa.drv */
481 if(RegOpenKeyW(HKEY_CURRENT_USER
, drv_keyW
, &key
) == ERROR_SUCCESS
){
482 if(RegQueryValueExW(key
, value_name
, 0, &type
,
483 (BYTE
*)reg_devices
, &size
) == ERROR_SUCCESS
){
484 WCHAR
*p
= reg_devices
;
486 if(type
!= REG_MULTI_SZ
){
487 ERR("Registry ALSA device list value type must be REG_MULTI_SZ\n");
495 WideCharToMultiByte(CP_UNIXCP
, 0, p
, -1, devname
, sizeof(devname
), NULL
, NULL
);
497 if(alsa_try_open(devname
, stream
)){
499 *ids
= HeapReAlloc(GetProcessHeap(), 0, *ids
, sizeof(WCHAR
*) * (*num
+ 1));
500 *guids
= HeapReAlloc(GetProcessHeap(), 0, *guids
, sizeof(GUID
) * (*num
+ 1));
502 *ids
= HeapAlloc(GetProcessHeap(), 0, sizeof(WCHAR
*));
503 *guids
= HeapAlloc(GetProcessHeap(), 0, sizeof(GUID
));
505 (*ids
)[*num
] = construct_device_id(flow
, p
, NULL
);
506 get_device_guid(flow
, devname
, &(*guids
)[*num
]);
510 p
+= lstrlenW(p
) + 1;
518 static HRESULT
alsa_enum_devices(EDataFlow flow
, WCHAR
***ids
, GUID
**guids
,
521 snd_pcm_stream_t stream
= (flow
== eRender
? SND_PCM_STREAM_PLAYBACK
:
522 SND_PCM_STREAM_CAPTURE
);
528 if(alsa_try_open(defname
, stream
)){
529 *ids
= HeapAlloc(GetProcessHeap(), 0, sizeof(WCHAR
*));
530 (*ids
)[0] = construct_device_id(flow
, defaultW
, NULL
);
531 *guids
= HeapAlloc(GetProcessHeap(), 0, sizeof(GUID
));
532 get_device_guid(flow
, defname
, &(*guids
)[0]);
536 get_reg_devices(flow
, stream
, ids
, guids
, num
);
538 for(err
= snd_card_next(&card
); card
!= -1 && err
>= 0;
539 err
= snd_card_next(&card
)){
546 sprintf(cardpath
, "hw:%u", card
);
548 if((err
= snd_ctl_open(&ctl
, cardpath
, 0)) < 0){
549 WARN("Unable to open ctl for ALSA device %s: %d (%s)\n", cardpath
,
550 err
, snd_strerror(err
));
554 if(snd_card_get_name(card
, &cardname
) < 0) {
555 /* FIXME: Should be localized */
556 static const WCHAR nameW
[] = {'U','n','k','n','o','w','n',' ','s','o','u','n','d','c','a','r','d',0};
557 WARN("Unable to get card name for ALSA device %s: %d (%s)\n",
558 cardpath
, err
, snd_strerror(err
));
559 alsa_get_card_devices(flow
, stream
, ids
, guids
, num
, ctl
, card
, nameW
);
561 len
= MultiByteToWideChar(CP_UNIXCP
, 0, cardname
, -1, NULL
, 0);
562 cardnameW
= HeapAlloc(GetProcessHeap(), 0, len
* sizeof(WCHAR
));
567 return E_OUTOFMEMORY
;
569 MultiByteToWideChar(CP_UNIXCP
, 0, cardname
, -1, cardnameW
, len
);
571 alsa_get_card_devices(flow
, stream
, ids
, guids
, num
, ctl
, card
, cardnameW
);
573 HeapFree(GetProcessHeap(), 0, cardnameW
);
581 WARN("Got a failure during card enumeration: %d (%s)\n",
582 err
, snd_strerror(err
));
587 HRESULT WINAPI
AUDDRV_GetEndpointIDs(EDataFlow flow
, WCHAR
***ids
, GUID
**guids
,
588 UINT
*num
, UINT
*def_index
)
592 TRACE("%d %p %p %p %p\n", flow
, ids
, guids
, num
, def_index
);
597 hr
= alsa_enum_devices(flow
, ids
, guids
, num
);
600 for(i
= 0; i
< *num
; ++i
)
601 HeapFree(GetProcessHeap(), 0, (*ids
)[i
]);
602 HeapFree(GetProcessHeap(), 0, *ids
);
603 HeapFree(GetProcessHeap(), 0, *guids
);
604 return E_OUTOFMEMORY
;
607 TRACE("Enumerated %u devices\n", *num
);
610 HeapFree(GetProcessHeap(), 0, *ids
);
612 HeapFree(GetProcessHeap(), 0, *guids
);
621 /* Using the pulse PCM device from alsa-plugins 1.0.24 triggers a bug
622 * which causes audio to cease playing after a few minutes of playback.
623 * Setting handle_underrun=1 on pulse-backed ALSA devices seems to work
624 * around this issue. */
625 static snd_config_t
*make_handle_underrun_config(const char *name
)
627 snd_config_t
*lconf
, *dev_node
, *hu_node
, *type_node
;
628 char dev_node_name
[64];
629 const char *type_str
;
634 if((err
= snd_config_copy(&lconf
, snd_config
)) < 0){
635 WARN("snd_config_copy failed: %d (%s)\n", err
, snd_strerror(err
));
639 sprintf(dev_node_name
, "pcm.%s", name
);
640 err
= snd_config_search(lconf
, dev_node_name
, &dev_node
);
642 snd_config_delete(lconf
);
646 snd_config_delete(lconf
);
647 WARN("snd_config_search failed: %d (%s)\n", err
, snd_strerror(err
));
651 /* ALSA is extremely fragile. If it runs into a config setting it doesn't
652 * recognize, it tends to fail or assert. So we only want to inject
653 * handle_underrun=1 on devices that we know will recognize it. */
654 err
= snd_config_search(dev_node
, "type", &type_node
);
656 snd_config_delete(lconf
);
660 snd_config_delete(lconf
);
661 WARN("snd_config_search failed: %d (%s)\n", err
, snd_strerror(err
));
665 if((err
= snd_config_get_string(type_node
, &type_str
)) < 0){
666 snd_config_delete(lconf
);
670 if(strcmp(type_str
, "pulse") != 0){
671 snd_config_delete(lconf
);
675 err
= snd_config_search(dev_node
, "handle_underrun", &hu_node
);
677 /* user already has an explicit handle_underrun setting, so don't
678 * use a local config */
679 snd_config_delete(lconf
);
683 snd_config_delete(lconf
);
684 WARN("snd_config_search failed: %d (%s)\n", err
, snd_strerror(err
));
688 if((err
= snd_config_imake_integer(&hu_node
, "handle_underrun", 1)) < 0){
689 snd_config_delete(lconf
);
690 WARN("snd_config_imake_integer failed: %d (%s)\n", err
,
695 if((err
= snd_config_add(dev_node
, hu_node
)) < 0){
696 snd_config_delete(lconf
);
697 WARN("snd_config_add failed: %d (%s)\n", err
, snd_strerror(err
));
704 static BOOL
get_alsa_name_by_guid(GUID
*guid
, char *name
, DWORD name_size
, EDataFlow
*flow
)
711 if(RegOpenKeyExW(HKEY_CURRENT_USER
, drv_key_devicesW
, 0, KEY_READ
, &devices_key
) != ERROR_SUCCESS
){
712 ERR("No devices found in registry?\n");
721 key_name_size
= sizeof(key_name
)/sizeof(WCHAR
);
722 if(RegEnumKeyExW(devices_key
, i
, key_name
, &key_name_size
, NULL
,
723 NULL
, NULL
, NULL
) != ERROR_SUCCESS
)
726 if(RegOpenKeyExW(devices_key
, key_name
, 0, KEY_READ
, &key
) != ERROR_SUCCESS
){
727 WARN("Couldn't open key: %s\n", wine_dbgstr_w(key_name
));
731 size
= sizeof(reg_guid
);
732 if(RegQueryValueExW(key
, guidW
, 0, &type
,
733 (BYTE
*)®_guid
, &size
) == ERROR_SUCCESS
){
734 if(IsEqualGUID(®_guid
, guid
)){
736 RegCloseKey(devices_key
);
738 TRACE("Found matching device key: %s\n", wine_dbgstr_w(key_name
));
740 if(key_name
[0] == '0')
742 else if(key_name
[0] == '1')
745 ERR("Unknown device type: %c\n", key_name
[0]);
749 WideCharToMultiByte(CP_UNIXCP
, 0, key_name
+ 2, -1, name
, name_size
, NULL
, NULL
);
760 RegCloseKey(devices_key
);
762 WARN("No matching device in registry for GUID %s\n", debugstr_guid(guid
));
767 HRESULT WINAPI
AUDDRV_GetAudioEndpoint(GUID
*guid
, IMMDevice
*dev
, IAudioClient
**out
)
771 snd_pcm_stream_t stream
;
773 static BOOL handle_underrun
= TRUE
;
778 TRACE("%s %p %p\n", debugstr_guid(guid
), dev
, out
);
780 if(!get_alsa_name_by_guid(guid
, alsa_name
, sizeof(alsa_name
), &dataflow
))
781 return AUDCLNT_E_DEVICE_INVALIDATED
;
783 This
= HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY
, sizeof(ACImpl
));
785 return E_OUTOFMEMORY
;
787 This
->IAudioClient_iface
.lpVtbl
= &AudioClient_Vtbl
;
788 This
->IAudioRenderClient_iface
.lpVtbl
= &AudioRenderClient_Vtbl
;
789 This
->IAudioCaptureClient_iface
.lpVtbl
= &AudioCaptureClient_Vtbl
;
790 This
->IAudioClock_iface
.lpVtbl
= &AudioClock_Vtbl
;
791 This
->IAudioClock2_iface
.lpVtbl
= &AudioClock2_Vtbl
;
792 This
->IAudioStreamVolume_iface
.lpVtbl
= &AudioStreamVolume_Vtbl
;
794 if(dataflow
== eRender
)
795 stream
= SND_PCM_STREAM_PLAYBACK
;
796 else if(dataflow
== eCapture
)
797 stream
= SND_PCM_STREAM_CAPTURE
;
799 HeapFree(GetProcessHeap(), 0, This
);
803 hr
= CoCreateFreeThreadedMarshaler((IUnknown
*)&This
->IAudioClient_iface
,
804 (IUnknown
**)&This
->pUnkFTMarshal
);
806 HeapFree(GetProcessHeap(), 0, This
);
810 This
->dataflow
= dataflow
;
811 if(handle_underrun
&& ((lconf
= make_handle_underrun_config(alsa_name
)))){
812 err
= snd_pcm_open_lconf(&This
->pcm_handle
, alsa_name
, stream
, SND_PCM_NONBLOCK
, lconf
);
813 TRACE("Opening PCM device \"%s\" with handle_underrun: %d\n", alsa_name
, err
);
814 snd_config_delete(lconf
);
815 /* Pulse <= 2010 returns EINVAL, it does not know handle_underrun. */
817 ERR_(winediag
)("PulseAudio \"%s\" %d without handle_underrun. Audio may hang."
818 " Please upgrade to alsa_plugins >= 1.0.24\n", alsa_name
, err
);
819 handle_underrun
= FALSE
;
824 err
= snd_pcm_open(&This
->pcm_handle
, alsa_name
, stream
, SND_PCM_NONBLOCK
);
827 HeapFree(GetProcessHeap(), 0, This
);
828 WARN("Unable to open PCM \"%s\": %d (%s)\n", alsa_name
, err
, snd_strerror(err
));
831 return AUDCLNT_E_DEVICE_IN_USE
;
833 return AUDCLNT_E_ENDPOINT_CREATE_FAILED
;
837 This
->hw_params
= HeapAlloc(GetProcessHeap(), 0,
838 snd_pcm_hw_params_sizeof());
839 if(!This
->hw_params
){
840 snd_pcm_close(This
->pcm_handle
);
841 HeapFree(GetProcessHeap(), 0, This
);
842 return E_OUTOFMEMORY
;
845 InitializeCriticalSection(&This
->lock
);
846 This
->lock
.DebugInfo
->Spare
[0] = (DWORD_PTR
)(__FILE__
": ACImpl.lock");
849 IMMDevice_AddRef(This
->parent
);
851 *out
= &This
->IAudioClient_iface
;
852 IAudioClient_AddRef(&This
->IAudioClient_iface
);
857 static HRESULT WINAPI
AudioClient_QueryInterface(IAudioClient
*iface
,
858 REFIID riid
, void **ppv
)
860 ACImpl
*This
= impl_from_IAudioClient(iface
);
861 TRACE("(%p)->(%s, %p)\n", iface
, debugstr_guid(riid
), ppv
);
866 if(IsEqualIID(riid
, &IID_IUnknown
) || IsEqualIID(riid
, &IID_IAudioClient
))
868 else if(IsEqualIID(riid
, &IID_IMarshal
))
869 return IUnknown_QueryInterface(This
->pUnkFTMarshal
, riid
, ppv
);
872 IUnknown_AddRef((IUnknown
*)*ppv
);
875 WARN("Unknown interface %s\n", debugstr_guid(riid
));
876 return E_NOINTERFACE
;
879 static ULONG WINAPI
AudioClient_AddRef(IAudioClient
*iface
)
881 ACImpl
*This
= impl_from_IAudioClient(iface
);
883 ref
= InterlockedIncrement(&This
->ref
);
884 TRACE("(%p) Refcount now %u\n", This
, ref
);
888 static ULONG WINAPI
AudioClient_Release(IAudioClient
*iface
)
890 ACImpl
*This
= impl_from_IAudioClient(iface
);
892 ref
= InterlockedDecrement(&This
->ref
);
893 TRACE("(%p) Refcount now %u\n", This
, ref
);
895 IAudioClient_Stop(iface
);
896 IMMDevice_Release(This
->parent
);
897 IUnknown_Release(This
->pUnkFTMarshal
);
898 This
->lock
.DebugInfo
->Spare
[0] = 0;
899 DeleteCriticalSection(&This
->lock
);
900 snd_pcm_drop(This
->pcm_handle
);
901 snd_pcm_close(This
->pcm_handle
);
903 EnterCriticalSection(&g_sessions_lock
);
904 list_remove(&This
->entry
);
905 LeaveCriticalSection(&g_sessions_lock
);
907 HeapFree(GetProcessHeap(), 0, This
->vols
);
908 HeapFree(GetProcessHeap(), 0, This
->local_buffer
);
909 HeapFree(GetProcessHeap(), 0, This
->remapping_buf
);
910 HeapFree(GetProcessHeap(), 0, This
->tmp_buffer
);
911 HeapFree(GetProcessHeap(), 0, This
->hw_params
);
912 CoTaskMemFree(This
->fmt
);
913 HeapFree(GetProcessHeap(), 0, This
);
918 static void dump_fmt(const WAVEFORMATEX
*fmt
)
920 TRACE("wFormatTag: 0x%x (", fmt
->wFormatTag
);
921 switch(fmt
->wFormatTag
){
922 case WAVE_FORMAT_PCM
:
923 TRACE("WAVE_FORMAT_PCM");
925 case WAVE_FORMAT_IEEE_FLOAT
:
926 TRACE("WAVE_FORMAT_IEEE_FLOAT");
928 case WAVE_FORMAT_EXTENSIBLE
:
929 TRACE("WAVE_FORMAT_EXTENSIBLE");
937 TRACE("nChannels: %u\n", fmt
->nChannels
);
938 TRACE("nSamplesPerSec: %u\n", fmt
->nSamplesPerSec
);
939 TRACE("nAvgBytesPerSec: %u\n", fmt
->nAvgBytesPerSec
);
940 TRACE("nBlockAlign: %u\n", fmt
->nBlockAlign
);
941 TRACE("wBitsPerSample: %u\n", fmt
->wBitsPerSample
);
942 TRACE("cbSize: %u\n", fmt
->cbSize
);
944 if(fmt
->wFormatTag
== WAVE_FORMAT_EXTENSIBLE
){
945 WAVEFORMATEXTENSIBLE
*fmtex
= (void*)fmt
;
946 TRACE("dwChannelMask: %08x\n", fmtex
->dwChannelMask
);
947 TRACE("Samples: %04x\n", fmtex
->Samples
.wReserved
);
948 TRACE("SubFormat: %s\n", wine_dbgstr_guid(&fmtex
->SubFormat
));
952 static WAVEFORMATEX
*clone_format(const WAVEFORMATEX
*fmt
)
957 if(fmt
->wFormatTag
== WAVE_FORMAT_EXTENSIBLE
)
958 size
= sizeof(WAVEFORMATEXTENSIBLE
);
960 size
= sizeof(WAVEFORMATEX
);
962 ret
= CoTaskMemAlloc(size
);
966 memcpy(ret
, fmt
, size
);
968 ret
->cbSize
= size
- sizeof(WAVEFORMATEX
);
973 static snd_pcm_format_t
alsa_format(const WAVEFORMATEX
*fmt
)
975 snd_pcm_format_t format
= SND_PCM_FORMAT_UNKNOWN
;
976 const WAVEFORMATEXTENSIBLE
*fmtex
= (const WAVEFORMATEXTENSIBLE
*)fmt
;
978 if(fmt
->wFormatTag
== WAVE_FORMAT_PCM
||
979 (fmt
->wFormatTag
== WAVE_FORMAT_EXTENSIBLE
&&
980 IsEqualGUID(&fmtex
->SubFormat
, &KSDATAFORMAT_SUBTYPE_PCM
))){
981 if(fmt
->wBitsPerSample
== 8)
982 format
= SND_PCM_FORMAT_U8
;
983 else if(fmt
->wBitsPerSample
== 16)
984 format
= SND_PCM_FORMAT_S16_LE
;
985 else if(fmt
->wBitsPerSample
== 24)
986 format
= SND_PCM_FORMAT_S24_3LE
;
987 else if(fmt
->wBitsPerSample
== 32)
988 format
= SND_PCM_FORMAT_S32_LE
;
990 WARN("Unsupported bit depth: %u\n", fmt
->wBitsPerSample
);
991 if(fmt
->wFormatTag
== WAVE_FORMAT_EXTENSIBLE
&&
992 fmt
->wBitsPerSample
!= fmtex
->Samples
.wValidBitsPerSample
){
993 if(fmtex
->Samples
.wValidBitsPerSample
== 20 && fmt
->wBitsPerSample
== 24)
994 format
= SND_PCM_FORMAT_S20_3LE
;
996 WARN("Unsupported ValidBits: %u\n", fmtex
->Samples
.wValidBitsPerSample
);
998 }else if(fmt
->wFormatTag
== WAVE_FORMAT_IEEE_FLOAT
||
999 (fmt
->wFormatTag
== WAVE_FORMAT_EXTENSIBLE
&&
1000 IsEqualGUID(&fmtex
->SubFormat
, &KSDATAFORMAT_SUBTYPE_IEEE_FLOAT
))){
1001 if(fmt
->wBitsPerSample
== 32)
1002 format
= SND_PCM_FORMAT_FLOAT_LE
;
1003 else if(fmt
->wBitsPerSample
== 64)
1004 format
= SND_PCM_FORMAT_FLOAT64_LE
;
1006 WARN("Unsupported float size: %u\n", fmt
->wBitsPerSample
);
1008 WARN("Unknown wave format: %04x\n", fmt
->wFormatTag
);
1012 static void session_init_vols(AudioSession
*session
, UINT channels
)
1014 if(session
->channel_count
< channels
){
1017 if(session
->channel_vols
)
1018 session
->channel_vols
= HeapReAlloc(GetProcessHeap(), 0,
1019 session
->channel_vols
, sizeof(float) * channels
);
1021 session
->channel_vols
= HeapAlloc(GetProcessHeap(), 0,
1022 sizeof(float) * channels
);
1023 if(!session
->channel_vols
)
1026 for(i
= session
->channel_count
; i
< channels
; ++i
)
1027 session
->channel_vols
[i
] = 1.f
;
1029 session
->channel_count
= channels
;
1033 static AudioSession
*create_session(const GUID
*guid
, IMMDevice
*device
,
1038 ret
= HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY
, sizeof(AudioSession
));
1042 memcpy(&ret
->guid
, guid
, sizeof(GUID
));
1044 ret
->device
= device
;
1046 list_init(&ret
->clients
);
1048 list_add_head(&g_sessions
, &ret
->entry
);
1050 InitializeCriticalSection(&ret
->lock
);
1051 ret
->lock
.DebugInfo
->Spare
[0] = (DWORD_PTR
)(__FILE__
": AudioSession.lock");
1053 session_init_vols(ret
, num_channels
);
1055 ret
->master_vol
= 1.f
;
1060 /* if channels == 0, then this will return or create a session with
1061 * matching dataflow and GUID. otherwise, channels must also match */
1062 static HRESULT
get_audio_session(const GUID
*sessionguid
,
1063 IMMDevice
*device
, UINT channels
, AudioSession
**out
)
1065 AudioSession
*session
;
1067 if(!sessionguid
|| IsEqualGUID(sessionguid
, &GUID_NULL
)){
1068 *out
= create_session(&GUID_NULL
, device
, channels
);
1070 return E_OUTOFMEMORY
;
1076 LIST_FOR_EACH_ENTRY(session
, &g_sessions
, AudioSession
, entry
){
1077 if(session
->device
== device
&&
1078 IsEqualGUID(sessionguid
, &session
->guid
)){
1079 session_init_vols(session
, channels
);
1086 *out
= create_session(sessionguid
, device
, channels
);
1088 return E_OUTOFMEMORY
;
1094 static int alsa_channel_index(DWORD flag
)
1097 case SPEAKER_FRONT_LEFT
:
1099 case SPEAKER_FRONT_RIGHT
:
1101 case SPEAKER_BACK_LEFT
:
1103 case SPEAKER_BACK_RIGHT
:
1105 case SPEAKER_FRONT_CENTER
:
1107 case SPEAKER_LOW_FREQUENCY
:
1109 case SPEAKER_SIDE_LEFT
:
1111 case SPEAKER_SIDE_RIGHT
:
1117 static BOOL
need_remapping(ACImpl
*This
, const WAVEFORMATEX
*fmt
)
1120 for(i
= 0; i
< fmt
->nChannels
; ++i
){
1121 if(This
->alsa_channel_map
[i
] != i
)
1127 static DWORD
get_channel_mask(unsigned int channels
)
1133 return KSAUDIO_SPEAKER_MONO
;
1135 return KSAUDIO_SPEAKER_STEREO
;
1137 return KSAUDIO_SPEAKER_STEREO
| SPEAKER_LOW_FREQUENCY
;
1139 return KSAUDIO_SPEAKER_QUAD
; /* not _SURROUND */
1141 return KSAUDIO_SPEAKER_QUAD
| SPEAKER_LOW_FREQUENCY
;
1143 return KSAUDIO_SPEAKER_5POINT1
; /* not 5POINT1_SURROUND */
1145 return KSAUDIO_SPEAKER_5POINT1
| SPEAKER_BACK_CENTER
;
1147 return KSAUDIO_SPEAKER_7POINT1_SURROUND
; /* Vista deprecates 7POINT1 */
1149 FIXME("Unknown speaker configuration: %u\n", channels
);
1153 static HRESULT
map_channels(ACImpl
*This
, const WAVEFORMATEX
*fmt
)
1155 if(fmt
->wFormatTag
== WAVE_FORMAT_EXTENSIBLE
|| fmt
->nChannels
> 2){
1156 WAVEFORMATEXTENSIBLE
*fmtex
= (void*)fmt
;
1157 DWORD mask
, flag
= SPEAKER_FRONT_LEFT
;
1160 if(fmt
->wFormatTag
== WAVE_FORMAT_EXTENSIBLE
&&
1161 fmtex
->dwChannelMask
!= 0)
1162 mask
= fmtex
->dwChannelMask
;
1164 mask
= get_channel_mask(fmt
->nChannels
);
1166 This
->alsa_channels
= 0;
1168 while(i
< fmt
->nChannels
&& !(flag
& SPEAKER_RESERVED
)){
1170 This
->alsa_channel_map
[i
] = alsa_channel_index(flag
);
1171 TRACE("Mapping mmdevapi channel %u (0x%x) to ALSA channel %d\n",
1172 i
, flag
, This
->alsa_channel_map
[i
]);
1173 if(This
->alsa_channel_map
[i
] >= This
->alsa_channels
)
1174 This
->alsa_channels
= This
->alsa_channel_map
[i
] + 1;
1180 while(i
< fmt
->nChannels
){
1181 This
->alsa_channel_map
[i
] = This
->alsa_channels
;
1182 TRACE("Mapping mmdevapi channel %u to ALSA channel %d\n",
1183 i
, This
->alsa_channel_map
[i
]);
1184 ++This
->alsa_channels
;
1188 for(i
= 0; i
< fmt
->nChannels
; ++i
){
1189 if(This
->alsa_channel_map
[i
] == -1){
1190 This
->alsa_channel_map
[i
] = This
->alsa_channels
;
1191 ++This
->alsa_channels
;
1192 TRACE("Remapping mmdevapi channel %u to ALSA channel %d\n",
1193 i
, This
->alsa_channel_map
[i
]);
1197 This
->need_remapping
= need_remapping(This
, fmt
);
1199 TRACE("need_remapping: %u, alsa_channels: %d\n", This
->need_remapping
, This
->alsa_channels
);
1201 This
->need_remapping
= FALSE
;
1202 This
->alsa_channels
= fmt
->nChannels
;
1203 TRACE("need_remapping: %u, alsa_channels: %d\n", This
->need_remapping
, This
->alsa_channels
);
1209 static HRESULT WINAPI
AudioClient_Initialize(IAudioClient
*iface
,
1210 AUDCLNT_SHAREMODE mode
, DWORD flags
, REFERENCE_TIME duration
,
1211 REFERENCE_TIME period
, const WAVEFORMATEX
*fmt
,
1212 const GUID
*sessionguid
)
1214 ACImpl
*This
= impl_from_IAudioClient(iface
);
1215 snd_pcm_sw_params_t
*sw_params
= NULL
;
1216 snd_pcm_format_t format
;
1217 unsigned int rate
, alsa_period_us
;
1221 TRACE("(%p)->(%x, %x, %s, %s, %p, %s)\n", This
, mode
, flags
,
1222 wine_dbgstr_longlong(duration
), wine_dbgstr_longlong(period
), fmt
, debugstr_guid(sessionguid
));
1227 if(mode
!= AUDCLNT_SHAREMODE_SHARED
&& mode
!= AUDCLNT_SHAREMODE_EXCLUSIVE
)
1228 return AUDCLNT_E_NOT_INITIALIZED
;
1230 if(flags
& ~(AUDCLNT_STREAMFLAGS_CROSSPROCESS
|
1231 AUDCLNT_STREAMFLAGS_LOOPBACK
|
1232 AUDCLNT_STREAMFLAGS_EVENTCALLBACK
|
1233 AUDCLNT_STREAMFLAGS_NOPERSIST
|
1234 AUDCLNT_STREAMFLAGS_RATEADJUST
|
1235 AUDCLNT_SESSIONFLAGS_EXPIREWHENUNOWNED
|
1236 AUDCLNT_SESSIONFLAGS_DISPLAY_HIDE
|
1237 AUDCLNT_SESSIONFLAGS_DISPLAY_HIDEWHENEXPIRED
)){
1238 TRACE("Unknown flags: %08x\n", flags
);
1239 return E_INVALIDARG
;
1242 if(mode
== AUDCLNT_SHAREMODE_SHARED
){
1243 period
= DefaultPeriod
;
1244 if( duration
< 3 * period
)
1245 duration
= 3 * period
;
1247 if(fmt
->wFormatTag
== WAVE_FORMAT_EXTENSIBLE
){
1248 if(((WAVEFORMATEXTENSIBLE
*)fmt
)->dwChannelMask
== 0 ||
1249 ((WAVEFORMATEXTENSIBLE
*)fmt
)->dwChannelMask
& SPEAKER_RESERVED
)
1250 return AUDCLNT_E_UNSUPPORTED_FORMAT
;
1254 period
= DefaultPeriod
; /* not minimum */
1255 if(period
< MinimumPeriod
|| period
> 5000000)
1256 return AUDCLNT_E_INVALID_DEVICE_PERIOD
;
1257 if(duration
> 20000000) /* the smaller the period, the lower this limit */
1258 return AUDCLNT_E_BUFFER_SIZE_ERROR
;
1259 if(flags
& AUDCLNT_STREAMFLAGS_EVENTCALLBACK
){
1260 if(duration
!= period
)
1261 return AUDCLNT_E_BUFDURATION_PERIOD_NOT_EQUAL
;
1262 FIXME("EXCLUSIVE mode with EVENTCALLBACK\n");
1263 return AUDCLNT_E_DEVICE_IN_USE
;
1265 if( duration
< 8 * period
)
1266 duration
= 8 * period
; /* may grow above 2s */
1270 EnterCriticalSection(&This
->lock
);
1273 LeaveCriticalSection(&This
->lock
);
1274 return AUDCLNT_E_ALREADY_INITIALIZED
;
1279 if(FAILED(map_channels(This
, fmt
))){
1280 WARN("map_channels failed\n");
1281 hr
= AUDCLNT_E_ENDPOINT_CREATE_FAILED
;
1285 if((err
= snd_pcm_hw_params_any(This
->pcm_handle
, This
->hw_params
)) < 0){
1286 WARN("Unable to get hw_params: %d (%s)\n", err
, snd_strerror(err
));
1287 hr
= AUDCLNT_E_ENDPOINT_CREATE_FAILED
;
1291 if((err
= snd_pcm_hw_params_set_access(This
->pcm_handle
, This
->hw_params
,
1292 SND_PCM_ACCESS_RW_INTERLEAVED
)) < 0){
1293 WARN("Unable to set access: %d (%s)\n", err
, snd_strerror(err
));
1294 hr
= AUDCLNT_E_ENDPOINT_CREATE_FAILED
;
1298 format
= alsa_format(fmt
);
1299 if (format
== SND_PCM_FORMAT_UNKNOWN
){
1300 hr
= AUDCLNT_E_UNSUPPORTED_FORMAT
;
1304 if((err
= snd_pcm_hw_params_set_format(This
->pcm_handle
, This
->hw_params
,
1306 WARN("Unable to set ALSA format to %u: %d (%s)\n", format
, err
,
1308 hr
= AUDCLNT_E_UNSUPPORTED_FORMAT
;
1312 This
->alsa_format
= format
;
1314 rate
= fmt
->nSamplesPerSec
;
1315 if((err
= snd_pcm_hw_params_set_rate_near(This
->pcm_handle
, This
->hw_params
,
1317 WARN("Unable to set rate to %u: %d (%s)\n", rate
, err
,
1319 hr
= AUDCLNT_E_UNSUPPORTED_FORMAT
;
1323 if((err
= snd_pcm_hw_params_set_channels(This
->pcm_handle
, This
->hw_params
,
1324 This
->alsa_channels
)) < 0){
1325 WARN("Unable to set channels to %u: %d (%s)\n", fmt
->nChannels
, err
,
1327 hr
= AUDCLNT_E_UNSUPPORTED_FORMAT
;
1331 This
->mmdev_period_rt
= period
;
1332 alsa_period_us
= This
->mmdev_period_rt
/ 10;
1333 if((err
= snd_pcm_hw_params_set_period_time_near(This
->pcm_handle
,
1334 This
->hw_params
, &alsa_period_us
, NULL
)) < 0)
1335 WARN("Unable to set period time near %u: %d (%s)\n", alsa_period_us
,
1336 err
, snd_strerror(err
));
1337 /* ALSA updates the output variable alsa_period_us */
1339 This
->mmdev_period_frames
= MulDiv(fmt
->nSamplesPerSec
,
1340 This
->mmdev_period_rt
, 10000000);
1342 /* Buffer 4 ALSA periods if large enough, else 4 mmdevapi periods */
1343 This
->alsa_bufsize_frames
= This
->mmdev_period_frames
* 4;
1344 if(err
< 0 || alsa_period_us
< period
/ 10)
1345 err
= snd_pcm_hw_params_set_buffer_size_near(This
->pcm_handle
,
1346 This
->hw_params
, &This
->alsa_bufsize_frames
);
1348 unsigned int periods
= 4;
1349 err
= snd_pcm_hw_params_set_periods_near(This
->pcm_handle
, This
->hw_params
, &periods
, NULL
);
1352 WARN("Unable to set buffer size: %d (%s)\n", err
, snd_strerror(err
));
1354 if((err
= snd_pcm_hw_params(This
->pcm_handle
, This
->hw_params
)) < 0){
1355 WARN("Unable to set hw params: %d (%s)\n", err
, snd_strerror(err
));
1356 hr
= AUDCLNT_E_ENDPOINT_CREATE_FAILED
;
1360 if((err
= snd_pcm_hw_params_get_period_size(This
->hw_params
,
1361 &This
->alsa_period_frames
, NULL
)) < 0){
1362 WARN("Unable to get period size: %d (%s)\n", err
, snd_strerror(err
));
1363 hr
= AUDCLNT_E_ENDPOINT_CREATE_FAILED
;
1367 if((err
= snd_pcm_hw_params_get_buffer_size(This
->hw_params
,
1368 &This
->alsa_bufsize_frames
)) < 0){
1369 WARN("Unable to get buffer size: %d (%s)\n", err
, snd_strerror(err
));
1370 hr
= AUDCLNT_E_ENDPOINT_CREATE_FAILED
;
1374 sw_params
= HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY
, snd_pcm_sw_params_sizeof());
1380 if((err
= snd_pcm_sw_params_current(This
->pcm_handle
, sw_params
)) < 0){
1381 WARN("Unable to get sw_params: %d (%s)\n", err
, snd_strerror(err
));
1382 hr
= AUDCLNT_E_ENDPOINT_CREATE_FAILED
;
1386 if((err
= snd_pcm_sw_params_set_start_threshold(This
->pcm_handle
,
1387 sw_params
, 1)) < 0){
1388 WARN("Unable set start threshold to 0: %d (%s)\n", err
, snd_strerror(err
));
1389 hr
= AUDCLNT_E_ENDPOINT_CREATE_FAILED
;
1393 if((err
= snd_pcm_sw_params_set_stop_threshold(This
->pcm_handle
,
1394 sw_params
, This
->alsa_bufsize_frames
)) < 0){
1395 WARN("Unable set stop threshold to %lu: %d (%s)\n",
1396 This
->alsa_bufsize_frames
, err
, snd_strerror(err
));
1397 hr
= AUDCLNT_E_ENDPOINT_CREATE_FAILED
;
1401 if((err
= snd_pcm_sw_params(This
->pcm_handle
, sw_params
)) < 0){
1402 WARN("Unable to set sw params: %d (%s)\n", err
, snd_strerror(err
));
1403 hr
= AUDCLNT_E_ENDPOINT_CREATE_FAILED
;
1407 if((err
= snd_pcm_prepare(This
->pcm_handle
)) < 0){
1408 WARN("Unable to prepare device: %d (%s)\n", err
, snd_strerror(err
));
1409 hr
= AUDCLNT_E_ENDPOINT_CREATE_FAILED
;
1413 /* Bear in mind weird situations where
1414 * ALSA period (50ms) > mmdevapi buffer (3x10ms)
1415 * or surprising rounding as seen with 22050x8x1 with Pulse:
1416 * ALSA period 220 vs. 221 frames in mmdevapi and
1417 * buffer 883 vs. 2205 frames in mmdevapi! */
1418 This
->bufsize_frames
= MulDiv(duration
, fmt
->nSamplesPerSec
, 10000000);
1419 if(mode
== AUDCLNT_SHAREMODE_EXCLUSIVE
)
1420 This
->bufsize_frames
-= This
->bufsize_frames
% This
->mmdev_period_frames
;
1421 This
->hidden_frames
= This
->alsa_period_frames
+ This
->mmdev_period_frames
+
1422 MulDiv(fmt
->nSamplesPerSec
, EXTRA_SAFE_RT
, 10000000);
1424 /* Check if the ALSA buffer is so small that it will run out before
1425 * the next MMDevAPI period tick occurs. Allow a little wiggle room
1426 * with 120% of the period time. */
1427 if(This
->alsa_bufsize_frames
< 1.2 * This
->mmdev_period_frames
)
1428 FIXME("ALSA buffer time is too small. Expect underruns. (%lu < %u * 1.2)\n",
1429 This
->alsa_bufsize_frames
, This
->mmdev_period_frames
);
1431 This
->local_buffer
= HeapAlloc(GetProcessHeap(), 0,
1432 This
->bufsize_frames
* fmt
->nBlockAlign
);
1433 if(!This
->local_buffer
){
1437 if (fmt
->wBitsPerSample
== 8)
1438 memset(This
->local_buffer
, 128, This
->bufsize_frames
* fmt
->nBlockAlign
);
1440 memset(This
->local_buffer
, 0, This
->bufsize_frames
* fmt
->nBlockAlign
);
1442 This
->fmt
= clone_format(fmt
);
1448 This
->vols
= HeapAlloc(GetProcessHeap(), 0, fmt
->nChannels
* sizeof(float));
1454 for(i
= 0; i
< fmt
->nChannels
; ++i
)
1455 This
->vols
[i
] = 1.f
;
1458 This
->flags
= flags
;
1460 EnterCriticalSection(&g_sessions_lock
);
1462 hr
= get_audio_session(sessionguid
, This
->parent
, fmt
->nChannels
,
1465 LeaveCriticalSection(&g_sessions_lock
);
1469 list_add_tail(&This
->session
->clients
, &This
->entry
);
1471 LeaveCriticalSection(&g_sessions_lock
);
1473 This
->initted
= TRUE
;
1475 TRACE("ALSA period: %lu frames\n", This
->alsa_period_frames
);
1476 TRACE("ALSA buffer: %lu frames\n", This
->alsa_bufsize_frames
);
1477 TRACE("MMDevice period: %u frames\n", This
->mmdev_period_frames
);
1478 TRACE("MMDevice buffer: %u frames\n", This
->bufsize_frames
);
1481 HeapFree(GetProcessHeap(), 0, sw_params
);
1483 HeapFree(GetProcessHeap(), 0, This
->local_buffer
);
1484 This
->local_buffer
= NULL
;
1485 CoTaskMemFree(This
->fmt
);
1487 HeapFree(GetProcessHeap(), 0, This
->vols
);
1491 LeaveCriticalSection(&This
->lock
);
1496 static HRESULT WINAPI
AudioClient_GetBufferSize(IAudioClient
*iface
,
1499 ACImpl
*This
= impl_from_IAudioClient(iface
);
1501 TRACE("(%p)->(%p)\n", This
, out
);
1506 EnterCriticalSection(&This
->lock
);
1509 LeaveCriticalSection(&This
->lock
);
1510 return AUDCLNT_E_NOT_INITIALIZED
;
1513 *out
= This
->bufsize_frames
;
1515 LeaveCriticalSection(&This
->lock
);
1520 static HRESULT WINAPI
AudioClient_GetStreamLatency(IAudioClient
*iface
,
1521 REFERENCE_TIME
*latency
)
1523 ACImpl
*This
= impl_from_IAudioClient(iface
);
1525 TRACE("(%p)->(%p)\n", This
, latency
);
1530 EnterCriticalSection(&This
->lock
);
1533 LeaveCriticalSection(&This
->lock
);
1534 return AUDCLNT_E_NOT_INITIALIZED
;
1537 /* Hide some frames in the ALSA buffer. Allows us to return GetCurrentPadding=0
1538 * yet have enough data left to play (as if it were in native's mixer). Add:
1539 * + mmdevapi_period such that at the end of it, ALSA still has data;
1540 * + EXTRA_SAFE (~4ms) to allow for late callback invocation / fluctuation;
1541 * + alsa_period such that ALSA always has at least one period to play. */
1542 if(This
->dataflow
== eRender
)
1543 *latency
= MulDiv(This
->hidden_frames
, 10000000, This
->fmt
->nSamplesPerSec
);
1545 *latency
= MulDiv(This
->alsa_period_frames
, 10000000, This
->fmt
->nSamplesPerSec
)
1546 + This
->mmdev_period_rt
;
1548 LeaveCriticalSection(&This
->lock
);
1553 static HRESULT WINAPI
AudioClient_GetCurrentPadding(IAudioClient
*iface
,
1556 ACImpl
*This
= impl_from_IAudioClient(iface
);
1558 TRACE("(%p)->(%p)\n", This
, out
);
1563 EnterCriticalSection(&This
->lock
);
1566 LeaveCriticalSection(&This
->lock
);
1567 return AUDCLNT_E_NOT_INITIALIZED
;
1570 /* padding is solely updated at callback time in shared mode */
1571 *out
= This
->held_frames
;
1573 LeaveCriticalSection(&This
->lock
);
1575 TRACE("pad: %u\n", *out
);
1580 static HRESULT WINAPI
AudioClient_IsFormatSupported(IAudioClient
*iface
,
1581 AUDCLNT_SHAREMODE mode
, const WAVEFORMATEX
*fmt
,
1584 ACImpl
*This
= impl_from_IAudioClient(iface
);
1585 snd_pcm_format_mask_t
*formats
= NULL
;
1586 snd_pcm_format_t format
;
1588 WAVEFORMATEX
*closest
= NULL
;
1589 unsigned int max
= 0, min
= 0;
1592 TRACE("(%p)->(%x, %p, %p)\n", This
, mode
, fmt
, out
);
1594 if(!fmt
|| (mode
== AUDCLNT_SHAREMODE_SHARED
&& !out
))
1597 if(mode
!= AUDCLNT_SHAREMODE_SHARED
&& mode
!= AUDCLNT_SHAREMODE_EXCLUSIVE
)
1598 return E_INVALIDARG
;
1600 if(fmt
->wFormatTag
== WAVE_FORMAT_EXTENSIBLE
&&
1601 fmt
->cbSize
< sizeof(WAVEFORMATEXTENSIBLE
) - sizeof(WAVEFORMATEX
))
1602 return E_INVALIDARG
;
1608 if(mode
!= AUDCLNT_SHAREMODE_SHARED
)
1612 if(fmt
->wFormatTag
== WAVE_FORMAT_EXTENSIBLE
&&
1613 (fmt
->nAvgBytesPerSec
== 0 ||
1614 fmt
->nBlockAlign
== 0 ||
1615 ((WAVEFORMATEXTENSIBLE
*)fmt
)->Samples
.wValidBitsPerSample
> fmt
->wBitsPerSample
))
1616 return E_INVALIDARG
;
1618 if(fmt
->nChannels
== 0)
1619 return AUDCLNT_E_UNSUPPORTED_FORMAT
;
1621 EnterCriticalSection(&This
->lock
);
1623 if((err
= snd_pcm_hw_params_any(This
->pcm_handle
, This
->hw_params
)) < 0){
1624 hr
= AUDCLNT_E_DEVICE_INVALIDATED
;
1628 formats
= HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY
,
1629 snd_pcm_format_mask_sizeof());
1635 snd_pcm_hw_params_get_format_mask(This
->hw_params
, formats
);
1636 format
= alsa_format(fmt
);
1637 if (format
== SND_PCM_FORMAT_UNKNOWN
||
1638 !snd_pcm_format_mask_test(formats
, format
)){
1639 hr
= AUDCLNT_E_UNSUPPORTED_FORMAT
;
1643 closest
= clone_format(fmt
);
1649 if((err
= snd_pcm_hw_params_get_rate_min(This
->hw_params
, &min
, NULL
)) < 0){
1650 hr
= AUDCLNT_E_DEVICE_INVALIDATED
;
1651 WARN("Unable to get min rate: %d (%s)\n", err
, snd_strerror(err
));
1655 if((err
= snd_pcm_hw_params_get_rate_max(This
->hw_params
, &max
, NULL
)) < 0){
1656 hr
= AUDCLNT_E_DEVICE_INVALIDATED
;
1657 WARN("Unable to get max rate: %d (%s)\n", err
, snd_strerror(err
));
1661 if(fmt
->nSamplesPerSec
< min
|| fmt
->nSamplesPerSec
> max
){
1662 hr
= AUDCLNT_E_UNSUPPORTED_FORMAT
;
1666 if((err
= snd_pcm_hw_params_get_channels_min(This
->hw_params
, &min
)) < 0){
1667 hr
= AUDCLNT_E_DEVICE_INVALIDATED
;
1668 WARN("Unable to get min channels: %d (%s)\n", err
, snd_strerror(err
));
1672 if((err
= snd_pcm_hw_params_get_channels_max(This
->hw_params
, &max
)) < 0){
1673 hr
= AUDCLNT_E_DEVICE_INVALIDATED
;
1674 WARN("Unable to get max channels: %d (%s)\n", err
, snd_strerror(err
));
1677 if(fmt
->nChannels
> max
){
1679 closest
->nChannels
= max
;
1680 }else if(fmt
->nChannels
< min
){
1682 closest
->nChannels
= min
;
1685 if(FAILED(map_channels(This
, fmt
))){
1686 hr
= AUDCLNT_E_DEVICE_INVALIDATED
;
1687 WARN("map_channels failed\n");
1690 if(This
->alsa_channels
> max
){
1692 closest
->nChannels
= max
;
1695 if(closest
->wFormatTag
== WAVE_FORMAT_EXTENSIBLE
)
1696 ((WAVEFORMATEXTENSIBLE
*)closest
)->dwChannelMask
= get_channel_mask(closest
->nChannels
);
1698 if(fmt
->nBlockAlign
!= fmt
->nChannels
* fmt
->wBitsPerSample
/ 8 ||
1699 fmt
->nAvgBytesPerSec
!= fmt
->nBlockAlign
* fmt
->nSamplesPerSec
||
1700 (fmt
->wFormatTag
== WAVE_FORMAT_EXTENSIBLE
&&
1701 ((WAVEFORMATEXTENSIBLE
*)fmt
)->Samples
.wValidBitsPerSample
< fmt
->wBitsPerSample
))
1704 if(mode
== AUDCLNT_SHAREMODE_EXCLUSIVE
&&
1705 fmt
->wFormatTag
== WAVE_FORMAT_EXTENSIBLE
){
1706 if(((WAVEFORMATEXTENSIBLE
*)fmt
)->dwChannelMask
== 0 ||
1707 ((WAVEFORMATEXTENSIBLE
*)fmt
)->dwChannelMask
& SPEAKER_RESERVED
)
1712 LeaveCriticalSection(&This
->lock
);
1713 HeapFree(GetProcessHeap(), 0, formats
);
1715 if(hr
== S_FALSE
&& !out
)
1716 hr
= AUDCLNT_E_UNSUPPORTED_FORMAT
;
1718 if(hr
== S_FALSE
&& out
) {
1719 closest
->nBlockAlign
=
1720 closest
->nChannels
* closest
->wBitsPerSample
/ 8;
1721 closest
->nAvgBytesPerSec
=
1722 closest
->nBlockAlign
* closest
->nSamplesPerSec
;
1723 if(closest
->wFormatTag
== WAVE_FORMAT_EXTENSIBLE
)
1724 ((WAVEFORMATEXTENSIBLE
*)closest
)->Samples
.wValidBitsPerSample
= closest
->wBitsPerSample
;
1727 CoTaskMemFree(closest
);
1729 TRACE("returning: %08x\n", hr
);
1733 static HRESULT WINAPI
AudioClient_GetMixFormat(IAudioClient
*iface
,
1734 WAVEFORMATEX
**pwfx
)
1736 ACImpl
*This
= impl_from_IAudioClient(iface
);
1737 WAVEFORMATEXTENSIBLE
*fmt
;
1738 snd_pcm_format_mask_t
*formats
;
1739 unsigned int max_rate
, max_channels
;
1743 TRACE("(%p)->(%p)\n", This
, pwfx
);
1749 fmt
= CoTaskMemAlloc(sizeof(WAVEFORMATEXTENSIBLE
));
1751 return E_OUTOFMEMORY
;
1753 formats
= HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY
, snd_pcm_format_mask_sizeof());
1756 return E_OUTOFMEMORY
;
1759 EnterCriticalSection(&This
->lock
);
1761 if((err
= snd_pcm_hw_params_any(This
->pcm_handle
, This
->hw_params
)) < 0){
1762 WARN("Unable to get hw_params: %d (%s)\n", err
, snd_strerror(err
));
1763 hr
= AUDCLNT_E_DEVICE_INVALIDATED
;
1767 snd_pcm_hw_params_get_format_mask(This
->hw_params
, formats
);
1769 fmt
->Format
.wFormatTag
= WAVE_FORMAT_EXTENSIBLE
;
1770 if(snd_pcm_format_mask_test(formats
, SND_PCM_FORMAT_FLOAT_LE
)){
1771 fmt
->Format
.wBitsPerSample
= 32;
1772 fmt
->SubFormat
= KSDATAFORMAT_SUBTYPE_IEEE_FLOAT
;
1773 }else if(snd_pcm_format_mask_test(formats
, SND_PCM_FORMAT_S16_LE
)){
1774 fmt
->Format
.wBitsPerSample
= 16;
1775 fmt
->SubFormat
= KSDATAFORMAT_SUBTYPE_PCM
;
1776 }else if(snd_pcm_format_mask_test(formats
, SND_PCM_FORMAT_U8
)){
1777 fmt
->Format
.wBitsPerSample
= 8;
1778 fmt
->SubFormat
= KSDATAFORMAT_SUBTYPE_PCM
;
1779 }else if(snd_pcm_format_mask_test(formats
, SND_PCM_FORMAT_S32_LE
)){
1780 fmt
->Format
.wBitsPerSample
= 32;
1781 fmt
->SubFormat
= KSDATAFORMAT_SUBTYPE_PCM
;
1782 }else if(snd_pcm_format_mask_test(formats
, SND_PCM_FORMAT_S24_3LE
)){
1783 fmt
->Format
.wBitsPerSample
= 24;
1784 fmt
->SubFormat
= KSDATAFORMAT_SUBTYPE_PCM
;
1786 ERR("Didn't recognize any available ALSA formats\n");
1787 hr
= AUDCLNT_E_DEVICE_INVALIDATED
;
1791 if((err
= snd_pcm_hw_params_get_channels_max(This
->hw_params
,
1792 &max_channels
)) < 0){
1793 WARN("Unable to get max channels: %d (%s)\n", err
, snd_strerror(err
));
1794 hr
= AUDCLNT_E_DEVICE_INVALIDATED
;
1798 if(max_channels
> 2)
1799 fmt
->Format
.nChannels
= 2;
1801 fmt
->Format
.nChannels
= max_channels
;
1803 fmt
->dwChannelMask
= get_channel_mask(fmt
->Format
.nChannels
);
1805 if((err
= snd_pcm_hw_params_get_rate_max(This
->hw_params
, &max_rate
,
1807 WARN("Unable to get max rate: %d (%s)\n", err
, snd_strerror(err
));
1808 hr
= AUDCLNT_E_DEVICE_INVALIDATED
;
1812 if(max_rate
>= 48000)
1813 fmt
->Format
.nSamplesPerSec
= 48000;
1814 else if(max_rate
>= 44100)
1815 fmt
->Format
.nSamplesPerSec
= 44100;
1816 else if(max_rate
>= 22050)
1817 fmt
->Format
.nSamplesPerSec
= 22050;
1818 else if(max_rate
>= 11025)
1819 fmt
->Format
.nSamplesPerSec
= 11025;
1820 else if(max_rate
>= 8000)
1821 fmt
->Format
.nSamplesPerSec
= 8000;
1823 ERR("Unknown max rate: %u\n", max_rate
);
1824 hr
= AUDCLNT_E_DEVICE_INVALIDATED
;
1828 fmt
->Format
.nBlockAlign
= (fmt
->Format
.wBitsPerSample
*
1829 fmt
->Format
.nChannels
) / 8;
1830 fmt
->Format
.nAvgBytesPerSec
= fmt
->Format
.nSamplesPerSec
*
1831 fmt
->Format
.nBlockAlign
;
1833 fmt
->Samples
.wValidBitsPerSample
= fmt
->Format
.wBitsPerSample
;
1834 fmt
->Format
.cbSize
= sizeof(WAVEFORMATEXTENSIBLE
) - sizeof(WAVEFORMATEX
);
1836 dump_fmt((WAVEFORMATEX
*)fmt
);
1837 *pwfx
= (WAVEFORMATEX
*)fmt
;
1840 LeaveCriticalSection(&This
->lock
);
1843 HeapFree(GetProcessHeap(), 0, formats
);
1848 static HRESULT WINAPI
AudioClient_GetDevicePeriod(IAudioClient
*iface
,
1849 REFERENCE_TIME
*defperiod
, REFERENCE_TIME
*minperiod
)
1851 ACImpl
*This
= impl_from_IAudioClient(iface
);
1853 TRACE("(%p)->(%p, %p)\n", This
, defperiod
, minperiod
);
1855 if(!defperiod
&& !minperiod
)
1859 *defperiod
= DefaultPeriod
;
1861 *minperiod
= MinimumPeriod
;
1866 static BYTE
*remap_channels(ACImpl
*This
, BYTE
*buf
, snd_pcm_uframes_t frames
)
1868 snd_pcm_uframes_t i
;
1870 UINT bytes_per_sample
= This
->fmt
->wBitsPerSample
/ 8;
1872 if(!This
->need_remapping
)
1875 if(!This
->remapping_buf
){
1876 This
->remapping_buf
= HeapAlloc(GetProcessHeap(), 0,
1877 bytes_per_sample
* This
->alsa_channels
* frames
);
1878 This
->remapping_buf_frames
= frames
;
1879 }else if(This
->remapping_buf_frames
< frames
){
1880 This
->remapping_buf
= HeapReAlloc(GetProcessHeap(), 0, This
->remapping_buf
,
1881 bytes_per_sample
* This
->alsa_channels
* frames
);
1882 This
->remapping_buf_frames
= frames
;
1885 snd_pcm_format_set_silence(This
->alsa_format
, This
->remapping_buf
,
1886 frames
* This
->alsa_channels
);
1888 switch(This
->fmt
->wBitsPerSample
){
1890 UINT8
*tgt_buf
, *src_buf
;
1891 tgt_buf
= This
->remapping_buf
;
1893 for(i
= 0; i
< frames
; ++i
){
1894 for(c
= 0; c
< This
->fmt
->nChannels
; ++c
)
1895 tgt_buf
[This
->alsa_channel_map
[c
]] = src_buf
[c
];
1896 tgt_buf
+= This
->alsa_channels
;
1897 src_buf
+= This
->fmt
->nChannels
;
1902 UINT16
*tgt_buf
, *src_buf
;
1903 tgt_buf
= (UINT16
*)This
->remapping_buf
;
1904 src_buf
= (UINT16
*)buf
;
1905 for(i
= 0; i
< frames
; ++i
){
1906 for(c
= 0; c
< This
->fmt
->nChannels
; ++c
)
1907 tgt_buf
[This
->alsa_channel_map
[c
]] = src_buf
[c
];
1908 tgt_buf
+= This
->alsa_channels
;
1909 src_buf
+= This
->fmt
->nChannels
;
1914 UINT32
*tgt_buf
, *src_buf
;
1915 tgt_buf
= (UINT32
*)This
->remapping_buf
;
1916 src_buf
= (UINT32
*)buf
;
1917 for(i
= 0; i
< frames
; ++i
){
1918 for(c
= 0; c
< This
->fmt
->nChannels
; ++c
)
1919 tgt_buf
[This
->alsa_channel_map
[c
]] = src_buf
[c
];
1920 tgt_buf
+= This
->alsa_channels
;
1921 src_buf
+= This
->fmt
->nChannels
;
1926 BYTE
*tgt_buf
, *src_buf
;
1927 tgt_buf
= This
->remapping_buf
;
1929 for(i
= 0; i
< frames
; ++i
){
1930 for(c
= 0; c
< This
->fmt
->nChannels
; ++c
)
1931 memcpy(&tgt_buf
[This
->alsa_channel_map
[c
] * bytes_per_sample
],
1932 &src_buf
[c
* bytes_per_sample
], bytes_per_sample
);
1933 tgt_buf
+= This
->alsa_channels
* bytes_per_sample
;
1934 src_buf
+= This
->fmt
->nChannels
* bytes_per_sample
;
1940 return This
->remapping_buf
;
1943 static snd_pcm_sframes_t
alsa_write_best_effort(snd_pcm_t
*handle
, BYTE
*buf
,
1944 snd_pcm_uframes_t frames
, ACImpl
*This
, BOOL mute
)
1946 snd_pcm_sframes_t written
;
1950 if((err
= snd_pcm_format_set_silence(This
->alsa_format
, buf
,
1951 frames
* This
->fmt
->nChannels
)) < 0)
1952 WARN("Setting buffer to silence failed: %d (%s)\n", err
,
1956 buf
= remap_channels(This
, buf
, frames
);
1958 written
= snd_pcm_writei(handle
, buf
, frames
);
1962 if(written
== -EAGAIN
)
1966 WARN("writei failed, recovering: %ld (%s)\n", written
,
1967 snd_strerror(written
));
1969 ret
= snd_pcm_recover(handle
, written
, 0);
1971 WARN("Could not recover: %d (%s)\n", ret
, snd_strerror(ret
));
1975 written
= snd_pcm_writei(handle
, buf
, frames
);
1981 /* The callback and mmdevapi API functions execute concurrently.
1982 * Shared state & life time after Start:
1983 * This constant until _Release
1984 *->pcm_handle likewise
1986 *->alsa_format, hidden_frames likewise
1987 *->local_buffer, bufsize_frames, alsa_bufsize_frames likewise
1988 *->event Read Only, even constant until _Release(!)
1989 *->started Read Only from cb POV, constant if _Stop kills the cb
1991 *->held_frames is the only R/W object.
1992 *->lcl_offs_frames/wri_offs_frames are written by one side exclusively:
1993 * lcl_offs_frames by CaptureClient & write callback
1994 * wri_offs_frames by read callback & RenderClient
1996 static void alsa_write_data(ACImpl
*This
)
1998 snd_pcm_sframes_t written
, in_alsa
;
1999 snd_pcm_uframes_t to_write
, avail
, write_limit
, max_period
;
2002 This
->local_buffer
+ This
->lcl_offs_frames
* This
->fmt
->nBlockAlign
;
2004 /* this call seems to be required to get an accurate snd_pcm_state() */
2005 avail
= snd_pcm_avail_update(This
->pcm_handle
);
2007 if(snd_pcm_state(This
->pcm_handle
) == SND_PCM_STATE_XRUN
||
2008 avail
> This
->alsa_bufsize_frames
){
2009 TRACE("XRun state avail %ld, recovering\n", avail
);
2011 avail
= This
->alsa_bufsize_frames
;
2013 if((err
= snd_pcm_recover(This
->pcm_handle
, -EPIPE
, 1)) < 0)
2014 WARN("snd_pcm_recover failed: %d (%s)\n", err
, snd_strerror(err
));
2016 if((err
= snd_pcm_reset(This
->pcm_handle
)) < 0)
2017 WARN("snd_pcm_reset failed: %d (%s)\n", err
, snd_strerror(err
));
2019 if((err
= snd_pcm_prepare(This
->pcm_handle
)) < 0)
2020 WARN("snd_pcm_prepare failed: %d (%s)\n", err
, snd_strerror(err
));
2022 TRACE("pad: %ld\n", This
->alsa_bufsize_frames
- avail
);
2024 if(This
->held_frames
== 0)
2027 if(This
->lcl_offs_frames
+ This
->held_frames
> This
->bufsize_frames
)
2028 to_write
= This
->bufsize_frames
- This
->lcl_offs_frames
;
2030 to_write
= This
->held_frames
;
2032 max_period
= max(This
->mmdev_period_frames
, This
->alsa_period_frames
);
2034 /* try to keep 3 ALSA periods or 3 MMDevAPI periods in the ALSA buffer and
2037 in_alsa
= This
->alsa_bufsize_frames
- avail
;
2038 while(in_alsa
+ write_limit
< max_period
* 3)
2039 write_limit
+= max_period
;
2040 if(write_limit
== 0)
2043 to_write
= min(to_write
, write_limit
);
2045 /* Add a lead-in when starting with too few frames to ensure
2046 * continuous rendering. Additional benefit: Force ALSA to start.
2047 * GetPosition continues to reflect the speaker position because
2048 * snd_pcm_delay includes buffered frames in its total delay
2049 * and last_pos_frames prevents moving backwards. */
2050 if(!in_alsa
&& This
->held_frames
< This
->hidden_frames
){
2051 UINT32 s_frames
= This
->hidden_frames
- This
->held_frames
;
2052 BYTE
*silence
= HeapAlloc(GetProcessHeap(), 0,
2053 s_frames
* This
->fmt
->nBlockAlign
);
2056 in_alsa
= alsa_write_best_effort(This
->pcm_handle
,
2057 silence
, s_frames
, This
, TRUE
);
2058 TRACE("lead-in %ld\n", in_alsa
);
2059 HeapFree(GetProcessHeap(), 0, silence
);
2063 WARN("Couldn't allocate lead-in, expect underrun\n");
2066 written
= alsa_write_best_effort(This
->pcm_handle
, buf
, to_write
, This
,
2067 This
->session
->mute
);
2069 WARN("Couldn't write: %ld (%s)\n", written
, snd_strerror(written
));
2073 This
->lcl_offs_frames
+= written
;
2074 This
->lcl_offs_frames
%= This
->bufsize_frames
;
2075 This
->held_frames
-= written
;
2077 if(written
< to_write
){
2078 /* ALSA buffer probably full */
2082 if(This
->held_frames
&& (written
< write_limit
)){
2083 /* wrapped and have some data back at the start to write */
2084 written
= alsa_write_best_effort(This
->pcm_handle
, This
->local_buffer
,
2085 min(This
->held_frames
, write_limit
- written
), This
,
2086 This
->session
->mute
);
2088 WARN("Couldn't write: %ld (%s)\n", written
, snd_strerror(written
));
2092 This
->lcl_offs_frames
+= written
;
2093 This
->lcl_offs_frames
%= This
->bufsize_frames
;
2094 This
->held_frames
-= written
;
2098 static void alsa_read_data(ACImpl
*This
)
2100 snd_pcm_sframes_t nread
;
2101 UINT32 pos
= This
->wri_offs_frames
, limit
= This
->held_frames
;
2103 /* FIXME: Detect overrun and signal DATA_DISCONTINUITY
2104 * How to count overrun frames and report them as position increase? */
2105 limit
= This
->bufsize_frames
- max(limit
, pos
);
2107 nread
= snd_pcm_readi(This
->pcm_handle
,
2108 This
->local_buffer
+ pos
* This
->fmt
->nBlockAlign
, limit
);
2109 TRACE("read %ld from %u limit %u\n", nread
, pos
, limit
);
2113 if(nread
== -EAGAIN
) /* no data yet */
2116 WARN("read failed, recovering: %ld (%s)\n", nread
, snd_strerror(nread
));
2118 ret
= snd_pcm_recover(This
->pcm_handle
, nread
, 0);
2120 WARN("Recover failed: %d (%s)\n", ret
, snd_strerror(ret
));
2124 nread
= snd_pcm_readi(This
->pcm_handle
,
2125 This
->local_buffer
+ pos
* This
->fmt
->nBlockAlign
, limit
);
2127 WARN("read failed: %ld (%s)\n", nread
, snd_strerror(nread
));
2132 if(This
->session
->mute
){
2134 if((err
= snd_pcm_format_set_silence(This
->alsa_format
,
2135 This
->local_buffer
+ pos
* This
->fmt
->nBlockAlign
,
2137 WARN("Setting buffer to silence failed: %d (%s)\n", err
,
2141 This
->wri_offs_frames
+= nread
;
2142 This
->wri_offs_frames
%= This
->bufsize_frames
;
2143 This
->held_frames
+= nread
;
2146 static void CALLBACK
alsa_push_buffer_data(void *user
, BOOLEAN timer
)
2148 ACImpl
*This
= user
;
2150 EnterCriticalSection(&This
->lock
);
2153 if(This
->dataflow
== eRender
)
2154 alsa_write_data(This
);
2155 else if(This
->dataflow
== eCapture
)
2156 alsa_read_data(This
);
2159 LeaveCriticalSection(&This
->lock
);
2162 SetEvent(This
->event
);
2165 static HRESULT WINAPI
AudioClient_Start(IAudioClient
*iface
)
2167 ACImpl
*This
= impl_from_IAudioClient(iface
);
2169 TRACE("(%p)\n", This
);
2171 EnterCriticalSection(&This
->lock
);
2174 LeaveCriticalSection(&This
->lock
);
2175 return AUDCLNT_E_NOT_INITIALIZED
;
2178 if((This
->flags
& AUDCLNT_STREAMFLAGS_EVENTCALLBACK
) && !This
->event
){
2179 LeaveCriticalSection(&This
->lock
);
2180 return AUDCLNT_E_EVENTHANDLE_NOT_SET
;
2184 LeaveCriticalSection(&This
->lock
);
2185 return AUDCLNT_E_NOT_STOPPED
;
2188 if(This
->dataflow
== eCapture
){
2189 /* dump any data that might be leftover in the ALSA capture buffer */
2190 snd_pcm_readi(This
->pcm_handle
, This
->local_buffer
,
2191 This
->bufsize_frames
);
2194 if(!CreateTimerQueueTimer(&This
->timer
, g_timer_q
, alsa_push_buffer_data
,
2195 This
, 0, This
->mmdev_period_rt
/ 10000, WT_EXECUTEINTIMERTHREAD
)){
2196 LeaveCriticalSection(&This
->lock
);
2197 WARN("Unable to create timer: %u\n", GetLastError());
2198 return E_OUTOFMEMORY
;
2201 This
->started
= TRUE
;
2203 LeaveCriticalSection(&This
->lock
);
2208 static HRESULT WINAPI
AudioClient_Stop(IAudioClient
*iface
)
2210 ACImpl
*This
= impl_from_IAudioClient(iface
);
2214 TRACE("(%p)\n", This
);
2216 EnterCriticalSection(&This
->lock
);
2219 LeaveCriticalSection(&This
->lock
);
2220 return AUDCLNT_E_NOT_INITIALIZED
;
2224 LeaveCriticalSection(&This
->lock
);
2228 /* Stop without losing written frames or position.
2229 * snd_pcm_pause would be appropriate but is unsupported by dmix.
2230 * snd_pcm_drain yields EAGAIN in NONBLOCK mode, except with Pulse. */
2232 event
= CreateEventW(NULL
, TRUE
, FALSE
, NULL
);
2233 wait
= !DeleteTimerQueueTimer(g_timer_q
, This
->timer
, event
);
2235 WARN("DeleteTimerQueueTimer error %u\n", GetLastError());
2236 wait
= wait
&& GetLastError() == ERROR_IO_PENDING
;
2238 This
->started
= FALSE
;
2240 LeaveCriticalSection(&This
->lock
);
2243 WaitForSingleObject(event
, INFINITE
);
2249 static HRESULT WINAPI
AudioClient_Reset(IAudioClient
*iface
)
2251 ACImpl
*This
= impl_from_IAudioClient(iface
);
2253 TRACE("(%p)\n", This
);
2255 EnterCriticalSection(&This
->lock
);
2258 LeaveCriticalSection(&This
->lock
);
2259 return AUDCLNT_E_NOT_INITIALIZED
;
2263 LeaveCriticalSection(&This
->lock
);
2264 return AUDCLNT_E_NOT_STOPPED
;
2267 if(This
->getbuf_last
){
2268 LeaveCriticalSection(&This
->lock
);
2269 return AUDCLNT_E_BUFFER_OPERATION_PENDING
;
2272 if(snd_pcm_drop(This
->pcm_handle
) < 0)
2273 WARN("snd_pcm_drop failed\n");
2275 if(snd_pcm_reset(This
->pcm_handle
) < 0)
2276 WARN("snd_pcm_reset failed\n");
2278 if(snd_pcm_prepare(This
->pcm_handle
) < 0)
2279 WARN("snd_pcm_prepare failed\n");
2281 if(This
->dataflow
== eRender
){
2282 This
->written_frames
= 0;
2283 This
->last_pos_frames
= 0;
2285 This
->written_frames
+= This
->held_frames
;
2287 This
->held_frames
= 0;
2288 This
->lcl_offs_frames
= 0;
2289 This
->wri_offs_frames
= 0;
2291 LeaveCriticalSection(&This
->lock
);
2296 static HRESULT WINAPI
AudioClient_SetEventHandle(IAudioClient
*iface
,
2299 ACImpl
*This
= impl_from_IAudioClient(iface
);
2301 TRACE("(%p)->(%p)\n", This
, event
);
2304 return E_INVALIDARG
;
2306 EnterCriticalSection(&This
->lock
);
2309 LeaveCriticalSection(&This
->lock
);
2310 return AUDCLNT_E_NOT_INITIALIZED
;
2313 if(!(This
->flags
& AUDCLNT_STREAMFLAGS_EVENTCALLBACK
)){
2314 LeaveCriticalSection(&This
->lock
);
2315 return AUDCLNT_E_EVENTHANDLE_NOT_EXPECTED
;
2319 LeaveCriticalSection(&This
->lock
);
2320 FIXME("called twice\n");
2321 return HRESULT_FROM_WIN32(ERROR_INVALID_NAME
);
2324 This
->event
= event
;
2326 LeaveCriticalSection(&This
->lock
);
2331 static HRESULT WINAPI
AudioClient_GetService(IAudioClient
*iface
, REFIID riid
,
2334 ACImpl
*This
= impl_from_IAudioClient(iface
);
2336 TRACE("(%p)->(%s, %p)\n", This
, debugstr_guid(riid
), ppv
);
2342 EnterCriticalSection(&This
->lock
);
2345 LeaveCriticalSection(&This
->lock
);
2346 return AUDCLNT_E_NOT_INITIALIZED
;
2349 if(IsEqualIID(riid
, &IID_IAudioRenderClient
)){
2350 if(This
->dataflow
!= eRender
){
2351 LeaveCriticalSection(&This
->lock
);
2352 return AUDCLNT_E_WRONG_ENDPOINT_TYPE
;
2354 IAudioRenderClient_AddRef(&This
->IAudioRenderClient_iface
);
2355 *ppv
= &This
->IAudioRenderClient_iface
;
2356 }else if(IsEqualIID(riid
, &IID_IAudioCaptureClient
)){
2357 if(This
->dataflow
!= eCapture
){
2358 LeaveCriticalSection(&This
->lock
);
2359 return AUDCLNT_E_WRONG_ENDPOINT_TYPE
;
2361 IAudioCaptureClient_AddRef(&This
->IAudioCaptureClient_iface
);
2362 *ppv
= &This
->IAudioCaptureClient_iface
;
2363 }else if(IsEqualIID(riid
, &IID_IAudioClock
)){
2364 IAudioClock_AddRef(&This
->IAudioClock_iface
);
2365 *ppv
= &This
->IAudioClock_iface
;
2366 }else if(IsEqualIID(riid
, &IID_IAudioStreamVolume
)){
2367 IAudioStreamVolume_AddRef(&This
->IAudioStreamVolume_iface
);
2368 *ppv
= &This
->IAudioStreamVolume_iface
;
2369 }else if(IsEqualIID(riid
, &IID_IAudioSessionControl
)){
2370 if(!This
->session_wrapper
){
2371 This
->session_wrapper
= AudioSessionWrapper_Create(This
);
2372 if(!This
->session_wrapper
){
2373 LeaveCriticalSection(&This
->lock
);
2374 return E_OUTOFMEMORY
;
2377 IAudioSessionControl2_AddRef(&This
->session_wrapper
->IAudioSessionControl2_iface
);
2379 *ppv
= &This
->session_wrapper
->IAudioSessionControl2_iface
;
2380 }else if(IsEqualIID(riid
, &IID_IChannelAudioVolume
)){
2381 if(!This
->session_wrapper
){
2382 This
->session_wrapper
= AudioSessionWrapper_Create(This
);
2383 if(!This
->session_wrapper
){
2384 LeaveCriticalSection(&This
->lock
);
2385 return E_OUTOFMEMORY
;
2388 IChannelAudioVolume_AddRef(&This
->session_wrapper
->IChannelAudioVolume_iface
);
2390 *ppv
= &This
->session_wrapper
->IChannelAudioVolume_iface
;
2391 }else if(IsEqualIID(riid
, &IID_ISimpleAudioVolume
)){
2392 if(!This
->session_wrapper
){
2393 This
->session_wrapper
= AudioSessionWrapper_Create(This
);
2394 if(!This
->session_wrapper
){
2395 LeaveCriticalSection(&This
->lock
);
2396 return E_OUTOFMEMORY
;
2399 ISimpleAudioVolume_AddRef(&This
->session_wrapper
->ISimpleAudioVolume_iface
);
2401 *ppv
= &This
->session_wrapper
->ISimpleAudioVolume_iface
;
2405 LeaveCriticalSection(&This
->lock
);
2409 LeaveCriticalSection(&This
->lock
);
2411 FIXME("stub %s\n", debugstr_guid(riid
));
2412 return E_NOINTERFACE
;
2415 static const IAudioClientVtbl AudioClient_Vtbl
=
2417 AudioClient_QueryInterface
,
2419 AudioClient_Release
,
2420 AudioClient_Initialize
,
2421 AudioClient_GetBufferSize
,
2422 AudioClient_GetStreamLatency
,
2423 AudioClient_GetCurrentPadding
,
2424 AudioClient_IsFormatSupported
,
2425 AudioClient_GetMixFormat
,
2426 AudioClient_GetDevicePeriod
,
2430 AudioClient_SetEventHandle
,
2431 AudioClient_GetService
2434 static HRESULT WINAPI
AudioRenderClient_QueryInterface(
2435 IAudioRenderClient
*iface
, REFIID riid
, void **ppv
)
2437 ACImpl
*This
= impl_from_IAudioRenderClient(iface
);
2438 TRACE("(%p)->(%s, %p)\n", iface
, debugstr_guid(riid
), ppv
);
2444 if(IsEqualIID(riid
, &IID_IUnknown
) ||
2445 IsEqualIID(riid
, &IID_IAudioRenderClient
))
2447 else if(IsEqualIID(riid
, &IID_IMarshal
))
2448 return IUnknown_QueryInterface(This
->pUnkFTMarshal
, riid
, ppv
);
2451 IUnknown_AddRef((IUnknown
*)*ppv
);
2455 WARN("Unknown interface %s\n", debugstr_guid(riid
));
2456 return E_NOINTERFACE
;
2459 static ULONG WINAPI
AudioRenderClient_AddRef(IAudioRenderClient
*iface
)
2461 ACImpl
*This
= impl_from_IAudioRenderClient(iface
);
2462 return AudioClient_AddRef(&This
->IAudioClient_iface
);
2465 static ULONG WINAPI
AudioRenderClient_Release(IAudioRenderClient
*iface
)
2467 ACImpl
*This
= impl_from_IAudioRenderClient(iface
);
2468 return AudioClient_Release(&This
->IAudioClient_iface
);
2471 static void silence_buffer(ACImpl
*This
, BYTE
*buffer
, UINT32 frames
)
2473 WAVEFORMATEXTENSIBLE
*fmtex
= (WAVEFORMATEXTENSIBLE
*)This
->fmt
;
2474 if((This
->fmt
->wFormatTag
== WAVE_FORMAT_PCM
||
2475 (This
->fmt
->wFormatTag
== WAVE_FORMAT_EXTENSIBLE
&&
2476 IsEqualGUID(&fmtex
->SubFormat
, &KSDATAFORMAT_SUBTYPE_PCM
))) &&
2477 This
->fmt
->wBitsPerSample
== 8)
2478 memset(buffer
, 128, frames
* This
->fmt
->nBlockAlign
);
2480 memset(buffer
, 0, frames
* This
->fmt
->nBlockAlign
);
2483 static HRESULT WINAPI
AudioRenderClient_GetBuffer(IAudioRenderClient
*iface
,
2484 UINT32 frames
, BYTE
**data
)
2486 ACImpl
*This
= impl_from_IAudioRenderClient(iface
);
2489 TRACE("(%p)->(%u, %p)\n", This
, frames
, data
);
2495 EnterCriticalSection(&This
->lock
);
2497 if(This
->getbuf_last
){
2498 LeaveCriticalSection(&This
->lock
);
2499 return AUDCLNT_E_OUT_OF_ORDER
;
2503 LeaveCriticalSection(&This
->lock
);
2507 /* held_frames == GetCurrentPadding_nolock(); */
2508 if(This
->held_frames
+ frames
> This
->bufsize_frames
){
2509 LeaveCriticalSection(&This
->lock
);
2510 return AUDCLNT_E_BUFFER_TOO_LARGE
;
2513 write_pos
= This
->wri_offs_frames
;
2514 if(write_pos
+ frames
> This
->bufsize_frames
){
2515 if(This
->tmp_buffer_frames
< frames
){
2516 HeapFree(GetProcessHeap(), 0, This
->tmp_buffer
);
2517 This
->tmp_buffer
= HeapAlloc(GetProcessHeap(), 0,
2518 frames
* This
->fmt
->nBlockAlign
);
2519 if(!This
->tmp_buffer
){
2520 LeaveCriticalSection(&This
->lock
);
2521 return E_OUTOFMEMORY
;
2523 This
->tmp_buffer_frames
= frames
;
2525 *data
= This
->tmp_buffer
;
2526 This
->getbuf_last
= -frames
;
2528 *data
= This
->local_buffer
+ write_pos
* This
->fmt
->nBlockAlign
;
2529 This
->getbuf_last
= frames
;
2532 silence_buffer(This
, *data
, frames
);
2534 LeaveCriticalSection(&This
->lock
);
2539 static void alsa_wrap_buffer(ACImpl
*This
, BYTE
*buffer
, UINT32 written_frames
)
2541 snd_pcm_uframes_t write_offs_frames
= This
->wri_offs_frames
;
2542 UINT32 write_offs_bytes
= write_offs_frames
* This
->fmt
->nBlockAlign
;
2543 snd_pcm_uframes_t chunk_frames
= This
->bufsize_frames
- write_offs_frames
;
2544 UINT32 chunk_bytes
= chunk_frames
* This
->fmt
->nBlockAlign
;
2545 UINT32 written_bytes
= written_frames
* This
->fmt
->nBlockAlign
;
2547 if(written_bytes
<= chunk_bytes
){
2548 memcpy(This
->local_buffer
+ write_offs_bytes
, buffer
, written_bytes
);
2550 memcpy(This
->local_buffer
+ write_offs_bytes
, buffer
, chunk_bytes
);
2551 memcpy(This
->local_buffer
, buffer
+ chunk_bytes
,
2552 written_bytes
- chunk_bytes
);
2556 static HRESULT WINAPI
AudioRenderClient_ReleaseBuffer(
2557 IAudioRenderClient
*iface
, UINT32 written_frames
, DWORD flags
)
2559 ACImpl
*This
= impl_from_IAudioRenderClient(iface
);
2562 TRACE("(%p)->(%u, %x)\n", This
, written_frames
, flags
);
2564 EnterCriticalSection(&This
->lock
);
2566 if(!written_frames
){
2567 This
->getbuf_last
= 0;
2568 LeaveCriticalSection(&This
->lock
);
2572 if(!This
->getbuf_last
){
2573 LeaveCriticalSection(&This
->lock
);
2574 return AUDCLNT_E_OUT_OF_ORDER
;
2577 if(written_frames
> (This
->getbuf_last
>= 0 ? This
->getbuf_last
: -This
->getbuf_last
)){
2578 LeaveCriticalSection(&This
->lock
);
2579 return AUDCLNT_E_INVALID_SIZE
;
2582 if(This
->getbuf_last
>= 0)
2583 buffer
= This
->local_buffer
+ This
->wri_offs_frames
* This
->fmt
->nBlockAlign
;
2585 buffer
= This
->tmp_buffer
;
2587 if(flags
& AUDCLNT_BUFFERFLAGS_SILENT
)
2588 silence_buffer(This
, buffer
, written_frames
);
2590 if(This
->getbuf_last
< 0)
2591 alsa_wrap_buffer(This
, buffer
, written_frames
);
2593 This
->wri_offs_frames
+= written_frames
;
2594 This
->wri_offs_frames
%= This
->bufsize_frames
;
2595 This
->held_frames
+= written_frames
;
2596 This
->written_frames
+= written_frames
;
2597 This
->getbuf_last
= 0;
2599 LeaveCriticalSection(&This
->lock
);
2604 static const IAudioRenderClientVtbl AudioRenderClient_Vtbl
= {
2605 AudioRenderClient_QueryInterface
,
2606 AudioRenderClient_AddRef
,
2607 AudioRenderClient_Release
,
2608 AudioRenderClient_GetBuffer
,
2609 AudioRenderClient_ReleaseBuffer
2612 static HRESULT WINAPI
AudioCaptureClient_QueryInterface(
2613 IAudioCaptureClient
*iface
, REFIID riid
, void **ppv
)
2615 ACImpl
*This
= impl_from_IAudioCaptureClient(iface
);
2616 TRACE("(%p)->(%s, %p)\n", iface
, debugstr_guid(riid
), ppv
);
2622 if(IsEqualIID(riid
, &IID_IUnknown
) ||
2623 IsEqualIID(riid
, &IID_IAudioCaptureClient
))
2625 else if(IsEqualIID(riid
, &IID_IMarshal
))
2626 return IUnknown_QueryInterface(This
->pUnkFTMarshal
, riid
, ppv
);
2629 IUnknown_AddRef((IUnknown
*)*ppv
);
2633 WARN("Unknown interface %s\n", debugstr_guid(riid
));
2634 return E_NOINTERFACE
;
2637 static ULONG WINAPI
AudioCaptureClient_AddRef(IAudioCaptureClient
*iface
)
2639 ACImpl
*This
= impl_from_IAudioCaptureClient(iface
);
2640 return IAudioClient_AddRef(&This
->IAudioClient_iface
);
2643 static ULONG WINAPI
AudioCaptureClient_Release(IAudioCaptureClient
*iface
)
2645 ACImpl
*This
= impl_from_IAudioCaptureClient(iface
);
2646 return IAudioClient_Release(&This
->IAudioClient_iface
);
2649 static HRESULT WINAPI
AudioCaptureClient_GetBuffer(IAudioCaptureClient
*iface
,
2650 BYTE
**data
, UINT32
*frames
, DWORD
*flags
, UINT64
*devpos
,
2653 ACImpl
*This
= impl_from_IAudioCaptureClient(iface
);
2655 TRACE("(%p)->(%p, %p, %p, %p, %p)\n", This
, data
, frames
, flags
,
2658 if(!data
|| !frames
|| !flags
)
2661 EnterCriticalSection(&This
->lock
);
2663 if(This
->getbuf_last
){
2664 LeaveCriticalSection(&This
->lock
);
2665 return AUDCLNT_E_OUT_OF_ORDER
;
2668 /* hr = GetNextPacketSize(iface, frames); */
2669 if(This
->held_frames
< This
->mmdev_period_frames
){
2671 LeaveCriticalSection(&This
->lock
);
2672 return AUDCLNT_S_BUFFER_EMPTY
;
2674 *frames
= This
->mmdev_period_frames
;
2676 if(This
->lcl_offs_frames
+ *frames
> This
->bufsize_frames
){
2677 UINT32 chunk_bytes
, offs_bytes
, frames_bytes
;
2678 if(This
->tmp_buffer_frames
< *frames
){
2679 HeapFree(GetProcessHeap(), 0, This
->tmp_buffer
);
2680 This
->tmp_buffer
= HeapAlloc(GetProcessHeap(), 0,
2681 *frames
* This
->fmt
->nBlockAlign
);
2682 if(!This
->tmp_buffer
){
2683 LeaveCriticalSection(&This
->lock
);
2684 return E_OUTOFMEMORY
;
2686 This
->tmp_buffer_frames
= *frames
;
2689 *data
= This
->tmp_buffer
;
2690 chunk_bytes
= (This
->bufsize_frames
- This
->lcl_offs_frames
) *
2691 This
->fmt
->nBlockAlign
;
2692 offs_bytes
= This
->lcl_offs_frames
* This
->fmt
->nBlockAlign
;
2693 frames_bytes
= *frames
* This
->fmt
->nBlockAlign
;
2694 memcpy(This
->tmp_buffer
, This
->local_buffer
+ offs_bytes
, chunk_bytes
);
2695 memcpy(This
->tmp_buffer
+ chunk_bytes
, This
->local_buffer
,
2696 frames_bytes
- chunk_bytes
);
2698 *data
= This
->local_buffer
+
2699 This
->lcl_offs_frames
* This
->fmt
->nBlockAlign
;
2701 This
->getbuf_last
= *frames
;
2705 *devpos
= This
->written_frames
;
2706 if(qpcpos
){ /* fixme: qpc of recording time */
2707 LARGE_INTEGER stamp
, freq
;
2708 QueryPerformanceCounter(&stamp
);
2709 QueryPerformanceFrequency(&freq
);
2710 *qpcpos
= (stamp
.QuadPart
* (INT64
)10000000) / freq
.QuadPart
;
2713 LeaveCriticalSection(&This
->lock
);
2715 return *frames
? S_OK
: AUDCLNT_S_BUFFER_EMPTY
;
2718 static HRESULT WINAPI
AudioCaptureClient_ReleaseBuffer(
2719 IAudioCaptureClient
*iface
, UINT32 done
)
2721 ACImpl
*This
= impl_from_IAudioCaptureClient(iface
);
2723 TRACE("(%p)->(%u)\n", This
, done
);
2725 EnterCriticalSection(&This
->lock
);
2728 This
->getbuf_last
= 0;
2729 LeaveCriticalSection(&This
->lock
);
2733 if(!This
->getbuf_last
){
2734 LeaveCriticalSection(&This
->lock
);
2735 return AUDCLNT_E_OUT_OF_ORDER
;
2738 if(This
->getbuf_last
!= done
){
2739 LeaveCriticalSection(&This
->lock
);
2740 return AUDCLNT_E_INVALID_SIZE
;
2743 This
->written_frames
+= done
;
2744 This
->held_frames
-= done
;
2745 This
->lcl_offs_frames
+= done
;
2746 This
->lcl_offs_frames
%= This
->bufsize_frames
;
2747 This
->getbuf_last
= 0;
2749 LeaveCriticalSection(&This
->lock
);
2754 static HRESULT WINAPI
AudioCaptureClient_GetNextPacketSize(
2755 IAudioCaptureClient
*iface
, UINT32
*frames
)
2757 ACImpl
*This
= impl_from_IAudioCaptureClient(iface
);
2759 TRACE("(%p)->(%p)\n", This
, frames
);
2764 EnterCriticalSection(&This
->lock
);
2766 *frames
= This
->held_frames
< This
->mmdev_period_frames
? 0 : This
->mmdev_period_frames
;
2768 LeaveCriticalSection(&This
->lock
);
2773 static const IAudioCaptureClientVtbl AudioCaptureClient_Vtbl
=
2775 AudioCaptureClient_QueryInterface
,
2776 AudioCaptureClient_AddRef
,
2777 AudioCaptureClient_Release
,
2778 AudioCaptureClient_GetBuffer
,
2779 AudioCaptureClient_ReleaseBuffer
,
2780 AudioCaptureClient_GetNextPacketSize
2783 static HRESULT WINAPI
AudioClock_QueryInterface(IAudioClock
*iface
,
2784 REFIID riid
, void **ppv
)
2786 ACImpl
*This
= impl_from_IAudioClock(iface
);
2788 TRACE("(%p)->(%s, %p)\n", iface
, debugstr_guid(riid
), ppv
);
2794 if(IsEqualIID(riid
, &IID_IUnknown
) || IsEqualIID(riid
, &IID_IAudioClock
))
2796 else if(IsEqualIID(riid
, &IID_IAudioClock2
))
2797 *ppv
= &This
->IAudioClock2_iface
;
2799 IUnknown_AddRef((IUnknown
*)*ppv
);
2803 WARN("Unknown interface %s\n", debugstr_guid(riid
));
2804 return E_NOINTERFACE
;
2807 static ULONG WINAPI
AudioClock_AddRef(IAudioClock
*iface
)
2809 ACImpl
*This
= impl_from_IAudioClock(iface
);
2810 return IAudioClient_AddRef(&This
->IAudioClient_iface
);
2813 static ULONG WINAPI
AudioClock_Release(IAudioClock
*iface
)
2815 ACImpl
*This
= impl_from_IAudioClock(iface
);
2816 return IAudioClient_Release(&This
->IAudioClient_iface
);
2819 static HRESULT WINAPI
AudioClock_GetFrequency(IAudioClock
*iface
, UINT64
*freq
)
2821 ACImpl
*This
= impl_from_IAudioClock(iface
);
2823 TRACE("(%p)->(%p)\n", This
, freq
);
2825 if(This
->share
== AUDCLNT_SHAREMODE_SHARED
)
2826 *freq
= This
->fmt
->nSamplesPerSec
* This
->fmt
->nBlockAlign
;
2828 *freq
= This
->fmt
->nSamplesPerSec
;
2833 static HRESULT WINAPI
AudioClock_GetPosition(IAudioClock
*iface
, UINT64
*pos
,
2836 ACImpl
*This
= impl_from_IAudioClock(iface
);
2837 UINT64 written_frames
, position
;
2840 snd_pcm_state_t alsa_state
;
2841 snd_pcm_uframes_t avail_frames
;
2842 snd_pcm_sframes_t delay_frames
;
2844 TRACE("(%p)->(%p, %p)\n", This
, pos
, qpctime
);
2849 EnterCriticalSection(&This
->lock
);
2851 /* call required to get accurate snd_pcm_state() */
2852 avail_frames
= snd_pcm_avail_update(This
->pcm_handle
);
2853 alsa_state
= snd_pcm_state(This
->pcm_handle
);
2854 written_frames
= This
->written_frames
;
2855 held_frames
= This
->held_frames
;
2857 err
= snd_pcm_delay(This
->pcm_handle
, &delay_frames
);
2859 /* old Pulse, shortly after start */
2860 WARN("snd_pcm_delay failed in state %u: %d (%s)\n", alsa_state
, err
, snd_strerror(err
));
2863 if(This
->dataflow
== eRender
){
2864 position
= written_frames
- held_frames
; /* maximum */
2865 if(!This
->started
|| alsa_state
> SND_PCM_STATE_RUNNING
)
2866 ; /* mmdevapi stopped or ALSA underrun: pretend everything was played */
2867 else if(err
<0 || delay_frames
> position
- This
->last_pos_frames
)
2868 /* Pulse bug: past underrun, despite recovery, avail_frames & delay
2869 * may be larger than alsa_bufsize_frames, as if cumulating frames. */
2870 /* Pulse bug: EIO(-5) shortly after starting: nothing played */
2871 position
= This
->last_pos_frames
;
2872 else if(delay_frames
> 0)
2873 position
-= delay_frames
;
2875 position
= written_frames
+ held_frames
;
2877 /* ensure monotic growth */
2878 This
->last_pos_frames
= position
;
2880 LeaveCriticalSection(&This
->lock
);
2882 TRACE("frames written: %u, held: %u, avail: %ld, delay: %ld state %d, pos: %u\n",
2883 (UINT32
)(written_frames
%1000000000), held_frames
,
2884 avail_frames
, delay_frames
, alsa_state
, (UINT32
)(position
%1000000000));
2885 if(This
->share
== AUDCLNT_SHAREMODE_SHARED
)
2886 *pos
= position
* This
->fmt
->nBlockAlign
;
2891 LARGE_INTEGER stamp
, freq
;
2892 QueryPerformanceCounter(&stamp
);
2893 QueryPerformanceFrequency(&freq
);
2894 *qpctime
= (stamp
.QuadPart
* (INT64
)10000000) / freq
.QuadPart
;
2900 static HRESULT WINAPI
AudioClock_GetCharacteristics(IAudioClock
*iface
,
2903 ACImpl
*This
= impl_from_IAudioClock(iface
);
2905 TRACE("(%p)->(%p)\n", This
, chars
);
2910 *chars
= AUDIOCLOCK_CHARACTERISTIC_FIXED_FREQ
;
2915 static const IAudioClockVtbl AudioClock_Vtbl
=
2917 AudioClock_QueryInterface
,
2920 AudioClock_GetFrequency
,
2921 AudioClock_GetPosition
,
2922 AudioClock_GetCharacteristics
2925 static HRESULT WINAPI
AudioClock2_QueryInterface(IAudioClock2
*iface
,
2926 REFIID riid
, void **ppv
)
2928 ACImpl
*This
= impl_from_IAudioClock2(iface
);
2929 return IAudioClock_QueryInterface(&This
->IAudioClock_iface
, riid
, ppv
);
2932 static ULONG WINAPI
AudioClock2_AddRef(IAudioClock2
*iface
)
2934 ACImpl
*This
= impl_from_IAudioClock2(iface
);
2935 return IAudioClient_AddRef(&This
->IAudioClient_iface
);
2938 static ULONG WINAPI
AudioClock2_Release(IAudioClock2
*iface
)
2940 ACImpl
*This
= impl_from_IAudioClock2(iface
);
2941 return IAudioClient_Release(&This
->IAudioClient_iface
);
2944 static HRESULT WINAPI
AudioClock2_GetDevicePosition(IAudioClock2
*iface
,
2945 UINT64
*pos
, UINT64
*qpctime
)
2947 ACImpl
*This
= impl_from_IAudioClock2(iface
);
2949 FIXME("(%p)->(%p, %p)\n", This
, pos
, qpctime
);
2954 static const IAudioClock2Vtbl AudioClock2_Vtbl
=
2956 AudioClock2_QueryInterface
,
2958 AudioClock2_Release
,
2959 AudioClock2_GetDevicePosition
2962 static AudioSessionWrapper
*AudioSessionWrapper_Create(ACImpl
*client
)
2964 AudioSessionWrapper
*ret
;
2966 ret
= HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY
,
2967 sizeof(AudioSessionWrapper
));
2971 ret
->IAudioSessionControl2_iface
.lpVtbl
= &AudioSessionControl2_Vtbl
;
2972 ret
->ISimpleAudioVolume_iface
.lpVtbl
= &SimpleAudioVolume_Vtbl
;
2973 ret
->IChannelAudioVolume_iface
.lpVtbl
= &ChannelAudioVolume_Vtbl
;
2977 ret
->client
= client
;
2979 ret
->session
= client
->session
;
2980 AudioClient_AddRef(&client
->IAudioClient_iface
);
2986 static HRESULT WINAPI
AudioSessionControl_QueryInterface(
2987 IAudioSessionControl2
*iface
, REFIID riid
, void **ppv
)
2989 TRACE("(%p)->(%s, %p)\n", iface
, debugstr_guid(riid
), ppv
);
2995 if(IsEqualIID(riid
, &IID_IUnknown
) ||
2996 IsEqualIID(riid
, &IID_IAudioSessionControl
) ||
2997 IsEqualIID(riid
, &IID_IAudioSessionControl2
))
3000 IUnknown_AddRef((IUnknown
*)*ppv
);
3004 WARN("Unknown interface %s\n", debugstr_guid(riid
));
3005 return E_NOINTERFACE
;
3008 static ULONG WINAPI
AudioSessionControl_AddRef(IAudioSessionControl2
*iface
)
3010 AudioSessionWrapper
*This
= impl_from_IAudioSessionControl2(iface
);
3012 ref
= InterlockedIncrement(&This
->ref
);
3013 TRACE("(%p) Refcount now %u\n", This
, ref
);
3017 static ULONG WINAPI
AudioSessionControl_Release(IAudioSessionControl2
*iface
)
3019 AudioSessionWrapper
*This
= impl_from_IAudioSessionControl2(iface
);
3021 ref
= InterlockedDecrement(&This
->ref
);
3022 TRACE("(%p) Refcount now %u\n", This
, ref
);
3025 EnterCriticalSection(&This
->client
->lock
);
3026 This
->client
->session_wrapper
= NULL
;
3027 LeaveCriticalSection(&This
->client
->lock
);
3028 AudioClient_Release(&This
->client
->IAudioClient_iface
);
3030 HeapFree(GetProcessHeap(), 0, This
);
3035 static HRESULT WINAPI
AudioSessionControl_GetState(IAudioSessionControl2
*iface
,
3036 AudioSessionState
*state
)
3038 AudioSessionWrapper
*This
= impl_from_IAudioSessionControl2(iface
);
3041 TRACE("(%p)->(%p)\n", This
, state
);
3044 return NULL_PTR_ERR
;
3046 EnterCriticalSection(&g_sessions_lock
);
3048 if(list_empty(&This
->session
->clients
)){
3049 *state
= AudioSessionStateExpired
;
3050 LeaveCriticalSection(&g_sessions_lock
);
3054 LIST_FOR_EACH_ENTRY(client
, &This
->session
->clients
, ACImpl
, entry
){
3055 EnterCriticalSection(&client
->lock
);
3056 if(client
->started
){
3057 *state
= AudioSessionStateActive
;
3058 LeaveCriticalSection(&client
->lock
);
3059 LeaveCriticalSection(&g_sessions_lock
);
3062 LeaveCriticalSection(&client
->lock
);
3065 LeaveCriticalSection(&g_sessions_lock
);
3067 *state
= AudioSessionStateInactive
;
3072 static HRESULT WINAPI
AudioSessionControl_GetDisplayName(
3073 IAudioSessionControl2
*iface
, WCHAR
**name
)
3075 AudioSessionWrapper
*This
= impl_from_IAudioSessionControl2(iface
);
3077 FIXME("(%p)->(%p) - stub\n", This
, name
);
3082 static HRESULT WINAPI
AudioSessionControl_SetDisplayName(
3083 IAudioSessionControl2
*iface
, const WCHAR
*name
, const GUID
*session
)
3085 AudioSessionWrapper
*This
= impl_from_IAudioSessionControl2(iface
);
3087 FIXME("(%p)->(%p, %s) - stub\n", This
, name
, debugstr_guid(session
));
3092 static HRESULT WINAPI
AudioSessionControl_GetIconPath(
3093 IAudioSessionControl2
*iface
, WCHAR
**path
)
3095 AudioSessionWrapper
*This
= impl_from_IAudioSessionControl2(iface
);
3097 FIXME("(%p)->(%p) - stub\n", This
, path
);
3102 static HRESULT WINAPI
AudioSessionControl_SetIconPath(
3103 IAudioSessionControl2
*iface
, const WCHAR
*path
, const GUID
*session
)
3105 AudioSessionWrapper
*This
= impl_from_IAudioSessionControl2(iface
);
3107 FIXME("(%p)->(%p, %s) - stub\n", This
, path
, debugstr_guid(session
));
3112 static HRESULT WINAPI
AudioSessionControl_GetGroupingParam(
3113 IAudioSessionControl2
*iface
, GUID
*group
)
3115 AudioSessionWrapper
*This
= impl_from_IAudioSessionControl2(iface
);
3117 FIXME("(%p)->(%p) - stub\n", This
, group
);
3122 static HRESULT WINAPI
AudioSessionControl_SetGroupingParam(
3123 IAudioSessionControl2
*iface
, const GUID
*group
, const GUID
*session
)
3125 AudioSessionWrapper
*This
= impl_from_IAudioSessionControl2(iface
);
3127 FIXME("(%p)->(%s, %s) - stub\n", This
, debugstr_guid(group
),
3128 debugstr_guid(session
));
3133 static HRESULT WINAPI
AudioSessionControl_RegisterAudioSessionNotification(
3134 IAudioSessionControl2
*iface
, IAudioSessionEvents
*events
)
3136 AudioSessionWrapper
*This
= impl_from_IAudioSessionControl2(iface
);
3138 FIXME("(%p)->(%p) - stub\n", This
, events
);
3143 static HRESULT WINAPI
AudioSessionControl_UnregisterAudioSessionNotification(
3144 IAudioSessionControl2
*iface
, IAudioSessionEvents
*events
)
3146 AudioSessionWrapper
*This
= impl_from_IAudioSessionControl2(iface
);
3148 FIXME("(%p)->(%p) - stub\n", This
, events
);
3153 static HRESULT WINAPI
AudioSessionControl_GetSessionIdentifier(
3154 IAudioSessionControl2
*iface
, WCHAR
**id
)
3156 AudioSessionWrapper
*This
= impl_from_IAudioSessionControl2(iface
);
3158 FIXME("(%p)->(%p) - stub\n", This
, id
);
3163 static HRESULT WINAPI
AudioSessionControl_GetSessionInstanceIdentifier(
3164 IAudioSessionControl2
*iface
, WCHAR
**id
)
3166 AudioSessionWrapper
*This
= impl_from_IAudioSessionControl2(iface
);
3168 FIXME("(%p)->(%p) - stub\n", This
, id
);
3173 static HRESULT WINAPI
AudioSessionControl_GetProcessId(
3174 IAudioSessionControl2
*iface
, DWORD
*pid
)
3176 AudioSessionWrapper
*This
= impl_from_IAudioSessionControl2(iface
);
3178 TRACE("(%p)->(%p)\n", This
, pid
);
3183 *pid
= GetCurrentProcessId();
3188 static HRESULT WINAPI
AudioSessionControl_IsSystemSoundsSession(
3189 IAudioSessionControl2
*iface
)
3191 AudioSessionWrapper
*This
= impl_from_IAudioSessionControl2(iface
);
3193 TRACE("(%p)\n", This
);
3198 static HRESULT WINAPI
AudioSessionControl_SetDuckingPreference(
3199 IAudioSessionControl2
*iface
, BOOL optout
)
3201 AudioSessionWrapper
*This
= impl_from_IAudioSessionControl2(iface
);
3203 TRACE("(%p)->(%d)\n", This
, optout
);
3208 static const IAudioSessionControl2Vtbl AudioSessionControl2_Vtbl
=
3210 AudioSessionControl_QueryInterface
,
3211 AudioSessionControl_AddRef
,
3212 AudioSessionControl_Release
,
3213 AudioSessionControl_GetState
,
3214 AudioSessionControl_GetDisplayName
,
3215 AudioSessionControl_SetDisplayName
,
3216 AudioSessionControl_GetIconPath
,
3217 AudioSessionControl_SetIconPath
,
3218 AudioSessionControl_GetGroupingParam
,
3219 AudioSessionControl_SetGroupingParam
,
3220 AudioSessionControl_RegisterAudioSessionNotification
,
3221 AudioSessionControl_UnregisterAudioSessionNotification
,
3222 AudioSessionControl_GetSessionIdentifier
,
3223 AudioSessionControl_GetSessionInstanceIdentifier
,
3224 AudioSessionControl_GetProcessId
,
3225 AudioSessionControl_IsSystemSoundsSession
,
3226 AudioSessionControl_SetDuckingPreference
3229 static HRESULT WINAPI
SimpleAudioVolume_QueryInterface(
3230 ISimpleAudioVolume
*iface
, REFIID riid
, void **ppv
)
3232 TRACE("(%p)->(%s, %p)\n", iface
, debugstr_guid(riid
), ppv
);
3238 if(IsEqualIID(riid
, &IID_IUnknown
) ||
3239 IsEqualIID(riid
, &IID_ISimpleAudioVolume
))
3242 IUnknown_AddRef((IUnknown
*)*ppv
);
3246 WARN("Unknown interface %s\n", debugstr_guid(riid
));
3247 return E_NOINTERFACE
;
3250 static ULONG WINAPI
SimpleAudioVolume_AddRef(ISimpleAudioVolume
*iface
)
3252 AudioSessionWrapper
*This
= impl_from_ISimpleAudioVolume(iface
);
3253 return AudioSessionControl_AddRef(&This
->IAudioSessionControl2_iface
);
3256 static ULONG WINAPI
SimpleAudioVolume_Release(ISimpleAudioVolume
*iface
)
3258 AudioSessionWrapper
*This
= impl_from_ISimpleAudioVolume(iface
);
3259 return AudioSessionControl_Release(&This
->IAudioSessionControl2_iface
);
3262 static HRESULT WINAPI
SimpleAudioVolume_SetMasterVolume(
3263 ISimpleAudioVolume
*iface
, float level
, const GUID
*context
)
3265 AudioSessionWrapper
*This
= impl_from_ISimpleAudioVolume(iface
);
3266 AudioSession
*session
= This
->session
;
3268 TRACE("(%p)->(%f, %s)\n", session
, level
, wine_dbgstr_guid(context
));
3270 if(level
< 0.f
|| level
> 1.f
)
3271 return E_INVALIDARG
;
3274 FIXME("Notifications not supported yet\n");
3276 TRACE("ALSA does not support volume control\n");
3278 EnterCriticalSection(&session
->lock
);
3280 session
->master_vol
= level
;
3282 LeaveCriticalSection(&session
->lock
);
3287 static HRESULT WINAPI
SimpleAudioVolume_GetMasterVolume(
3288 ISimpleAudioVolume
*iface
, float *level
)
3290 AudioSessionWrapper
*This
= impl_from_ISimpleAudioVolume(iface
);
3291 AudioSession
*session
= This
->session
;
3293 TRACE("(%p)->(%p)\n", session
, level
);
3296 return NULL_PTR_ERR
;
3298 *level
= session
->master_vol
;
3303 static HRESULT WINAPI
SimpleAudioVolume_SetMute(ISimpleAudioVolume
*iface
,
3304 BOOL mute
, const GUID
*context
)
3306 AudioSessionWrapper
*This
= impl_from_ISimpleAudioVolume(iface
);
3307 AudioSession
*session
= This
->session
;
3309 TRACE("(%p)->(%u, %p)\n", session
, mute
, context
);
3312 FIXME("Notifications not supported yet\n");
3314 session
->mute
= mute
;
3319 static HRESULT WINAPI
SimpleAudioVolume_GetMute(ISimpleAudioVolume
*iface
,
3322 AudioSessionWrapper
*This
= impl_from_ISimpleAudioVolume(iface
);
3323 AudioSession
*session
= This
->session
;
3325 TRACE("(%p)->(%p)\n", session
, mute
);
3328 return NULL_PTR_ERR
;
3330 *mute
= session
->mute
;
3335 static const ISimpleAudioVolumeVtbl SimpleAudioVolume_Vtbl
=
3337 SimpleAudioVolume_QueryInterface
,
3338 SimpleAudioVolume_AddRef
,
3339 SimpleAudioVolume_Release
,
3340 SimpleAudioVolume_SetMasterVolume
,
3341 SimpleAudioVolume_GetMasterVolume
,
3342 SimpleAudioVolume_SetMute
,
3343 SimpleAudioVolume_GetMute
3346 static HRESULT WINAPI
AudioStreamVolume_QueryInterface(
3347 IAudioStreamVolume
*iface
, REFIID riid
, void **ppv
)
3349 TRACE("(%p)->(%s, %p)\n", iface
, debugstr_guid(riid
), ppv
);
3355 if(IsEqualIID(riid
, &IID_IUnknown
) ||
3356 IsEqualIID(riid
, &IID_IAudioStreamVolume
))
3359 IUnknown_AddRef((IUnknown
*)*ppv
);
3363 WARN("Unknown interface %s\n", debugstr_guid(riid
));
3364 return E_NOINTERFACE
;
3367 static ULONG WINAPI
AudioStreamVolume_AddRef(IAudioStreamVolume
*iface
)
3369 ACImpl
*This
= impl_from_IAudioStreamVolume(iface
);
3370 return IAudioClient_AddRef(&This
->IAudioClient_iface
);
3373 static ULONG WINAPI
AudioStreamVolume_Release(IAudioStreamVolume
*iface
)
3375 ACImpl
*This
= impl_from_IAudioStreamVolume(iface
);
3376 return IAudioClient_Release(&This
->IAudioClient_iface
);
3379 static HRESULT WINAPI
AudioStreamVolume_GetChannelCount(
3380 IAudioStreamVolume
*iface
, UINT32
*out
)
3382 ACImpl
*This
= impl_from_IAudioStreamVolume(iface
);
3384 TRACE("(%p)->(%p)\n", This
, out
);
3389 *out
= This
->fmt
->nChannels
;
3394 static HRESULT WINAPI
AudioStreamVolume_SetChannelVolume(
3395 IAudioStreamVolume
*iface
, UINT32 index
, float level
)
3397 ACImpl
*This
= impl_from_IAudioStreamVolume(iface
);
3399 TRACE("(%p)->(%d, %f)\n", This
, index
, level
);
3401 if(level
< 0.f
|| level
> 1.f
)
3402 return E_INVALIDARG
;
3404 if(index
>= This
->fmt
->nChannels
)
3405 return E_INVALIDARG
;
3407 TRACE("ALSA does not support volume control\n");
3409 EnterCriticalSection(&This
->lock
);
3411 This
->vols
[index
] = level
;
3413 LeaveCriticalSection(&This
->lock
);
3418 static HRESULT WINAPI
AudioStreamVolume_GetChannelVolume(
3419 IAudioStreamVolume
*iface
, UINT32 index
, float *level
)
3421 ACImpl
*This
= impl_from_IAudioStreamVolume(iface
);
3423 TRACE("(%p)->(%d, %p)\n", This
, index
, level
);
3428 if(index
>= This
->fmt
->nChannels
)
3429 return E_INVALIDARG
;
3431 *level
= This
->vols
[index
];
3436 static HRESULT WINAPI
AudioStreamVolume_SetAllVolumes(
3437 IAudioStreamVolume
*iface
, UINT32 count
, const float *levels
)
3439 ACImpl
*This
= impl_from_IAudioStreamVolume(iface
);
3442 TRACE("(%p)->(%d, %p)\n", This
, count
, levels
);
3447 if(count
!= This
->fmt
->nChannels
)
3448 return E_INVALIDARG
;
3450 TRACE("ALSA does not support volume control\n");
3452 EnterCriticalSection(&This
->lock
);
3454 for(i
= 0; i
< count
; ++i
)
3455 This
->vols
[i
] = levels
[i
];
3457 LeaveCriticalSection(&This
->lock
);
3462 static HRESULT WINAPI
AudioStreamVolume_GetAllVolumes(
3463 IAudioStreamVolume
*iface
, UINT32 count
, float *levels
)
3465 ACImpl
*This
= impl_from_IAudioStreamVolume(iface
);
3468 TRACE("(%p)->(%d, %p)\n", This
, count
, levels
);
3473 if(count
!= This
->fmt
->nChannels
)
3474 return E_INVALIDARG
;
3476 EnterCriticalSection(&This
->lock
);
3478 for(i
= 0; i
< count
; ++i
)
3479 levels
[i
] = This
->vols
[i
];
3481 LeaveCriticalSection(&This
->lock
);
3486 static const IAudioStreamVolumeVtbl AudioStreamVolume_Vtbl
=
3488 AudioStreamVolume_QueryInterface
,
3489 AudioStreamVolume_AddRef
,
3490 AudioStreamVolume_Release
,
3491 AudioStreamVolume_GetChannelCount
,
3492 AudioStreamVolume_SetChannelVolume
,
3493 AudioStreamVolume_GetChannelVolume
,
3494 AudioStreamVolume_SetAllVolumes
,
3495 AudioStreamVolume_GetAllVolumes
3498 static HRESULT WINAPI
ChannelAudioVolume_QueryInterface(
3499 IChannelAudioVolume
*iface
, REFIID riid
, void **ppv
)
3501 TRACE("(%p)->(%s, %p)\n", iface
, debugstr_guid(riid
), ppv
);
3507 if(IsEqualIID(riid
, &IID_IUnknown
) ||
3508 IsEqualIID(riid
, &IID_IChannelAudioVolume
))
3511 IUnknown_AddRef((IUnknown
*)*ppv
);
3515 WARN("Unknown interface %s\n", debugstr_guid(riid
));
3516 return E_NOINTERFACE
;
3519 static ULONG WINAPI
ChannelAudioVolume_AddRef(IChannelAudioVolume
*iface
)
3521 AudioSessionWrapper
*This
= impl_from_IChannelAudioVolume(iface
);
3522 return AudioSessionControl_AddRef(&This
->IAudioSessionControl2_iface
);
3525 static ULONG WINAPI
ChannelAudioVolume_Release(IChannelAudioVolume
*iface
)
3527 AudioSessionWrapper
*This
= impl_from_IChannelAudioVolume(iface
);
3528 return AudioSessionControl_Release(&This
->IAudioSessionControl2_iface
);
3531 static HRESULT WINAPI
ChannelAudioVolume_GetChannelCount(
3532 IChannelAudioVolume
*iface
, UINT32
*out
)
3534 AudioSessionWrapper
*This
= impl_from_IChannelAudioVolume(iface
);
3535 AudioSession
*session
= This
->session
;
3537 TRACE("(%p)->(%p)\n", session
, out
);
3540 return NULL_PTR_ERR
;
3542 *out
= session
->channel_count
;
3547 static HRESULT WINAPI
ChannelAudioVolume_SetChannelVolume(
3548 IChannelAudioVolume
*iface
, UINT32 index
, float level
,
3549 const GUID
*context
)
3551 AudioSessionWrapper
*This
= impl_from_IChannelAudioVolume(iface
);
3552 AudioSession
*session
= This
->session
;
3554 TRACE("(%p)->(%d, %f, %s)\n", session
, index
, level
,
3555 wine_dbgstr_guid(context
));
3557 if(level
< 0.f
|| level
> 1.f
)
3558 return E_INVALIDARG
;
3560 if(index
>= session
->channel_count
)
3561 return E_INVALIDARG
;
3564 FIXME("Notifications not supported yet\n");
3566 TRACE("ALSA does not support volume control\n");
3568 EnterCriticalSection(&session
->lock
);
3570 session
->channel_vols
[index
] = level
;
3572 LeaveCriticalSection(&session
->lock
);
3577 static HRESULT WINAPI
ChannelAudioVolume_GetChannelVolume(
3578 IChannelAudioVolume
*iface
, UINT32 index
, float *level
)
3580 AudioSessionWrapper
*This
= impl_from_IChannelAudioVolume(iface
);
3581 AudioSession
*session
= This
->session
;
3583 TRACE("(%p)->(%d, %p)\n", session
, index
, level
);
3586 return NULL_PTR_ERR
;
3588 if(index
>= session
->channel_count
)
3589 return E_INVALIDARG
;
3591 *level
= session
->channel_vols
[index
];
3596 static HRESULT WINAPI
ChannelAudioVolume_SetAllVolumes(
3597 IChannelAudioVolume
*iface
, UINT32 count
, const float *levels
,
3598 const GUID
*context
)
3600 AudioSessionWrapper
*This
= impl_from_IChannelAudioVolume(iface
);
3601 AudioSession
*session
= This
->session
;
3604 TRACE("(%p)->(%d, %p, %s)\n", session
, count
, levels
,
3605 wine_dbgstr_guid(context
));
3608 return NULL_PTR_ERR
;
3610 if(count
!= session
->channel_count
)
3611 return E_INVALIDARG
;
3614 FIXME("Notifications not supported yet\n");
3616 TRACE("ALSA does not support volume control\n");
3618 EnterCriticalSection(&session
->lock
);
3620 for(i
= 0; i
< count
; ++i
)
3621 session
->channel_vols
[i
] = levels
[i
];
3623 LeaveCriticalSection(&session
->lock
);
3628 static HRESULT WINAPI
ChannelAudioVolume_GetAllVolumes(
3629 IChannelAudioVolume
*iface
, UINT32 count
, float *levels
)
3631 AudioSessionWrapper
*This
= impl_from_IChannelAudioVolume(iface
);
3632 AudioSession
*session
= This
->session
;
3635 TRACE("(%p)->(%d, %p)\n", session
, count
, levels
);
3638 return NULL_PTR_ERR
;
3640 if(count
!= session
->channel_count
)
3641 return E_INVALIDARG
;
3643 for(i
= 0; i
< count
; ++i
)
3644 levels
[i
] = session
->channel_vols
[i
];
3649 static const IChannelAudioVolumeVtbl ChannelAudioVolume_Vtbl
=
3651 ChannelAudioVolume_QueryInterface
,
3652 ChannelAudioVolume_AddRef
,
3653 ChannelAudioVolume_Release
,
3654 ChannelAudioVolume_GetChannelCount
,
3655 ChannelAudioVolume_SetChannelVolume
,
3656 ChannelAudioVolume_GetChannelVolume
,
3657 ChannelAudioVolume_SetAllVolumes
,
3658 ChannelAudioVolume_GetAllVolumes
3661 static HRESULT WINAPI
AudioSessionManager_QueryInterface(IAudioSessionManager2
*iface
,
3662 REFIID riid
, void **ppv
)
3664 TRACE("(%p)->(%s, %p)\n", iface
, debugstr_guid(riid
), ppv
);
3670 if(IsEqualIID(riid
, &IID_IUnknown
) ||
3671 IsEqualIID(riid
, &IID_IAudioSessionManager
) ||
3672 IsEqualIID(riid
, &IID_IAudioSessionManager2
))
3675 IUnknown_AddRef((IUnknown
*)*ppv
);
3679 WARN("Unknown interface %s\n", debugstr_guid(riid
));
3680 return E_NOINTERFACE
;
3683 static ULONG WINAPI
AudioSessionManager_AddRef(IAudioSessionManager2
*iface
)
3685 SessionMgr
*This
= impl_from_IAudioSessionManager2(iface
);
3687 ref
= InterlockedIncrement(&This
->ref
);
3688 TRACE("(%p) Refcount now %u\n", This
, ref
);
3692 static ULONG WINAPI
AudioSessionManager_Release(IAudioSessionManager2
*iface
)
3694 SessionMgr
*This
= impl_from_IAudioSessionManager2(iface
);
3696 ref
= InterlockedDecrement(&This
->ref
);
3697 TRACE("(%p) Refcount now %u\n", This
, ref
);
3699 HeapFree(GetProcessHeap(), 0, This
);
3703 static HRESULT WINAPI
AudioSessionManager_GetAudioSessionControl(
3704 IAudioSessionManager2
*iface
, const GUID
*session_guid
, DWORD flags
,
3705 IAudioSessionControl
**out
)
3707 SessionMgr
*This
= impl_from_IAudioSessionManager2(iface
);
3708 AudioSession
*session
;
3709 AudioSessionWrapper
*wrapper
;
3712 TRACE("(%p)->(%s, %x, %p)\n", This
, debugstr_guid(session_guid
),
3715 hr
= get_audio_session(session_guid
, This
->device
, 0, &session
);
3719 wrapper
= AudioSessionWrapper_Create(NULL
);
3721 return E_OUTOFMEMORY
;
3723 wrapper
->session
= session
;
3725 *out
= (IAudioSessionControl
*)&wrapper
->IAudioSessionControl2_iface
;
3730 static HRESULT WINAPI
AudioSessionManager_GetSimpleAudioVolume(
3731 IAudioSessionManager2
*iface
, const GUID
*session_guid
, DWORD flags
,
3732 ISimpleAudioVolume
**out
)
3734 SessionMgr
*This
= impl_from_IAudioSessionManager2(iface
);
3735 AudioSession
*session
;
3736 AudioSessionWrapper
*wrapper
;
3739 TRACE("(%p)->(%s, %x, %p)\n", This
, debugstr_guid(session_guid
),
3742 hr
= get_audio_session(session_guid
, This
->device
, 0, &session
);
3746 wrapper
= AudioSessionWrapper_Create(NULL
);
3748 return E_OUTOFMEMORY
;
3750 wrapper
->session
= session
;
3752 *out
= &wrapper
->ISimpleAudioVolume_iface
;
3757 static HRESULT WINAPI
AudioSessionManager_GetSessionEnumerator(
3758 IAudioSessionManager2
*iface
, IAudioSessionEnumerator
**out
)
3760 SessionMgr
*This
= impl_from_IAudioSessionManager2(iface
);
3761 FIXME("(%p)->(%p) - stub\n", This
, out
);
3765 static HRESULT WINAPI
AudioSessionManager_RegisterSessionNotification(
3766 IAudioSessionManager2
*iface
, IAudioSessionNotification
*notification
)
3768 SessionMgr
*This
= impl_from_IAudioSessionManager2(iface
);
3769 FIXME("(%p)->(%p) - stub\n", This
, notification
);
3773 static HRESULT WINAPI
AudioSessionManager_UnregisterSessionNotification(
3774 IAudioSessionManager2
*iface
, IAudioSessionNotification
*notification
)
3776 SessionMgr
*This
= impl_from_IAudioSessionManager2(iface
);
3777 FIXME("(%p)->(%p) - stub\n", This
, notification
);
3781 static HRESULT WINAPI
AudioSessionManager_RegisterDuckNotification(
3782 IAudioSessionManager2
*iface
, const WCHAR
*session_id
,
3783 IAudioVolumeDuckNotification
*notification
)
3785 SessionMgr
*This
= impl_from_IAudioSessionManager2(iface
);
3786 FIXME("(%p)->(%p) - stub\n", This
, notification
);
3790 static HRESULT WINAPI
AudioSessionManager_UnregisterDuckNotification(
3791 IAudioSessionManager2
*iface
,
3792 IAudioVolumeDuckNotification
*notification
)
3794 SessionMgr
*This
= impl_from_IAudioSessionManager2(iface
);
3795 FIXME("(%p)->(%p) - stub\n", This
, notification
);
3799 static const IAudioSessionManager2Vtbl AudioSessionManager2_Vtbl
=
3801 AudioSessionManager_QueryInterface
,
3802 AudioSessionManager_AddRef
,
3803 AudioSessionManager_Release
,
3804 AudioSessionManager_GetAudioSessionControl
,
3805 AudioSessionManager_GetSimpleAudioVolume
,
3806 AudioSessionManager_GetSessionEnumerator
,
3807 AudioSessionManager_RegisterSessionNotification
,
3808 AudioSessionManager_UnregisterSessionNotification
,
3809 AudioSessionManager_RegisterDuckNotification
,
3810 AudioSessionManager_UnregisterDuckNotification
3813 HRESULT WINAPI
AUDDRV_GetAudioSessionManager(IMMDevice
*device
,
3814 IAudioSessionManager2
**out
)
3818 This
= HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY
, sizeof(SessionMgr
));
3820 return E_OUTOFMEMORY
;
3822 This
->IAudioSessionManager2_iface
.lpVtbl
= &AudioSessionManager2_Vtbl
;
3823 This
->device
= device
;
3826 *out
= &This
->IAudioSessionManager2_iface
;