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;
57 typedef struct ACImpl ACImpl
;
59 typedef struct _AudioSession
{
70 CRITICAL_SECTION lock
;
75 typedef struct _AudioSessionWrapper
{
76 IAudioSessionControl2 IAudioSessionControl2_iface
;
77 IChannelAudioVolume IChannelAudioVolume_iface
;
78 ISimpleAudioVolume ISimpleAudioVolume_iface
;
83 AudioSession
*session
;
84 } AudioSessionWrapper
;
87 IAudioClient IAudioClient_iface
;
88 IAudioRenderClient IAudioRenderClient_iface
;
89 IAudioCaptureClient IAudioCaptureClient_iface
;
90 IAudioClock IAudioClock_iface
;
91 IAudioClock2 IAudioClock2_iface
;
92 IAudioStreamVolume IAudioStreamVolume_iface
;
96 snd_pcm_t
*pcm_handle
;
97 snd_pcm_uframes_t alsa_bufsize_frames
;
98 snd_pcm_hw_params_t
*hw_params
; /* does not hold state between calls */
99 snd_pcm_format_t alsa_format
;
106 AUDCLNT_SHAREMODE share
;
110 BOOL initted
, started
;
111 REFERENCE_TIME mmdev_period_rt
;
112 UINT64 written_frames
;
113 UINT32 bufsize_frames
, held_frames
, tmp_buffer_frames
;
114 UINT32 lcl_offs_frames
; /* offs into local_buffer where valid data starts */
117 BYTE
*local_buffer
, *tmp_buffer
;
120 CRITICAL_SECTION lock
;
122 AudioSession
*session
;
123 AudioSessionWrapper
*session_wrapper
;
130 LOCKED_NORMAL
, /* public buffer piece is from local_buffer */
131 LOCKED_WRAPPED
/* public buffer piece is wrapped around, in tmp_buffer */
134 typedef struct _SessionMgr
{
135 IAudioSessionManager2 IAudioSessionManager2_iface
;
142 static HANDLE g_timer_q
;
144 static CRITICAL_SECTION g_sessions_lock
;
145 static CRITICAL_SECTION_DEBUG g_sessions_lock_debug
=
147 0, 0, &g_sessions_lock
,
148 { &g_sessions_lock_debug
.ProcessLocksList
, &g_sessions_lock_debug
.ProcessLocksList
},
149 0, 0, { (DWORD_PTR
)(__FILE__
": g_sessions_lock") }
151 static CRITICAL_SECTION g_sessions_lock
= { &g_sessions_lock_debug
, -1, 0, 0, 0, 0 };
152 static struct list g_sessions
= LIST_INIT(g_sessions
);
154 static const WCHAR defaultW
[] = {'d','e','f','a','u','l','t',0};
155 static const char defname
[] = "default";
157 static const IAudioClientVtbl AudioClient_Vtbl
;
158 static const IAudioRenderClientVtbl AudioRenderClient_Vtbl
;
159 static const IAudioCaptureClientVtbl AudioCaptureClient_Vtbl
;
160 static const IAudioSessionControl2Vtbl AudioSessionControl2_Vtbl
;
161 static const ISimpleAudioVolumeVtbl SimpleAudioVolume_Vtbl
;
162 static const IAudioClockVtbl AudioClock_Vtbl
;
163 static const IAudioClock2Vtbl AudioClock2_Vtbl
;
164 static const IAudioStreamVolumeVtbl AudioStreamVolume_Vtbl
;
165 static const IChannelAudioVolumeVtbl ChannelAudioVolume_Vtbl
;
166 static const IAudioSessionManager2Vtbl AudioSessionManager2_Vtbl
;
168 static AudioSessionWrapper
*AudioSessionWrapper_Create(ACImpl
*client
);
170 static inline ACImpl
*impl_from_IAudioClient(IAudioClient
*iface
)
172 return CONTAINING_RECORD(iface
, ACImpl
, IAudioClient_iface
);
175 static inline ACImpl
*impl_from_IAudioRenderClient(IAudioRenderClient
*iface
)
177 return CONTAINING_RECORD(iface
, ACImpl
, IAudioRenderClient_iface
);
180 static inline ACImpl
*impl_from_IAudioCaptureClient(IAudioCaptureClient
*iface
)
182 return CONTAINING_RECORD(iface
, ACImpl
, IAudioCaptureClient_iface
);
185 static inline AudioSessionWrapper
*impl_from_IAudioSessionControl2(IAudioSessionControl2
*iface
)
187 return CONTAINING_RECORD(iface
, AudioSessionWrapper
, IAudioSessionControl2_iface
);
190 static inline AudioSessionWrapper
*impl_from_ISimpleAudioVolume(ISimpleAudioVolume
*iface
)
192 return CONTAINING_RECORD(iface
, AudioSessionWrapper
, ISimpleAudioVolume_iface
);
195 static inline AudioSessionWrapper
*impl_from_IChannelAudioVolume(IChannelAudioVolume
*iface
)
197 return CONTAINING_RECORD(iface
, AudioSessionWrapper
, IChannelAudioVolume_iface
);
200 static inline ACImpl
*impl_from_IAudioClock(IAudioClock
*iface
)
202 return CONTAINING_RECORD(iface
, ACImpl
, IAudioClock_iface
);
205 static inline ACImpl
*impl_from_IAudioClock2(IAudioClock2
*iface
)
207 return CONTAINING_RECORD(iface
, ACImpl
, IAudioClock2_iface
);
210 static inline ACImpl
*impl_from_IAudioStreamVolume(IAudioStreamVolume
*iface
)
212 return CONTAINING_RECORD(iface
, ACImpl
, IAudioStreamVolume_iface
);
215 static inline SessionMgr
*impl_from_IAudioSessionManager2(IAudioSessionManager2
*iface
)
217 return CONTAINING_RECORD(iface
, SessionMgr
, IAudioSessionManager2_iface
);
220 BOOL WINAPI
DllMain(HINSTANCE dll
, DWORD reason
, void *reserved
)
222 if(reason
== DLL_PROCESS_ATTACH
){
223 g_timer_q
= CreateTimerQueue();
231 /* From <dlls/mmdevapi/mmdevapi.h> */
232 enum DriverPriority
{
233 Priority_Unavailable
= 0,
239 int WINAPI
AUDDRV_GetPriority(void)
241 return Priority_Neutral
;
244 static BOOL
alsa_try_open(const char *devnode
, snd_pcm_stream_t stream
)
249 if((err
= snd_pcm_open(&handle
, devnode
, stream
, SND_PCM_NONBLOCK
)) < 0){
250 WARN("The device \"%s\" failed to open: %d (%s).\n",
251 devnode
, err
, snd_strerror(err
));
255 snd_pcm_close(handle
);
259 static HRESULT
alsa_get_card_devices(snd_pcm_stream_t stream
, WCHAR
**ids
, char **keys
,
260 UINT
*num
, snd_ctl_t
*ctl
, int card
, const WCHAR
*cardnameW
)
262 static const WCHAR dashW
[] = {' ','-',' ',0};
264 snd_pcm_info_t
*info
;
266 info
= HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY
, snd_pcm_info_sizeof());
268 return E_OUTOFMEMORY
;
270 snd_pcm_info_set_subdevice(info
, 0);
271 snd_pcm_info_set_stream(info
, stream
);
274 for(err
= snd_ctl_pcm_next_device(ctl
, &device
); device
!= -1 && err
>= 0;
275 err
= snd_ctl_pcm_next_device(ctl
, &device
)){
279 snd_pcm_info_set_device(info
, device
);
281 if((err
= snd_ctl_pcm_info(ctl
, info
)) < 0){
283 /* This device doesn't have the right stream direction */
286 WARN("Failed to get info for card %d, device %d: %d (%s)\n",
287 card
, device
, err
, snd_strerror(err
));
291 sprintf(devnode
, "plughw:%d,%d", card
, device
);
292 if(!alsa_try_open(devnode
, stream
))
298 devname
= snd_pcm_info_get_name(info
);
300 WARN("Unable to get device name for card %d, device %d\n", card
,
305 cardlen
= lstrlenW(cardnameW
);
306 len
= MultiByteToWideChar(CP_UNIXCP
, 0, devname
, -1, NULL
, 0);
307 len
+= lstrlenW(dashW
);
309 ids
[*num
] = HeapAlloc(GetProcessHeap(), 0, len
* sizeof(WCHAR
));
311 HeapFree(GetProcessHeap(), 0, info
);
312 return E_OUTOFMEMORY
;
314 memcpy(ids
[*num
], cardnameW
, cardlen
* sizeof(WCHAR
));
315 memcpy(ids
[*num
] + cardlen
, dashW
, lstrlenW(dashW
) * sizeof(WCHAR
));
316 cardlen
+= lstrlenW(dashW
);
317 MultiByteToWideChar(CP_UNIXCP
, 0, devname
, -1, ids
[*num
] + cardlen
,
320 keys
[*num
] = HeapAlloc(GetProcessHeap(), 0, 32);
322 HeapFree(GetProcessHeap(), 0, info
);
323 HeapFree(GetProcessHeap(), 0, ids
[*num
]);
324 return E_OUTOFMEMORY
;
326 memcpy(keys
[*num
], devnode
, sizeof(devnode
));
332 HeapFree(GetProcessHeap(), 0, info
);
335 WARN("Got a failure during device enumeration on card %d: %d (%s)\n",
336 card
, err
, snd_strerror(err
));
341 static HRESULT
alsa_enum_devices(EDataFlow flow
, WCHAR
**ids
, char **keys
,
344 snd_pcm_stream_t stream
= (flow
== eRender
? SND_PCM_STREAM_PLAYBACK
:
345 SND_PCM_STREAM_CAPTURE
);
351 if(alsa_try_open(defname
, stream
)){
353 *ids
= HeapAlloc(GetProcessHeap(), 0, sizeof(defaultW
));
354 memcpy(*ids
, defaultW
, sizeof(defaultW
));
355 *keys
= HeapAlloc(GetProcessHeap(), 0, sizeof(defname
));
356 memcpy(*keys
, defname
, sizeof(defname
));
361 for(err
= snd_card_next(&card
); card
!= -1 && err
>= 0;
362 err
= snd_card_next(&card
)){
364 const char *cardname
;
369 sprintf(cardpath
, "hw:%u", card
);
371 if((err
= snd_ctl_open(&ctl
, cardpath
, 0)) < 0){
372 WARN("Unable to open ctl for ALSA device %s: %d (%s)\n", cardpath
,
373 err
, snd_strerror(err
));
377 if((err
= snd_card_get_name(card
, (char **)&cardname
)) < 0){
378 WARN("Unable to get card name for ALSA device %s: %d (%s)\n",
379 cardpath
, err
, snd_strerror(err
));
380 /* FIXME: Should be localized */
381 cardname
= "Unknown soundcard";
384 len
= MultiByteToWideChar(CP_UNIXCP
, 0, cardname
, -1, NULL
, 0);
385 cardnameW
= HeapAlloc(GetProcessHeap(), 0, len
* sizeof(WCHAR
));
388 return E_OUTOFMEMORY
;
390 MultiByteToWideChar(CP_UNIXCP
, 0, cardname
, -1, cardnameW
, len
);
392 alsa_get_card_devices(stream
, ids
, keys
, num
, ctl
, card
, cardnameW
);
394 HeapFree(GetProcessHeap(), 0, cardnameW
);
400 WARN("Got a failure during card enumeration: %d (%s)\n",
401 err
, snd_strerror(err
));
406 HRESULT WINAPI
AUDDRV_GetEndpointIDs(EDataFlow flow
, WCHAR
***ids
, char ***keys
,
407 UINT
*num
, UINT
*def_index
)
411 TRACE("%d %p %p %p %p\n", flow
, ids
, keys
, num
, def_index
);
413 hr
= alsa_enum_devices(flow
, NULL
, NULL
, num
);
424 *ids
= HeapAlloc(GetProcessHeap(), 0, *num
* sizeof(WCHAR
*));
425 *keys
= HeapAlloc(GetProcessHeap(), 0, *num
* sizeof(char *));
427 HeapFree(GetProcessHeap(), 0, *ids
);
428 HeapFree(GetProcessHeap(), 0, *keys
);
429 return E_OUTOFMEMORY
;
434 hr
= alsa_enum_devices(flow
, *ids
, *keys
, num
);
437 for(i
= 0; i
< *num
; ++i
){
438 HeapFree(GetProcessHeap(), 0, (*ids
)[i
]);
439 HeapFree(GetProcessHeap(), 0, (*keys
)[i
]);
441 HeapFree(GetProcessHeap(), 0, *ids
);
442 HeapFree(GetProcessHeap(), 0, *keys
);
443 return E_OUTOFMEMORY
;
449 /* Using the pulse PCM device from alsa-plugins 1.0.24 triggers a bug
450 * which causes audio to cease playing after a few minutes of playback.
451 * Setting handle_underrun=1 on pulse-backed ALSA devices seems to work
452 * around this issue. */
453 static snd_config_t
*make_handle_underrun_config(const char *name
)
455 snd_config_t
*lconf
, *dev_node
, *hu_node
, *type_node
;
456 char dev_node_name
[64];
457 const char *type_str
;
462 if((err
= snd_config_copy(&lconf
, snd_config
)) < 0){
463 WARN("snd_config_copy failed: %d (%s)\n", err
, snd_strerror(err
));
467 sprintf(dev_node_name
, "pcm.%s", name
);
468 err
= snd_config_search(lconf
, dev_node_name
, &dev_node
);
470 snd_config_delete(lconf
);
474 snd_config_delete(lconf
);
475 WARN("snd_config_search failed: %d (%s)\n", err
, snd_strerror(err
));
479 /* ALSA is extremely fragile. If it runs into a config setting it doesn't
480 * recognize, it tends to fail or assert. So we only want to inject
481 * handle_underrun=1 on devices that we know will recognize it. */
482 err
= snd_config_search(dev_node
, "type", &type_node
);
484 snd_config_delete(lconf
);
488 snd_config_delete(lconf
);
489 WARN("snd_config_search failed: %d (%s)\n", err
, snd_strerror(err
));
493 if((err
= snd_config_get_string(type_node
, &type_str
)) < 0){
494 snd_config_delete(lconf
);
498 if(strcmp(type_str
, "pulse") != 0){
499 snd_config_delete(lconf
);
503 err
= snd_config_search(dev_node
, "handle_underrun", &hu_node
);
505 /* user already has an explicit handle_underrun setting, so don't
506 * use a local config */
507 snd_config_delete(lconf
);
511 snd_config_delete(lconf
);
512 WARN("snd_config_search failed: %d (%s)\n", err
, snd_strerror(err
));
516 if((err
= snd_config_imake_integer(&hu_node
, "handle_underrun", 1)) < 0){
517 snd_config_delete(lconf
);
518 WARN("snd_config_imake_integer failed: %d (%s)\n", err
,
523 if((err
= snd_config_add(dev_node
, hu_node
)) < 0){
524 snd_config_delete(lconf
);
525 WARN("snd_config_add failed: %d (%s)\n", err
, snd_strerror(err
));
532 HRESULT WINAPI
AUDDRV_GetAudioEndpoint(const char *key
, IMMDevice
*dev
,
533 EDataFlow dataflow
, IAudioClient
**out
)
537 snd_pcm_stream_t stream
;
539 static int handle_underrun
= 1;
541 TRACE("\"%s\" %p %d %p\n", key
, dev
, dataflow
, out
);
543 This
= HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY
, sizeof(ACImpl
));
545 return E_OUTOFMEMORY
;
547 This
->IAudioClient_iface
.lpVtbl
= &AudioClient_Vtbl
;
548 This
->IAudioRenderClient_iface
.lpVtbl
= &AudioRenderClient_Vtbl
;
549 This
->IAudioCaptureClient_iface
.lpVtbl
= &AudioCaptureClient_Vtbl
;
550 This
->IAudioClock_iface
.lpVtbl
= &AudioClock_Vtbl
;
551 This
->IAudioClock2_iface
.lpVtbl
= &AudioClock2_Vtbl
;
552 This
->IAudioStreamVolume_iface
.lpVtbl
= &AudioStreamVolume_Vtbl
;
554 if(dataflow
== eRender
)
555 stream
= SND_PCM_STREAM_PLAYBACK
;
556 else if(dataflow
== eCapture
)
557 stream
= SND_PCM_STREAM_CAPTURE
;
559 HeapFree(GetProcessHeap(), 0, This
);
563 This
->dataflow
= dataflow
;
564 if(handle_underrun
&& ((lconf
= make_handle_underrun_config(key
)))){
565 err
= snd_pcm_open_lconf(&This
->pcm_handle
, key
, stream
, SND_PCM_NONBLOCK
, lconf
);
566 TRACE("Opening PCM device \"%s\" with handle_underrun: %d\n", key
, err
);
567 snd_config_delete(lconf
);
568 /* Pulse <= 2010 returns EINVAL, it does not know handle_underrun. */
570 ERR_(winediag
)("PulseAudio \"%s\" %d without handle_underrun. Audio may hang."
571 " Please upgrade to alsa_plugins >= 1.0.24\n", key
, err
);
577 err
= snd_pcm_open(&This
->pcm_handle
, key
, stream
, SND_PCM_NONBLOCK
);
580 HeapFree(GetProcessHeap(), 0, This
);
581 WARN("Unable to open PCM \"%s\": %d (%s)\n", key
, err
, snd_strerror(err
));
585 This
->hw_params
= HeapAlloc(GetProcessHeap(), 0,
586 snd_pcm_hw_params_sizeof());
587 if(!This
->hw_params
){
588 snd_pcm_close(This
->pcm_handle
);
589 HeapFree(GetProcessHeap(), 0, This
);
590 return E_OUTOFMEMORY
;
593 InitializeCriticalSection(&This
->lock
);
594 This
->lock
.DebugInfo
->Spare
[0] = (DWORD_PTR
)(__FILE__
": ACImpl.lock");
597 IMMDevice_AddRef(This
->parent
);
599 *out
= &This
->IAudioClient_iface
;
600 IAudioClient_AddRef(&This
->IAudioClient_iface
);
605 static HRESULT WINAPI
AudioClient_QueryInterface(IAudioClient
*iface
,
606 REFIID riid
, void **ppv
)
608 TRACE("(%p)->(%s, %p)\n", iface
, debugstr_guid(riid
), ppv
);
613 if(IsEqualIID(riid
, &IID_IUnknown
) || IsEqualIID(riid
, &IID_IAudioClient
))
616 IUnknown_AddRef((IUnknown
*)*ppv
);
619 WARN("Unknown interface %s\n", debugstr_guid(riid
));
620 return E_NOINTERFACE
;
623 static ULONG WINAPI
AudioClient_AddRef(IAudioClient
*iface
)
625 ACImpl
*This
= impl_from_IAudioClient(iface
);
627 ref
= InterlockedIncrement(&This
->ref
);
628 TRACE("(%p) Refcount now %u\n", This
, ref
);
632 static ULONG WINAPI
AudioClient_Release(IAudioClient
*iface
)
634 ACImpl
*This
= impl_from_IAudioClient(iface
);
636 ref
= InterlockedDecrement(&This
->ref
);
637 TRACE("(%p) Refcount now %u\n", This
, ref
);
639 IAudioClient_Stop(iface
);
640 IMMDevice_Release(This
->parent
);
641 This
->lock
.DebugInfo
->Spare
[0] = 0;
642 DeleteCriticalSection(&This
->lock
);
643 snd_pcm_drop(This
->pcm_handle
);
644 snd_pcm_close(This
->pcm_handle
);
646 EnterCriticalSection(&g_sessions_lock
);
647 list_remove(&This
->entry
);
648 LeaveCriticalSection(&g_sessions_lock
);
650 HeapFree(GetProcessHeap(), 0, This
->vols
);
651 HeapFree(GetProcessHeap(), 0, This
->local_buffer
);
652 HeapFree(GetProcessHeap(), 0, This
->tmp_buffer
);
653 HeapFree(GetProcessHeap(), 0, This
->hw_params
);
654 CoTaskMemFree(This
->fmt
);
655 HeapFree(GetProcessHeap(), 0, This
);
660 static void dump_fmt(const WAVEFORMATEX
*fmt
)
662 TRACE("wFormatTag: 0x%x (", fmt
->wFormatTag
);
663 switch(fmt
->wFormatTag
){
664 case WAVE_FORMAT_PCM
:
665 TRACE("WAVE_FORMAT_PCM");
667 case WAVE_FORMAT_IEEE_FLOAT
:
668 TRACE("WAVE_FORMAT_IEEE_FLOAT");
670 case WAVE_FORMAT_EXTENSIBLE
:
671 TRACE("WAVE_FORMAT_EXTENSIBLE");
679 TRACE("nChannels: %u\n", fmt
->nChannels
);
680 TRACE("nSamplesPerSec: %u\n", fmt
->nSamplesPerSec
);
681 TRACE("nAvgBytesPerSec: %u\n", fmt
->nAvgBytesPerSec
);
682 TRACE("nBlockAlign: %u\n", fmt
->nBlockAlign
);
683 TRACE("wBitsPerSample: %u\n", fmt
->wBitsPerSample
);
684 TRACE("cbSize: %u\n", fmt
->cbSize
);
686 if(fmt
->wFormatTag
== WAVE_FORMAT_EXTENSIBLE
){
687 WAVEFORMATEXTENSIBLE
*fmtex
= (void*)fmt
;
688 TRACE("dwChannelMask: %08x\n", fmtex
->dwChannelMask
);
689 TRACE("Samples: %04x\n", fmtex
->Samples
.wReserved
);
690 TRACE("SubFormat: %s\n", wine_dbgstr_guid(&fmtex
->SubFormat
));
694 static WAVEFORMATEX
*clone_format(const WAVEFORMATEX
*fmt
)
699 if(fmt
->wFormatTag
== WAVE_FORMAT_EXTENSIBLE
)
700 size
= sizeof(WAVEFORMATEXTENSIBLE
);
702 size
= sizeof(WAVEFORMATEX
);
704 ret
= CoTaskMemAlloc(size
);
708 memcpy(ret
, fmt
, size
);
710 ret
->cbSize
= size
- sizeof(WAVEFORMATEX
);
715 static void session_init_vols(AudioSession
*session
, UINT channels
)
717 if(session
->channel_count
< channels
){
720 if(session
->channel_vols
)
721 session
->channel_vols
= HeapReAlloc(GetProcessHeap(), 0,
722 session
->channel_vols
, sizeof(float) * channels
);
724 session
->channel_vols
= HeapAlloc(GetProcessHeap(), 0,
725 sizeof(float) * channels
);
726 if(!session
->channel_vols
)
729 for(i
= session
->channel_count
; i
< channels
; ++i
)
730 session
->channel_vols
[i
] = 1.f
;
732 session
->channel_count
= channels
;
736 static AudioSession
*create_session(const GUID
*guid
, IMMDevice
*device
,
741 ret
= HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY
, sizeof(AudioSession
));
745 memcpy(&ret
->guid
, guid
, sizeof(GUID
));
747 ret
->device
= device
;
749 list_init(&ret
->clients
);
751 list_add_head(&g_sessions
, &ret
->entry
);
753 InitializeCriticalSection(&ret
->lock
);
754 ret
->lock
.DebugInfo
->Spare
[0] = (DWORD_PTR
)(__FILE__
": AudioSession.lock");
756 session_init_vols(ret
, num_channels
);
758 ret
->master_vol
= 1.f
;
763 /* if channels == 0, then this will return or create a session with
764 * matching dataflow and GUID. otherwise, channels must also match */
765 static HRESULT
get_audio_session(const GUID
*sessionguid
,
766 IMMDevice
*device
, UINT channels
, AudioSession
**out
)
768 AudioSession
*session
;
770 if(!sessionguid
|| IsEqualGUID(sessionguid
, &GUID_NULL
)){
771 *out
= create_session(&GUID_NULL
, device
, channels
);
773 return E_OUTOFMEMORY
;
779 LIST_FOR_EACH_ENTRY(session
, &g_sessions
, AudioSession
, entry
){
780 if(session
->device
== device
&&
781 IsEqualGUID(sessionguid
, &session
->guid
)){
782 session_init_vols(session
, channels
);
789 *out
= create_session(sessionguid
, device
, channels
);
791 return E_OUTOFMEMORY
;
797 static HRESULT WINAPI
AudioClient_Initialize(IAudioClient
*iface
,
798 AUDCLNT_SHAREMODE mode
, DWORD flags
, REFERENCE_TIME duration
,
799 REFERENCE_TIME period
, const WAVEFORMATEX
*fmt
,
800 const GUID
*sessionguid
)
802 ACImpl
*This
= impl_from_IAudioClient(iface
);
803 snd_pcm_sw_params_t
*sw_params
= NULL
;
804 snd_pcm_format_t format
;
805 snd_pcm_uframes_t alsa_period_frames
;
806 const WAVEFORMATEXTENSIBLE
*fmtex
= (const WAVEFORMATEXTENSIBLE
*)fmt
;
807 unsigned int rate
, mmdev_period_frames
, alsa_period_us
;
811 TRACE("(%p)->(%x, %x, %s, %s, %p, %s)\n", This
, mode
, flags
,
812 wine_dbgstr_longlong(duration
), wine_dbgstr_longlong(period
), fmt
, debugstr_guid(sessionguid
));
817 if(mode
!= AUDCLNT_SHAREMODE_SHARED
&& mode
!= AUDCLNT_SHAREMODE_EXCLUSIVE
)
818 return AUDCLNT_E_NOT_INITIALIZED
;
820 if(flags
& ~(AUDCLNT_STREAMFLAGS_CROSSPROCESS
|
821 AUDCLNT_STREAMFLAGS_LOOPBACK
|
822 AUDCLNT_STREAMFLAGS_EVENTCALLBACK
|
823 AUDCLNT_STREAMFLAGS_NOPERSIST
|
824 AUDCLNT_STREAMFLAGS_RATEADJUST
|
825 AUDCLNT_SESSIONFLAGS_EXPIREWHENUNOWNED
|
826 AUDCLNT_SESSIONFLAGS_DISPLAY_HIDE
|
827 AUDCLNT_SESSIONFLAGS_DISPLAY_HIDEWHENEXPIRED
)){
828 TRACE("Unknown flags: %08x\n", flags
);
833 duration
= 300000; /* 0.03s */
835 EnterCriticalSection(&This
->lock
);
838 LeaveCriticalSection(&This
->lock
);
839 return AUDCLNT_E_ALREADY_INITIALIZED
;
844 if((err
= snd_pcm_hw_params_any(This
->pcm_handle
, This
->hw_params
)) < 0){
845 WARN("Unable to get hw_params: %d (%s)\n", err
, snd_strerror(err
));
850 if((err
= snd_pcm_hw_params_set_access(This
->pcm_handle
, This
->hw_params
,
851 SND_PCM_ACCESS_RW_INTERLEAVED
)) < 0){
852 WARN("Unable to set access: %d (%s)\n", err
, snd_strerror(err
));
857 if(fmt
->wFormatTag
== WAVE_FORMAT_PCM
||
858 (fmt
->wFormatTag
== WAVE_FORMAT_EXTENSIBLE
&&
859 IsEqualGUID(&fmtex
->SubFormat
, &KSDATAFORMAT_SUBTYPE_PCM
))){
860 if(fmt
->wBitsPerSample
== 8)
861 format
= SND_PCM_FORMAT_U8
;
862 else if(fmt
->wBitsPerSample
== 16)
863 format
= SND_PCM_FORMAT_S16_LE
;
864 else if(fmt
->wBitsPerSample
== 24)
865 format
= SND_PCM_FORMAT_S24_3LE
;
866 else if(fmt
->wBitsPerSample
== 32)
867 format
= SND_PCM_FORMAT_S32_LE
;
869 WARN("Unsupported bit depth: %u\n", fmt
->wBitsPerSample
);
870 hr
= AUDCLNT_E_UNSUPPORTED_FORMAT
;
873 }else if(fmt
->wFormatTag
== WAVE_FORMAT_IEEE_FLOAT
||
874 (fmt
->wFormatTag
== WAVE_FORMAT_EXTENSIBLE
&&
875 IsEqualGUID(&fmtex
->SubFormat
, &KSDATAFORMAT_SUBTYPE_IEEE_FLOAT
))){
876 if(fmt
->wBitsPerSample
== 32)
877 format
= SND_PCM_FORMAT_FLOAT_LE
;
878 else if(fmt
->wBitsPerSample
== 64)
879 format
= SND_PCM_FORMAT_FLOAT64_LE
;
881 WARN("Unsupported float size: %u\n", fmt
->wBitsPerSample
);
882 hr
= AUDCLNT_E_UNSUPPORTED_FORMAT
;
886 WARN("Unknown wave format: %04x\n", fmt
->wFormatTag
);
887 hr
= AUDCLNT_E_UNSUPPORTED_FORMAT
;
891 if((err
= snd_pcm_hw_params_set_format(This
->pcm_handle
, This
->hw_params
,
893 WARN("Unable to set ALSA format to %u: %d (%s)\n", format
, err
,
899 This
->alsa_format
= format
;
901 rate
= fmt
->nSamplesPerSec
;
902 if((err
= snd_pcm_hw_params_set_rate_near(This
->pcm_handle
, This
->hw_params
,
904 WARN("Unable to set rate to %u: %d (%s)\n", rate
, err
,
910 if((err
= snd_pcm_hw_params_set_channels(This
->pcm_handle
, This
->hw_params
,
911 fmt
->nChannels
)) < 0){
912 WARN("Unable to set channels to %u: %d (%s)\n", fmt
->nChannels
, err
,
918 alsa_period_us
= duration
/ 100; /* duration / 10 converted to us */
919 if((err
= snd_pcm_hw_params_set_period_time_near(This
->pcm_handle
,
920 This
->hw_params
, &alsa_period_us
, NULL
)) < 0){
921 WARN("Unable to set period time near %u: %d (%s)\n", alsa_period_us
,
922 err
, snd_strerror(err
));
927 if((err
= snd_pcm_hw_params(This
->pcm_handle
, This
->hw_params
)) < 0){
928 WARN("Unable to set hw params: %d (%s)\n", err
, snd_strerror(err
));
933 if((err
= snd_pcm_hw_params_current(This
->pcm_handle
, This
->hw_params
)) < 0){
934 WARN("Unable to get current hw params: %d (%s)\n", err
, snd_strerror(err
));
939 if((err
= snd_pcm_hw_params_get_period_size(This
->hw_params
,
940 &alsa_period_frames
, NULL
)) < 0){
941 WARN("Unable to get period size: %d (%s)\n", err
, snd_strerror(err
));
945 TRACE("alsa_period_frames: %lu\n", alsa_period_frames
);
947 if((err
= snd_pcm_hw_params_get_buffer_size(This
->hw_params
,
948 &This
->alsa_bufsize_frames
)) < 0){
949 WARN("Unable to get buffer size: %d (%s)\n", err
, snd_strerror(err
));
954 sw_params
= HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY
, snd_pcm_sw_params_sizeof());
960 if((err
= snd_pcm_sw_params_current(This
->pcm_handle
, sw_params
)) < 0){
961 WARN("Unable to get sw_params: %d (%s)\n", err
, snd_strerror(err
));
966 if((err
= snd_pcm_sw_params_set_start_threshold(This
->pcm_handle
,
968 WARN("Unable set start threshold to 0: %d (%s)\n", err
, snd_strerror(err
));
973 if((err
= snd_pcm_sw_params_set_stop_threshold(This
->pcm_handle
,
974 sw_params
, This
->alsa_bufsize_frames
)) < 0){
975 WARN("Unable set stop threshold to %lu: %d (%s)\n",
976 This
->alsa_bufsize_frames
, err
, snd_strerror(err
));
981 if((err
= snd_pcm_sw_params(This
->pcm_handle
, sw_params
)) < 0){
982 WARN("Unable set sw params: %d (%s)\n", err
, snd_strerror(err
));
987 if((err
= snd_pcm_prepare(This
->pcm_handle
)) < 0){
988 WARN("Unable to prepare device: %d (%s)\n", err
, snd_strerror(err
));
994 This
->mmdev_period_rt
= period
;
996 This
->mmdev_period_rt
= DefaultPeriod
;
998 /* Check if the ALSA buffer is so small that it will run out before
999 * the next MMDevAPI period tick occurs. Allow a little wiggle room
1000 * with 120% of the period time. */
1001 mmdev_period_frames
= fmt
->nSamplesPerSec
* (This
->mmdev_period_rt
/ 10000000.);
1002 if(This
->alsa_bufsize_frames
< 1.2 * mmdev_period_frames
)
1003 FIXME("ALSA buffer time is smaller than our period time. Expect underruns. (%lu < %u)\n",
1004 This
->alsa_bufsize_frames
, mmdev_period_frames
);
1006 This
->bufsize_frames
= ceil((duration
/ 10000000.) * fmt
->nSamplesPerSec
);
1007 This
->local_buffer
= HeapAlloc(GetProcessHeap(), 0,
1008 This
->bufsize_frames
* fmt
->nBlockAlign
);
1009 if(!This
->local_buffer
){
1013 if (fmt
->wBitsPerSample
== 8)
1014 memset(This
->local_buffer
, 128, This
->bufsize_frames
* fmt
->nBlockAlign
);
1016 memset(This
->local_buffer
, 0, This
->bufsize_frames
* fmt
->nBlockAlign
);
1018 This
->fmt
= clone_format(fmt
);
1024 This
->vols
= HeapAlloc(GetProcessHeap(), 0, fmt
->nChannels
* sizeof(float));
1030 for(i
= 0; i
< fmt
->nChannels
; ++i
)
1031 This
->vols
[i
] = 1.f
;
1034 This
->flags
= flags
;
1036 EnterCriticalSection(&g_sessions_lock
);
1038 hr
= get_audio_session(sessionguid
, This
->parent
, fmt
->nChannels
,
1041 LeaveCriticalSection(&g_sessions_lock
);
1045 list_add_tail(&This
->session
->clients
, &This
->entry
);
1047 LeaveCriticalSection(&g_sessions_lock
);
1049 This
->initted
= TRUE
;
1052 HeapFree(GetProcessHeap(), 0, sw_params
);
1054 HeapFree(GetProcessHeap(), 0, This
->local_buffer
);
1055 This
->local_buffer
= NULL
;
1056 CoTaskMemFree(This
->fmt
);
1058 HeapFree(GetProcessHeap(), 0, This
->vols
);
1062 LeaveCriticalSection(&This
->lock
);
1067 static HRESULT WINAPI
AudioClient_GetBufferSize(IAudioClient
*iface
,
1070 ACImpl
*This
= impl_from_IAudioClient(iface
);
1072 TRACE("(%p)->(%p)\n", This
, out
);
1077 EnterCriticalSection(&This
->lock
);
1080 LeaveCriticalSection(&This
->lock
);
1081 return AUDCLNT_E_NOT_INITIALIZED
;
1084 *out
= This
->bufsize_frames
;
1086 LeaveCriticalSection(&This
->lock
);
1091 static HRESULT WINAPI
AudioClient_GetStreamLatency(IAudioClient
*iface
,
1092 REFERENCE_TIME
*latency
)
1094 ACImpl
*This
= impl_from_IAudioClient(iface
);
1096 TRACE("(%p)->(%p)\n", This
, latency
);
1101 EnterCriticalSection(&This
->lock
);
1104 LeaveCriticalSection(&This
->lock
);
1105 return AUDCLNT_E_NOT_INITIALIZED
;
1108 LeaveCriticalSection(&This
->lock
);
1115 static HRESULT WINAPI
AudioClient_GetCurrentPadding(IAudioClient
*iface
,
1118 ACImpl
*This
= impl_from_IAudioClient(iface
);
1120 TRACE("(%p)->(%p)\n", This
, out
);
1125 EnterCriticalSection(&This
->lock
);
1128 LeaveCriticalSection(&This
->lock
);
1129 return AUDCLNT_E_NOT_INITIALIZED
;
1132 if(This
->dataflow
== eRender
){
1133 snd_pcm_sframes_t avail_frames
;
1135 avail_frames
= snd_pcm_avail_update(This
->pcm_handle
);
1136 if(This
->alsa_bufsize_frames
< avail_frames
||
1137 snd_pcm_state(This
->pcm_handle
) == SND_PCM_STATE_XRUN
)
1138 *out
= This
->held_frames
;
1140 *out
= This
->alsa_bufsize_frames
- avail_frames
+ This
->held_frames
;
1141 TRACE("PCM state: %u, avail: %ld, pad: %u\n",
1142 snd_pcm_state(This
->pcm_handle
), avail_frames
, *out
);
1143 }else if(This
->dataflow
== eCapture
){
1144 *out
= This
->held_frames
;
1146 LeaveCriticalSection(&This
->lock
);
1147 return E_UNEXPECTED
;
1150 LeaveCriticalSection(&This
->lock
);
1155 static DWORD
get_channel_mask(unsigned int channels
)
1161 return KSAUDIO_SPEAKER_MONO
;
1163 return KSAUDIO_SPEAKER_STEREO
;
1165 return KSAUDIO_SPEAKER_STEREO
| SPEAKER_LOW_FREQUENCY
;
1167 return KSAUDIO_SPEAKER_QUAD
; /* not _SURROUND */
1169 return KSAUDIO_SPEAKER_QUAD
| SPEAKER_LOW_FREQUENCY
;
1171 return KSAUDIO_SPEAKER_5POINT1
; /* not 5POINT1_SURROUND */
1173 return KSAUDIO_SPEAKER_5POINT1
| SPEAKER_BACK_CENTER
;
1175 return KSAUDIO_SPEAKER_7POINT1
; /* not 7POINT1_SURROUND */
1177 FIXME("Unknown speaker configuration: %u\n", channels
);
1181 static HRESULT WINAPI
AudioClient_IsFormatSupported(IAudioClient
*iface
,
1182 AUDCLNT_SHAREMODE mode
, const WAVEFORMATEX
*fmt
,
1185 ACImpl
*This
= impl_from_IAudioClient(iface
);
1186 snd_pcm_format_mask_t
*formats
= NULL
;
1188 WAVEFORMATEX
*closest
= NULL
;
1189 const WAVEFORMATEXTENSIBLE
*fmtex
= (const WAVEFORMATEXTENSIBLE
*)fmt
;
1190 unsigned int max
= 0, min
= 0;
1193 TRACE("(%p)->(%x, %p, %p)\n", This
, mode
, fmt
, out
);
1195 if(!fmt
|| (mode
== AUDCLNT_SHAREMODE_SHARED
&& !out
))
1198 if(mode
!= AUDCLNT_SHAREMODE_SHARED
&& mode
!= AUDCLNT_SHAREMODE_EXCLUSIVE
)
1199 return E_INVALIDARG
;
1201 if(fmt
->wFormatTag
== WAVE_FORMAT_EXTENSIBLE
&&
1202 fmt
->cbSize
< sizeof(WAVEFORMATEXTENSIBLE
) - sizeof(WAVEFORMATEX
))
1203 return E_INVALIDARG
;
1209 if(mode
!= AUDCLNT_SHAREMODE_SHARED
)
1213 EnterCriticalSection(&This
->lock
);
1215 if((err
= snd_pcm_hw_params_any(This
->pcm_handle
, This
->hw_params
)) < 0){
1220 formats
= HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY
,
1221 snd_pcm_format_mask_sizeof());
1227 snd_pcm_hw_params_get_format_mask(This
->hw_params
, formats
);
1229 if(fmt
->wFormatTag
== WAVE_FORMAT_PCM
||
1230 (fmt
->wFormatTag
== WAVE_FORMAT_EXTENSIBLE
&&
1231 IsEqualGUID(&fmtex
->SubFormat
, &KSDATAFORMAT_SUBTYPE_PCM
))){
1232 switch(fmt
->wBitsPerSample
){
1234 if(!snd_pcm_format_mask_test(formats
, SND_PCM_FORMAT_U8
)){
1235 hr
= AUDCLNT_E_UNSUPPORTED_FORMAT
;
1240 if(!snd_pcm_format_mask_test(formats
, SND_PCM_FORMAT_S16_LE
)){
1241 hr
= AUDCLNT_E_UNSUPPORTED_FORMAT
;
1246 if(!snd_pcm_format_mask_test(formats
, SND_PCM_FORMAT_S24_3LE
)){
1247 hr
= AUDCLNT_E_UNSUPPORTED_FORMAT
;
1252 if(!snd_pcm_format_mask_test(formats
, SND_PCM_FORMAT_S32_LE
)){
1253 hr
= AUDCLNT_E_UNSUPPORTED_FORMAT
;
1258 hr
= AUDCLNT_E_UNSUPPORTED_FORMAT
;
1261 }else if(fmt
->wFormatTag
== WAVE_FORMAT_IEEE_FLOAT
||
1262 (fmt
->wFormatTag
== WAVE_FORMAT_EXTENSIBLE
&&
1263 IsEqualGUID(&fmtex
->SubFormat
, &KSDATAFORMAT_SUBTYPE_IEEE_FLOAT
))){
1264 switch(fmt
->wBitsPerSample
){
1266 if(!snd_pcm_format_mask_test(formats
, SND_PCM_FORMAT_FLOAT_LE
)){
1267 hr
= AUDCLNT_E_UNSUPPORTED_FORMAT
;
1272 if(!snd_pcm_format_mask_test(formats
, SND_PCM_FORMAT_FLOAT64_LE
)){
1273 hr
= AUDCLNT_E_UNSUPPORTED_FORMAT
;
1278 hr
= AUDCLNT_E_UNSUPPORTED_FORMAT
;
1282 hr
= AUDCLNT_E_UNSUPPORTED_FORMAT
;
1286 closest
= clone_format(fmt
);
1292 if((err
= snd_pcm_hw_params_get_rate_min(This
->hw_params
, &min
, NULL
)) < 0){
1294 WARN("Unable to get min rate: %d (%s)\n", err
, snd_strerror(err
));
1298 if((err
= snd_pcm_hw_params_get_rate_max(This
->hw_params
, &max
, NULL
)) < 0){
1300 WARN("Unable to get max rate: %d (%s)\n", err
, snd_strerror(err
));
1304 if(fmt
->nSamplesPerSec
< min
|| fmt
->nSamplesPerSec
> max
){
1305 hr
= AUDCLNT_E_UNSUPPORTED_FORMAT
;
1309 if((err
= snd_pcm_hw_params_get_channels_min(This
->hw_params
, &min
)) < 0){
1311 WARN("Unable to get min channels: %d (%s)\n", err
, snd_strerror(err
));
1315 if((err
= snd_pcm_hw_params_get_channels_max(This
->hw_params
, &max
)) < 0){
1317 WARN("Unable to get max channels: %d (%s)\n", err
, snd_strerror(err
));
1322 if(fmt
->nChannels
> max
){
1324 closest
->nChannels
= max
;
1325 }else if(fmt
->nChannels
< min
){
1327 closest
->nChannels
= min
;
1330 if(closest
->wFormatTag
== WAVE_FORMAT_EXTENSIBLE
){
1331 DWORD mask
= get_channel_mask(closest
->nChannels
);
1333 ((WAVEFORMATEXTENSIBLE
*)closest
)->dwChannelMask
= mask
;
1335 if(fmt
->wFormatTag
== WAVE_FORMAT_EXTENSIBLE
&&
1336 fmtex
->dwChannelMask
!= 0 &&
1337 fmtex
->dwChannelMask
!= mask
)
1342 LeaveCriticalSection(&This
->lock
);
1343 HeapFree(GetProcessHeap(), 0, formats
);
1345 if(hr
== S_FALSE
&& !out
)
1346 hr
= AUDCLNT_E_UNSUPPORTED_FORMAT
;
1348 if(hr
== S_FALSE
&& out
) {
1349 closest
->nBlockAlign
=
1350 closest
->nChannels
* closest
->wBitsPerSample
/ 8;
1351 closest
->nAvgBytesPerSec
=
1352 closest
->nBlockAlign
* closest
->nSamplesPerSec
;
1355 CoTaskMemFree(closest
);
1357 TRACE("returning: %08x\n", hr
);
1361 static HRESULT WINAPI
AudioClient_GetMixFormat(IAudioClient
*iface
,
1362 WAVEFORMATEX
**pwfx
)
1364 ACImpl
*This
= impl_from_IAudioClient(iface
);
1365 WAVEFORMATEXTENSIBLE
*fmt
;
1366 snd_pcm_format_mask_t
*formats
;
1367 unsigned int max_rate
, max_channels
;
1371 TRACE("(%p)->(%p)\n", This
, pwfx
);
1377 fmt
= CoTaskMemAlloc(sizeof(WAVEFORMATEXTENSIBLE
));
1379 return E_OUTOFMEMORY
;
1381 formats
= HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY
, snd_pcm_format_mask_sizeof());
1384 return E_OUTOFMEMORY
;
1387 EnterCriticalSection(&This
->lock
);
1389 if((err
= snd_pcm_hw_params_any(This
->pcm_handle
, This
->hw_params
)) < 0){
1390 WARN("Unable to get hw_params: %d (%s)\n", err
, snd_strerror(err
));
1395 snd_pcm_hw_params_get_format_mask(This
->hw_params
, formats
);
1397 fmt
->Format
.wFormatTag
= WAVE_FORMAT_EXTENSIBLE
;
1398 if(snd_pcm_format_mask_test(formats
, SND_PCM_FORMAT_FLOAT_LE
)){
1399 fmt
->Format
.wBitsPerSample
= 32;
1400 fmt
->SubFormat
= KSDATAFORMAT_SUBTYPE_IEEE_FLOAT
;
1401 }else if(snd_pcm_format_mask_test(formats
, SND_PCM_FORMAT_S16_LE
)){
1402 fmt
->Format
.wBitsPerSample
= 16;
1403 fmt
->SubFormat
= KSDATAFORMAT_SUBTYPE_PCM
;
1404 }else if(snd_pcm_format_mask_test(formats
, SND_PCM_FORMAT_U8
)){
1405 fmt
->Format
.wBitsPerSample
= 8;
1406 fmt
->SubFormat
= KSDATAFORMAT_SUBTYPE_PCM
;
1407 }else if(snd_pcm_format_mask_test(formats
, SND_PCM_FORMAT_S32_LE
)){
1408 fmt
->Format
.wBitsPerSample
= 32;
1409 fmt
->SubFormat
= KSDATAFORMAT_SUBTYPE_PCM
;
1410 }else if(snd_pcm_format_mask_test(formats
, SND_PCM_FORMAT_S24_3LE
)){
1411 fmt
->Format
.wBitsPerSample
= 24;
1412 fmt
->SubFormat
= KSDATAFORMAT_SUBTYPE_PCM
;
1414 ERR("Didn't recognize any available ALSA formats\n");
1419 if((err
= snd_pcm_hw_params_get_channels_max(This
->hw_params
,
1420 &max_channels
)) < 0){
1421 WARN("Unable to get max channels: %d (%s)\n", err
, snd_strerror(err
));
1426 if(max_channels
> 2){
1427 FIXME("Don't know what to do with %u channels, pretending there's "
1428 "only 2 channels\n", max_channels
);
1429 fmt
->Format
.nChannels
= 2;
1431 fmt
->Format
.nChannels
= max_channels
;
1433 fmt
->dwChannelMask
= get_channel_mask(fmt
->Format
.nChannels
);
1435 if((err
= snd_pcm_hw_params_get_rate_max(This
->hw_params
, &max_rate
,
1437 WARN("Unable to get max rate: %d (%s)\n", err
, snd_strerror(err
));
1442 if(max_rate
>= 48000)
1443 fmt
->Format
.nSamplesPerSec
= 48000;
1444 else if(max_rate
>= 44100)
1445 fmt
->Format
.nSamplesPerSec
= 44100;
1446 else if(max_rate
>= 22050)
1447 fmt
->Format
.nSamplesPerSec
= 22050;
1448 else if(max_rate
>= 11025)
1449 fmt
->Format
.nSamplesPerSec
= 11025;
1450 else if(max_rate
>= 8000)
1451 fmt
->Format
.nSamplesPerSec
= 8000;
1453 ERR("Unknown max rate: %u\n", max_rate
);
1458 fmt
->Format
.nBlockAlign
= (fmt
->Format
.wBitsPerSample
*
1459 fmt
->Format
.nChannels
) / 8;
1460 fmt
->Format
.nAvgBytesPerSec
= fmt
->Format
.nSamplesPerSec
*
1461 fmt
->Format
.nBlockAlign
;
1463 fmt
->Samples
.wValidBitsPerSample
= fmt
->Format
.wBitsPerSample
;
1464 fmt
->Format
.cbSize
= sizeof(WAVEFORMATEXTENSIBLE
) - sizeof(WAVEFORMATEX
);
1466 dump_fmt((WAVEFORMATEX
*)fmt
);
1467 *pwfx
= (WAVEFORMATEX
*)fmt
;
1470 LeaveCriticalSection(&This
->lock
);
1473 HeapFree(GetProcessHeap(), 0, formats
);
1478 static HRESULT WINAPI
AudioClient_GetDevicePeriod(IAudioClient
*iface
,
1479 REFERENCE_TIME
*defperiod
, REFERENCE_TIME
*minperiod
)
1481 ACImpl
*This
= impl_from_IAudioClient(iface
);
1483 TRACE("(%p)->(%p, %p)\n", This
, defperiod
, minperiod
);
1485 if(!defperiod
&& !minperiod
)
1489 *defperiod
= DefaultPeriod
;
1491 *minperiod
= MinimumPeriod
;
1496 static snd_pcm_sframes_t
alsa_write_best_effort(snd_pcm_t
*handle
, BYTE
*buf
,
1497 snd_pcm_uframes_t frames
, ACImpl
*This
)
1499 snd_pcm_sframes_t written
;
1501 if(This
->session
->mute
){
1503 if((err
= snd_pcm_format_set_silence(This
->alsa_format
, buf
,
1504 frames
* This
->fmt
->nChannels
)) < 0)
1505 WARN("Setting buffer to silence failed: %d (%s)\n", err
,
1509 written
= snd_pcm_writei(handle
, buf
, frames
);
1513 if(written
== -EAGAIN
)
1517 WARN("writei failed, recovering: %ld (%s)\n", written
,
1518 snd_strerror(written
));
1520 ret
= snd_pcm_recover(handle
, written
, 0);
1522 WARN("Could not recover: %d (%s)\n", ret
, snd_strerror(ret
));
1526 written
= snd_pcm_writei(handle
, buf
, frames
);
1532 static void alsa_write_data(ACImpl
*This
)
1534 snd_pcm_sframes_t written
;
1535 snd_pcm_uframes_t to_write
, avail
;
1538 This
->local_buffer
+ (This
->lcl_offs_frames
* This
->fmt
->nBlockAlign
);
1540 /* this call seems to be required to get an accurate snd_pcm_state() */
1541 avail
= snd_pcm_avail_update(This
->pcm_handle
);
1543 if(snd_pcm_state(This
->pcm_handle
) == SND_PCM_STATE_XRUN
||
1544 avail
> This
->alsa_bufsize_frames
){
1545 TRACE("XRun state, recovering\n");
1547 if((err
= snd_pcm_recover(This
->pcm_handle
, -EPIPE
, 1)) < 0)
1548 WARN("snd_pcm_recover failed: %d (%s)\n", err
, snd_strerror(err
));
1550 if((err
= snd_pcm_reset(This
->pcm_handle
)) < 0)
1551 WARN("snd_pcm_reset failed: %d (%s)\n", err
, snd_strerror(err
));
1553 if((err
= snd_pcm_prepare(This
->pcm_handle
)) < 0)
1554 WARN("snd_pcm_prepare failed: %d (%s)\n", err
, snd_strerror(err
));
1557 if(This
->held_frames
== 0)
1560 if(This
->lcl_offs_frames
+ This
->held_frames
> This
->bufsize_frames
)
1561 to_write
= This
->bufsize_frames
- This
->lcl_offs_frames
;
1563 to_write
= This
->held_frames
;
1565 written
= alsa_write_best_effort(This
->pcm_handle
, buf
, to_write
, This
);
1567 WARN("Couldn't write: %ld (%s)\n", written
, snd_strerror(written
));
1571 This
->lcl_offs_frames
+= written
;
1572 This
->lcl_offs_frames
%= This
->bufsize_frames
;
1573 This
->held_frames
-= written
;
1575 if(written
< to_write
){
1576 /* ALSA buffer probably full */
1580 if(This
->held_frames
){
1581 /* wrapped and have some data back at the start to write */
1582 written
= alsa_write_best_effort(This
->pcm_handle
, This
->local_buffer
,
1583 This
->held_frames
, This
);
1585 WARN("Couldn't write: %ld (%s)\n", written
, snd_strerror(written
));
1589 This
->lcl_offs_frames
+= written
;
1590 This
->lcl_offs_frames
%= This
->bufsize_frames
;
1591 This
->held_frames
-= written
;
1595 static void alsa_read_data(ACImpl
*This
)
1597 snd_pcm_sframes_t pos
, readable
, nread
;
1599 pos
= (This
->held_frames
+ This
->lcl_offs_frames
) % This
->bufsize_frames
;
1600 readable
= This
->bufsize_frames
- pos
;
1602 nread
= snd_pcm_readi(This
->pcm_handle
,
1603 This
->local_buffer
+ pos
* This
->fmt
->nBlockAlign
, readable
);
1607 WARN("read failed, recovering: %ld (%s)\n", nread
, snd_strerror(nread
));
1609 ret
= snd_pcm_recover(This
->pcm_handle
, nread
, 0);
1611 WARN("Recover failed: %d (%s)\n", ret
, snd_strerror(ret
));
1615 nread
= snd_pcm_readi(This
->pcm_handle
,
1616 This
->local_buffer
+ pos
* This
->fmt
->nBlockAlign
, readable
);
1618 WARN("read failed: %ld (%s)\n", nread
, snd_strerror(nread
));
1623 if(This
->session
->mute
){
1625 if((err
= snd_pcm_format_set_silence(This
->alsa_format
,
1626 This
->local_buffer
+ pos
* This
->fmt
->nBlockAlign
,
1628 WARN("Setting buffer to silence failed: %d (%s)\n", err
,
1632 This
->held_frames
+= nread
;
1634 if(This
->held_frames
> This
->bufsize_frames
){
1635 WARN("Overflow of unread data\n");
1636 This
->lcl_offs_frames
+= This
->held_frames
;
1637 This
->lcl_offs_frames
%= This
->bufsize_frames
;
1638 This
->held_frames
= This
->bufsize_frames
;
1642 static void CALLBACK
alsa_push_buffer_data(void *user
, BOOLEAN timer
)
1644 ACImpl
*This
= user
;
1646 EnterCriticalSection(&This
->lock
);
1649 if(This
->dataflow
== eRender
)
1650 alsa_write_data(This
);
1651 else if(This
->dataflow
== eCapture
)
1652 alsa_read_data(This
);
1655 SetEvent(This
->event
);
1658 LeaveCriticalSection(&This
->lock
);
1661 static HRESULT WINAPI
AudioClient_Start(IAudioClient
*iface
)
1663 ACImpl
*This
= impl_from_IAudioClient(iface
);
1665 TRACE("(%p)\n", This
);
1667 EnterCriticalSection(&This
->lock
);
1670 LeaveCriticalSection(&This
->lock
);
1671 return AUDCLNT_E_NOT_INITIALIZED
;
1674 if((This
->flags
& AUDCLNT_STREAMFLAGS_EVENTCALLBACK
) && !This
->event
){
1675 LeaveCriticalSection(&This
->lock
);
1676 return AUDCLNT_E_EVENTHANDLE_NOT_SET
;
1680 LeaveCriticalSection(&This
->lock
);
1681 return AUDCLNT_E_NOT_STOPPED
;
1684 if(This
->dataflow
== eCapture
){
1685 /* dump any data that might be leftover in the ALSA capture buffer */
1686 snd_pcm_readi(This
->pcm_handle
, This
->local_buffer
,
1687 This
->bufsize_frames
);
1690 if(!CreateTimerQueueTimer(&This
->timer
, g_timer_q
, alsa_push_buffer_data
,
1691 This
, 0, This
->mmdev_period_rt
/ 10000, WT_EXECUTEINTIMERTHREAD
)){
1692 LeaveCriticalSection(&This
->lock
);
1693 WARN("Unable to create timer: %u\n", GetLastError());
1697 This
->started
= TRUE
;
1699 LeaveCriticalSection(&This
->lock
);
1704 static HRESULT WINAPI
AudioClient_Stop(IAudioClient
*iface
)
1706 ACImpl
*This
= impl_from_IAudioClient(iface
);
1710 TRACE("(%p)\n", This
);
1712 EnterCriticalSection(&This
->lock
);
1715 LeaveCriticalSection(&This
->lock
);
1716 return AUDCLNT_E_NOT_INITIALIZED
;
1720 LeaveCriticalSection(&This
->lock
);
1724 if(snd_pcm_drop(This
->pcm_handle
) < 0)
1725 WARN("snd_pcm_drop failed\n");
1727 if(snd_pcm_reset(This
->pcm_handle
) < 0)
1728 WARN("snd_pcm_reset failed\n");
1730 if(snd_pcm_prepare(This
->pcm_handle
) < 0)
1731 WARN("snd_pcm_prepare failed\n");
1733 event
= CreateEventW(NULL
, TRUE
, FALSE
, NULL
);
1734 wait
= !DeleteTimerQueueTimer(g_timer_q
, This
->timer
, event
);
1736 WARN("DeleteTimerQueueTimer error %u\n", GetLastError());
1737 wait
= wait
&& GetLastError() == ERROR_IO_PENDING
;
1739 This
->started
= FALSE
;
1741 LeaveCriticalSection(&This
->lock
);
1744 WaitForSingleObject(event
, INFINITE
);
1750 static HRESULT WINAPI
AudioClient_Reset(IAudioClient
*iface
)
1752 ACImpl
*This
= impl_from_IAudioClient(iface
);
1754 TRACE("(%p)\n", This
);
1756 EnterCriticalSection(&This
->lock
);
1759 LeaveCriticalSection(&This
->lock
);
1760 return AUDCLNT_E_NOT_INITIALIZED
;
1764 LeaveCriticalSection(&This
->lock
);
1765 return AUDCLNT_E_NOT_STOPPED
;
1768 if(This
->buf_state
!= NOT_LOCKED
){
1769 LeaveCriticalSection(&This
->lock
);
1770 return AUDCLNT_E_BUFFER_OPERATION_PENDING
;
1773 if(snd_pcm_drop(This
->pcm_handle
) < 0)
1774 WARN("snd_pcm_drop failed\n");
1776 if(snd_pcm_reset(This
->pcm_handle
) < 0)
1777 WARN("snd_pcm_reset failed\n");
1779 if(snd_pcm_prepare(This
->pcm_handle
) < 0)
1780 WARN("snd_pcm_prepare failed\n");
1782 This
->held_frames
= 0;
1783 This
->written_frames
= 0;
1784 This
->lcl_offs_frames
= 0;
1786 LeaveCriticalSection(&This
->lock
);
1791 static HRESULT WINAPI
AudioClient_SetEventHandle(IAudioClient
*iface
,
1794 ACImpl
*This
= impl_from_IAudioClient(iface
);
1796 TRACE("(%p)->(%p)\n", This
, event
);
1799 return E_INVALIDARG
;
1801 EnterCriticalSection(&This
->lock
);
1804 LeaveCriticalSection(&This
->lock
);
1805 return AUDCLNT_E_NOT_INITIALIZED
;
1808 if(!(This
->flags
& AUDCLNT_STREAMFLAGS_EVENTCALLBACK
)){
1809 LeaveCriticalSection(&This
->lock
);
1810 return AUDCLNT_E_EVENTHANDLE_NOT_EXPECTED
;
1813 This
->event
= event
;
1815 LeaveCriticalSection(&This
->lock
);
1820 static HRESULT WINAPI
AudioClient_GetService(IAudioClient
*iface
, REFIID riid
,
1823 ACImpl
*This
= impl_from_IAudioClient(iface
);
1825 TRACE("(%p)->(%s, %p)\n", This
, debugstr_guid(riid
), ppv
);
1831 EnterCriticalSection(&This
->lock
);
1834 LeaveCriticalSection(&This
->lock
);
1835 return AUDCLNT_E_NOT_INITIALIZED
;
1838 if(IsEqualIID(riid
, &IID_IAudioRenderClient
)){
1839 if(This
->dataflow
!= eRender
){
1840 LeaveCriticalSection(&This
->lock
);
1841 return AUDCLNT_E_WRONG_ENDPOINT_TYPE
;
1843 IAudioRenderClient_AddRef(&This
->IAudioRenderClient_iface
);
1844 *ppv
= &This
->IAudioRenderClient_iface
;
1845 }else if(IsEqualIID(riid
, &IID_IAudioCaptureClient
)){
1846 if(This
->dataflow
!= eCapture
){
1847 LeaveCriticalSection(&This
->lock
);
1848 return AUDCLNT_E_WRONG_ENDPOINT_TYPE
;
1850 IAudioCaptureClient_AddRef(&This
->IAudioCaptureClient_iface
);
1851 *ppv
= &This
->IAudioCaptureClient_iface
;
1852 }else if(IsEqualIID(riid
, &IID_IAudioClock
)){
1853 IAudioClock_AddRef(&This
->IAudioClock_iface
);
1854 *ppv
= &This
->IAudioClock_iface
;
1855 }else if(IsEqualIID(riid
, &IID_IAudioStreamVolume
)){
1856 IAudioStreamVolume_AddRef(&This
->IAudioStreamVolume_iface
);
1857 *ppv
= &This
->IAudioStreamVolume_iface
;
1858 }else if(IsEqualIID(riid
, &IID_IAudioSessionControl
)){
1859 if(!This
->session_wrapper
){
1860 This
->session_wrapper
= AudioSessionWrapper_Create(This
);
1861 if(!This
->session_wrapper
){
1862 LeaveCriticalSection(&This
->lock
);
1863 return E_OUTOFMEMORY
;
1866 IAudioSessionControl2_AddRef(&This
->session_wrapper
->IAudioSessionControl2_iface
);
1868 *ppv
= &This
->session_wrapper
->IAudioSessionControl2_iface
;
1869 }else if(IsEqualIID(riid
, &IID_IChannelAudioVolume
)){
1870 if(!This
->session_wrapper
){
1871 This
->session_wrapper
= AudioSessionWrapper_Create(This
);
1872 if(!This
->session_wrapper
){
1873 LeaveCriticalSection(&This
->lock
);
1874 return E_OUTOFMEMORY
;
1877 IChannelAudioVolume_AddRef(&This
->session_wrapper
->IChannelAudioVolume_iface
);
1879 *ppv
= &This
->session_wrapper
->IChannelAudioVolume_iface
;
1880 }else if(IsEqualIID(riid
, &IID_ISimpleAudioVolume
)){
1881 if(!This
->session_wrapper
){
1882 This
->session_wrapper
= AudioSessionWrapper_Create(This
);
1883 if(!This
->session_wrapper
){
1884 LeaveCriticalSection(&This
->lock
);
1885 return E_OUTOFMEMORY
;
1888 ISimpleAudioVolume_AddRef(&This
->session_wrapper
->ISimpleAudioVolume_iface
);
1890 *ppv
= &This
->session_wrapper
->ISimpleAudioVolume_iface
;
1894 LeaveCriticalSection(&This
->lock
);
1898 LeaveCriticalSection(&This
->lock
);
1900 FIXME("stub %s\n", debugstr_guid(riid
));
1901 return E_NOINTERFACE
;
1904 static const IAudioClientVtbl AudioClient_Vtbl
=
1906 AudioClient_QueryInterface
,
1908 AudioClient_Release
,
1909 AudioClient_Initialize
,
1910 AudioClient_GetBufferSize
,
1911 AudioClient_GetStreamLatency
,
1912 AudioClient_GetCurrentPadding
,
1913 AudioClient_IsFormatSupported
,
1914 AudioClient_GetMixFormat
,
1915 AudioClient_GetDevicePeriod
,
1919 AudioClient_SetEventHandle
,
1920 AudioClient_GetService
1923 static HRESULT WINAPI
AudioRenderClient_QueryInterface(
1924 IAudioRenderClient
*iface
, REFIID riid
, void **ppv
)
1926 TRACE("(%p)->(%s, %p)\n", iface
, debugstr_guid(riid
), ppv
);
1932 if(IsEqualIID(riid
, &IID_IUnknown
) ||
1933 IsEqualIID(riid
, &IID_IAudioRenderClient
))
1936 IUnknown_AddRef((IUnknown
*)*ppv
);
1940 WARN("Unknown interface %s\n", debugstr_guid(riid
));
1941 return E_NOINTERFACE
;
1944 static ULONG WINAPI
AudioRenderClient_AddRef(IAudioRenderClient
*iface
)
1946 ACImpl
*This
= impl_from_IAudioRenderClient(iface
);
1947 return AudioClient_AddRef(&This
->IAudioClient_iface
);
1950 static ULONG WINAPI
AudioRenderClient_Release(IAudioRenderClient
*iface
)
1952 ACImpl
*This
= impl_from_IAudioRenderClient(iface
);
1953 return AudioClient_Release(&This
->IAudioClient_iface
);
1956 static HRESULT WINAPI
AudioRenderClient_GetBuffer(IAudioRenderClient
*iface
,
1957 UINT32 frames
, BYTE
**data
)
1959 ACImpl
*This
= impl_from_IAudioRenderClient(iface
);
1964 TRACE("(%p)->(%u, %p)\n", This
, frames
, data
);
1969 EnterCriticalSection(&This
->lock
);
1971 if(This
->buf_state
!= NOT_LOCKED
){
1972 LeaveCriticalSection(&This
->lock
);
1973 return AUDCLNT_E_OUT_OF_ORDER
;
1977 This
->buf_state
= LOCKED_NORMAL
;
1978 LeaveCriticalSection(&This
->lock
);
1982 hr
= IAudioClient_GetCurrentPadding(&This
->IAudioClient_iface
, &pad
);
1984 LeaveCriticalSection(&This
->lock
);
1988 if(pad
+ frames
> This
->bufsize_frames
){
1989 LeaveCriticalSection(&This
->lock
);
1990 return AUDCLNT_E_BUFFER_TOO_LARGE
;
1994 (This
->lcl_offs_frames
+ This
->held_frames
) % This
->bufsize_frames
;
1995 if(write_pos
+ frames
> This
->bufsize_frames
){
1996 if(This
->tmp_buffer_frames
< frames
){
1997 if(This
->tmp_buffer
)
1998 This
->tmp_buffer
= HeapReAlloc(GetProcessHeap(), 0,
1999 This
->tmp_buffer
, frames
* This
->fmt
->nBlockAlign
);
2001 This
->tmp_buffer
= HeapAlloc(GetProcessHeap(), 0,
2002 frames
* This
->fmt
->nBlockAlign
);
2003 if(!This
->tmp_buffer
){
2004 LeaveCriticalSection(&This
->lock
);
2005 return E_OUTOFMEMORY
;
2007 This
->tmp_buffer_frames
= frames
;
2009 *data
= This
->tmp_buffer
;
2010 This
->buf_state
= LOCKED_WRAPPED
;
2012 *data
= This
->local_buffer
+ write_pos
* This
->fmt
->nBlockAlign
;
2013 This
->buf_state
= LOCKED_NORMAL
;
2016 LeaveCriticalSection(&This
->lock
);
2021 static void alsa_wrap_buffer(ACImpl
*This
, BYTE
*buffer
, UINT32 written_frames
)
2023 snd_pcm_uframes_t write_offs_frames
=
2024 (This
->lcl_offs_frames
+ This
->held_frames
) % This
->bufsize_frames
;
2025 UINT32 write_offs_bytes
= write_offs_frames
* This
->fmt
->nBlockAlign
;
2026 snd_pcm_uframes_t chunk_frames
= This
->bufsize_frames
- write_offs_frames
;
2027 UINT32 chunk_bytes
= chunk_frames
* This
->fmt
->nBlockAlign
;
2028 UINT32 written_bytes
= written_frames
* This
->fmt
->nBlockAlign
;
2030 if(written_bytes
<= chunk_bytes
){
2031 memcpy(This
->local_buffer
+ write_offs_bytes
, buffer
, written_bytes
);
2033 memcpy(This
->local_buffer
+ write_offs_bytes
, buffer
, chunk_bytes
);
2034 memcpy(This
->local_buffer
, buffer
+ chunk_bytes
,
2035 written_bytes
- chunk_bytes
);
2039 static HRESULT WINAPI
AudioRenderClient_ReleaseBuffer(
2040 IAudioRenderClient
*iface
, UINT32 written_frames
, DWORD flags
)
2042 ACImpl
*This
= impl_from_IAudioRenderClient(iface
);
2045 TRACE("(%p)->(%u, %x)\n", This
, written_frames
, flags
);
2047 EnterCriticalSection(&This
->lock
);
2049 if(This
->buf_state
== NOT_LOCKED
|| !written_frames
){
2050 This
->buf_state
= NOT_LOCKED
;
2051 LeaveCriticalSection(&This
->lock
);
2052 return written_frames
? AUDCLNT_E_OUT_OF_ORDER
: S_OK
;
2055 if(This
->buf_state
== LOCKED_NORMAL
)
2056 buffer
= This
->local_buffer
+ This
->fmt
->nBlockAlign
*
2057 ((This
->lcl_offs_frames
+ This
->held_frames
) % This
->bufsize_frames
);
2059 buffer
= This
->tmp_buffer
;
2061 if(flags
& AUDCLNT_BUFFERFLAGS_SILENT
){
2062 if(This
->fmt
->wBitsPerSample
== 8)
2063 memset(buffer
, 128, written_frames
* This
->fmt
->nBlockAlign
);
2065 memset(buffer
, 0, written_frames
* This
->fmt
->nBlockAlign
);
2068 if(This
->buf_state
== LOCKED_WRAPPED
)
2069 alsa_wrap_buffer(This
, buffer
, written_frames
);
2071 This
->held_frames
+= written_frames
;
2072 This
->written_frames
+= written_frames
;
2073 This
->buf_state
= NOT_LOCKED
;
2075 LeaveCriticalSection(&This
->lock
);
2080 static const IAudioRenderClientVtbl AudioRenderClient_Vtbl
= {
2081 AudioRenderClient_QueryInterface
,
2082 AudioRenderClient_AddRef
,
2083 AudioRenderClient_Release
,
2084 AudioRenderClient_GetBuffer
,
2085 AudioRenderClient_ReleaseBuffer
2088 static HRESULT WINAPI
AudioCaptureClient_QueryInterface(
2089 IAudioCaptureClient
*iface
, REFIID riid
, void **ppv
)
2091 TRACE("(%p)->(%s, %p)\n", iface
, debugstr_guid(riid
), ppv
);
2097 if(IsEqualIID(riid
, &IID_IUnknown
) ||
2098 IsEqualIID(riid
, &IID_IAudioCaptureClient
))
2101 IUnknown_AddRef((IUnknown
*)*ppv
);
2105 WARN("Unknown interface %s\n", debugstr_guid(riid
));
2106 return E_NOINTERFACE
;
2109 static ULONG WINAPI
AudioCaptureClient_AddRef(IAudioCaptureClient
*iface
)
2111 ACImpl
*This
= impl_from_IAudioCaptureClient(iface
);
2112 return IAudioClient_AddRef(&This
->IAudioClient_iface
);
2115 static ULONG WINAPI
AudioCaptureClient_Release(IAudioCaptureClient
*iface
)
2117 ACImpl
*This
= impl_from_IAudioCaptureClient(iface
);
2118 return IAudioClient_Release(&This
->IAudioClient_iface
);
2121 static HRESULT WINAPI
AudioCaptureClient_GetBuffer(IAudioCaptureClient
*iface
,
2122 BYTE
**data
, UINT32
*frames
, DWORD
*flags
, UINT64
*devpos
,
2125 ACImpl
*This
= impl_from_IAudioCaptureClient(iface
);
2128 TRACE("(%p)->(%p, %p, %p, %p, %p)\n", This
, data
, frames
, flags
,
2131 if(!data
|| !frames
|| !flags
)
2134 EnterCriticalSection(&This
->lock
);
2136 if(This
->buf_state
!= NOT_LOCKED
){
2137 LeaveCriticalSection(&This
->lock
);
2138 return AUDCLNT_E_OUT_OF_ORDER
;
2141 hr
= IAudioCaptureClient_GetNextPacketSize(iface
, frames
);
2143 LeaveCriticalSection(&This
->lock
);
2149 if(This
->lcl_offs_frames
+ *frames
> This
->bufsize_frames
){
2150 UINT32 chunk_bytes
, offs_bytes
, frames_bytes
;
2151 if(This
->tmp_buffer_frames
< *frames
){
2152 if(This
->tmp_buffer
)
2153 This
->tmp_buffer
= HeapReAlloc(GetProcessHeap(), 0,
2154 This
->tmp_buffer
, *frames
* This
->fmt
->nBlockAlign
);
2156 This
->tmp_buffer
= HeapAlloc(GetProcessHeap(), 0,
2157 *frames
* This
->fmt
->nBlockAlign
);
2158 if(!This
->tmp_buffer
){
2159 LeaveCriticalSection(&This
->lock
);
2160 return E_OUTOFMEMORY
;
2162 This
->tmp_buffer_frames
= *frames
;
2165 *data
= This
->tmp_buffer
;
2166 chunk_bytes
= (This
->bufsize_frames
- This
->lcl_offs_frames
) *
2167 This
->fmt
->nBlockAlign
;
2168 offs_bytes
= This
->lcl_offs_frames
* This
->fmt
->nBlockAlign
;
2169 frames_bytes
= *frames
* This
->fmt
->nBlockAlign
;
2170 memcpy(This
->tmp_buffer
, This
->local_buffer
+ offs_bytes
, chunk_bytes
);
2171 memcpy(This
->tmp_buffer
+ chunk_bytes
, This
->local_buffer
,
2172 frames_bytes
- chunk_bytes
);
2174 *data
= This
->local_buffer
+
2175 This
->lcl_offs_frames
* This
->fmt
->nBlockAlign
;
2177 This
->buf_state
= LOCKED_NORMAL
;
2179 if(devpos
|| qpcpos
)
2180 IAudioClock_GetPosition(&This
->IAudioClock_iface
, devpos
, qpcpos
);
2182 LeaveCriticalSection(&This
->lock
);
2184 return *frames
? S_OK
: AUDCLNT_S_BUFFER_EMPTY
;
2187 static HRESULT WINAPI
AudioCaptureClient_ReleaseBuffer(
2188 IAudioCaptureClient
*iface
, UINT32 done
)
2190 ACImpl
*This
= impl_from_IAudioCaptureClient(iface
);
2192 TRACE("(%p)->(%u)\n", This
, done
);
2194 EnterCriticalSection(&This
->lock
);
2196 if(This
->buf_state
== NOT_LOCKED
){
2197 LeaveCriticalSection(&This
->lock
);
2198 return AUDCLNT_E_OUT_OF_ORDER
;
2201 This
->held_frames
-= done
;
2202 This
->lcl_offs_frames
+= done
;
2203 This
->lcl_offs_frames
%= This
->bufsize_frames
;
2205 This
->buf_state
= NOT_LOCKED
;
2207 LeaveCriticalSection(&This
->lock
);
2212 static HRESULT WINAPI
AudioCaptureClient_GetNextPacketSize(
2213 IAudioCaptureClient
*iface
, UINT32
*frames
)
2215 ACImpl
*This
= impl_from_IAudioCaptureClient(iface
);
2217 TRACE("(%p)->(%p)\n", This
, frames
);
2219 return AudioClient_GetCurrentPadding(&This
->IAudioClient_iface
, frames
);
2222 static const IAudioCaptureClientVtbl AudioCaptureClient_Vtbl
=
2224 AudioCaptureClient_QueryInterface
,
2225 AudioCaptureClient_AddRef
,
2226 AudioCaptureClient_Release
,
2227 AudioCaptureClient_GetBuffer
,
2228 AudioCaptureClient_ReleaseBuffer
,
2229 AudioCaptureClient_GetNextPacketSize
2232 static HRESULT WINAPI
AudioClock_QueryInterface(IAudioClock
*iface
,
2233 REFIID riid
, void **ppv
)
2235 ACImpl
*This
= impl_from_IAudioClock(iface
);
2237 TRACE("(%p)->(%s, %p)\n", iface
, debugstr_guid(riid
), ppv
);
2243 if(IsEqualIID(riid
, &IID_IUnknown
) || IsEqualIID(riid
, &IID_IAudioClock
))
2245 else if(IsEqualIID(riid
, &IID_IAudioClock2
))
2246 *ppv
= &This
->IAudioClock2_iface
;
2248 IUnknown_AddRef((IUnknown
*)*ppv
);
2252 WARN("Unknown interface %s\n", debugstr_guid(riid
));
2253 return E_NOINTERFACE
;
2256 static ULONG WINAPI
AudioClock_AddRef(IAudioClock
*iface
)
2258 ACImpl
*This
= impl_from_IAudioClock(iface
);
2259 return IAudioClient_AddRef(&This
->IAudioClient_iface
);
2262 static ULONG WINAPI
AudioClock_Release(IAudioClock
*iface
)
2264 ACImpl
*This
= impl_from_IAudioClock(iface
);
2265 return IAudioClient_Release(&This
->IAudioClient_iface
);
2268 static HRESULT WINAPI
AudioClock_GetFrequency(IAudioClock
*iface
, UINT64
*freq
)
2270 ACImpl
*This
= impl_from_IAudioClock(iface
);
2272 TRACE("(%p)->(%p)\n", This
, freq
);
2274 *freq
= This
->fmt
->nSamplesPerSec
;
2279 static HRESULT WINAPI
AudioClock_GetPosition(IAudioClock
*iface
, UINT64
*pos
,
2282 ACImpl
*This
= impl_from_IAudioClock(iface
);
2286 TRACE("(%p)->(%p, %p)\n", This
, pos
, qpctime
);
2291 EnterCriticalSection(&This
->lock
);
2293 hr
= IAudioClient_GetCurrentPadding(&This
->IAudioClient_iface
, &pad
);
2295 LeaveCriticalSection(&This
->lock
);
2299 if(This
->dataflow
== eRender
)
2300 *pos
= This
->written_frames
- pad
;
2301 else if(This
->dataflow
== eCapture
)
2302 *pos
= This
->written_frames
+ pad
;
2304 LeaveCriticalSection(&This
->lock
);
2307 LARGE_INTEGER stamp
, freq
;
2308 QueryPerformanceCounter(&stamp
);
2309 QueryPerformanceFrequency(&freq
);
2310 *qpctime
= (stamp
.QuadPart
* (INT64
)10000000) / freq
.QuadPart
;
2316 static HRESULT WINAPI
AudioClock_GetCharacteristics(IAudioClock
*iface
,
2319 ACImpl
*This
= impl_from_IAudioClock(iface
);
2321 TRACE("(%p)->(%p)\n", This
, chars
);
2326 *chars
= AUDIOCLOCK_CHARACTERISTIC_FIXED_FREQ
;
2331 static const IAudioClockVtbl AudioClock_Vtbl
=
2333 AudioClock_QueryInterface
,
2336 AudioClock_GetFrequency
,
2337 AudioClock_GetPosition
,
2338 AudioClock_GetCharacteristics
2341 static HRESULT WINAPI
AudioClock2_QueryInterface(IAudioClock2
*iface
,
2342 REFIID riid
, void **ppv
)
2344 ACImpl
*This
= impl_from_IAudioClock2(iface
);
2345 return IAudioClock_QueryInterface(&This
->IAudioClock_iface
, riid
, ppv
);
2348 static ULONG WINAPI
AudioClock2_AddRef(IAudioClock2
*iface
)
2350 ACImpl
*This
= impl_from_IAudioClock2(iface
);
2351 return IAudioClient_AddRef(&This
->IAudioClient_iface
);
2354 static ULONG WINAPI
AudioClock2_Release(IAudioClock2
*iface
)
2356 ACImpl
*This
= impl_from_IAudioClock2(iface
);
2357 return IAudioClient_Release(&This
->IAudioClient_iface
);
2360 static HRESULT WINAPI
AudioClock2_GetDevicePosition(IAudioClock2
*iface
,
2361 UINT64
*pos
, UINT64
*qpctime
)
2363 ACImpl
*This
= impl_from_IAudioClock2(iface
);
2365 FIXME("(%p)->(%p, %p)\n", This
, pos
, qpctime
);
2370 static const IAudioClock2Vtbl AudioClock2_Vtbl
=
2372 AudioClock2_QueryInterface
,
2374 AudioClock2_Release
,
2375 AudioClock2_GetDevicePosition
2378 static AudioSessionWrapper
*AudioSessionWrapper_Create(ACImpl
*client
)
2380 AudioSessionWrapper
*ret
;
2382 ret
= HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY
,
2383 sizeof(AudioSessionWrapper
));
2387 ret
->IAudioSessionControl2_iface
.lpVtbl
= &AudioSessionControl2_Vtbl
;
2388 ret
->ISimpleAudioVolume_iface
.lpVtbl
= &SimpleAudioVolume_Vtbl
;
2389 ret
->IChannelAudioVolume_iface
.lpVtbl
= &ChannelAudioVolume_Vtbl
;
2393 ret
->client
= client
;
2395 ret
->session
= client
->session
;
2396 AudioClient_AddRef(&client
->IAudioClient_iface
);
2402 static HRESULT WINAPI
AudioSessionControl_QueryInterface(
2403 IAudioSessionControl2
*iface
, REFIID riid
, void **ppv
)
2405 TRACE("(%p)->(%s, %p)\n", iface
, debugstr_guid(riid
), ppv
);
2411 if(IsEqualIID(riid
, &IID_IUnknown
) ||
2412 IsEqualIID(riid
, &IID_IAudioSessionControl
) ||
2413 IsEqualIID(riid
, &IID_IAudioSessionControl2
))
2416 IUnknown_AddRef((IUnknown
*)*ppv
);
2420 WARN("Unknown interface %s\n", debugstr_guid(riid
));
2421 return E_NOINTERFACE
;
2424 static ULONG WINAPI
AudioSessionControl_AddRef(IAudioSessionControl2
*iface
)
2426 AudioSessionWrapper
*This
= impl_from_IAudioSessionControl2(iface
);
2428 ref
= InterlockedIncrement(&This
->ref
);
2429 TRACE("(%p) Refcount now %u\n", This
, ref
);
2433 static ULONG WINAPI
AudioSessionControl_Release(IAudioSessionControl2
*iface
)
2435 AudioSessionWrapper
*This
= impl_from_IAudioSessionControl2(iface
);
2437 ref
= InterlockedDecrement(&This
->ref
);
2438 TRACE("(%p) Refcount now %u\n", This
, ref
);
2441 EnterCriticalSection(&This
->client
->lock
);
2442 This
->client
->session_wrapper
= NULL
;
2443 LeaveCriticalSection(&This
->client
->lock
);
2444 AudioClient_Release(&This
->client
->IAudioClient_iface
);
2446 HeapFree(GetProcessHeap(), 0, This
);
2451 static HRESULT WINAPI
AudioSessionControl_GetState(IAudioSessionControl2
*iface
,
2452 AudioSessionState
*state
)
2454 AudioSessionWrapper
*This
= impl_from_IAudioSessionControl2(iface
);
2457 TRACE("(%p)->(%p)\n", This
, state
);
2460 return NULL_PTR_ERR
;
2462 EnterCriticalSection(&g_sessions_lock
);
2464 if(list_empty(&This
->session
->clients
)){
2465 *state
= AudioSessionStateExpired
;
2466 LeaveCriticalSection(&g_sessions_lock
);
2470 LIST_FOR_EACH_ENTRY(client
, &This
->session
->clients
, ACImpl
, entry
){
2471 EnterCriticalSection(&client
->lock
);
2472 if(client
->started
){
2473 *state
= AudioSessionStateActive
;
2474 LeaveCriticalSection(&client
->lock
);
2475 LeaveCriticalSection(&g_sessions_lock
);
2478 LeaveCriticalSection(&client
->lock
);
2481 LeaveCriticalSection(&g_sessions_lock
);
2483 *state
= AudioSessionStateInactive
;
2488 static HRESULT WINAPI
AudioSessionControl_GetDisplayName(
2489 IAudioSessionControl2
*iface
, WCHAR
**name
)
2491 AudioSessionWrapper
*This
= impl_from_IAudioSessionControl2(iface
);
2493 FIXME("(%p)->(%p) - stub\n", This
, name
);
2498 static HRESULT WINAPI
AudioSessionControl_SetDisplayName(
2499 IAudioSessionControl2
*iface
, const WCHAR
*name
, const GUID
*session
)
2501 AudioSessionWrapper
*This
= impl_from_IAudioSessionControl2(iface
);
2503 FIXME("(%p)->(%p, %s) - stub\n", This
, name
, debugstr_guid(session
));
2508 static HRESULT WINAPI
AudioSessionControl_GetIconPath(
2509 IAudioSessionControl2
*iface
, WCHAR
**path
)
2511 AudioSessionWrapper
*This
= impl_from_IAudioSessionControl2(iface
);
2513 FIXME("(%p)->(%p) - stub\n", This
, path
);
2518 static HRESULT WINAPI
AudioSessionControl_SetIconPath(
2519 IAudioSessionControl2
*iface
, const WCHAR
*path
, const GUID
*session
)
2521 AudioSessionWrapper
*This
= impl_from_IAudioSessionControl2(iface
);
2523 FIXME("(%p)->(%p, %s) - stub\n", This
, path
, debugstr_guid(session
));
2528 static HRESULT WINAPI
AudioSessionControl_GetGroupingParam(
2529 IAudioSessionControl2
*iface
, GUID
*group
)
2531 AudioSessionWrapper
*This
= impl_from_IAudioSessionControl2(iface
);
2533 FIXME("(%p)->(%p) - stub\n", This
, group
);
2538 static HRESULT WINAPI
AudioSessionControl_SetGroupingParam(
2539 IAudioSessionControl2
*iface
, const GUID
*group
, const GUID
*session
)
2541 AudioSessionWrapper
*This
= impl_from_IAudioSessionControl2(iface
);
2543 FIXME("(%p)->(%s, %s) - stub\n", This
, debugstr_guid(group
),
2544 debugstr_guid(session
));
2549 static HRESULT WINAPI
AudioSessionControl_RegisterAudioSessionNotification(
2550 IAudioSessionControl2
*iface
, IAudioSessionEvents
*events
)
2552 AudioSessionWrapper
*This
= impl_from_IAudioSessionControl2(iface
);
2554 FIXME("(%p)->(%p) - stub\n", This
, events
);
2559 static HRESULT WINAPI
AudioSessionControl_UnregisterAudioSessionNotification(
2560 IAudioSessionControl2
*iface
, IAudioSessionEvents
*events
)
2562 AudioSessionWrapper
*This
= impl_from_IAudioSessionControl2(iface
);
2564 FIXME("(%p)->(%p) - stub\n", This
, events
);
2569 static HRESULT WINAPI
AudioSessionControl_GetSessionIdentifier(
2570 IAudioSessionControl2
*iface
, WCHAR
**id
)
2572 AudioSessionWrapper
*This
= impl_from_IAudioSessionControl2(iface
);
2574 FIXME("(%p)->(%p) - stub\n", This
, id
);
2579 static HRESULT WINAPI
AudioSessionControl_GetSessionInstanceIdentifier(
2580 IAudioSessionControl2
*iface
, WCHAR
**id
)
2582 AudioSessionWrapper
*This
= impl_from_IAudioSessionControl2(iface
);
2584 FIXME("(%p)->(%p) - stub\n", This
, id
);
2589 static HRESULT WINAPI
AudioSessionControl_GetProcessId(
2590 IAudioSessionControl2
*iface
, DWORD
*pid
)
2592 AudioSessionWrapper
*This
= impl_from_IAudioSessionControl2(iface
);
2594 TRACE("(%p)->(%p)\n", This
, pid
);
2599 *pid
= GetCurrentProcessId();
2604 static HRESULT WINAPI
AudioSessionControl_IsSystemSoundsSession(
2605 IAudioSessionControl2
*iface
)
2607 AudioSessionWrapper
*This
= impl_from_IAudioSessionControl2(iface
);
2609 TRACE("(%p)\n", This
);
2614 static HRESULT WINAPI
AudioSessionControl_SetDuckingPreference(
2615 IAudioSessionControl2
*iface
, BOOL optout
)
2617 AudioSessionWrapper
*This
= impl_from_IAudioSessionControl2(iface
);
2619 TRACE("(%p)->(%d)\n", This
, optout
);
2624 static const IAudioSessionControl2Vtbl AudioSessionControl2_Vtbl
=
2626 AudioSessionControl_QueryInterface
,
2627 AudioSessionControl_AddRef
,
2628 AudioSessionControl_Release
,
2629 AudioSessionControl_GetState
,
2630 AudioSessionControl_GetDisplayName
,
2631 AudioSessionControl_SetDisplayName
,
2632 AudioSessionControl_GetIconPath
,
2633 AudioSessionControl_SetIconPath
,
2634 AudioSessionControl_GetGroupingParam
,
2635 AudioSessionControl_SetGroupingParam
,
2636 AudioSessionControl_RegisterAudioSessionNotification
,
2637 AudioSessionControl_UnregisterAudioSessionNotification
,
2638 AudioSessionControl_GetSessionIdentifier
,
2639 AudioSessionControl_GetSessionInstanceIdentifier
,
2640 AudioSessionControl_GetProcessId
,
2641 AudioSessionControl_IsSystemSoundsSession
,
2642 AudioSessionControl_SetDuckingPreference
2645 static HRESULT WINAPI
SimpleAudioVolume_QueryInterface(
2646 ISimpleAudioVolume
*iface
, REFIID riid
, void **ppv
)
2648 TRACE("(%p)->(%s, %p)\n", iface
, debugstr_guid(riid
), ppv
);
2654 if(IsEqualIID(riid
, &IID_IUnknown
) ||
2655 IsEqualIID(riid
, &IID_ISimpleAudioVolume
))
2658 IUnknown_AddRef((IUnknown
*)*ppv
);
2662 WARN("Unknown interface %s\n", debugstr_guid(riid
));
2663 return E_NOINTERFACE
;
2666 static ULONG WINAPI
SimpleAudioVolume_AddRef(ISimpleAudioVolume
*iface
)
2668 AudioSessionWrapper
*This
= impl_from_ISimpleAudioVolume(iface
);
2669 return AudioSessionControl_AddRef(&This
->IAudioSessionControl2_iface
);
2672 static ULONG WINAPI
SimpleAudioVolume_Release(ISimpleAudioVolume
*iface
)
2674 AudioSessionWrapper
*This
= impl_from_ISimpleAudioVolume(iface
);
2675 return AudioSessionControl_Release(&This
->IAudioSessionControl2_iface
);
2678 static HRESULT WINAPI
SimpleAudioVolume_SetMasterVolume(
2679 ISimpleAudioVolume
*iface
, float level
, const GUID
*context
)
2681 AudioSessionWrapper
*This
= impl_from_ISimpleAudioVolume(iface
);
2682 AudioSession
*session
= This
->session
;
2684 TRACE("(%p)->(%f, %s)\n", session
, level
, wine_dbgstr_guid(context
));
2686 if(level
< 0.f
|| level
> 1.f
)
2687 return E_INVALIDARG
;
2690 FIXME("Notifications not supported yet\n");
2692 TRACE("ALSA does not support volume control\n");
2694 EnterCriticalSection(&session
->lock
);
2696 session
->master_vol
= level
;
2698 LeaveCriticalSection(&session
->lock
);
2703 static HRESULT WINAPI
SimpleAudioVolume_GetMasterVolume(
2704 ISimpleAudioVolume
*iface
, float *level
)
2706 AudioSessionWrapper
*This
= impl_from_ISimpleAudioVolume(iface
);
2707 AudioSession
*session
= This
->session
;
2709 TRACE("(%p)->(%p)\n", session
, level
);
2712 return NULL_PTR_ERR
;
2714 *level
= session
->master_vol
;
2719 static HRESULT WINAPI
SimpleAudioVolume_SetMute(ISimpleAudioVolume
*iface
,
2720 BOOL mute
, const GUID
*context
)
2722 AudioSessionWrapper
*This
= impl_from_ISimpleAudioVolume(iface
);
2723 AudioSession
*session
= This
->session
;
2725 TRACE("(%p)->(%u, %p)\n", session
, mute
, context
);
2728 FIXME("Notifications not supported yet\n");
2730 session
->mute
= mute
;
2735 static HRESULT WINAPI
SimpleAudioVolume_GetMute(ISimpleAudioVolume
*iface
,
2738 AudioSessionWrapper
*This
= impl_from_ISimpleAudioVolume(iface
);
2739 AudioSession
*session
= This
->session
;
2741 TRACE("(%p)->(%p)\n", session
, mute
);
2744 return NULL_PTR_ERR
;
2746 *mute
= session
->mute
;
2751 static const ISimpleAudioVolumeVtbl SimpleAudioVolume_Vtbl
=
2753 SimpleAudioVolume_QueryInterface
,
2754 SimpleAudioVolume_AddRef
,
2755 SimpleAudioVolume_Release
,
2756 SimpleAudioVolume_SetMasterVolume
,
2757 SimpleAudioVolume_GetMasterVolume
,
2758 SimpleAudioVolume_SetMute
,
2759 SimpleAudioVolume_GetMute
2762 static HRESULT WINAPI
AudioStreamVolume_QueryInterface(
2763 IAudioStreamVolume
*iface
, REFIID riid
, void **ppv
)
2765 TRACE("(%p)->(%s, %p)\n", iface
, debugstr_guid(riid
), ppv
);
2771 if(IsEqualIID(riid
, &IID_IUnknown
) ||
2772 IsEqualIID(riid
, &IID_IAudioStreamVolume
))
2775 IUnknown_AddRef((IUnknown
*)*ppv
);
2779 WARN("Unknown interface %s\n", debugstr_guid(riid
));
2780 return E_NOINTERFACE
;
2783 static ULONG WINAPI
AudioStreamVolume_AddRef(IAudioStreamVolume
*iface
)
2785 ACImpl
*This
= impl_from_IAudioStreamVolume(iface
);
2786 return IAudioClient_AddRef(&This
->IAudioClient_iface
);
2789 static ULONG WINAPI
AudioStreamVolume_Release(IAudioStreamVolume
*iface
)
2791 ACImpl
*This
= impl_from_IAudioStreamVolume(iface
);
2792 return IAudioClient_Release(&This
->IAudioClient_iface
);
2795 static HRESULT WINAPI
AudioStreamVolume_GetChannelCount(
2796 IAudioStreamVolume
*iface
, UINT32
*out
)
2798 ACImpl
*This
= impl_from_IAudioStreamVolume(iface
);
2800 TRACE("(%p)->(%p)\n", This
, out
);
2805 *out
= This
->fmt
->nChannels
;
2810 static HRESULT WINAPI
AudioStreamVolume_SetChannelVolume(
2811 IAudioStreamVolume
*iface
, UINT32 index
, float level
)
2813 ACImpl
*This
= impl_from_IAudioStreamVolume(iface
);
2815 TRACE("(%p)->(%d, %f)\n", This
, index
, level
);
2817 if(level
< 0.f
|| level
> 1.f
)
2818 return E_INVALIDARG
;
2820 if(index
>= This
->fmt
->nChannels
)
2821 return E_INVALIDARG
;
2823 TRACE("ALSA does not support volume control\n");
2825 EnterCriticalSection(&This
->lock
);
2827 This
->vols
[index
] = level
;
2829 LeaveCriticalSection(&This
->lock
);
2834 static HRESULT WINAPI
AudioStreamVolume_GetChannelVolume(
2835 IAudioStreamVolume
*iface
, UINT32 index
, float *level
)
2837 ACImpl
*This
= impl_from_IAudioStreamVolume(iface
);
2839 TRACE("(%p)->(%d, %p)\n", This
, index
, level
);
2844 if(index
>= This
->fmt
->nChannels
)
2845 return E_INVALIDARG
;
2847 *level
= This
->vols
[index
];
2852 static HRESULT WINAPI
AudioStreamVolume_SetAllVolumes(
2853 IAudioStreamVolume
*iface
, UINT32 count
, const float *levels
)
2855 ACImpl
*This
= impl_from_IAudioStreamVolume(iface
);
2858 TRACE("(%p)->(%d, %p)\n", This
, count
, levels
);
2863 if(count
!= This
->fmt
->nChannels
)
2864 return E_INVALIDARG
;
2866 TRACE("ALSA does not support volume control\n");
2868 EnterCriticalSection(&This
->lock
);
2870 for(i
= 0; i
< count
; ++i
)
2871 This
->vols
[i
] = levels
[i
];
2873 LeaveCriticalSection(&This
->lock
);
2878 static HRESULT WINAPI
AudioStreamVolume_GetAllVolumes(
2879 IAudioStreamVolume
*iface
, UINT32 count
, float *levels
)
2881 ACImpl
*This
= impl_from_IAudioStreamVolume(iface
);
2884 TRACE("(%p)->(%d, %p)\n", This
, count
, levels
);
2889 if(count
!= This
->fmt
->nChannels
)
2890 return E_INVALIDARG
;
2892 EnterCriticalSection(&This
->lock
);
2894 for(i
= 0; i
< count
; ++i
)
2895 levels
[i
] = This
->vols
[i
];
2897 LeaveCriticalSection(&This
->lock
);
2902 static const IAudioStreamVolumeVtbl AudioStreamVolume_Vtbl
=
2904 AudioStreamVolume_QueryInterface
,
2905 AudioStreamVolume_AddRef
,
2906 AudioStreamVolume_Release
,
2907 AudioStreamVolume_GetChannelCount
,
2908 AudioStreamVolume_SetChannelVolume
,
2909 AudioStreamVolume_GetChannelVolume
,
2910 AudioStreamVolume_SetAllVolumes
,
2911 AudioStreamVolume_GetAllVolumes
2914 static HRESULT WINAPI
ChannelAudioVolume_QueryInterface(
2915 IChannelAudioVolume
*iface
, REFIID riid
, void **ppv
)
2917 TRACE("(%p)->(%s, %p)\n", iface
, debugstr_guid(riid
), ppv
);
2923 if(IsEqualIID(riid
, &IID_IUnknown
) ||
2924 IsEqualIID(riid
, &IID_IChannelAudioVolume
))
2927 IUnknown_AddRef((IUnknown
*)*ppv
);
2931 WARN("Unknown interface %s\n", debugstr_guid(riid
));
2932 return E_NOINTERFACE
;
2935 static ULONG WINAPI
ChannelAudioVolume_AddRef(IChannelAudioVolume
*iface
)
2937 AudioSessionWrapper
*This
= impl_from_IChannelAudioVolume(iface
);
2938 return AudioSessionControl_AddRef(&This
->IAudioSessionControl2_iface
);
2941 static ULONG WINAPI
ChannelAudioVolume_Release(IChannelAudioVolume
*iface
)
2943 AudioSessionWrapper
*This
= impl_from_IChannelAudioVolume(iface
);
2944 return AudioSessionControl_Release(&This
->IAudioSessionControl2_iface
);
2947 static HRESULT WINAPI
ChannelAudioVolume_GetChannelCount(
2948 IChannelAudioVolume
*iface
, UINT32
*out
)
2950 AudioSessionWrapper
*This
= impl_from_IChannelAudioVolume(iface
);
2951 AudioSession
*session
= This
->session
;
2953 TRACE("(%p)->(%p)\n", session
, out
);
2956 return NULL_PTR_ERR
;
2958 *out
= session
->channel_count
;
2963 static HRESULT WINAPI
ChannelAudioVolume_SetChannelVolume(
2964 IChannelAudioVolume
*iface
, UINT32 index
, float level
,
2965 const GUID
*context
)
2967 AudioSessionWrapper
*This
= impl_from_IChannelAudioVolume(iface
);
2968 AudioSession
*session
= This
->session
;
2970 TRACE("(%p)->(%d, %f, %s)\n", session
, index
, level
,
2971 wine_dbgstr_guid(context
));
2973 if(level
< 0.f
|| level
> 1.f
)
2974 return E_INVALIDARG
;
2976 if(index
>= session
->channel_count
)
2977 return E_INVALIDARG
;
2980 FIXME("Notifications not supported yet\n");
2982 TRACE("ALSA does not support volume control\n");
2984 EnterCriticalSection(&session
->lock
);
2986 session
->channel_vols
[index
] = level
;
2988 LeaveCriticalSection(&session
->lock
);
2993 static HRESULT WINAPI
ChannelAudioVolume_GetChannelVolume(
2994 IChannelAudioVolume
*iface
, UINT32 index
, float *level
)
2996 AudioSessionWrapper
*This
= impl_from_IChannelAudioVolume(iface
);
2997 AudioSession
*session
= This
->session
;
2999 TRACE("(%p)->(%d, %p)\n", session
, index
, level
);
3002 return NULL_PTR_ERR
;
3004 if(index
>= session
->channel_count
)
3005 return E_INVALIDARG
;
3007 *level
= session
->channel_vols
[index
];
3012 static HRESULT WINAPI
ChannelAudioVolume_SetAllVolumes(
3013 IChannelAudioVolume
*iface
, UINT32 count
, const float *levels
,
3014 const GUID
*context
)
3016 AudioSessionWrapper
*This
= impl_from_IChannelAudioVolume(iface
);
3017 AudioSession
*session
= This
->session
;
3020 TRACE("(%p)->(%d, %p, %s)\n", session
, count
, levels
,
3021 wine_dbgstr_guid(context
));
3024 return NULL_PTR_ERR
;
3026 if(count
!= session
->channel_count
)
3027 return E_INVALIDARG
;
3030 FIXME("Notifications not supported yet\n");
3032 TRACE("ALSA does not support volume control\n");
3034 EnterCriticalSection(&session
->lock
);
3036 for(i
= 0; i
< count
; ++i
)
3037 session
->channel_vols
[i
] = levels
[i
];
3039 LeaveCriticalSection(&session
->lock
);
3044 static HRESULT WINAPI
ChannelAudioVolume_GetAllVolumes(
3045 IChannelAudioVolume
*iface
, UINT32 count
, float *levels
)
3047 AudioSessionWrapper
*This
= impl_from_IChannelAudioVolume(iface
);
3048 AudioSession
*session
= This
->session
;
3051 TRACE("(%p)->(%d, %p)\n", session
, count
, levels
);
3054 return NULL_PTR_ERR
;
3056 if(count
!= session
->channel_count
)
3057 return E_INVALIDARG
;
3059 for(i
= 0; i
< count
; ++i
)
3060 levels
[i
] = session
->channel_vols
[i
];
3065 static const IChannelAudioVolumeVtbl ChannelAudioVolume_Vtbl
=
3067 ChannelAudioVolume_QueryInterface
,
3068 ChannelAudioVolume_AddRef
,
3069 ChannelAudioVolume_Release
,
3070 ChannelAudioVolume_GetChannelCount
,
3071 ChannelAudioVolume_SetChannelVolume
,
3072 ChannelAudioVolume_GetChannelVolume
,
3073 ChannelAudioVolume_SetAllVolumes
,
3074 ChannelAudioVolume_GetAllVolumes
3077 static HRESULT WINAPI
AudioSessionManager_QueryInterface(IAudioSessionManager2
*iface
,
3078 REFIID riid
, void **ppv
)
3080 TRACE("(%p)->(%s, %p)\n", iface
, debugstr_guid(riid
), ppv
);
3086 if(IsEqualIID(riid
, &IID_IUnknown
) ||
3087 IsEqualIID(riid
, &IID_IAudioSessionManager
) ||
3088 IsEqualIID(riid
, &IID_IAudioSessionManager2
))
3091 IUnknown_AddRef((IUnknown
*)*ppv
);
3095 WARN("Unknown interface %s\n", debugstr_guid(riid
));
3096 return E_NOINTERFACE
;
3099 static ULONG WINAPI
AudioSessionManager_AddRef(IAudioSessionManager2
*iface
)
3101 SessionMgr
*This
= impl_from_IAudioSessionManager2(iface
);
3103 ref
= InterlockedIncrement(&This
->ref
);
3104 TRACE("(%p) Refcount now %u\n", This
, ref
);
3108 static ULONG WINAPI
AudioSessionManager_Release(IAudioSessionManager2
*iface
)
3110 SessionMgr
*This
= impl_from_IAudioSessionManager2(iface
);
3112 ref
= InterlockedDecrement(&This
->ref
);
3113 TRACE("(%p) Refcount now %u\n", This
, ref
);
3115 HeapFree(GetProcessHeap(), 0, This
);
3119 static HRESULT WINAPI
AudioSessionManager_GetAudioSessionControl(
3120 IAudioSessionManager2
*iface
, const GUID
*session_guid
, DWORD flags
,
3121 IAudioSessionControl
**out
)
3123 SessionMgr
*This
= impl_from_IAudioSessionManager2(iface
);
3124 AudioSession
*session
;
3125 AudioSessionWrapper
*wrapper
;
3128 TRACE("(%p)->(%s, %x, %p)\n", This
, debugstr_guid(session_guid
),
3131 hr
= get_audio_session(session_guid
, This
->device
, 0, &session
);
3135 wrapper
= AudioSessionWrapper_Create(NULL
);
3137 return E_OUTOFMEMORY
;
3139 wrapper
->session
= session
;
3141 *out
= (IAudioSessionControl
*)&wrapper
->IAudioSessionControl2_iface
;
3146 static HRESULT WINAPI
AudioSessionManager_GetSimpleAudioVolume(
3147 IAudioSessionManager2
*iface
, const GUID
*session_guid
, DWORD flags
,
3148 ISimpleAudioVolume
**out
)
3150 SessionMgr
*This
= impl_from_IAudioSessionManager2(iface
);
3151 AudioSession
*session
;
3152 AudioSessionWrapper
*wrapper
;
3155 TRACE("(%p)->(%s, %x, %p)\n", This
, debugstr_guid(session_guid
),
3158 hr
= get_audio_session(session_guid
, This
->device
, 0, &session
);
3162 wrapper
= AudioSessionWrapper_Create(NULL
);
3164 return E_OUTOFMEMORY
;
3166 wrapper
->session
= session
;
3168 *out
= &wrapper
->ISimpleAudioVolume_iface
;
3173 static HRESULT WINAPI
AudioSessionManager_GetSessionEnumerator(
3174 IAudioSessionManager2
*iface
, IAudioSessionEnumerator
**out
)
3176 SessionMgr
*This
= impl_from_IAudioSessionManager2(iface
);
3177 FIXME("(%p)->(%p) - stub\n", This
, out
);
3181 static HRESULT WINAPI
AudioSessionManager_RegisterSessionNotification(
3182 IAudioSessionManager2
*iface
, IAudioSessionNotification
*notification
)
3184 SessionMgr
*This
= impl_from_IAudioSessionManager2(iface
);
3185 FIXME("(%p)->(%p) - stub\n", This
, notification
);
3189 static HRESULT WINAPI
AudioSessionManager_UnregisterSessionNotification(
3190 IAudioSessionManager2
*iface
, IAudioSessionNotification
*notification
)
3192 SessionMgr
*This
= impl_from_IAudioSessionManager2(iface
);
3193 FIXME("(%p)->(%p) - stub\n", This
, notification
);
3197 static HRESULT WINAPI
AudioSessionManager_RegisterDuckNotification(
3198 IAudioSessionManager2
*iface
, const WCHAR
*session_id
,
3199 IAudioVolumeDuckNotification
*notification
)
3201 SessionMgr
*This
= impl_from_IAudioSessionManager2(iface
);
3202 FIXME("(%p)->(%p) - stub\n", This
, notification
);
3206 static HRESULT WINAPI
AudioSessionManager_UnregisterDuckNotification(
3207 IAudioSessionManager2
*iface
,
3208 IAudioVolumeDuckNotification
*notification
)
3210 SessionMgr
*This
= impl_from_IAudioSessionManager2(iface
);
3211 FIXME("(%p)->(%p) - stub\n", This
, notification
);
3215 static const IAudioSessionManager2Vtbl AudioSessionManager2_Vtbl
=
3217 AudioSessionManager_QueryInterface
,
3218 AudioSessionManager_AddRef
,
3219 AudioSessionManager_Release
,
3220 AudioSessionManager_GetAudioSessionControl
,
3221 AudioSessionManager_GetSimpleAudioVolume
,
3222 AudioSessionManager_GetSessionEnumerator
,
3223 AudioSessionManager_RegisterSessionNotification
,
3224 AudioSessionManager_UnregisterSessionNotification
,
3225 AudioSessionManager_RegisterDuckNotification
,
3226 AudioSessionManager_UnregisterDuckNotification
3229 HRESULT WINAPI
AUDDRV_GetAudioSessionManager(IMMDevice
*device
,
3230 IAudioSessionManager2
**out
)
3234 This
= HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY
, sizeof(SessionMgr
));
3236 return E_OUTOFMEMORY
;
3238 This
->IAudioSessionManager2_iface
.lpVtbl
= &AudioSessionManager2_Vtbl
;
3239 This
->device
= device
;
3242 *out
= &This
->IAudioSessionManager2_iface
;