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
, alsa_period_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
, last_pos_frames
;
113 UINT32 bufsize_frames
, held_frames
, tmp_buffer_frames
, mmdev_period_frames
;
114 UINT32 lcl_offs_frames
; /* offs into local_buffer where valid data starts */
117 BYTE
*local_buffer
, *tmp_buffer
;
118 long getbuf_last
; /* <0 when using tmp_buffer */
120 CRITICAL_SECTION lock
;
122 AudioSession
*session
;
123 AudioSessionWrapper
*session_wrapper
;
128 typedef struct _SessionMgr
{
129 IAudioSessionManager2 IAudioSessionManager2_iface
;
136 static HANDLE g_timer_q
;
138 static CRITICAL_SECTION g_sessions_lock
;
139 static CRITICAL_SECTION_DEBUG g_sessions_lock_debug
=
141 0, 0, &g_sessions_lock
,
142 { &g_sessions_lock_debug
.ProcessLocksList
, &g_sessions_lock_debug
.ProcessLocksList
},
143 0, 0, { (DWORD_PTR
)(__FILE__
": g_sessions_lock") }
145 static CRITICAL_SECTION g_sessions_lock
= { &g_sessions_lock_debug
, -1, 0, 0, 0, 0 };
146 static struct list g_sessions
= LIST_INIT(g_sessions
);
148 static const WCHAR defaultW
[] = {'d','e','f','a','u','l','t',0};
149 static const char defname
[] = "default";
151 static const IAudioClientVtbl AudioClient_Vtbl
;
152 static const IAudioRenderClientVtbl AudioRenderClient_Vtbl
;
153 static const IAudioCaptureClientVtbl AudioCaptureClient_Vtbl
;
154 static const IAudioSessionControl2Vtbl AudioSessionControl2_Vtbl
;
155 static const ISimpleAudioVolumeVtbl SimpleAudioVolume_Vtbl
;
156 static const IAudioClockVtbl AudioClock_Vtbl
;
157 static const IAudioClock2Vtbl AudioClock2_Vtbl
;
158 static const IAudioStreamVolumeVtbl AudioStreamVolume_Vtbl
;
159 static const IChannelAudioVolumeVtbl ChannelAudioVolume_Vtbl
;
160 static const IAudioSessionManager2Vtbl AudioSessionManager2_Vtbl
;
162 static AudioSessionWrapper
*AudioSessionWrapper_Create(ACImpl
*client
);
164 static inline ACImpl
*impl_from_IAudioClient(IAudioClient
*iface
)
166 return CONTAINING_RECORD(iface
, ACImpl
, IAudioClient_iface
);
169 static inline ACImpl
*impl_from_IAudioRenderClient(IAudioRenderClient
*iface
)
171 return CONTAINING_RECORD(iface
, ACImpl
, IAudioRenderClient_iface
);
174 static inline ACImpl
*impl_from_IAudioCaptureClient(IAudioCaptureClient
*iface
)
176 return CONTAINING_RECORD(iface
, ACImpl
, IAudioCaptureClient_iface
);
179 static inline AudioSessionWrapper
*impl_from_IAudioSessionControl2(IAudioSessionControl2
*iface
)
181 return CONTAINING_RECORD(iface
, AudioSessionWrapper
, IAudioSessionControl2_iface
);
184 static inline AudioSessionWrapper
*impl_from_ISimpleAudioVolume(ISimpleAudioVolume
*iface
)
186 return CONTAINING_RECORD(iface
, AudioSessionWrapper
, ISimpleAudioVolume_iface
);
189 static inline AudioSessionWrapper
*impl_from_IChannelAudioVolume(IChannelAudioVolume
*iface
)
191 return CONTAINING_RECORD(iface
, AudioSessionWrapper
, IChannelAudioVolume_iface
);
194 static inline ACImpl
*impl_from_IAudioClock(IAudioClock
*iface
)
196 return CONTAINING_RECORD(iface
, ACImpl
, IAudioClock_iface
);
199 static inline ACImpl
*impl_from_IAudioClock2(IAudioClock2
*iface
)
201 return CONTAINING_RECORD(iface
, ACImpl
, IAudioClock2_iface
);
204 static inline ACImpl
*impl_from_IAudioStreamVolume(IAudioStreamVolume
*iface
)
206 return CONTAINING_RECORD(iface
, ACImpl
, IAudioStreamVolume_iface
);
209 static inline SessionMgr
*impl_from_IAudioSessionManager2(IAudioSessionManager2
*iface
)
211 return CONTAINING_RECORD(iface
, SessionMgr
, IAudioSessionManager2_iface
);
214 BOOL WINAPI
DllMain(HINSTANCE dll
, DWORD reason
, void *reserved
)
218 case DLL_PROCESS_ATTACH
:
219 g_timer_q
= CreateTimerQueue();
224 case DLL_PROCESS_DETACH
:
225 DeleteCriticalSection(&g_sessions_lock
);
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 snd_pcm_format_t
alsa_format(const WAVEFORMATEX
*fmt
)
717 snd_pcm_format_t format
= SND_PCM_FORMAT_UNKNOWN
;
718 const WAVEFORMATEXTENSIBLE
*fmtex
= (const WAVEFORMATEXTENSIBLE
*)fmt
;
720 if(fmt
->wFormatTag
== WAVE_FORMAT_PCM
||
721 (fmt
->wFormatTag
== WAVE_FORMAT_EXTENSIBLE
&&
722 IsEqualGUID(&fmtex
->SubFormat
, &KSDATAFORMAT_SUBTYPE_PCM
))){
723 if(fmt
->wBitsPerSample
== 8)
724 format
= SND_PCM_FORMAT_U8
;
725 else if(fmt
->wBitsPerSample
== 16)
726 format
= SND_PCM_FORMAT_S16_LE
;
727 else if(fmt
->wBitsPerSample
== 24)
728 format
= SND_PCM_FORMAT_S24_3LE
;
729 else if(fmt
->wBitsPerSample
== 32)
730 format
= SND_PCM_FORMAT_S32_LE
;
732 WARN("Unsupported bit depth: %u\n", fmt
->wBitsPerSample
);
733 if(fmt
->wFormatTag
== WAVE_FORMAT_EXTENSIBLE
&&
734 fmt
->wBitsPerSample
!= fmtex
->Samples
.wValidBitsPerSample
){
735 if(fmtex
->Samples
.wValidBitsPerSample
== 20 && fmt
->wBitsPerSample
== 24)
736 format
= SND_PCM_FORMAT_S20_3LE
;
738 WARN("Unsupported ValidBits: %u\n", fmtex
->Samples
.wValidBitsPerSample
);
739 format
= SND_PCM_FORMAT_UNKNOWN
;
742 }else if(fmt
->wFormatTag
== WAVE_FORMAT_IEEE_FLOAT
||
743 (fmt
->wFormatTag
== WAVE_FORMAT_EXTENSIBLE
&&
744 IsEqualGUID(&fmtex
->SubFormat
, &KSDATAFORMAT_SUBTYPE_IEEE_FLOAT
))){
745 if(fmt
->wBitsPerSample
== 32)
746 format
= SND_PCM_FORMAT_FLOAT_LE
;
747 else if(fmt
->wBitsPerSample
== 64)
748 format
= SND_PCM_FORMAT_FLOAT64_LE
;
750 WARN("Unsupported float size: %u\n", fmt
->wBitsPerSample
);
752 WARN("Unknown wave format: %04x\n", fmt
->wFormatTag
);
756 static void session_init_vols(AudioSession
*session
, UINT channels
)
758 if(session
->channel_count
< channels
){
761 if(session
->channel_vols
)
762 session
->channel_vols
= HeapReAlloc(GetProcessHeap(), 0,
763 session
->channel_vols
, sizeof(float) * channels
);
765 session
->channel_vols
= HeapAlloc(GetProcessHeap(), 0,
766 sizeof(float) * channels
);
767 if(!session
->channel_vols
)
770 for(i
= session
->channel_count
; i
< channels
; ++i
)
771 session
->channel_vols
[i
] = 1.f
;
773 session
->channel_count
= channels
;
777 static AudioSession
*create_session(const GUID
*guid
, IMMDevice
*device
,
782 ret
= HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY
, sizeof(AudioSession
));
786 memcpy(&ret
->guid
, guid
, sizeof(GUID
));
788 ret
->device
= device
;
790 list_init(&ret
->clients
);
792 list_add_head(&g_sessions
, &ret
->entry
);
794 InitializeCriticalSection(&ret
->lock
);
795 ret
->lock
.DebugInfo
->Spare
[0] = (DWORD_PTR
)(__FILE__
": AudioSession.lock");
797 session_init_vols(ret
, num_channels
);
799 ret
->master_vol
= 1.f
;
804 /* if channels == 0, then this will return or create a session with
805 * matching dataflow and GUID. otherwise, channels must also match */
806 static HRESULT
get_audio_session(const GUID
*sessionguid
,
807 IMMDevice
*device
, UINT channels
, AudioSession
**out
)
809 AudioSession
*session
;
811 if(!sessionguid
|| IsEqualGUID(sessionguid
, &GUID_NULL
)){
812 *out
= create_session(&GUID_NULL
, device
, channels
);
814 return E_OUTOFMEMORY
;
820 LIST_FOR_EACH_ENTRY(session
, &g_sessions
, AudioSession
, entry
){
821 if(session
->device
== device
&&
822 IsEqualGUID(sessionguid
, &session
->guid
)){
823 session_init_vols(session
, channels
);
830 *out
= create_session(sessionguid
, device
, channels
);
832 return E_OUTOFMEMORY
;
838 static HRESULT WINAPI
AudioClient_Initialize(IAudioClient
*iface
,
839 AUDCLNT_SHAREMODE mode
, DWORD flags
, REFERENCE_TIME duration
,
840 REFERENCE_TIME period
, const WAVEFORMATEX
*fmt
,
841 const GUID
*sessionguid
)
843 ACImpl
*This
= impl_from_IAudioClient(iface
);
844 snd_pcm_sw_params_t
*sw_params
= NULL
;
845 snd_pcm_format_t format
;
846 unsigned int rate
, alsa_period_us
;
850 TRACE("(%p)->(%x, %x, %s, %s, %p, %s)\n", This
, mode
, flags
,
851 wine_dbgstr_longlong(duration
), wine_dbgstr_longlong(period
), fmt
, debugstr_guid(sessionguid
));
856 if(mode
!= AUDCLNT_SHAREMODE_SHARED
&& mode
!= AUDCLNT_SHAREMODE_EXCLUSIVE
)
857 return AUDCLNT_E_NOT_INITIALIZED
;
859 if(flags
& ~(AUDCLNT_STREAMFLAGS_CROSSPROCESS
|
860 AUDCLNT_STREAMFLAGS_LOOPBACK
|
861 AUDCLNT_STREAMFLAGS_EVENTCALLBACK
|
862 AUDCLNT_STREAMFLAGS_NOPERSIST
|
863 AUDCLNT_STREAMFLAGS_RATEADJUST
|
864 AUDCLNT_SESSIONFLAGS_EXPIREWHENUNOWNED
|
865 AUDCLNT_SESSIONFLAGS_DISPLAY_HIDE
|
866 AUDCLNT_SESSIONFLAGS_DISPLAY_HIDEWHENEXPIRED
)){
867 TRACE("Unknown flags: %08x\n", flags
);
871 if(mode
== AUDCLNT_SHAREMODE_SHARED
){
872 period
= DefaultPeriod
;
873 if( duration
< 3 * period
)
874 duration
= 3 * period
;
877 period
= DefaultPeriod
; /* not minimum */
878 if(period
< MinimumPeriod
|| period
> 5000000)
879 return AUDCLNT_E_INVALID_DEVICE_PERIOD
;
880 if(duration
> 20000000) /* the smaller the period, the lower this limit */
881 return AUDCLNT_E_BUFFER_SIZE_ERROR
;
882 if(flags
& AUDCLNT_STREAMFLAGS_EVENTCALLBACK
){
883 if(duration
!= period
)
884 return AUDCLNT_E_BUFDURATION_PERIOD_NOT_EQUAL
;
885 FIXME("EXCLUSIVE mode with EVENTCALLBACK\n");
886 return AUDCLNT_E_DEVICE_IN_USE
;
888 if( duration
< 8 * period
)
889 duration
= 8 * period
; /* may grow above 2s */
893 EnterCriticalSection(&This
->lock
);
896 LeaveCriticalSection(&This
->lock
);
897 return AUDCLNT_E_ALREADY_INITIALIZED
;
902 if((err
= snd_pcm_hw_params_any(This
->pcm_handle
, This
->hw_params
)) < 0){
903 WARN("Unable to get hw_params: %d (%s)\n", err
, snd_strerror(err
));
908 if((err
= snd_pcm_hw_params_set_access(This
->pcm_handle
, This
->hw_params
,
909 SND_PCM_ACCESS_RW_INTERLEAVED
)) < 0){
910 WARN("Unable to set access: %d (%s)\n", err
, snd_strerror(err
));
915 format
= alsa_format(fmt
);
916 if (format
== SND_PCM_FORMAT_UNKNOWN
){
917 hr
= AUDCLNT_E_UNSUPPORTED_FORMAT
;
921 if((err
= snd_pcm_hw_params_set_format(This
->pcm_handle
, This
->hw_params
,
923 WARN("Unable to set ALSA format to %u: %d (%s)\n", format
, err
,
929 This
->alsa_format
= format
;
931 rate
= fmt
->nSamplesPerSec
;
932 if((err
= snd_pcm_hw_params_set_rate_near(This
->pcm_handle
, This
->hw_params
,
934 WARN("Unable to set rate to %u: %d (%s)\n", rate
, err
,
940 if((err
= snd_pcm_hw_params_set_channels(This
->pcm_handle
, This
->hw_params
,
941 fmt
->nChannels
)) < 0){
942 WARN("Unable to set channels to %u: %d (%s)\n", fmt
->nChannels
, err
,
948 This
->mmdev_period_rt
= period
;
949 alsa_period_us
= This
->mmdev_period_rt
/ 10;
950 if((err
= snd_pcm_hw_params_set_period_time_near(This
->pcm_handle
,
951 This
->hw_params
, &alsa_period_us
, NULL
)) < 0)
952 WARN("Unable to set period time near %u: %d (%s)\n", alsa_period_us
,
953 err
, snd_strerror(err
));
954 /* ALSA updates the output variable alsa_period_us */
956 This
->mmdev_period_frames
= MulDiv(fmt
->nSamplesPerSec
,
957 This
->mmdev_period_rt
, 10000000);
959 This
->bufsize_frames
= MulDiv(duration
, fmt
->nSamplesPerSec
, 10000000);
961 /* Buffer 4 ALSA periods if large enough, else 4 mmdevapi periods */
962 This
->alsa_bufsize_frames
= This
->mmdev_period_frames
* 4;
963 if(err
< 0 || alsa_period_us
< period
/ 10)
964 err
= snd_pcm_hw_params_set_buffer_size_near(This
->pcm_handle
,
965 This
->hw_params
, &This
->alsa_bufsize_frames
);
967 unsigned int periods
= 4;
968 err
= snd_pcm_hw_params_set_periods_near(This
->pcm_handle
, This
->hw_params
, &periods
, NULL
);
971 WARN("Unable to set buffer size: %d (%s)\n", err
, snd_strerror(err
));
973 if((err
= snd_pcm_hw_params(This
->pcm_handle
, This
->hw_params
)) < 0){
974 WARN("Unable to set hw params: %d (%s)\n", err
, snd_strerror(err
));
979 if((err
= snd_pcm_hw_params_get_period_size(This
->hw_params
,
980 &This
->alsa_period_frames
, NULL
)) < 0){
981 WARN("Unable to get period size: %d (%s)\n", err
, snd_strerror(err
));
986 if((err
= snd_pcm_hw_params_get_buffer_size(This
->hw_params
,
987 &This
->alsa_bufsize_frames
)) < 0){
988 WARN("Unable to get buffer size: %d (%s)\n", err
, snd_strerror(err
));
993 sw_params
= HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY
, snd_pcm_sw_params_sizeof());
999 if((err
= snd_pcm_sw_params_current(This
->pcm_handle
, sw_params
)) < 0){
1000 WARN("Unable to get sw_params: %d (%s)\n", err
, snd_strerror(err
));
1005 if((err
= snd_pcm_sw_params_set_start_threshold(This
->pcm_handle
,
1006 sw_params
, 1)) < 0){
1007 WARN("Unable set start threshold to 0: %d (%s)\n", err
, snd_strerror(err
));
1012 if((err
= snd_pcm_sw_params_set_stop_threshold(This
->pcm_handle
,
1013 sw_params
, This
->alsa_bufsize_frames
)) < 0){
1014 WARN("Unable set stop threshold to %lu: %d (%s)\n",
1015 This
->alsa_bufsize_frames
, err
, snd_strerror(err
));
1020 if((err
= snd_pcm_sw_params(This
->pcm_handle
, sw_params
)) < 0){
1021 WARN("Unable set sw params: %d (%s)\n", err
, snd_strerror(err
));
1026 if((err
= snd_pcm_prepare(This
->pcm_handle
)) < 0){
1027 WARN("Unable to prepare device: %d (%s)\n", err
, snd_strerror(err
));
1032 /* Check if the ALSA buffer is so small that it will run out before
1033 * the next MMDevAPI period tick occurs. Allow a little wiggle room
1034 * with 120% of the period time. */
1035 if(This
->alsa_bufsize_frames
< 1.2 * This
->mmdev_period_frames
)
1036 FIXME("ALSA buffer time is too small. Expect underruns. (%lu < %u * 1.2)\n",
1037 This
->alsa_bufsize_frames
, This
->mmdev_period_frames
);
1039 This
->local_buffer
= HeapAlloc(GetProcessHeap(), 0,
1040 This
->bufsize_frames
* fmt
->nBlockAlign
);
1041 if(!This
->local_buffer
){
1045 if (fmt
->wBitsPerSample
== 8)
1046 memset(This
->local_buffer
, 128, This
->bufsize_frames
* fmt
->nBlockAlign
);
1048 memset(This
->local_buffer
, 0, This
->bufsize_frames
* fmt
->nBlockAlign
);
1050 This
->fmt
= clone_format(fmt
);
1056 This
->vols
= HeapAlloc(GetProcessHeap(), 0, fmt
->nChannels
* sizeof(float));
1062 for(i
= 0; i
< fmt
->nChannels
; ++i
)
1063 This
->vols
[i
] = 1.f
;
1066 This
->flags
= flags
;
1068 EnterCriticalSection(&g_sessions_lock
);
1070 hr
= get_audio_session(sessionguid
, This
->parent
, fmt
->nChannels
,
1073 LeaveCriticalSection(&g_sessions_lock
);
1077 list_add_tail(&This
->session
->clients
, &This
->entry
);
1079 LeaveCriticalSection(&g_sessions_lock
);
1081 This
->initted
= TRUE
;
1083 TRACE("ALSA period: %lu frames\n", This
->alsa_period_frames
);
1084 TRACE("ALSA buffer: %lu frames\n", This
->alsa_bufsize_frames
);
1085 TRACE("MMDevice period: %u frames\n", This
->mmdev_period_frames
);
1086 TRACE("MMDevice buffer: %u frames\n", This
->bufsize_frames
);
1089 HeapFree(GetProcessHeap(), 0, sw_params
);
1091 HeapFree(GetProcessHeap(), 0, This
->local_buffer
);
1092 This
->local_buffer
= NULL
;
1093 CoTaskMemFree(This
->fmt
);
1095 HeapFree(GetProcessHeap(), 0, This
->vols
);
1099 LeaveCriticalSection(&This
->lock
);
1104 static HRESULT WINAPI
AudioClient_GetBufferSize(IAudioClient
*iface
,
1107 ACImpl
*This
= impl_from_IAudioClient(iface
);
1109 TRACE("(%p)->(%p)\n", This
, out
);
1114 EnterCriticalSection(&This
->lock
);
1117 LeaveCriticalSection(&This
->lock
);
1118 return AUDCLNT_E_NOT_INITIALIZED
;
1121 *out
= This
->bufsize_frames
;
1123 LeaveCriticalSection(&This
->lock
);
1128 static HRESULT WINAPI
AudioClient_GetStreamLatency(IAudioClient
*iface
,
1129 REFERENCE_TIME
*latency
)
1131 ACImpl
*This
= impl_from_IAudioClient(iface
);
1133 TRACE("(%p)->(%p)\n", This
, latency
);
1138 EnterCriticalSection(&This
->lock
);
1141 LeaveCriticalSection(&This
->lock
);
1142 return AUDCLNT_E_NOT_INITIALIZED
;
1145 LeaveCriticalSection(&This
->lock
);
1147 /* one mmdevapi period plus one period we hide in the ALSA buffer */
1148 *latency
= MulDiv(This
->alsa_period_frames
, 10000000, This
->fmt
->nSamplesPerSec
)
1149 + This
->mmdev_period_rt
;
1154 static HRESULT WINAPI
AudioClient_GetCurrentPadding(IAudioClient
*iface
,
1157 ACImpl
*This
= impl_from_IAudioClient(iface
);
1159 TRACE("(%p)->(%p)\n", This
, out
);
1164 EnterCriticalSection(&This
->lock
);
1167 LeaveCriticalSection(&This
->lock
);
1168 return AUDCLNT_E_NOT_INITIALIZED
;
1171 *out
= This
->held_frames
;
1173 /* call required to get accurate snd_pcm_state() */
1174 snd_pcm_avail_update(This
->pcm_handle
);
1175 TRACE("pad: %u, state: %u\n", *out
, snd_pcm_state(This
->pcm_handle
));
1177 LeaveCriticalSection(&This
->lock
);
1182 static DWORD
get_channel_mask(unsigned int channels
)
1188 return KSAUDIO_SPEAKER_MONO
;
1190 return KSAUDIO_SPEAKER_STEREO
;
1192 return KSAUDIO_SPEAKER_STEREO
| SPEAKER_LOW_FREQUENCY
;
1194 return KSAUDIO_SPEAKER_QUAD
; /* not _SURROUND */
1196 return KSAUDIO_SPEAKER_QUAD
| SPEAKER_LOW_FREQUENCY
;
1198 return KSAUDIO_SPEAKER_5POINT1
; /* not 5POINT1_SURROUND */
1200 return KSAUDIO_SPEAKER_5POINT1
| SPEAKER_BACK_CENTER
;
1202 return KSAUDIO_SPEAKER_7POINT1_SURROUND
; /* Vista deprecates 7POINT1 */
1204 FIXME("Unknown speaker configuration: %u\n", channels
);
1208 static HRESULT WINAPI
AudioClient_IsFormatSupported(IAudioClient
*iface
,
1209 AUDCLNT_SHAREMODE mode
, const WAVEFORMATEX
*fmt
,
1212 ACImpl
*This
= impl_from_IAudioClient(iface
);
1213 snd_pcm_format_mask_t
*formats
= NULL
;
1214 snd_pcm_format_t format
;
1216 WAVEFORMATEX
*closest
= NULL
;
1217 const WAVEFORMATEXTENSIBLE
*fmtex
= (const WAVEFORMATEXTENSIBLE
*)fmt
;
1218 unsigned int max
= 0, min
= 0;
1221 TRACE("(%p)->(%x, %p, %p)\n", This
, mode
, fmt
, out
);
1223 if(!fmt
|| (mode
== AUDCLNT_SHAREMODE_SHARED
&& !out
))
1226 if(mode
!= AUDCLNT_SHAREMODE_SHARED
&& mode
!= AUDCLNT_SHAREMODE_EXCLUSIVE
)
1227 return E_INVALIDARG
;
1229 if(fmt
->wFormatTag
== WAVE_FORMAT_EXTENSIBLE
&&
1230 fmt
->cbSize
< sizeof(WAVEFORMATEXTENSIBLE
) - sizeof(WAVEFORMATEX
))
1231 return E_INVALIDARG
;
1237 if(mode
!= AUDCLNT_SHAREMODE_SHARED
)
1241 EnterCriticalSection(&This
->lock
);
1243 if((err
= snd_pcm_hw_params_any(This
->pcm_handle
, This
->hw_params
)) < 0){
1248 formats
= HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY
,
1249 snd_pcm_format_mask_sizeof());
1255 snd_pcm_hw_params_get_format_mask(This
->hw_params
, formats
);
1256 format
= alsa_format(fmt
);
1257 if (format
== SND_PCM_FORMAT_UNKNOWN
||
1258 !snd_pcm_format_mask_test(formats
, format
)){
1259 hr
= AUDCLNT_E_UNSUPPORTED_FORMAT
;
1263 closest
= clone_format(fmt
);
1269 if((err
= snd_pcm_hw_params_get_rate_min(This
->hw_params
, &min
, NULL
)) < 0){
1271 WARN("Unable to get min rate: %d (%s)\n", err
, snd_strerror(err
));
1275 if((err
= snd_pcm_hw_params_get_rate_max(This
->hw_params
, &max
, NULL
)) < 0){
1277 WARN("Unable to get max rate: %d (%s)\n", err
, snd_strerror(err
));
1281 if(fmt
->nSamplesPerSec
< min
|| fmt
->nSamplesPerSec
> max
){
1282 hr
= AUDCLNT_E_UNSUPPORTED_FORMAT
;
1286 if((err
= snd_pcm_hw_params_get_channels_min(This
->hw_params
, &min
)) < 0){
1288 WARN("Unable to get min channels: %d (%s)\n", err
, snd_strerror(err
));
1292 if((err
= snd_pcm_hw_params_get_channels_max(This
->hw_params
, &max
)) < 0){
1294 WARN("Unable to get max channels: %d (%s)\n", err
, snd_strerror(err
));
1299 if(fmt
->nChannels
> max
){
1301 closest
->nChannels
= max
;
1302 }else if(fmt
->nChannels
< min
){
1304 closest
->nChannels
= min
;
1307 if(closest
->wFormatTag
== WAVE_FORMAT_EXTENSIBLE
){
1308 DWORD mask
= get_channel_mask(closest
->nChannels
);
1310 ((WAVEFORMATEXTENSIBLE
*)closest
)->dwChannelMask
= mask
;
1312 if(fmt
->wFormatTag
== WAVE_FORMAT_EXTENSIBLE
&&
1313 fmtex
->dwChannelMask
!= 0 &&
1314 fmtex
->dwChannelMask
!= mask
)
1319 LeaveCriticalSection(&This
->lock
);
1320 HeapFree(GetProcessHeap(), 0, formats
);
1322 if(hr
== S_FALSE
&& !out
)
1323 hr
= AUDCLNT_E_UNSUPPORTED_FORMAT
;
1325 if(hr
== S_FALSE
&& out
) {
1326 closest
->nBlockAlign
=
1327 closest
->nChannels
* closest
->wBitsPerSample
/ 8;
1328 closest
->nAvgBytesPerSec
=
1329 closest
->nBlockAlign
* closest
->nSamplesPerSec
;
1332 CoTaskMemFree(closest
);
1334 TRACE("returning: %08x\n", hr
);
1338 static HRESULT WINAPI
AudioClient_GetMixFormat(IAudioClient
*iface
,
1339 WAVEFORMATEX
**pwfx
)
1341 ACImpl
*This
= impl_from_IAudioClient(iface
);
1342 WAVEFORMATEXTENSIBLE
*fmt
;
1343 snd_pcm_format_mask_t
*formats
;
1344 unsigned int max_rate
, max_channels
;
1348 TRACE("(%p)->(%p)\n", This
, pwfx
);
1354 fmt
= CoTaskMemAlloc(sizeof(WAVEFORMATEXTENSIBLE
));
1356 return E_OUTOFMEMORY
;
1358 formats
= HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY
, snd_pcm_format_mask_sizeof());
1361 return E_OUTOFMEMORY
;
1364 EnterCriticalSection(&This
->lock
);
1366 if((err
= snd_pcm_hw_params_any(This
->pcm_handle
, This
->hw_params
)) < 0){
1367 WARN("Unable to get hw_params: %d (%s)\n", err
, snd_strerror(err
));
1372 snd_pcm_hw_params_get_format_mask(This
->hw_params
, formats
);
1374 fmt
->Format
.wFormatTag
= WAVE_FORMAT_EXTENSIBLE
;
1375 if(snd_pcm_format_mask_test(formats
, SND_PCM_FORMAT_FLOAT_LE
)){
1376 fmt
->Format
.wBitsPerSample
= 32;
1377 fmt
->SubFormat
= KSDATAFORMAT_SUBTYPE_IEEE_FLOAT
;
1378 }else if(snd_pcm_format_mask_test(formats
, SND_PCM_FORMAT_S16_LE
)){
1379 fmt
->Format
.wBitsPerSample
= 16;
1380 fmt
->SubFormat
= KSDATAFORMAT_SUBTYPE_PCM
;
1381 }else if(snd_pcm_format_mask_test(formats
, SND_PCM_FORMAT_U8
)){
1382 fmt
->Format
.wBitsPerSample
= 8;
1383 fmt
->SubFormat
= KSDATAFORMAT_SUBTYPE_PCM
;
1384 }else if(snd_pcm_format_mask_test(formats
, SND_PCM_FORMAT_S32_LE
)){
1385 fmt
->Format
.wBitsPerSample
= 32;
1386 fmt
->SubFormat
= KSDATAFORMAT_SUBTYPE_PCM
;
1387 }else if(snd_pcm_format_mask_test(formats
, SND_PCM_FORMAT_S24_3LE
)){
1388 fmt
->Format
.wBitsPerSample
= 24;
1389 fmt
->SubFormat
= KSDATAFORMAT_SUBTYPE_PCM
;
1391 ERR("Didn't recognize any available ALSA formats\n");
1396 if((err
= snd_pcm_hw_params_get_channels_max(This
->hw_params
,
1397 &max_channels
)) < 0){
1398 WARN("Unable to get max channels: %d (%s)\n", err
, snd_strerror(err
));
1403 if(max_channels
> 2)
1404 fmt
->Format
.nChannels
= 2;
1406 fmt
->Format
.nChannels
= max_channels
;
1408 fmt
->dwChannelMask
= get_channel_mask(fmt
->Format
.nChannels
);
1410 if((err
= snd_pcm_hw_params_get_rate_max(This
->hw_params
, &max_rate
,
1412 WARN("Unable to get max rate: %d (%s)\n", err
, snd_strerror(err
));
1417 if(max_rate
>= 48000)
1418 fmt
->Format
.nSamplesPerSec
= 48000;
1419 else if(max_rate
>= 44100)
1420 fmt
->Format
.nSamplesPerSec
= 44100;
1421 else if(max_rate
>= 22050)
1422 fmt
->Format
.nSamplesPerSec
= 22050;
1423 else if(max_rate
>= 11025)
1424 fmt
->Format
.nSamplesPerSec
= 11025;
1425 else if(max_rate
>= 8000)
1426 fmt
->Format
.nSamplesPerSec
= 8000;
1428 ERR("Unknown max rate: %u\n", max_rate
);
1433 fmt
->Format
.nBlockAlign
= (fmt
->Format
.wBitsPerSample
*
1434 fmt
->Format
.nChannels
) / 8;
1435 fmt
->Format
.nAvgBytesPerSec
= fmt
->Format
.nSamplesPerSec
*
1436 fmt
->Format
.nBlockAlign
;
1438 fmt
->Samples
.wValidBitsPerSample
= fmt
->Format
.wBitsPerSample
;
1439 fmt
->Format
.cbSize
= sizeof(WAVEFORMATEXTENSIBLE
) - sizeof(WAVEFORMATEX
);
1441 dump_fmt((WAVEFORMATEX
*)fmt
);
1442 *pwfx
= (WAVEFORMATEX
*)fmt
;
1445 LeaveCriticalSection(&This
->lock
);
1448 HeapFree(GetProcessHeap(), 0, formats
);
1453 static HRESULT WINAPI
AudioClient_GetDevicePeriod(IAudioClient
*iface
,
1454 REFERENCE_TIME
*defperiod
, REFERENCE_TIME
*minperiod
)
1456 ACImpl
*This
= impl_from_IAudioClient(iface
);
1458 TRACE("(%p)->(%p, %p)\n", This
, defperiod
, minperiod
);
1460 if(!defperiod
&& !minperiod
)
1464 *defperiod
= DefaultPeriod
;
1466 *minperiod
= MinimumPeriod
;
1471 static snd_pcm_sframes_t
alsa_write_best_effort(snd_pcm_t
*handle
, BYTE
*buf
,
1472 snd_pcm_uframes_t frames
, ACImpl
*This
)
1474 snd_pcm_sframes_t written
;
1476 if(This
->session
->mute
){
1478 if((err
= snd_pcm_format_set_silence(This
->alsa_format
, buf
,
1479 frames
* This
->fmt
->nChannels
)) < 0)
1480 WARN("Setting buffer to silence failed: %d (%s)\n", err
,
1484 written
= snd_pcm_writei(handle
, buf
, frames
);
1488 if(written
== -EAGAIN
)
1492 WARN("writei failed, recovering: %ld (%s)\n", written
,
1493 snd_strerror(written
));
1495 ret
= snd_pcm_recover(handle
, written
, 0);
1497 WARN("Could not recover: %d (%s)\n", ret
, snd_strerror(ret
));
1501 written
= snd_pcm_writei(handle
, buf
, frames
);
1507 static void alsa_write_data(ACImpl
*This
)
1509 snd_pcm_sframes_t written
;
1510 snd_pcm_uframes_t to_write
, avail
, write_limit
, max_period
, in_alsa
;
1513 This
->local_buffer
+ (This
->lcl_offs_frames
* This
->fmt
->nBlockAlign
);
1515 /* this call seems to be required to get an accurate snd_pcm_state() */
1516 avail
= snd_pcm_avail_update(This
->pcm_handle
);
1518 if(snd_pcm_state(This
->pcm_handle
) == SND_PCM_STATE_XRUN
||
1519 avail
> This
->alsa_bufsize_frames
){
1520 TRACE("XRun state, recovering\n");
1522 avail
= This
->alsa_bufsize_frames
;
1524 if((err
= snd_pcm_recover(This
->pcm_handle
, -EPIPE
, 1)) < 0)
1525 WARN("snd_pcm_recover failed: %d (%s)\n", err
, snd_strerror(err
));
1527 if((err
= snd_pcm_reset(This
->pcm_handle
)) < 0)
1528 WARN("snd_pcm_reset failed: %d (%s)\n", err
, snd_strerror(err
));
1530 if((err
= snd_pcm_prepare(This
->pcm_handle
)) < 0)
1531 WARN("snd_pcm_prepare failed: %d (%s)\n", err
, snd_strerror(err
));
1534 if(This
->held_frames
== 0)
1537 if(This
->lcl_offs_frames
+ This
->held_frames
> This
->bufsize_frames
)
1538 to_write
= This
->bufsize_frames
- This
->lcl_offs_frames
;
1540 to_write
= This
->held_frames
;
1542 max_period
= max(This
->mmdev_period_frames
, This
->alsa_period_frames
);
1544 /* try to keep 3 ALSA periods or 3 MMDevAPI periods in the ALSA buffer and
1547 in_alsa
= This
->alsa_bufsize_frames
- avail
;
1548 while(in_alsa
+ write_limit
< max_period
* 3)
1549 write_limit
+= max_period
;
1550 if(write_limit
== 0)
1553 to_write
= min(to_write
, write_limit
);
1555 written
= alsa_write_best_effort(This
->pcm_handle
, buf
, to_write
, This
);
1557 WARN("Couldn't write: %ld (%s)\n", written
, snd_strerror(written
));
1561 This
->lcl_offs_frames
+= written
;
1562 This
->lcl_offs_frames
%= This
->bufsize_frames
;
1563 This
->held_frames
-= written
;
1565 if(written
< to_write
){
1566 /* ALSA buffer probably full */
1570 if(This
->held_frames
&& (written
< write_limit
)){
1571 /* wrapped and have some data back at the start to write */
1572 written
= alsa_write_best_effort(This
->pcm_handle
, This
->local_buffer
,
1573 min(This
->held_frames
, write_limit
- written
), This
);
1575 WARN("Couldn't write: %ld (%s)\n", written
, snd_strerror(written
));
1579 This
->lcl_offs_frames
+= written
;
1580 This
->lcl_offs_frames
%= This
->bufsize_frames
;
1581 This
->held_frames
-= written
;
1585 static void alsa_read_data(ACImpl
*This
)
1587 snd_pcm_sframes_t pos
, readable
, nread
;
1589 pos
= (This
->held_frames
+ This
->lcl_offs_frames
) % This
->bufsize_frames
;
1590 readable
= This
->bufsize_frames
- pos
;
1592 nread
= snd_pcm_readi(This
->pcm_handle
,
1593 This
->local_buffer
+ pos
* This
->fmt
->nBlockAlign
, readable
);
1597 WARN("read failed, recovering: %ld (%s)\n", nread
, snd_strerror(nread
));
1599 ret
= snd_pcm_recover(This
->pcm_handle
, nread
, 0);
1601 WARN("Recover failed: %d (%s)\n", ret
, snd_strerror(ret
));
1605 nread
= snd_pcm_readi(This
->pcm_handle
,
1606 This
->local_buffer
+ pos
* This
->fmt
->nBlockAlign
, readable
);
1608 WARN("read failed: %ld (%s)\n", nread
, snd_strerror(nread
));
1613 if(This
->session
->mute
){
1615 if((err
= snd_pcm_format_set_silence(This
->alsa_format
,
1616 This
->local_buffer
+ pos
* This
->fmt
->nBlockAlign
,
1618 WARN("Setting buffer to silence failed: %d (%s)\n", err
,
1622 This
->held_frames
+= nread
;
1624 if(This
->held_frames
> This
->bufsize_frames
){
1625 WARN("Overflow of unread data\n");
1626 This
->lcl_offs_frames
+= This
->held_frames
;
1627 This
->lcl_offs_frames
%= This
->bufsize_frames
;
1628 This
->held_frames
= This
->bufsize_frames
;
1632 static void CALLBACK
alsa_push_buffer_data(void *user
, BOOLEAN timer
)
1634 ACImpl
*This
= user
;
1636 EnterCriticalSection(&This
->lock
);
1639 if(This
->dataflow
== eRender
)
1640 alsa_write_data(This
);
1641 else if(This
->dataflow
== eCapture
)
1642 alsa_read_data(This
);
1645 SetEvent(This
->event
);
1648 LeaveCriticalSection(&This
->lock
);
1651 static HRESULT WINAPI
AudioClient_Start(IAudioClient
*iface
)
1653 ACImpl
*This
= impl_from_IAudioClient(iface
);
1655 TRACE("(%p)\n", This
);
1657 EnterCriticalSection(&This
->lock
);
1660 LeaveCriticalSection(&This
->lock
);
1661 return AUDCLNT_E_NOT_INITIALIZED
;
1664 if((This
->flags
& AUDCLNT_STREAMFLAGS_EVENTCALLBACK
) && !This
->event
){
1665 LeaveCriticalSection(&This
->lock
);
1666 return AUDCLNT_E_EVENTHANDLE_NOT_SET
;
1670 LeaveCriticalSection(&This
->lock
);
1671 return AUDCLNT_E_NOT_STOPPED
;
1674 if(This
->dataflow
== eCapture
){
1675 /* dump any data that might be leftover in the ALSA capture buffer */
1676 snd_pcm_readi(This
->pcm_handle
, This
->local_buffer
,
1677 This
->bufsize_frames
);
1680 if(!CreateTimerQueueTimer(&This
->timer
, g_timer_q
, alsa_push_buffer_data
,
1681 This
, 0, This
->mmdev_period_rt
/ 10000, WT_EXECUTEINTIMERTHREAD
)){
1682 LeaveCriticalSection(&This
->lock
);
1683 WARN("Unable to create timer: %u\n", GetLastError());
1687 This
->started
= TRUE
;
1689 LeaveCriticalSection(&This
->lock
);
1694 static HRESULT WINAPI
AudioClient_Stop(IAudioClient
*iface
)
1696 ACImpl
*This
= impl_from_IAudioClient(iface
);
1700 TRACE("(%p)\n", This
);
1702 EnterCriticalSection(&This
->lock
);
1705 LeaveCriticalSection(&This
->lock
);
1706 return AUDCLNT_E_NOT_INITIALIZED
;
1710 LeaveCriticalSection(&This
->lock
);
1714 /* Stop without losing written frames or position.
1715 * snd_pcm_pause would be appropriate but is unsupported by dmix.
1716 * snd_pcm_drain yields EAGAIN in NONBLOCK mode, except with Pulse. */
1718 event
= CreateEventW(NULL
, TRUE
, FALSE
, NULL
);
1719 wait
= !DeleteTimerQueueTimer(g_timer_q
, This
->timer
, event
);
1721 WARN("DeleteTimerQueueTimer error %u\n", GetLastError());
1722 wait
= wait
&& GetLastError() == ERROR_IO_PENDING
;
1724 This
->started
= FALSE
;
1726 LeaveCriticalSection(&This
->lock
);
1729 WaitForSingleObject(event
, INFINITE
);
1735 static HRESULT WINAPI
AudioClient_Reset(IAudioClient
*iface
)
1737 ACImpl
*This
= impl_from_IAudioClient(iface
);
1739 TRACE("(%p)\n", This
);
1741 EnterCriticalSection(&This
->lock
);
1744 LeaveCriticalSection(&This
->lock
);
1745 return AUDCLNT_E_NOT_INITIALIZED
;
1749 LeaveCriticalSection(&This
->lock
);
1750 return AUDCLNT_E_NOT_STOPPED
;
1753 if(This
->getbuf_last
){
1754 LeaveCriticalSection(&This
->lock
);
1755 return AUDCLNT_E_BUFFER_OPERATION_PENDING
;
1758 if(snd_pcm_drop(This
->pcm_handle
) < 0)
1759 WARN("snd_pcm_drop failed\n");
1761 if(snd_pcm_reset(This
->pcm_handle
) < 0)
1762 WARN("snd_pcm_reset failed\n");
1764 if(snd_pcm_prepare(This
->pcm_handle
) < 0)
1765 WARN("snd_pcm_prepare failed\n");
1767 if(This
->dataflow
== eRender
){
1768 This
->written_frames
= 0;
1769 This
->last_pos_frames
= 0;
1771 This
->written_frames
+= This
->held_frames
;
1773 This
->held_frames
= 0;
1774 This
->lcl_offs_frames
= 0;
1776 LeaveCriticalSection(&This
->lock
);
1781 static HRESULT WINAPI
AudioClient_SetEventHandle(IAudioClient
*iface
,
1784 ACImpl
*This
= impl_from_IAudioClient(iface
);
1786 TRACE("(%p)->(%p)\n", This
, event
);
1789 return E_INVALIDARG
;
1791 EnterCriticalSection(&This
->lock
);
1794 LeaveCriticalSection(&This
->lock
);
1795 return AUDCLNT_E_NOT_INITIALIZED
;
1798 if(!(This
->flags
& AUDCLNT_STREAMFLAGS_EVENTCALLBACK
)){
1799 LeaveCriticalSection(&This
->lock
);
1800 return AUDCLNT_E_EVENTHANDLE_NOT_EXPECTED
;
1803 This
->event
= event
;
1805 LeaveCriticalSection(&This
->lock
);
1810 static HRESULT WINAPI
AudioClient_GetService(IAudioClient
*iface
, REFIID riid
,
1813 ACImpl
*This
= impl_from_IAudioClient(iface
);
1815 TRACE("(%p)->(%s, %p)\n", This
, debugstr_guid(riid
), ppv
);
1821 EnterCriticalSection(&This
->lock
);
1824 LeaveCriticalSection(&This
->lock
);
1825 return AUDCLNT_E_NOT_INITIALIZED
;
1828 if(IsEqualIID(riid
, &IID_IAudioRenderClient
)){
1829 if(This
->dataflow
!= eRender
){
1830 LeaveCriticalSection(&This
->lock
);
1831 return AUDCLNT_E_WRONG_ENDPOINT_TYPE
;
1833 IAudioRenderClient_AddRef(&This
->IAudioRenderClient_iface
);
1834 *ppv
= &This
->IAudioRenderClient_iface
;
1835 }else if(IsEqualIID(riid
, &IID_IAudioCaptureClient
)){
1836 if(This
->dataflow
!= eCapture
){
1837 LeaveCriticalSection(&This
->lock
);
1838 return AUDCLNT_E_WRONG_ENDPOINT_TYPE
;
1840 IAudioCaptureClient_AddRef(&This
->IAudioCaptureClient_iface
);
1841 *ppv
= &This
->IAudioCaptureClient_iface
;
1842 }else if(IsEqualIID(riid
, &IID_IAudioClock
)){
1843 IAudioClock_AddRef(&This
->IAudioClock_iface
);
1844 *ppv
= &This
->IAudioClock_iface
;
1845 }else if(IsEqualIID(riid
, &IID_IAudioStreamVolume
)){
1846 IAudioStreamVolume_AddRef(&This
->IAudioStreamVolume_iface
);
1847 *ppv
= &This
->IAudioStreamVolume_iface
;
1848 }else if(IsEqualIID(riid
, &IID_IAudioSessionControl
)){
1849 if(!This
->session_wrapper
){
1850 This
->session_wrapper
= AudioSessionWrapper_Create(This
);
1851 if(!This
->session_wrapper
){
1852 LeaveCriticalSection(&This
->lock
);
1853 return E_OUTOFMEMORY
;
1856 IAudioSessionControl2_AddRef(&This
->session_wrapper
->IAudioSessionControl2_iface
);
1858 *ppv
= &This
->session_wrapper
->IAudioSessionControl2_iface
;
1859 }else if(IsEqualIID(riid
, &IID_IChannelAudioVolume
)){
1860 if(!This
->session_wrapper
){
1861 This
->session_wrapper
= AudioSessionWrapper_Create(This
);
1862 if(!This
->session_wrapper
){
1863 LeaveCriticalSection(&This
->lock
);
1864 return E_OUTOFMEMORY
;
1867 IChannelAudioVolume_AddRef(&This
->session_wrapper
->IChannelAudioVolume_iface
);
1869 *ppv
= &This
->session_wrapper
->IChannelAudioVolume_iface
;
1870 }else if(IsEqualIID(riid
, &IID_ISimpleAudioVolume
)){
1871 if(!This
->session_wrapper
){
1872 This
->session_wrapper
= AudioSessionWrapper_Create(This
);
1873 if(!This
->session_wrapper
){
1874 LeaveCriticalSection(&This
->lock
);
1875 return E_OUTOFMEMORY
;
1878 ISimpleAudioVolume_AddRef(&This
->session_wrapper
->ISimpleAudioVolume_iface
);
1880 *ppv
= &This
->session_wrapper
->ISimpleAudioVolume_iface
;
1884 LeaveCriticalSection(&This
->lock
);
1888 LeaveCriticalSection(&This
->lock
);
1890 FIXME("stub %s\n", debugstr_guid(riid
));
1891 return E_NOINTERFACE
;
1894 static const IAudioClientVtbl AudioClient_Vtbl
=
1896 AudioClient_QueryInterface
,
1898 AudioClient_Release
,
1899 AudioClient_Initialize
,
1900 AudioClient_GetBufferSize
,
1901 AudioClient_GetStreamLatency
,
1902 AudioClient_GetCurrentPadding
,
1903 AudioClient_IsFormatSupported
,
1904 AudioClient_GetMixFormat
,
1905 AudioClient_GetDevicePeriod
,
1909 AudioClient_SetEventHandle
,
1910 AudioClient_GetService
1913 static HRESULT WINAPI
AudioRenderClient_QueryInterface(
1914 IAudioRenderClient
*iface
, REFIID riid
, void **ppv
)
1916 TRACE("(%p)->(%s, %p)\n", iface
, debugstr_guid(riid
), ppv
);
1922 if(IsEqualIID(riid
, &IID_IUnknown
) ||
1923 IsEqualIID(riid
, &IID_IAudioRenderClient
))
1926 IUnknown_AddRef((IUnknown
*)*ppv
);
1930 WARN("Unknown interface %s\n", debugstr_guid(riid
));
1931 return E_NOINTERFACE
;
1934 static ULONG WINAPI
AudioRenderClient_AddRef(IAudioRenderClient
*iface
)
1936 ACImpl
*This
= impl_from_IAudioRenderClient(iface
);
1937 return AudioClient_AddRef(&This
->IAudioClient_iface
);
1940 static ULONG WINAPI
AudioRenderClient_Release(IAudioRenderClient
*iface
)
1942 ACImpl
*This
= impl_from_IAudioRenderClient(iface
);
1943 return AudioClient_Release(&This
->IAudioClient_iface
);
1946 static HRESULT WINAPI
AudioRenderClient_GetBuffer(IAudioRenderClient
*iface
,
1947 UINT32 frames
, BYTE
**data
)
1949 ACImpl
*This
= impl_from_IAudioRenderClient(iface
);
1954 TRACE("(%p)->(%u, %p)\n", This
, frames
, data
);
1960 EnterCriticalSection(&This
->lock
);
1962 if(This
->getbuf_last
){
1963 LeaveCriticalSection(&This
->lock
);
1964 return AUDCLNT_E_OUT_OF_ORDER
;
1968 LeaveCriticalSection(&This
->lock
);
1972 hr
= IAudioClient_GetCurrentPadding(&This
->IAudioClient_iface
, &pad
);
1974 LeaveCriticalSection(&This
->lock
);
1978 if(pad
+ frames
> This
->bufsize_frames
){
1979 LeaveCriticalSection(&This
->lock
);
1980 return AUDCLNT_E_BUFFER_TOO_LARGE
;
1984 (This
->lcl_offs_frames
+ This
->held_frames
) % This
->bufsize_frames
;
1985 if(write_pos
+ frames
> This
->bufsize_frames
){
1986 if(This
->tmp_buffer_frames
< frames
){
1987 HeapFree(GetProcessHeap(), 0, This
->tmp_buffer
);
1988 This
->tmp_buffer
= HeapAlloc(GetProcessHeap(), 0,
1989 frames
* This
->fmt
->nBlockAlign
);
1990 if(!This
->tmp_buffer
){
1991 LeaveCriticalSection(&This
->lock
);
1992 return E_OUTOFMEMORY
;
1994 This
->tmp_buffer_frames
= frames
;
1996 *data
= This
->tmp_buffer
;
1997 This
->getbuf_last
= -frames
;
1999 *data
= This
->local_buffer
+ write_pos
* This
->fmt
->nBlockAlign
;
2000 This
->getbuf_last
= frames
;
2003 LeaveCriticalSection(&This
->lock
);
2008 static void alsa_wrap_buffer(ACImpl
*This
, BYTE
*buffer
, UINT32 written_frames
)
2010 snd_pcm_uframes_t write_offs_frames
=
2011 (This
->lcl_offs_frames
+ This
->held_frames
) % This
->bufsize_frames
;
2012 UINT32 write_offs_bytes
= write_offs_frames
* This
->fmt
->nBlockAlign
;
2013 snd_pcm_uframes_t chunk_frames
= This
->bufsize_frames
- write_offs_frames
;
2014 UINT32 chunk_bytes
= chunk_frames
* This
->fmt
->nBlockAlign
;
2015 UINT32 written_bytes
= written_frames
* This
->fmt
->nBlockAlign
;
2017 if(written_bytes
<= chunk_bytes
){
2018 memcpy(This
->local_buffer
+ write_offs_bytes
, buffer
, written_bytes
);
2020 memcpy(This
->local_buffer
+ write_offs_bytes
, buffer
, chunk_bytes
);
2021 memcpy(This
->local_buffer
, buffer
+ chunk_bytes
,
2022 written_bytes
- chunk_bytes
);
2026 static HRESULT WINAPI
AudioRenderClient_ReleaseBuffer(
2027 IAudioRenderClient
*iface
, UINT32 written_frames
, DWORD flags
)
2029 ACImpl
*This
= impl_from_IAudioRenderClient(iface
);
2032 TRACE("(%p)->(%u, %x)\n", This
, written_frames
, flags
);
2034 EnterCriticalSection(&This
->lock
);
2036 if(!written_frames
){
2037 This
->getbuf_last
= 0;
2038 LeaveCriticalSection(&This
->lock
);
2042 if(!This
->getbuf_last
){
2043 LeaveCriticalSection(&This
->lock
);
2044 return AUDCLNT_E_OUT_OF_ORDER
;
2047 if(written_frames
> (This
->getbuf_last
>= 0 ? This
->getbuf_last
: -This
->getbuf_last
)){
2048 LeaveCriticalSection(&This
->lock
);
2049 return AUDCLNT_E_INVALID_SIZE
;
2052 if(This
->getbuf_last
>= 0)
2053 buffer
= This
->local_buffer
+ This
->fmt
->nBlockAlign
*
2054 ((This
->lcl_offs_frames
+ This
->held_frames
) % This
->bufsize_frames
);
2056 buffer
= This
->tmp_buffer
;
2058 if(flags
& AUDCLNT_BUFFERFLAGS_SILENT
){
2059 if(This
->fmt
->wBitsPerSample
== 8)
2060 memset(buffer
, 128, written_frames
* This
->fmt
->nBlockAlign
);
2062 memset(buffer
, 0, written_frames
* This
->fmt
->nBlockAlign
);
2065 if(This
->getbuf_last
< 0)
2066 alsa_wrap_buffer(This
, buffer
, written_frames
);
2068 This
->held_frames
+= written_frames
;
2069 This
->written_frames
+= written_frames
;
2070 This
->getbuf_last
= 0;
2072 LeaveCriticalSection(&This
->lock
);
2077 static const IAudioRenderClientVtbl AudioRenderClient_Vtbl
= {
2078 AudioRenderClient_QueryInterface
,
2079 AudioRenderClient_AddRef
,
2080 AudioRenderClient_Release
,
2081 AudioRenderClient_GetBuffer
,
2082 AudioRenderClient_ReleaseBuffer
2085 static HRESULT WINAPI
AudioCaptureClient_QueryInterface(
2086 IAudioCaptureClient
*iface
, REFIID riid
, void **ppv
)
2088 TRACE("(%p)->(%s, %p)\n", iface
, debugstr_guid(riid
), ppv
);
2094 if(IsEqualIID(riid
, &IID_IUnknown
) ||
2095 IsEqualIID(riid
, &IID_IAudioCaptureClient
))
2098 IUnknown_AddRef((IUnknown
*)*ppv
);
2102 WARN("Unknown interface %s\n", debugstr_guid(riid
));
2103 return E_NOINTERFACE
;
2106 static ULONG WINAPI
AudioCaptureClient_AddRef(IAudioCaptureClient
*iface
)
2108 ACImpl
*This
= impl_from_IAudioCaptureClient(iface
);
2109 return IAudioClient_AddRef(&This
->IAudioClient_iface
);
2112 static ULONG WINAPI
AudioCaptureClient_Release(IAudioCaptureClient
*iface
)
2114 ACImpl
*This
= impl_from_IAudioCaptureClient(iface
);
2115 return IAudioClient_Release(&This
->IAudioClient_iface
);
2118 static HRESULT WINAPI
AudioCaptureClient_GetBuffer(IAudioCaptureClient
*iface
,
2119 BYTE
**data
, UINT32
*frames
, DWORD
*flags
, UINT64
*devpos
,
2122 ACImpl
*This
= impl_from_IAudioCaptureClient(iface
);
2124 TRACE("(%p)->(%p, %p, %p, %p, %p)\n", This
, data
, frames
, flags
,
2127 if(!data
|| !frames
|| !flags
)
2130 EnterCriticalSection(&This
->lock
);
2132 if(This
->getbuf_last
){
2133 LeaveCriticalSection(&This
->lock
);
2134 return AUDCLNT_E_OUT_OF_ORDER
;
2137 /* hr = GetNextPacketSize(iface, frames); */
2138 if(This
->held_frames
< This
->mmdev_period_frames
){
2140 LeaveCriticalSection(&This
->lock
);
2141 return AUDCLNT_S_BUFFER_EMPTY
;
2143 *frames
= This
->mmdev_period_frames
;
2145 if(This
->lcl_offs_frames
+ *frames
> This
->bufsize_frames
){
2146 UINT32 chunk_bytes
, offs_bytes
, frames_bytes
;
2147 if(This
->tmp_buffer_frames
< *frames
){
2148 HeapFree(GetProcessHeap(), 0, This
->tmp_buffer
);
2149 This
->tmp_buffer
= HeapAlloc(GetProcessHeap(), 0,
2150 *frames
* This
->fmt
->nBlockAlign
);
2151 if(!This
->tmp_buffer
){
2152 LeaveCriticalSection(&This
->lock
);
2153 return E_OUTOFMEMORY
;
2155 This
->tmp_buffer_frames
= *frames
;
2158 *data
= This
->tmp_buffer
;
2159 chunk_bytes
= (This
->bufsize_frames
- This
->lcl_offs_frames
) *
2160 This
->fmt
->nBlockAlign
;
2161 offs_bytes
= This
->lcl_offs_frames
* This
->fmt
->nBlockAlign
;
2162 frames_bytes
= *frames
* This
->fmt
->nBlockAlign
;
2163 memcpy(This
->tmp_buffer
, This
->local_buffer
+ offs_bytes
, chunk_bytes
);
2164 memcpy(This
->tmp_buffer
+ chunk_bytes
, This
->local_buffer
,
2165 frames_bytes
- chunk_bytes
);
2167 *data
= This
->local_buffer
+
2168 This
->lcl_offs_frames
* This
->fmt
->nBlockAlign
;
2170 This
->getbuf_last
= *frames
;
2174 *devpos
= This
->written_frames
;
2175 if(qpcpos
){ /* fixme: qpc of recording time */
2176 LARGE_INTEGER stamp
, freq
;
2177 QueryPerformanceCounter(&stamp
);
2178 QueryPerformanceFrequency(&freq
);
2179 *qpcpos
= (stamp
.QuadPart
* (INT64
)10000000) / freq
.QuadPart
;
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
);
2197 This
->getbuf_last
= 0;
2198 LeaveCriticalSection(&This
->lock
);
2202 if(!This
->getbuf_last
){
2203 LeaveCriticalSection(&This
->lock
);
2204 return AUDCLNT_E_OUT_OF_ORDER
;
2207 if(This
->getbuf_last
!= done
){
2208 LeaveCriticalSection(&This
->lock
);
2209 return AUDCLNT_E_INVALID_SIZE
;
2212 This
->written_frames
+= done
;
2213 This
->held_frames
-= done
;
2214 This
->lcl_offs_frames
+= done
;
2215 This
->lcl_offs_frames
%= This
->bufsize_frames
;
2216 This
->getbuf_last
= 0;
2218 LeaveCriticalSection(&This
->lock
);
2223 static HRESULT WINAPI
AudioCaptureClient_GetNextPacketSize(
2224 IAudioCaptureClient
*iface
, UINT32
*frames
)
2226 ACImpl
*This
= impl_from_IAudioCaptureClient(iface
);
2228 TRACE("(%p)->(%p)\n", This
, frames
);
2233 EnterCriticalSection(&This
->lock
);
2235 *frames
= This
->held_frames
< This
->mmdev_period_frames
? 0 : This
->mmdev_period_frames
;
2237 LeaveCriticalSection(&This
->lock
);
2242 static const IAudioCaptureClientVtbl AudioCaptureClient_Vtbl
=
2244 AudioCaptureClient_QueryInterface
,
2245 AudioCaptureClient_AddRef
,
2246 AudioCaptureClient_Release
,
2247 AudioCaptureClient_GetBuffer
,
2248 AudioCaptureClient_ReleaseBuffer
,
2249 AudioCaptureClient_GetNextPacketSize
2252 static HRESULT WINAPI
AudioClock_QueryInterface(IAudioClock
*iface
,
2253 REFIID riid
, void **ppv
)
2255 ACImpl
*This
= impl_from_IAudioClock(iface
);
2257 TRACE("(%p)->(%s, %p)\n", iface
, debugstr_guid(riid
), ppv
);
2263 if(IsEqualIID(riid
, &IID_IUnknown
) || IsEqualIID(riid
, &IID_IAudioClock
))
2265 else if(IsEqualIID(riid
, &IID_IAudioClock2
))
2266 *ppv
= &This
->IAudioClock2_iface
;
2268 IUnknown_AddRef((IUnknown
*)*ppv
);
2272 WARN("Unknown interface %s\n", debugstr_guid(riid
));
2273 return E_NOINTERFACE
;
2276 static ULONG WINAPI
AudioClock_AddRef(IAudioClock
*iface
)
2278 ACImpl
*This
= impl_from_IAudioClock(iface
);
2279 return IAudioClient_AddRef(&This
->IAudioClient_iface
);
2282 static ULONG WINAPI
AudioClock_Release(IAudioClock
*iface
)
2284 ACImpl
*This
= impl_from_IAudioClock(iface
);
2285 return IAudioClient_Release(&This
->IAudioClient_iface
);
2288 static HRESULT WINAPI
AudioClock_GetFrequency(IAudioClock
*iface
, UINT64
*freq
)
2290 ACImpl
*This
= impl_from_IAudioClock(iface
);
2292 TRACE("(%p)->(%p)\n", This
, freq
);
2294 *freq
= This
->fmt
->nSamplesPerSec
;
2299 static HRESULT WINAPI
AudioClock_GetPosition(IAudioClock
*iface
, UINT64
*pos
,
2302 ACImpl
*This
= impl_from_IAudioClock(iface
);
2303 UINT64 written_frames
, position
;
2306 snd_pcm_state_t alsa_state
;
2307 snd_pcm_uframes_t avail_frames
;
2308 snd_pcm_sframes_t delay_frames
;
2310 TRACE("(%p)->(%p, %p)\n", This
, pos
, qpctime
);
2315 EnterCriticalSection(&This
->lock
);
2317 /* call required to get accurate snd_pcm_state() */
2318 avail_frames
= snd_pcm_avail_update(This
->pcm_handle
);
2319 alsa_state
= snd_pcm_state(This
->pcm_handle
);
2320 written_frames
= This
->written_frames
;
2321 held_frames
= This
->held_frames
;
2323 err
= snd_pcm_delay(This
->pcm_handle
, &delay_frames
);
2325 /* old Pulse, shortly after start */
2326 WARN("snd_pcm_delay failed in state %u: %d (%s)\n", alsa_state
, err
, snd_strerror(err
));
2329 if(This
->dataflow
== eRender
){
2330 position
= written_frames
- held_frames
; /* maximum */
2331 if(!This
->started
|| alsa_state
> SND_PCM_STATE_RUNNING
)
2332 ; /* mmdevapi stopped or ALSA underrun: pretend everything was played */
2333 else if(err
<0 || delay_frames
> position
- This
->last_pos_frames
)
2334 /* Pulse bug: past underrun, despite recovery, avail_frames & delay
2335 * may be larger than alsa_bufsize_frames, as if cumulating frames. */
2336 /* Pulse bug: EIO(-5) shortly after starting: nothing played */
2337 position
= This
->last_pos_frames
;
2338 else if(delay_frames
> 0)
2339 position
-= delay_frames
;
2341 position
= written_frames
+ held_frames
;
2343 /* ensure monotic growth */
2344 This
->last_pos_frames
= position
;
2346 LeaveCriticalSection(&This
->lock
);
2348 TRACE("frames written: %u, held: %u, avail: %ld, delay: %ld state %d, pos: %u\n",
2349 (UINT32
)(written_frames
%1000000000), held_frames
,
2350 avail_frames
, delay_frames
, alsa_state
, (UINT32
)(position
%1000000000));
2354 LARGE_INTEGER stamp
, freq
;
2355 QueryPerformanceCounter(&stamp
);
2356 QueryPerformanceFrequency(&freq
);
2357 *qpctime
= (stamp
.QuadPart
* (INT64
)10000000) / freq
.QuadPart
;
2363 static HRESULT WINAPI
AudioClock_GetCharacteristics(IAudioClock
*iface
,
2366 ACImpl
*This
= impl_from_IAudioClock(iface
);
2368 TRACE("(%p)->(%p)\n", This
, chars
);
2373 *chars
= AUDIOCLOCK_CHARACTERISTIC_FIXED_FREQ
;
2378 static const IAudioClockVtbl AudioClock_Vtbl
=
2380 AudioClock_QueryInterface
,
2383 AudioClock_GetFrequency
,
2384 AudioClock_GetPosition
,
2385 AudioClock_GetCharacteristics
2388 static HRESULT WINAPI
AudioClock2_QueryInterface(IAudioClock2
*iface
,
2389 REFIID riid
, void **ppv
)
2391 ACImpl
*This
= impl_from_IAudioClock2(iface
);
2392 return IAudioClock_QueryInterface(&This
->IAudioClock_iface
, riid
, ppv
);
2395 static ULONG WINAPI
AudioClock2_AddRef(IAudioClock2
*iface
)
2397 ACImpl
*This
= impl_from_IAudioClock2(iface
);
2398 return IAudioClient_AddRef(&This
->IAudioClient_iface
);
2401 static ULONG WINAPI
AudioClock2_Release(IAudioClock2
*iface
)
2403 ACImpl
*This
= impl_from_IAudioClock2(iface
);
2404 return IAudioClient_Release(&This
->IAudioClient_iface
);
2407 static HRESULT WINAPI
AudioClock2_GetDevicePosition(IAudioClock2
*iface
,
2408 UINT64
*pos
, UINT64
*qpctime
)
2410 ACImpl
*This
= impl_from_IAudioClock2(iface
);
2412 FIXME("(%p)->(%p, %p)\n", This
, pos
, qpctime
);
2417 static const IAudioClock2Vtbl AudioClock2_Vtbl
=
2419 AudioClock2_QueryInterface
,
2421 AudioClock2_Release
,
2422 AudioClock2_GetDevicePosition
2425 static AudioSessionWrapper
*AudioSessionWrapper_Create(ACImpl
*client
)
2427 AudioSessionWrapper
*ret
;
2429 ret
= HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY
,
2430 sizeof(AudioSessionWrapper
));
2434 ret
->IAudioSessionControl2_iface
.lpVtbl
= &AudioSessionControl2_Vtbl
;
2435 ret
->ISimpleAudioVolume_iface
.lpVtbl
= &SimpleAudioVolume_Vtbl
;
2436 ret
->IChannelAudioVolume_iface
.lpVtbl
= &ChannelAudioVolume_Vtbl
;
2440 ret
->client
= client
;
2442 ret
->session
= client
->session
;
2443 AudioClient_AddRef(&client
->IAudioClient_iface
);
2449 static HRESULT WINAPI
AudioSessionControl_QueryInterface(
2450 IAudioSessionControl2
*iface
, REFIID riid
, void **ppv
)
2452 TRACE("(%p)->(%s, %p)\n", iface
, debugstr_guid(riid
), ppv
);
2458 if(IsEqualIID(riid
, &IID_IUnknown
) ||
2459 IsEqualIID(riid
, &IID_IAudioSessionControl
) ||
2460 IsEqualIID(riid
, &IID_IAudioSessionControl2
))
2463 IUnknown_AddRef((IUnknown
*)*ppv
);
2467 WARN("Unknown interface %s\n", debugstr_guid(riid
));
2468 return E_NOINTERFACE
;
2471 static ULONG WINAPI
AudioSessionControl_AddRef(IAudioSessionControl2
*iface
)
2473 AudioSessionWrapper
*This
= impl_from_IAudioSessionControl2(iface
);
2475 ref
= InterlockedIncrement(&This
->ref
);
2476 TRACE("(%p) Refcount now %u\n", This
, ref
);
2480 static ULONG WINAPI
AudioSessionControl_Release(IAudioSessionControl2
*iface
)
2482 AudioSessionWrapper
*This
= impl_from_IAudioSessionControl2(iface
);
2484 ref
= InterlockedDecrement(&This
->ref
);
2485 TRACE("(%p) Refcount now %u\n", This
, ref
);
2488 EnterCriticalSection(&This
->client
->lock
);
2489 This
->client
->session_wrapper
= NULL
;
2490 LeaveCriticalSection(&This
->client
->lock
);
2491 AudioClient_Release(&This
->client
->IAudioClient_iface
);
2493 HeapFree(GetProcessHeap(), 0, This
);
2498 static HRESULT WINAPI
AudioSessionControl_GetState(IAudioSessionControl2
*iface
,
2499 AudioSessionState
*state
)
2501 AudioSessionWrapper
*This
= impl_from_IAudioSessionControl2(iface
);
2504 TRACE("(%p)->(%p)\n", This
, state
);
2507 return NULL_PTR_ERR
;
2509 EnterCriticalSection(&g_sessions_lock
);
2511 if(list_empty(&This
->session
->clients
)){
2512 *state
= AudioSessionStateExpired
;
2513 LeaveCriticalSection(&g_sessions_lock
);
2517 LIST_FOR_EACH_ENTRY(client
, &This
->session
->clients
, ACImpl
, entry
){
2518 EnterCriticalSection(&client
->lock
);
2519 if(client
->started
){
2520 *state
= AudioSessionStateActive
;
2521 LeaveCriticalSection(&client
->lock
);
2522 LeaveCriticalSection(&g_sessions_lock
);
2525 LeaveCriticalSection(&client
->lock
);
2528 LeaveCriticalSection(&g_sessions_lock
);
2530 *state
= AudioSessionStateInactive
;
2535 static HRESULT WINAPI
AudioSessionControl_GetDisplayName(
2536 IAudioSessionControl2
*iface
, WCHAR
**name
)
2538 AudioSessionWrapper
*This
= impl_from_IAudioSessionControl2(iface
);
2540 FIXME("(%p)->(%p) - stub\n", This
, name
);
2545 static HRESULT WINAPI
AudioSessionControl_SetDisplayName(
2546 IAudioSessionControl2
*iface
, const WCHAR
*name
, const GUID
*session
)
2548 AudioSessionWrapper
*This
= impl_from_IAudioSessionControl2(iface
);
2550 FIXME("(%p)->(%p, %s) - stub\n", This
, name
, debugstr_guid(session
));
2555 static HRESULT WINAPI
AudioSessionControl_GetIconPath(
2556 IAudioSessionControl2
*iface
, WCHAR
**path
)
2558 AudioSessionWrapper
*This
= impl_from_IAudioSessionControl2(iface
);
2560 FIXME("(%p)->(%p) - stub\n", This
, path
);
2565 static HRESULT WINAPI
AudioSessionControl_SetIconPath(
2566 IAudioSessionControl2
*iface
, const WCHAR
*path
, const GUID
*session
)
2568 AudioSessionWrapper
*This
= impl_from_IAudioSessionControl2(iface
);
2570 FIXME("(%p)->(%p, %s) - stub\n", This
, path
, debugstr_guid(session
));
2575 static HRESULT WINAPI
AudioSessionControl_GetGroupingParam(
2576 IAudioSessionControl2
*iface
, GUID
*group
)
2578 AudioSessionWrapper
*This
= impl_from_IAudioSessionControl2(iface
);
2580 FIXME("(%p)->(%p) - stub\n", This
, group
);
2585 static HRESULT WINAPI
AudioSessionControl_SetGroupingParam(
2586 IAudioSessionControl2
*iface
, const GUID
*group
, const GUID
*session
)
2588 AudioSessionWrapper
*This
= impl_from_IAudioSessionControl2(iface
);
2590 FIXME("(%p)->(%s, %s) - stub\n", This
, debugstr_guid(group
),
2591 debugstr_guid(session
));
2596 static HRESULT WINAPI
AudioSessionControl_RegisterAudioSessionNotification(
2597 IAudioSessionControl2
*iface
, IAudioSessionEvents
*events
)
2599 AudioSessionWrapper
*This
= impl_from_IAudioSessionControl2(iface
);
2601 FIXME("(%p)->(%p) - stub\n", This
, events
);
2606 static HRESULT WINAPI
AudioSessionControl_UnregisterAudioSessionNotification(
2607 IAudioSessionControl2
*iface
, IAudioSessionEvents
*events
)
2609 AudioSessionWrapper
*This
= impl_from_IAudioSessionControl2(iface
);
2611 FIXME("(%p)->(%p) - stub\n", This
, events
);
2616 static HRESULT WINAPI
AudioSessionControl_GetSessionIdentifier(
2617 IAudioSessionControl2
*iface
, WCHAR
**id
)
2619 AudioSessionWrapper
*This
= impl_from_IAudioSessionControl2(iface
);
2621 FIXME("(%p)->(%p) - stub\n", This
, id
);
2626 static HRESULT WINAPI
AudioSessionControl_GetSessionInstanceIdentifier(
2627 IAudioSessionControl2
*iface
, WCHAR
**id
)
2629 AudioSessionWrapper
*This
= impl_from_IAudioSessionControl2(iface
);
2631 FIXME("(%p)->(%p) - stub\n", This
, id
);
2636 static HRESULT WINAPI
AudioSessionControl_GetProcessId(
2637 IAudioSessionControl2
*iface
, DWORD
*pid
)
2639 AudioSessionWrapper
*This
= impl_from_IAudioSessionControl2(iface
);
2641 TRACE("(%p)->(%p)\n", This
, pid
);
2646 *pid
= GetCurrentProcessId();
2651 static HRESULT WINAPI
AudioSessionControl_IsSystemSoundsSession(
2652 IAudioSessionControl2
*iface
)
2654 AudioSessionWrapper
*This
= impl_from_IAudioSessionControl2(iface
);
2656 TRACE("(%p)\n", This
);
2661 static HRESULT WINAPI
AudioSessionControl_SetDuckingPreference(
2662 IAudioSessionControl2
*iface
, BOOL optout
)
2664 AudioSessionWrapper
*This
= impl_from_IAudioSessionControl2(iface
);
2666 TRACE("(%p)->(%d)\n", This
, optout
);
2671 static const IAudioSessionControl2Vtbl AudioSessionControl2_Vtbl
=
2673 AudioSessionControl_QueryInterface
,
2674 AudioSessionControl_AddRef
,
2675 AudioSessionControl_Release
,
2676 AudioSessionControl_GetState
,
2677 AudioSessionControl_GetDisplayName
,
2678 AudioSessionControl_SetDisplayName
,
2679 AudioSessionControl_GetIconPath
,
2680 AudioSessionControl_SetIconPath
,
2681 AudioSessionControl_GetGroupingParam
,
2682 AudioSessionControl_SetGroupingParam
,
2683 AudioSessionControl_RegisterAudioSessionNotification
,
2684 AudioSessionControl_UnregisterAudioSessionNotification
,
2685 AudioSessionControl_GetSessionIdentifier
,
2686 AudioSessionControl_GetSessionInstanceIdentifier
,
2687 AudioSessionControl_GetProcessId
,
2688 AudioSessionControl_IsSystemSoundsSession
,
2689 AudioSessionControl_SetDuckingPreference
2692 static HRESULT WINAPI
SimpleAudioVolume_QueryInterface(
2693 ISimpleAudioVolume
*iface
, REFIID riid
, void **ppv
)
2695 TRACE("(%p)->(%s, %p)\n", iface
, debugstr_guid(riid
), ppv
);
2701 if(IsEqualIID(riid
, &IID_IUnknown
) ||
2702 IsEqualIID(riid
, &IID_ISimpleAudioVolume
))
2705 IUnknown_AddRef((IUnknown
*)*ppv
);
2709 WARN("Unknown interface %s\n", debugstr_guid(riid
));
2710 return E_NOINTERFACE
;
2713 static ULONG WINAPI
SimpleAudioVolume_AddRef(ISimpleAudioVolume
*iface
)
2715 AudioSessionWrapper
*This
= impl_from_ISimpleAudioVolume(iface
);
2716 return AudioSessionControl_AddRef(&This
->IAudioSessionControl2_iface
);
2719 static ULONG WINAPI
SimpleAudioVolume_Release(ISimpleAudioVolume
*iface
)
2721 AudioSessionWrapper
*This
= impl_from_ISimpleAudioVolume(iface
);
2722 return AudioSessionControl_Release(&This
->IAudioSessionControl2_iface
);
2725 static HRESULT WINAPI
SimpleAudioVolume_SetMasterVolume(
2726 ISimpleAudioVolume
*iface
, float level
, const GUID
*context
)
2728 AudioSessionWrapper
*This
= impl_from_ISimpleAudioVolume(iface
);
2729 AudioSession
*session
= This
->session
;
2731 TRACE("(%p)->(%f, %s)\n", session
, level
, wine_dbgstr_guid(context
));
2733 if(level
< 0.f
|| level
> 1.f
)
2734 return E_INVALIDARG
;
2737 FIXME("Notifications not supported yet\n");
2739 TRACE("ALSA does not support volume control\n");
2741 EnterCriticalSection(&session
->lock
);
2743 session
->master_vol
= level
;
2745 LeaveCriticalSection(&session
->lock
);
2750 static HRESULT WINAPI
SimpleAudioVolume_GetMasterVolume(
2751 ISimpleAudioVolume
*iface
, float *level
)
2753 AudioSessionWrapper
*This
= impl_from_ISimpleAudioVolume(iface
);
2754 AudioSession
*session
= This
->session
;
2756 TRACE("(%p)->(%p)\n", session
, level
);
2759 return NULL_PTR_ERR
;
2761 *level
= session
->master_vol
;
2766 static HRESULT WINAPI
SimpleAudioVolume_SetMute(ISimpleAudioVolume
*iface
,
2767 BOOL mute
, const GUID
*context
)
2769 AudioSessionWrapper
*This
= impl_from_ISimpleAudioVolume(iface
);
2770 AudioSession
*session
= This
->session
;
2772 TRACE("(%p)->(%u, %p)\n", session
, mute
, context
);
2775 FIXME("Notifications not supported yet\n");
2777 session
->mute
= mute
;
2782 static HRESULT WINAPI
SimpleAudioVolume_GetMute(ISimpleAudioVolume
*iface
,
2785 AudioSessionWrapper
*This
= impl_from_ISimpleAudioVolume(iface
);
2786 AudioSession
*session
= This
->session
;
2788 TRACE("(%p)->(%p)\n", session
, mute
);
2791 return NULL_PTR_ERR
;
2793 *mute
= session
->mute
;
2798 static const ISimpleAudioVolumeVtbl SimpleAudioVolume_Vtbl
=
2800 SimpleAudioVolume_QueryInterface
,
2801 SimpleAudioVolume_AddRef
,
2802 SimpleAudioVolume_Release
,
2803 SimpleAudioVolume_SetMasterVolume
,
2804 SimpleAudioVolume_GetMasterVolume
,
2805 SimpleAudioVolume_SetMute
,
2806 SimpleAudioVolume_GetMute
2809 static HRESULT WINAPI
AudioStreamVolume_QueryInterface(
2810 IAudioStreamVolume
*iface
, REFIID riid
, void **ppv
)
2812 TRACE("(%p)->(%s, %p)\n", iface
, debugstr_guid(riid
), ppv
);
2818 if(IsEqualIID(riid
, &IID_IUnknown
) ||
2819 IsEqualIID(riid
, &IID_IAudioStreamVolume
))
2822 IUnknown_AddRef((IUnknown
*)*ppv
);
2826 WARN("Unknown interface %s\n", debugstr_guid(riid
));
2827 return E_NOINTERFACE
;
2830 static ULONG WINAPI
AudioStreamVolume_AddRef(IAudioStreamVolume
*iface
)
2832 ACImpl
*This
= impl_from_IAudioStreamVolume(iface
);
2833 return IAudioClient_AddRef(&This
->IAudioClient_iface
);
2836 static ULONG WINAPI
AudioStreamVolume_Release(IAudioStreamVolume
*iface
)
2838 ACImpl
*This
= impl_from_IAudioStreamVolume(iface
);
2839 return IAudioClient_Release(&This
->IAudioClient_iface
);
2842 static HRESULT WINAPI
AudioStreamVolume_GetChannelCount(
2843 IAudioStreamVolume
*iface
, UINT32
*out
)
2845 ACImpl
*This
= impl_from_IAudioStreamVolume(iface
);
2847 TRACE("(%p)->(%p)\n", This
, out
);
2852 *out
= This
->fmt
->nChannels
;
2857 static HRESULT WINAPI
AudioStreamVolume_SetChannelVolume(
2858 IAudioStreamVolume
*iface
, UINT32 index
, float level
)
2860 ACImpl
*This
= impl_from_IAudioStreamVolume(iface
);
2862 TRACE("(%p)->(%d, %f)\n", This
, index
, level
);
2864 if(level
< 0.f
|| level
> 1.f
)
2865 return E_INVALIDARG
;
2867 if(index
>= This
->fmt
->nChannels
)
2868 return E_INVALIDARG
;
2870 TRACE("ALSA does not support volume control\n");
2872 EnterCriticalSection(&This
->lock
);
2874 This
->vols
[index
] = level
;
2876 LeaveCriticalSection(&This
->lock
);
2881 static HRESULT WINAPI
AudioStreamVolume_GetChannelVolume(
2882 IAudioStreamVolume
*iface
, UINT32 index
, float *level
)
2884 ACImpl
*This
= impl_from_IAudioStreamVolume(iface
);
2886 TRACE("(%p)->(%d, %p)\n", This
, index
, level
);
2891 if(index
>= This
->fmt
->nChannels
)
2892 return E_INVALIDARG
;
2894 *level
= This
->vols
[index
];
2899 static HRESULT WINAPI
AudioStreamVolume_SetAllVolumes(
2900 IAudioStreamVolume
*iface
, UINT32 count
, const float *levels
)
2902 ACImpl
*This
= impl_from_IAudioStreamVolume(iface
);
2905 TRACE("(%p)->(%d, %p)\n", This
, count
, levels
);
2910 if(count
!= This
->fmt
->nChannels
)
2911 return E_INVALIDARG
;
2913 TRACE("ALSA does not support volume control\n");
2915 EnterCriticalSection(&This
->lock
);
2917 for(i
= 0; i
< count
; ++i
)
2918 This
->vols
[i
] = levels
[i
];
2920 LeaveCriticalSection(&This
->lock
);
2925 static HRESULT WINAPI
AudioStreamVolume_GetAllVolumes(
2926 IAudioStreamVolume
*iface
, UINT32 count
, float *levels
)
2928 ACImpl
*This
= impl_from_IAudioStreamVolume(iface
);
2931 TRACE("(%p)->(%d, %p)\n", This
, count
, levels
);
2936 if(count
!= This
->fmt
->nChannels
)
2937 return E_INVALIDARG
;
2939 EnterCriticalSection(&This
->lock
);
2941 for(i
= 0; i
< count
; ++i
)
2942 levels
[i
] = This
->vols
[i
];
2944 LeaveCriticalSection(&This
->lock
);
2949 static const IAudioStreamVolumeVtbl AudioStreamVolume_Vtbl
=
2951 AudioStreamVolume_QueryInterface
,
2952 AudioStreamVolume_AddRef
,
2953 AudioStreamVolume_Release
,
2954 AudioStreamVolume_GetChannelCount
,
2955 AudioStreamVolume_SetChannelVolume
,
2956 AudioStreamVolume_GetChannelVolume
,
2957 AudioStreamVolume_SetAllVolumes
,
2958 AudioStreamVolume_GetAllVolumes
2961 static HRESULT WINAPI
ChannelAudioVolume_QueryInterface(
2962 IChannelAudioVolume
*iface
, REFIID riid
, void **ppv
)
2964 TRACE("(%p)->(%s, %p)\n", iface
, debugstr_guid(riid
), ppv
);
2970 if(IsEqualIID(riid
, &IID_IUnknown
) ||
2971 IsEqualIID(riid
, &IID_IChannelAudioVolume
))
2974 IUnknown_AddRef((IUnknown
*)*ppv
);
2978 WARN("Unknown interface %s\n", debugstr_guid(riid
));
2979 return E_NOINTERFACE
;
2982 static ULONG WINAPI
ChannelAudioVolume_AddRef(IChannelAudioVolume
*iface
)
2984 AudioSessionWrapper
*This
= impl_from_IChannelAudioVolume(iface
);
2985 return AudioSessionControl_AddRef(&This
->IAudioSessionControl2_iface
);
2988 static ULONG WINAPI
ChannelAudioVolume_Release(IChannelAudioVolume
*iface
)
2990 AudioSessionWrapper
*This
= impl_from_IChannelAudioVolume(iface
);
2991 return AudioSessionControl_Release(&This
->IAudioSessionControl2_iface
);
2994 static HRESULT WINAPI
ChannelAudioVolume_GetChannelCount(
2995 IChannelAudioVolume
*iface
, UINT32
*out
)
2997 AudioSessionWrapper
*This
= impl_from_IChannelAudioVolume(iface
);
2998 AudioSession
*session
= This
->session
;
3000 TRACE("(%p)->(%p)\n", session
, out
);
3003 return NULL_PTR_ERR
;
3005 *out
= session
->channel_count
;
3010 static HRESULT WINAPI
ChannelAudioVolume_SetChannelVolume(
3011 IChannelAudioVolume
*iface
, UINT32 index
, float level
,
3012 const GUID
*context
)
3014 AudioSessionWrapper
*This
= impl_from_IChannelAudioVolume(iface
);
3015 AudioSession
*session
= This
->session
;
3017 TRACE("(%p)->(%d, %f, %s)\n", session
, index
, level
,
3018 wine_dbgstr_guid(context
));
3020 if(level
< 0.f
|| level
> 1.f
)
3021 return E_INVALIDARG
;
3023 if(index
>= session
->channel_count
)
3024 return E_INVALIDARG
;
3027 FIXME("Notifications not supported yet\n");
3029 TRACE("ALSA does not support volume control\n");
3031 EnterCriticalSection(&session
->lock
);
3033 session
->channel_vols
[index
] = level
;
3035 LeaveCriticalSection(&session
->lock
);
3040 static HRESULT WINAPI
ChannelAudioVolume_GetChannelVolume(
3041 IChannelAudioVolume
*iface
, UINT32 index
, float *level
)
3043 AudioSessionWrapper
*This
= impl_from_IChannelAudioVolume(iface
);
3044 AudioSession
*session
= This
->session
;
3046 TRACE("(%p)->(%d, %p)\n", session
, index
, level
);
3049 return NULL_PTR_ERR
;
3051 if(index
>= session
->channel_count
)
3052 return E_INVALIDARG
;
3054 *level
= session
->channel_vols
[index
];
3059 static HRESULT WINAPI
ChannelAudioVolume_SetAllVolumes(
3060 IChannelAudioVolume
*iface
, UINT32 count
, const float *levels
,
3061 const GUID
*context
)
3063 AudioSessionWrapper
*This
= impl_from_IChannelAudioVolume(iface
);
3064 AudioSession
*session
= This
->session
;
3067 TRACE("(%p)->(%d, %p, %s)\n", session
, count
, levels
,
3068 wine_dbgstr_guid(context
));
3071 return NULL_PTR_ERR
;
3073 if(count
!= session
->channel_count
)
3074 return E_INVALIDARG
;
3077 FIXME("Notifications not supported yet\n");
3079 TRACE("ALSA does not support volume control\n");
3081 EnterCriticalSection(&session
->lock
);
3083 for(i
= 0; i
< count
; ++i
)
3084 session
->channel_vols
[i
] = levels
[i
];
3086 LeaveCriticalSection(&session
->lock
);
3091 static HRESULT WINAPI
ChannelAudioVolume_GetAllVolumes(
3092 IChannelAudioVolume
*iface
, UINT32 count
, float *levels
)
3094 AudioSessionWrapper
*This
= impl_from_IChannelAudioVolume(iface
);
3095 AudioSession
*session
= This
->session
;
3098 TRACE("(%p)->(%d, %p)\n", session
, count
, levels
);
3101 return NULL_PTR_ERR
;
3103 if(count
!= session
->channel_count
)
3104 return E_INVALIDARG
;
3106 for(i
= 0; i
< count
; ++i
)
3107 levels
[i
] = session
->channel_vols
[i
];
3112 static const IChannelAudioVolumeVtbl ChannelAudioVolume_Vtbl
=
3114 ChannelAudioVolume_QueryInterface
,
3115 ChannelAudioVolume_AddRef
,
3116 ChannelAudioVolume_Release
,
3117 ChannelAudioVolume_GetChannelCount
,
3118 ChannelAudioVolume_SetChannelVolume
,
3119 ChannelAudioVolume_GetChannelVolume
,
3120 ChannelAudioVolume_SetAllVolumes
,
3121 ChannelAudioVolume_GetAllVolumes
3124 static HRESULT WINAPI
AudioSessionManager_QueryInterface(IAudioSessionManager2
*iface
,
3125 REFIID riid
, void **ppv
)
3127 TRACE("(%p)->(%s, %p)\n", iface
, debugstr_guid(riid
), ppv
);
3133 if(IsEqualIID(riid
, &IID_IUnknown
) ||
3134 IsEqualIID(riid
, &IID_IAudioSessionManager
) ||
3135 IsEqualIID(riid
, &IID_IAudioSessionManager2
))
3138 IUnknown_AddRef((IUnknown
*)*ppv
);
3142 WARN("Unknown interface %s\n", debugstr_guid(riid
));
3143 return E_NOINTERFACE
;
3146 static ULONG WINAPI
AudioSessionManager_AddRef(IAudioSessionManager2
*iface
)
3148 SessionMgr
*This
= impl_from_IAudioSessionManager2(iface
);
3150 ref
= InterlockedIncrement(&This
->ref
);
3151 TRACE("(%p) Refcount now %u\n", This
, ref
);
3155 static ULONG WINAPI
AudioSessionManager_Release(IAudioSessionManager2
*iface
)
3157 SessionMgr
*This
= impl_from_IAudioSessionManager2(iface
);
3159 ref
= InterlockedDecrement(&This
->ref
);
3160 TRACE("(%p) Refcount now %u\n", This
, ref
);
3162 HeapFree(GetProcessHeap(), 0, This
);
3166 static HRESULT WINAPI
AudioSessionManager_GetAudioSessionControl(
3167 IAudioSessionManager2
*iface
, const GUID
*session_guid
, DWORD flags
,
3168 IAudioSessionControl
**out
)
3170 SessionMgr
*This
= impl_from_IAudioSessionManager2(iface
);
3171 AudioSession
*session
;
3172 AudioSessionWrapper
*wrapper
;
3175 TRACE("(%p)->(%s, %x, %p)\n", This
, debugstr_guid(session_guid
),
3178 hr
= get_audio_session(session_guid
, This
->device
, 0, &session
);
3182 wrapper
= AudioSessionWrapper_Create(NULL
);
3184 return E_OUTOFMEMORY
;
3186 wrapper
->session
= session
;
3188 *out
= (IAudioSessionControl
*)&wrapper
->IAudioSessionControl2_iface
;
3193 static HRESULT WINAPI
AudioSessionManager_GetSimpleAudioVolume(
3194 IAudioSessionManager2
*iface
, const GUID
*session_guid
, DWORD flags
,
3195 ISimpleAudioVolume
**out
)
3197 SessionMgr
*This
= impl_from_IAudioSessionManager2(iface
);
3198 AudioSession
*session
;
3199 AudioSessionWrapper
*wrapper
;
3202 TRACE("(%p)->(%s, %x, %p)\n", This
, debugstr_guid(session_guid
),
3205 hr
= get_audio_session(session_guid
, This
->device
, 0, &session
);
3209 wrapper
= AudioSessionWrapper_Create(NULL
);
3211 return E_OUTOFMEMORY
;
3213 wrapper
->session
= session
;
3215 *out
= &wrapper
->ISimpleAudioVolume_iface
;
3220 static HRESULT WINAPI
AudioSessionManager_GetSessionEnumerator(
3221 IAudioSessionManager2
*iface
, IAudioSessionEnumerator
**out
)
3223 SessionMgr
*This
= impl_from_IAudioSessionManager2(iface
);
3224 FIXME("(%p)->(%p) - stub\n", This
, out
);
3228 static HRESULT WINAPI
AudioSessionManager_RegisterSessionNotification(
3229 IAudioSessionManager2
*iface
, IAudioSessionNotification
*notification
)
3231 SessionMgr
*This
= impl_from_IAudioSessionManager2(iface
);
3232 FIXME("(%p)->(%p) - stub\n", This
, notification
);
3236 static HRESULT WINAPI
AudioSessionManager_UnregisterSessionNotification(
3237 IAudioSessionManager2
*iface
, IAudioSessionNotification
*notification
)
3239 SessionMgr
*This
= impl_from_IAudioSessionManager2(iface
);
3240 FIXME("(%p)->(%p) - stub\n", This
, notification
);
3244 static HRESULT WINAPI
AudioSessionManager_RegisterDuckNotification(
3245 IAudioSessionManager2
*iface
, const WCHAR
*session_id
,
3246 IAudioVolumeDuckNotification
*notification
)
3248 SessionMgr
*This
= impl_from_IAudioSessionManager2(iface
);
3249 FIXME("(%p)->(%p) - stub\n", This
, notification
);
3253 static HRESULT WINAPI
AudioSessionManager_UnregisterDuckNotification(
3254 IAudioSessionManager2
*iface
,
3255 IAudioVolumeDuckNotification
*notification
)
3257 SessionMgr
*This
= impl_from_IAudioSessionManager2(iface
);
3258 FIXME("(%p)->(%p) - stub\n", This
, notification
);
3262 static const IAudioSessionManager2Vtbl AudioSessionManager2_Vtbl
=
3264 AudioSessionManager_QueryInterface
,
3265 AudioSessionManager_AddRef
,
3266 AudioSessionManager_Release
,
3267 AudioSessionManager_GetAudioSessionControl
,
3268 AudioSessionManager_GetSimpleAudioVolume
,
3269 AudioSessionManager_GetSessionEnumerator
,
3270 AudioSessionManager_RegisterSessionNotification
,
3271 AudioSessionManager_UnregisterSessionNotification
,
3272 AudioSessionManager_RegisterDuckNotification
,
3273 AudioSessionManager_UnregisterDuckNotification
3276 HRESULT WINAPI
AUDDRV_GetAudioSessionManager(IMMDevice
*device
,
3277 IAudioSessionManager2
**out
)
3281 This
= HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY
, sizeof(SessionMgr
));
3283 return E_OUTOFMEMORY
;
3285 This
->IAudioSessionManager2_iface
.lpVtbl
= &AudioSessionManager2_Vtbl
;
3286 This
->device
= device
;
3289 *out
= &This
->IAudioSessionManager2_iface
;