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"
40 #include "endpointvolume.h"
43 #include "audioclient.h"
44 #include "audiopolicy.h"
47 #include <alsa/asoundlib.h>
49 WINE_DEFAULT_DEBUG_CHANNEL(alsa
);
51 #define NULL_PTR_ERR MAKE_HRESULT(SEVERITY_ERROR, FACILITY_WIN32, RPC_X_NULL_REF_POINTER)
53 static const REFERENCE_TIME DefaultPeriod
= 200000;
54 static const REFERENCE_TIME MinimumPeriod
= 100000;
57 typedef struct ACImpl ACImpl
;
59 typedef struct _AudioSession
{
69 CRITICAL_SECTION lock
;
74 typedef struct _AudioSessionWrapper
{
75 IAudioSessionControl2 IAudioSessionControl2_iface
;
76 IChannelAudioVolume IChannelAudioVolume_iface
;
77 ISimpleAudioVolume ISimpleAudioVolume_iface
;
82 AudioSession
*session
;
83 } AudioSessionWrapper
;
86 IAudioClient IAudioClient_iface
;
87 IAudioRenderClient IAudioRenderClient_iface
;
88 IAudioCaptureClient IAudioCaptureClient_iface
;
89 IAudioClock IAudioClock_iface
;
90 IAudioClock2 IAudioClock2_iface
;
91 IAudioStreamVolume IAudioStreamVolume_iface
;
95 snd_pcm_t
*pcm_handle
;
96 snd_pcm_uframes_t period_alsa
, bufsize_alsa
;
97 snd_pcm_hw_params_t
*hw_params
; /* does not hold state between calls */
104 AUDCLNT_SHAREMODE share
;
108 BOOL initted
, started
;
109 UINT64 written_frames
, held_frames
, tmp_buffer_frames
;
110 UINT32 bufsize_frames
, period_us
;
111 UINT32 lcl_offs_frames
; /* offs into local_buffer where valid data starts */
114 BYTE
*local_buffer
, *tmp_buffer
;
117 CRITICAL_SECTION lock
;
119 AudioSession
*session
;
120 AudioSessionWrapper
*session_wrapper
;
127 LOCKED_NORMAL
, /* public buffer piece is from local_buffer */
128 LOCKED_WRAPPED
/* public buffer piece is wrapped around, in tmp_buffer */
131 static HANDLE g_timer_q
;
133 static CRITICAL_SECTION g_sessions_lock
;
134 static struct list g_sessions
= LIST_INIT(g_sessions
);
136 static const WCHAR defaultW
[] = {'d','e','f','a','u','l','t',0};
137 static const char defname
[] = "default";
139 static const IAudioClientVtbl AudioClient_Vtbl
;
140 static const IAudioRenderClientVtbl AudioRenderClient_Vtbl
;
141 static const IAudioCaptureClientVtbl AudioCaptureClient_Vtbl
;
142 static const IAudioSessionControl2Vtbl AudioSessionControl2_Vtbl
;
143 static const ISimpleAudioVolumeVtbl SimpleAudioVolume_Vtbl
;
144 static const IAudioClockVtbl AudioClock_Vtbl
;
145 static const IAudioClock2Vtbl AudioClock2_Vtbl
;
146 static const IAudioStreamVolumeVtbl AudioStreamVolume_Vtbl
;
147 static const IChannelAudioVolumeVtbl ChannelAudioVolume_Vtbl
;
149 int wine_snd_pcm_recover(snd_pcm_t
*pcm
, int err
, int silent
);
150 static AudioSessionWrapper
*AudioSessionWrapper_Create(ACImpl
*client
);
152 static inline ACImpl
*impl_from_IAudioClient(IAudioClient
*iface
)
154 return CONTAINING_RECORD(iface
, ACImpl
, IAudioClient_iface
);
157 static inline ACImpl
*impl_from_IAudioRenderClient(IAudioRenderClient
*iface
)
159 return CONTAINING_RECORD(iface
, ACImpl
, IAudioRenderClient_iface
);
162 static inline ACImpl
*impl_from_IAudioCaptureClient(IAudioCaptureClient
*iface
)
164 return CONTAINING_RECORD(iface
, ACImpl
, IAudioCaptureClient_iface
);
167 static inline AudioSessionWrapper
*impl_from_IAudioSessionControl2(IAudioSessionControl2
*iface
)
169 return CONTAINING_RECORD(iface
, AudioSessionWrapper
, IAudioSessionControl2_iface
);
172 static inline AudioSessionWrapper
*impl_from_ISimpleAudioVolume(ISimpleAudioVolume
*iface
)
174 return CONTAINING_RECORD(iface
, AudioSessionWrapper
, ISimpleAudioVolume_iface
);
177 static inline AudioSessionWrapper
*impl_from_IChannelAudioVolume(IChannelAudioVolume
*iface
)
179 return CONTAINING_RECORD(iface
, AudioSessionWrapper
, IChannelAudioVolume_iface
);
182 static inline ACImpl
*impl_from_IAudioClock(IAudioClock
*iface
)
184 return CONTAINING_RECORD(iface
, ACImpl
, IAudioClock_iface
);
187 static inline ACImpl
*impl_from_IAudioClock2(IAudioClock2
*iface
)
189 return CONTAINING_RECORD(iface
, ACImpl
, IAudioClock2_iface
);
192 static inline ACImpl
*impl_from_IAudioStreamVolume(IAudioStreamVolume
*iface
)
194 return CONTAINING_RECORD(iface
, ACImpl
, IAudioStreamVolume_iface
);
197 BOOL WINAPI
DllMain(HINSTANCE dll
, DWORD reason
, void *reserved
)
199 if(reason
== DLL_PROCESS_ATTACH
){
200 g_timer_q
= CreateTimerQueue();
204 InitializeCriticalSection(&g_sessions_lock
);
210 static HRESULT
alsa_get_card_devices(EDataFlow flow
, WCHAR
**ids
, char **keys
,
211 UINT
*num
, snd_ctl_t
*ctl
, int card
, const WCHAR
*cardnameW
)
213 static const WCHAR dashW
[] = {' ','-',' ',0};
215 snd_pcm_info_t
*info
;
217 info
= HeapAlloc(GetProcessHeap(), 0, snd_pcm_info_sizeof());
219 return E_OUTOFMEMORY
;
221 snd_pcm_info_set_subdevice(info
, 0);
222 snd_pcm_info_set_stream(info
,
223 flow
== eRender
? SND_PCM_STREAM_PLAYBACK
: SND_PCM_STREAM_CAPTURE
);
226 for(err
= snd_ctl_pcm_next_device(ctl
, &device
); device
!= -1 && err
>= 0;
227 err
= snd_ctl_pcm_next_device(ctl
, &device
)){
230 snd_pcm_info_set_device(info
, device
);
232 if((err
= snd_ctl_pcm_info(ctl
, info
)) < 0){
234 /* This device doesn't have the right stream direction */
237 WARN("Failed to get info for card %d, device %d: %d (%s)\n",
238 card
, device
, err
, snd_strerror(err
));
245 devname
= snd_pcm_info_get_name(info
);
247 WARN("Unable to get device name for card %d, device %d\n", card
,
252 cardlen
= lstrlenW(cardnameW
);
253 len
= MultiByteToWideChar(CP_UNIXCP
, 0, devname
, -1, NULL
, 0);
254 len
+= lstrlenW(dashW
);
256 ids
[*num
] = HeapAlloc(GetProcessHeap(), 0, len
* sizeof(WCHAR
));
258 HeapFree(GetProcessHeap(), 0, info
);
259 return E_OUTOFMEMORY
;
261 memcpy(ids
[*num
], cardnameW
, cardlen
* sizeof(WCHAR
));
262 memcpy(ids
[*num
] + cardlen
, dashW
, lstrlenW(dashW
) * sizeof(WCHAR
));
263 cardlen
+= lstrlenW(dashW
);
264 MultiByteToWideChar(CP_UNIXCP
, 0, devname
, -1, ids
[*num
] + cardlen
,
267 keys
[*num
] = HeapAlloc(GetProcessHeap(), 0, 32);
269 HeapFree(GetProcessHeap(), 0, info
);
270 HeapFree(GetProcessHeap(), 0, ids
[*num
]);
271 return E_OUTOFMEMORY
;
273 sprintf(keys
[*num
], "hw:%d,%d", card
, device
);
279 HeapFree(GetProcessHeap(), 0, info
);
282 WARN("Got a failure during device enumeration on card %d: %d (%s)\n",
283 card
, err
, snd_strerror(err
));
288 static HRESULT
alsa_enum_devices(EDataFlow flow
, WCHAR
**ids
, char **keys
,
295 for(err
= snd_card_next(&card
); card
!= -1 && err
>= 0;
296 err
= snd_card_next(&card
)){
298 const char *cardname
;
303 sprintf(cardpath
, "hw:%u", card
);
305 if((err
= snd_ctl_open(&ctl
, cardpath
, 0)) < 0){
306 WARN("Unable to open ctl for ALSA device %s: %d (%s)\n", cardpath
,
307 err
, snd_strerror(err
));
311 if((err
= snd_card_get_name(card
, (char **)&cardname
)) < 0){
312 WARN("Unable to get card name for ALSA device %s: %d (%s)\n",
313 cardpath
, err
, snd_strerror(err
));
314 /* FIXME: Should be localized */
315 cardname
= "Unknown soundcard";
318 len
= MultiByteToWideChar(CP_UNIXCP
, 0, cardname
, -1, NULL
, 0);
319 cardnameW
= HeapAlloc(GetProcessHeap(), 0, len
* sizeof(WCHAR
));
322 return E_OUTOFMEMORY
;
324 MultiByteToWideChar(CP_UNIXCP
, 0, cardname
, -1, cardnameW
, len
);
326 alsa_get_card_devices(flow
, ids
, keys
, num
, ctl
, card
, cardnameW
);
328 HeapFree(GetProcessHeap(), 0, cardnameW
);
334 WARN("Got a failure during card enumeration: %d (%s)\n",
335 err
, snd_strerror(err
));
340 HRESULT WINAPI
AUDDRV_GetEndpointIDs(EDataFlow flow
, WCHAR
***ids
, char ***keys
,
341 UINT
*num
, UINT
*def_index
)
345 TRACE("%d %p %p %p %p\n", flow
, ids
, keys
, num
, def_index
);
347 hr
= alsa_enum_devices(flow
, NULL
, NULL
, num
);
351 *ids
= HeapAlloc(GetProcessHeap(), 0, (*num
+ 1) * sizeof(WCHAR
*));
352 *keys
= HeapAlloc(GetProcessHeap(), 0, (*num
+ 1) * sizeof(char *));
354 HeapFree(GetProcessHeap(), 0, *ids
);
355 HeapFree(GetProcessHeap(), 0, *keys
);
356 return E_OUTOFMEMORY
;
359 (*ids
)[0] = HeapAlloc(GetProcessHeap(), 0, sizeof(defaultW
));
360 memcpy((*ids
)[0], defaultW
, sizeof(defaultW
));
361 (*keys
)[0] = HeapAlloc(GetProcessHeap(), 0, sizeof(defname
));
362 memcpy((*keys
)[0], defname
, sizeof(defname
));
365 hr
= alsa_enum_devices(flow
, (*ids
) + 1, (*keys
) + 1, num
);
368 for(i
= 0; i
< *num
; ++i
){
369 HeapFree(GetProcessHeap(), 0, (*ids
)[i
]);
370 HeapFree(GetProcessHeap(), 0, (*keys
)[i
]);
372 HeapFree(GetProcessHeap(), 0, *ids
);
373 HeapFree(GetProcessHeap(), 0, *keys
);
374 return E_OUTOFMEMORY
;
377 ++(*num
); /* for default device */
382 HRESULT WINAPI
AUDDRV_GetAudioEndpoint(const char *key
, IMMDevice
*dev
,
383 EDataFlow dataflow
, IAudioClient
**out
)
387 snd_pcm_stream_t stream
;
389 TRACE("\"%s\" %p %d %p\n", key
, dev
, dataflow
, out
);
391 This
= HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY
, sizeof(ACImpl
));
393 return E_OUTOFMEMORY
;
395 This
->IAudioClient_iface
.lpVtbl
= &AudioClient_Vtbl
;
396 This
->IAudioRenderClient_iface
.lpVtbl
= &AudioRenderClient_Vtbl
;
397 This
->IAudioCaptureClient_iface
.lpVtbl
= &AudioCaptureClient_Vtbl
;
398 This
->IAudioClock_iface
.lpVtbl
= &AudioClock_Vtbl
;
399 This
->IAudioClock2_iface
.lpVtbl
= &AudioClock2_Vtbl
;
400 This
->IAudioStreamVolume_iface
.lpVtbl
= &AudioStreamVolume_Vtbl
;
402 if(dataflow
== eRender
)
403 stream
= SND_PCM_STREAM_PLAYBACK
;
404 else if(dataflow
== eCapture
)
405 stream
= SND_PCM_STREAM_CAPTURE
;
407 HeapFree(GetProcessHeap(), 0, This
);
411 This
->dataflow
= dataflow
;
412 if((err
= snd_pcm_open(&This
->pcm_handle
, key
, stream
,
413 SND_PCM_NONBLOCK
)) < 0){
414 HeapFree(GetProcessHeap(), 0, This
);
415 WARN("Unable to open PCM \"%s\": %d (%s)\n", key
, err
,
420 This
->hw_params
= HeapAlloc(GetProcessHeap(), 0,
421 snd_pcm_hw_params_sizeof());
422 if(!This
->hw_params
){
423 HeapFree(GetProcessHeap(), 0, This
);
424 snd_pcm_close(This
->pcm_handle
);
425 return E_OUTOFMEMORY
;
428 InitializeCriticalSection(&This
->lock
);
431 IMMDevice_AddRef(This
->parent
);
433 *out
= &This
->IAudioClient_iface
;
434 IAudioClient_AddRef(&This
->IAudioClient_iface
);
439 static HRESULT WINAPI
AudioClient_QueryInterface(IAudioClient
*iface
,
440 REFIID riid
, void **ppv
)
442 TRACE("(%p)->(%s, %p)\n", iface
, debugstr_guid(riid
), ppv
);
447 if(IsEqualIID(riid
, &IID_IUnknown
) || IsEqualIID(riid
, &IID_IAudioClient
))
450 IUnknown_AddRef((IUnknown
*)*ppv
);
453 WARN("Unknown interface %s\n", debugstr_guid(riid
));
454 return E_NOINTERFACE
;
457 static ULONG WINAPI
AudioClient_AddRef(IAudioClient
*iface
)
459 ACImpl
*This
= impl_from_IAudioClient(iface
);
461 ref
= InterlockedIncrement(&This
->ref
);
462 TRACE("(%p) Refcount now %u\n", This
, ref
);
466 static ULONG WINAPI
AudioClient_Release(IAudioClient
*iface
)
468 ACImpl
*This
= impl_from_IAudioClient(iface
);
470 ref
= InterlockedDecrement(&This
->ref
);
471 TRACE("(%p) Refcount now %u\n", This
, ref
);
473 IAudioClient_Stop(iface
);
474 IMMDevice_Release(This
->parent
);
475 DeleteCriticalSection(&This
->lock
);
476 snd_pcm_drop(This
->pcm_handle
);
477 snd_pcm_close(This
->pcm_handle
);
479 EnterCriticalSection(&g_sessions_lock
);
480 list_remove(&This
->entry
);
481 if(list_empty(&This
->session
->clients
)){
482 list_remove(&This
->session
->entry
);
483 DeleteCriticalSection(&This
->session
->lock
);
484 HeapFree(GetProcessHeap(), 0, This
->session
->channel_vols
);
485 HeapFree(GetProcessHeap(), 0, This
->session
);
487 LeaveCriticalSection(&g_sessions_lock
);
489 HeapFree(GetProcessHeap(), 0, This
->vols
);
490 HeapFree(GetProcessHeap(), 0, This
->local_buffer
);
491 HeapFree(GetProcessHeap(), 0, This
->hw_params
);
492 CoTaskMemFree(This
->fmt
);
493 HeapFree(GetProcessHeap(), 0, This
);
498 static void dump_fmt(const WAVEFORMATEX
*fmt
)
500 TRACE("wFormatTag: 0x%x (", fmt
->wFormatTag
);
501 switch(fmt
->wFormatTag
){
502 case WAVE_FORMAT_PCM
:
503 TRACE("WAVE_FORMAT_PCM");
505 case WAVE_FORMAT_IEEE_FLOAT
:
506 TRACE("WAVE_FORMAT_IEEE_FLOAT");
508 case WAVE_FORMAT_EXTENSIBLE
:
509 TRACE("WAVE_FORMAT_EXTENSIBLE");
517 TRACE("nChannels: %u\n", fmt
->nChannels
);
518 TRACE("nSamplesPerSec: %u\n", fmt
->nSamplesPerSec
);
519 TRACE("nAvgBytesPerSec: %u\n", fmt
->nAvgBytesPerSec
);
520 TRACE("nBlockAlign: %u\n", fmt
->nBlockAlign
);
521 TRACE("wBitsPerSample: %u\n", fmt
->wBitsPerSample
);
522 TRACE("cbSize: %u\n", fmt
->cbSize
);
524 if(fmt
->wFormatTag
== WAVE_FORMAT_EXTENSIBLE
){
525 WAVEFORMATEXTENSIBLE
*fmtex
= (void*)fmt
;
526 TRACE("dwChannelMask: %08x\n", fmtex
->dwChannelMask
);
527 TRACE("Samples: %04x\n", fmtex
->Samples
.wReserved
);
528 TRACE("SubFormat: %s\n", wine_dbgstr_guid(&fmtex
->SubFormat
));
532 static WAVEFORMATEX
*clone_format(const WAVEFORMATEX
*fmt
)
537 if(fmt
->wFormatTag
== WAVE_FORMAT_EXTENSIBLE
)
538 size
= sizeof(WAVEFORMATEXTENSIBLE
);
540 size
= sizeof(WAVEFORMATEX
);
542 ret
= CoTaskMemAlloc(size
);
546 memcpy(ret
, fmt
, size
);
548 ret
->cbSize
= size
- sizeof(WAVEFORMATEX
);
553 static AudioSession
*create_session(const GUID
*guid
, EDataFlow flow
,
558 ret
= HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY
, sizeof(AudioSession
));
562 memcpy(&ret
->guid
, guid
, sizeof(GUID
));
564 ret
->dataflow
= flow
;
566 list_init(&ret
->clients
);
568 list_add_head(&g_sessions
, &ret
->entry
);
570 InitializeCriticalSection(&ret
->lock
);
572 ret
->channel_count
= num_channels
;
573 ret
->channel_vols
= HeapAlloc(GetProcessHeap(), 0,
574 sizeof(float) * num_channels
);
575 if(!ret
->channel_vols
){
576 HeapFree(GetProcessHeap(), 0, ret
);
580 for(; num_channels
> 0; --num_channels
)
581 ret
->channel_vols
[num_channels
- 1] = 1.f
;
583 ret
->master_vol
= 1.f
;
588 static HRESULT WINAPI
AudioClient_Initialize(IAudioClient
*iface
,
589 AUDCLNT_SHAREMODE mode
, DWORD flags
, REFERENCE_TIME duration
,
590 REFERENCE_TIME period
, const WAVEFORMATEX
*fmt
,
591 const GUID
*sessionguid
)
593 ACImpl
*This
= impl_from_IAudioClient(iface
);
594 snd_pcm_sw_params_t
*sw_params
= NULL
;
595 snd_pcm_format_t format
;
596 snd_pcm_uframes_t boundary
;
597 const WAVEFORMATEXTENSIBLE
*fmtex
= (const WAVEFORMATEXTENSIBLE
*)fmt
;
598 unsigned int time_us
, rate
;
602 TRACE("(%p)->(%x, %x, %s, %s, %p, %s)\n", This
, mode
, flags
,
603 wine_dbgstr_longlong(duration
), wine_dbgstr_longlong(period
), fmt
, debugstr_guid(sessionguid
));
608 if(mode
!= AUDCLNT_SHAREMODE_SHARED
&& mode
!= AUDCLNT_SHAREMODE_EXCLUSIVE
)
609 return AUDCLNT_E_NOT_INITIALIZED
;
611 if(flags
& ~(AUDCLNT_STREAMFLAGS_CROSSPROCESS
|
612 AUDCLNT_STREAMFLAGS_LOOPBACK
|
613 AUDCLNT_STREAMFLAGS_EVENTCALLBACK
|
614 AUDCLNT_STREAMFLAGS_NOPERSIST
|
615 AUDCLNT_STREAMFLAGS_RATEADJUST
|
616 AUDCLNT_SESSIONFLAGS_EXPIREWHENUNOWNED
|
617 AUDCLNT_SESSIONFLAGS_DISPLAY_HIDE
|
618 AUDCLNT_SESSIONFLAGS_DISPLAY_HIDEWHENEXPIRED
)){
619 TRACE("Unknown flags: %08x\n", flags
);
623 EnterCriticalSection(&This
->lock
);
626 LeaveCriticalSection(&This
->lock
);
627 return AUDCLNT_E_ALREADY_INITIALIZED
;
632 if((err
= snd_pcm_hw_params_any(This
->pcm_handle
, This
->hw_params
)) < 0){
633 WARN("Unable to get hw_params: %d (%s)\n", err
, snd_strerror(err
));
638 if((err
= snd_pcm_hw_params_set_access(This
->pcm_handle
, This
->hw_params
,
639 SND_PCM_ACCESS_RW_INTERLEAVED
)) < 0){
640 WARN("Unable to set access: %d (%s)\n", err
, snd_strerror(err
));
645 if(fmt
->wFormatTag
== WAVE_FORMAT_PCM
||
646 (fmt
->wFormatTag
== WAVE_FORMAT_EXTENSIBLE
&&
647 IsEqualGUID(&fmtex
->SubFormat
, &KSDATAFORMAT_SUBTYPE_PCM
))){
648 if(fmt
->wBitsPerSample
== 8)
649 format
= SND_PCM_FORMAT_U8
;
650 else if(fmt
->wBitsPerSample
== 16)
651 format
= SND_PCM_FORMAT_S16_LE
;
652 else if(fmt
->wBitsPerSample
== 24)
653 format
= SND_PCM_FORMAT_S24_3LE
;
654 else if(fmt
->wBitsPerSample
== 32)
655 format
= SND_PCM_FORMAT_S32_LE
;
657 WARN("Unsupported bit depth: %u\n", fmt
->wBitsPerSample
);
658 hr
= AUDCLNT_E_UNSUPPORTED_FORMAT
;
661 }else if(fmt
->wFormatTag
== WAVE_FORMAT_IEEE_FLOAT
||
662 (fmt
->wFormatTag
== WAVE_FORMAT_EXTENSIBLE
&&
663 IsEqualGUID(&fmtex
->SubFormat
, &KSDATAFORMAT_SUBTYPE_IEEE_FLOAT
))){
664 if(fmt
->wBitsPerSample
== 32)
665 format
= SND_PCM_FORMAT_FLOAT_LE
;
666 else if(fmt
->wBitsPerSample
== 64)
667 format
= SND_PCM_FORMAT_FLOAT64_LE
;
669 WARN("Unsupported float size: %u\n", fmt
->wBitsPerSample
);
670 hr
= AUDCLNT_E_UNSUPPORTED_FORMAT
;
674 WARN("Unknown wave format: %04x\n", fmt
->wFormatTag
);
675 hr
= AUDCLNT_E_UNSUPPORTED_FORMAT
;
679 if((err
= snd_pcm_hw_params_set_format(This
->pcm_handle
, This
->hw_params
,
681 WARN("Unable to set ALSA format to %u: %d (%s)\n", format
, err
,
687 rate
= fmt
->nSamplesPerSec
;
688 if((err
= snd_pcm_hw_params_set_rate_near(This
->pcm_handle
, This
->hw_params
,
690 WARN("Unable to set rate to %u: %d (%s)\n", rate
, err
,
696 if((err
= snd_pcm_hw_params_set_channels(This
->pcm_handle
, This
->hw_params
,
697 fmt
->nChannels
)) < 0){
698 WARN("Unable to set channels to %u: %d (%s)\n", fmt
->nChannels
, err
,
704 time_us
= MinimumPeriod
/ 10;
705 if((err
= snd_pcm_hw_params_set_period_time_near(This
->pcm_handle
,
706 This
->hw_params
, &time_us
, NULL
)) < 0){
707 WARN("Unable to set max period time to %u: %d (%s)\n", time_us
,
708 err
, snd_strerror(err
));
713 if((err
= snd_pcm_hw_params(This
->pcm_handle
, This
->hw_params
)) < 0){
714 WARN("Unable to set hw params: %d (%s)\n", err
, snd_strerror(err
));
719 sw_params
= HeapAlloc(GetProcessHeap(), 0, snd_pcm_sw_params_sizeof());
725 if((err
= snd_pcm_sw_params_current(This
->pcm_handle
, sw_params
)) < 0){
726 WARN("Unable to get sw_params: %d (%s)\n", err
, snd_strerror(err
));
731 This
->bufsize_frames
= ceil((duration
/ 10000000.) * fmt
->nSamplesPerSec
);
732 This
->local_buffer
= HeapAlloc(GetProcessHeap(), 0,
733 This
->bufsize_frames
* fmt
->nBlockAlign
);
734 if(!This
->local_buffer
){
738 if (fmt
->wBitsPerSample
== 8)
739 memset(This
->local_buffer
, 128, This
->bufsize_frames
* fmt
->nBlockAlign
);
741 memset(This
->local_buffer
, 0, This
->bufsize_frames
* fmt
->nBlockAlign
);
743 if((err
= snd_pcm_sw_params_get_boundary(sw_params
, &boundary
)) < 0){
744 WARN("Unable to get boundary: %d (%s)\n", err
, snd_strerror(err
));
749 if((err
= snd_pcm_sw_params_set_start_threshold(This
->pcm_handle
,
750 sw_params
, boundary
)) < 0){
751 WARN("Unable to set start threshold to %lx: %d (%s)\n", boundary
, err
,
757 if((err
= snd_pcm_sw_params_set_stop_threshold(This
->pcm_handle
,
758 sw_params
, boundary
)) < 0){
759 WARN("Unable to set stop threshold to %lx: %d (%s)\n", boundary
, err
,
765 if((err
= snd_pcm_sw_params_set_avail_min(This
->pcm_handle
,
767 WARN("Unable to set avail min to 0: %d (%s)\n", err
, snd_strerror(err
));
772 if((err
= snd_pcm_sw_params(This
->pcm_handle
, sw_params
)) < 0){
773 WARN("Unable to set sw params: %d (%s)\n", err
, snd_strerror(err
));
778 if((err
= snd_pcm_prepare(This
->pcm_handle
)) < 0){
779 WARN("Unable to prepare device: %d (%s)\n", err
, snd_strerror(err
));
784 if((err
= snd_pcm_hw_params_get_buffer_size(This
->hw_params
,
785 &This
->bufsize_alsa
)) < 0){
786 WARN("Unable to get buffer size: %d (%s)\n", err
, snd_strerror(err
));
791 if((err
= snd_pcm_hw_params_get_period_size(This
->hw_params
,
792 &This
->period_alsa
, NULL
)) < 0){
793 WARN("Unable to get period size: %d (%s)\n", err
, snd_strerror(err
));
798 if((err
= snd_pcm_hw_params_get_period_time(This
->hw_params
,
799 &This
->period_us
, NULL
)) < 0){
800 WARN("Unable to get period time: %d (%s)\n", err
, snd_strerror(err
));
805 This
->fmt
= clone_format(fmt
);
811 This
->vols
= HeapAlloc(GetProcessHeap(), 0, fmt
->nChannels
* sizeof(float));
817 for(i
= 0; i
< fmt
->nChannels
; ++i
)
823 EnterCriticalSection(&g_sessions_lock
);
825 if(!sessionguid
|| IsEqualGUID(sessionguid
, &GUID_NULL
)){
826 This
->session
= create_session(&GUID_NULL
, This
->dataflow
,
829 LeaveCriticalSection(&g_sessions_lock
);
834 AudioSession
*session
;
836 LIST_FOR_EACH_ENTRY(session
, &g_sessions
, AudioSession
, entry
){
837 if(IsEqualGUID(sessionguid
, &session
->guid
) &&
838 This
->dataflow
== session
->dataflow
){
839 if(session
->channel_count
!= fmt
->nChannels
){
840 LeaveCriticalSection(&g_sessions_lock
);
844 This
->session
= session
;
849 This
->session
= create_session(sessionguid
, This
->dataflow
,
852 LeaveCriticalSection(&g_sessions_lock
);
859 list_add_tail(&This
->session
->clients
, &This
->entry
);
861 LeaveCriticalSection(&g_sessions_lock
);
863 This
->initted
= TRUE
;
866 HeapFree(GetProcessHeap(), 0, sw_params
);
868 if(This
->local_buffer
){
869 HeapFree(GetProcessHeap(), 0, This
->local_buffer
);
870 This
->local_buffer
= NULL
;
873 HeapFree(GetProcessHeap(), 0, This
->fmt
);
877 HeapFree(GetProcessHeap(), 0, This
->vols
);
882 LeaveCriticalSection(&This
->lock
);
887 static HRESULT WINAPI
AudioClient_GetBufferSize(IAudioClient
*iface
,
890 ACImpl
*This
= impl_from_IAudioClient(iface
);
892 TRACE("(%p)->(%p)\n", This
, out
);
897 EnterCriticalSection(&This
->lock
);
900 LeaveCriticalSection(&This
->lock
);
901 return AUDCLNT_E_NOT_INITIALIZED
;
904 *out
= This
->bufsize_frames
;
906 LeaveCriticalSection(&This
->lock
);
911 static HRESULT WINAPI
AudioClient_GetStreamLatency(IAudioClient
*iface
,
912 REFERENCE_TIME
*latency
)
914 ACImpl
*This
= impl_from_IAudioClient(iface
);
916 TRACE("(%p)->(%p)\n", This
, latency
);
921 EnterCriticalSection(&This
->lock
);
924 LeaveCriticalSection(&This
->lock
);
925 return AUDCLNT_E_NOT_INITIALIZED
;
928 LeaveCriticalSection(&This
->lock
);
935 static HRESULT WINAPI
AudioClient_GetCurrentPadding(IAudioClient
*iface
,
938 ACImpl
*This
= impl_from_IAudioClient(iface
);
940 TRACE("(%p)->(%p)\n", This
, out
);
945 EnterCriticalSection(&This
->lock
);
948 LeaveCriticalSection(&This
->lock
);
949 return AUDCLNT_E_NOT_INITIALIZED
;
952 if(This
->dataflow
== eRender
){
953 snd_pcm_sframes_t avail_frames
;
955 avail_frames
= snd_pcm_avail_update(This
->pcm_handle
);
957 if(This
->bufsize_alsa
< avail_frames
){
958 WARN("Xrun detected\n");
959 *out
= This
->held_frames
;
961 *out
= This
->bufsize_alsa
- avail_frames
+ This
->held_frames
;
962 }else if(This
->dataflow
== eCapture
){
963 *out
= This
->held_frames
;
965 LeaveCriticalSection(&This
->lock
);
969 LeaveCriticalSection(&This
->lock
);
974 static DWORD
get_channel_mask(unsigned int channels
)
980 return SPEAKER_FRONT_CENTER
;
982 return SPEAKER_FRONT_LEFT
| SPEAKER_FRONT_RIGHT
;
984 return SPEAKER_FRONT_LEFT
| SPEAKER_FRONT_RIGHT
|
985 SPEAKER_LOW_FREQUENCY
;
987 return SPEAKER_FRONT_LEFT
| SPEAKER_FRONT_RIGHT
| SPEAKER_BACK_LEFT
|
990 return SPEAKER_FRONT_LEFT
| SPEAKER_FRONT_RIGHT
| SPEAKER_BACK_LEFT
|
991 SPEAKER_BACK_RIGHT
| SPEAKER_LOW_FREQUENCY
;
993 return SPEAKER_FRONT_LEFT
| SPEAKER_FRONT_RIGHT
| SPEAKER_BACK_LEFT
|
994 SPEAKER_BACK_RIGHT
| SPEAKER_LOW_FREQUENCY
| SPEAKER_FRONT_CENTER
;
996 return SPEAKER_FRONT_LEFT
| SPEAKER_FRONT_RIGHT
| SPEAKER_BACK_LEFT
|
997 SPEAKER_BACK_RIGHT
| SPEAKER_LOW_FREQUENCY
| SPEAKER_FRONT_CENTER
|
1000 FIXME("Unknown speaker configuration: %u\n", channels
);
1004 static HRESULT WINAPI
AudioClient_IsFormatSupported(IAudioClient
*iface
,
1005 AUDCLNT_SHAREMODE mode
, const WAVEFORMATEX
*fmt
,
1008 ACImpl
*This
= impl_from_IAudioClient(iface
);
1009 snd_pcm_format_mask_t
*formats
= NULL
;
1011 WAVEFORMATEX
*closest
= NULL
;
1012 const WAVEFORMATEXTENSIBLE
*fmtex
= (const WAVEFORMATEXTENSIBLE
*)fmt
;
1013 unsigned int max
= 0, min
= 0;
1016 TRACE("(%p)->(%x, %p, %p)\n", This
, mode
, fmt
, out
);
1018 if(!fmt
|| (mode
== AUDCLNT_SHAREMODE_SHARED
&& !out
))
1021 if(mode
!= AUDCLNT_SHAREMODE_SHARED
&& mode
!= AUDCLNT_SHAREMODE_EXCLUSIVE
)
1022 return E_INVALIDARG
;
1024 if(fmt
->wFormatTag
== WAVE_FORMAT_EXTENSIBLE
&&
1025 fmt
->cbSize
< sizeof(WAVEFORMATEXTENSIBLE
) - sizeof(WAVEFORMATEX
))
1026 return E_INVALIDARG
;
1030 EnterCriticalSection(&This
->lock
);
1032 if((err
= snd_pcm_hw_params_any(This
->pcm_handle
, This
->hw_params
)) < 0){
1037 formats
= HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY
,
1038 snd_pcm_format_mask_sizeof());
1044 snd_pcm_hw_params_get_format_mask(This
->hw_params
, formats
);
1046 if(fmt
->wFormatTag
== WAVE_FORMAT_PCM
||
1047 (fmt
->wFormatTag
== WAVE_FORMAT_EXTENSIBLE
&&
1048 IsEqualGUID(&fmtex
->SubFormat
, &KSDATAFORMAT_SUBTYPE_PCM
))){
1049 switch(fmt
->wBitsPerSample
){
1051 if(!snd_pcm_format_mask_test(formats
, SND_PCM_FORMAT_U8
)){
1052 hr
= AUDCLNT_E_UNSUPPORTED_FORMAT
;
1057 if(!snd_pcm_format_mask_test(formats
, SND_PCM_FORMAT_S16_LE
)){
1058 hr
= AUDCLNT_E_UNSUPPORTED_FORMAT
;
1063 if(!snd_pcm_format_mask_test(formats
, SND_PCM_FORMAT_S24_3LE
)){
1064 hr
= AUDCLNT_E_UNSUPPORTED_FORMAT
;
1069 if(!snd_pcm_format_mask_test(formats
, SND_PCM_FORMAT_S32_LE
)){
1070 hr
= AUDCLNT_E_UNSUPPORTED_FORMAT
;
1075 hr
= AUDCLNT_E_UNSUPPORTED_FORMAT
;
1078 }else if(fmt
->wFormatTag
== WAVE_FORMAT_IEEE_FLOAT
||
1079 (fmt
->wFormatTag
== WAVE_FORMAT_EXTENSIBLE
&&
1080 IsEqualGUID(&fmtex
->SubFormat
, &KSDATAFORMAT_SUBTYPE_IEEE_FLOAT
))){
1081 switch(fmt
->wBitsPerSample
){
1083 if(!snd_pcm_format_mask_test(formats
, SND_PCM_FORMAT_FLOAT_LE
)){
1084 hr
= AUDCLNT_E_UNSUPPORTED_FORMAT
;
1089 if(!snd_pcm_format_mask_test(formats
, SND_PCM_FORMAT_FLOAT64_LE
)){
1090 hr
= AUDCLNT_E_UNSUPPORTED_FORMAT
;
1095 hr
= AUDCLNT_E_UNSUPPORTED_FORMAT
;
1099 hr
= AUDCLNT_E_UNSUPPORTED_FORMAT
;
1103 closest
= clone_format(fmt
);
1109 if((err
= snd_pcm_hw_params_get_rate_min(This
->hw_params
, &min
, NULL
)) < 0){
1111 WARN("Unable to get min rate: %d (%s)\n", err
, snd_strerror(err
));
1115 if((err
= snd_pcm_hw_params_get_rate_max(This
->hw_params
, &max
, NULL
)) < 0){
1117 WARN("Unable to get max rate: %d (%s)\n", err
, snd_strerror(err
));
1121 if(fmt
->nSamplesPerSec
< min
|| fmt
->nSamplesPerSec
> max
||
1122 (fmt
->nSamplesPerSec
!= 48000 &&
1123 fmt
->nSamplesPerSec
!= 44100 &&
1124 fmt
->nSamplesPerSec
!= 22050 &&
1125 fmt
->nSamplesPerSec
!= 11025 &&
1126 fmt
->nSamplesPerSec
!= 8000)){
1127 hr
= AUDCLNT_E_UNSUPPORTED_FORMAT
;
1131 if((err
= snd_pcm_hw_params_get_channels_min(This
->hw_params
, &min
)) < 0){
1133 WARN("Unable to get min channels: %d (%s)\n", err
, snd_strerror(err
));
1137 if((err
= snd_pcm_hw_params_get_channels_max(This
->hw_params
, &max
)) < 0){
1139 WARN("Unable to get max channels: %d (%s)\n", err
, snd_strerror(err
));
1144 if(fmt
->nChannels
> max
){
1146 closest
->nChannels
= max
;
1147 }else if(fmt
->nChannels
< min
){
1149 closest
->nChannels
= min
;
1152 if(closest
->wFormatTag
== WAVE_FORMAT_EXTENSIBLE
){
1153 DWORD mask
= get_channel_mask(closest
->nChannels
);
1155 ((WAVEFORMATEXTENSIBLE
*)closest
)->dwChannelMask
= mask
;
1157 if(fmt
->wFormatTag
== WAVE_FORMAT_EXTENSIBLE
&&
1158 fmtex
->dwChannelMask
!= mask
)
1163 LeaveCriticalSection(&This
->lock
);
1164 HeapFree(GetProcessHeap(), 0, formats
);
1166 if(hr
== S_OK
|| !out
){
1167 CoTaskMemFree(closest
);
1171 closest
->nBlockAlign
=
1172 closest
->nChannels
* closest
->wBitsPerSample
/ 8;
1173 closest
->nAvgBytesPerSec
=
1174 closest
->nBlockAlign
* closest
->nSamplesPerSec
;
1178 TRACE("returning: %08x\n", hr
);
1182 static HRESULT WINAPI
AudioClient_GetMixFormat(IAudioClient
*iface
,
1183 WAVEFORMATEX
**pwfx
)
1185 ACImpl
*This
= impl_from_IAudioClient(iface
);
1186 WAVEFORMATEXTENSIBLE
*fmt
;
1187 snd_pcm_format_mask_t
*formats
;
1188 unsigned int max_rate
, max_channels
;
1192 TRACE("(%p)->(%p)\n", This
, pwfx
);
1197 *pwfx
= HeapAlloc(GetProcessHeap(), 0, sizeof(WAVEFORMATEXTENSIBLE
));
1199 return E_OUTOFMEMORY
;
1201 fmt
= (WAVEFORMATEXTENSIBLE
*)*pwfx
;
1203 formats
= HeapAlloc(GetProcessHeap(), 0, snd_pcm_format_mask_sizeof());
1205 HeapFree(GetProcessHeap(), 0, *pwfx
);
1206 return E_OUTOFMEMORY
;
1209 EnterCriticalSection(&This
->lock
);
1211 if((err
= snd_pcm_hw_params_any(This
->pcm_handle
, This
->hw_params
)) < 0){
1212 WARN("Unable to get hw_params: %d (%s)\n", err
, snd_strerror(err
));
1217 snd_pcm_hw_params_get_format_mask(This
->hw_params
, formats
);
1219 fmt
->Format
.wFormatTag
= WAVE_FORMAT_EXTENSIBLE
;
1220 if(snd_pcm_format_mask_test(formats
, SND_PCM_FORMAT_FLOAT_LE
)){
1221 fmt
->Format
.wBitsPerSample
= 32;
1222 fmt
->SubFormat
= KSDATAFORMAT_SUBTYPE_IEEE_FLOAT
;
1223 }else if(snd_pcm_format_mask_test(formats
, SND_PCM_FORMAT_S16_LE
)){
1224 fmt
->Format
.wBitsPerSample
= 16;
1225 fmt
->SubFormat
= KSDATAFORMAT_SUBTYPE_PCM
;
1226 }else if(snd_pcm_format_mask_test(formats
, SND_PCM_FORMAT_U8
)){
1227 fmt
->Format
.wBitsPerSample
= 8;
1228 fmt
->SubFormat
= KSDATAFORMAT_SUBTYPE_PCM
;
1229 }else if(snd_pcm_format_mask_test(formats
, SND_PCM_FORMAT_S32_LE
)){
1230 fmt
->Format
.wBitsPerSample
= 32;
1231 fmt
->SubFormat
= KSDATAFORMAT_SUBTYPE_PCM
;
1232 }else if(snd_pcm_format_mask_test(formats
, SND_PCM_FORMAT_S24_3LE
)){
1233 fmt
->Format
.wBitsPerSample
= 24;
1234 fmt
->SubFormat
= KSDATAFORMAT_SUBTYPE_PCM
;
1236 ERR("Didn't recognize any available ALSA formats\n");
1241 if((err
= snd_pcm_hw_params_get_channels_max(This
->hw_params
,
1242 &max_channels
)) < 0){
1243 WARN("Unable to get max channels: %d (%s)\n", err
, snd_strerror(err
));
1248 if(max_channels
> 2){
1249 FIXME("Don't know what to do with %u channels, pretending there's "
1250 "only 2 channels\n", max_channels
);
1251 fmt
->Format
.nChannels
= 2;
1253 fmt
->Format
.nChannels
= max_channels
;
1255 fmt
->dwChannelMask
= get_channel_mask(fmt
->Format
.nChannels
);
1257 if((err
= snd_pcm_hw_params_get_rate_max(This
->hw_params
, &max_rate
,
1259 WARN("Unable to get max rate: %d (%s)\n", err
, snd_strerror(err
));
1264 if(max_rate
>= 48000)
1265 fmt
->Format
.nSamplesPerSec
= 48000;
1266 else if(max_rate
>= 44100)
1267 fmt
->Format
.nSamplesPerSec
= 44100;
1268 else if(max_rate
>= 22050)
1269 fmt
->Format
.nSamplesPerSec
= 22050;
1270 else if(max_rate
>= 11025)
1271 fmt
->Format
.nSamplesPerSec
= 11025;
1272 else if(max_rate
>= 8000)
1273 fmt
->Format
.nSamplesPerSec
= 8000;
1275 ERR("Unknown max rate: %u\n", max_rate
);
1280 fmt
->Format
.nBlockAlign
= (fmt
->Format
.wBitsPerSample
*
1281 fmt
->Format
.nChannels
) / 8;
1282 fmt
->Format
.nAvgBytesPerSec
= fmt
->Format
.nSamplesPerSec
*
1283 fmt
->Format
.nBlockAlign
;
1285 fmt
->Samples
.wValidBitsPerSample
= fmt
->Format
.wBitsPerSample
;
1286 fmt
->Format
.cbSize
= sizeof(WAVEFORMATEXTENSIBLE
) - sizeof(WAVEFORMATEX
);
1288 dump_fmt((WAVEFORMATEX
*)fmt
);
1291 LeaveCriticalSection(&This
->lock
);
1293 HeapFree(GetProcessHeap(), 0, *pwfx
);
1294 HeapFree(GetProcessHeap(), 0, formats
);
1299 static HRESULT WINAPI
AudioClient_GetDevicePeriod(IAudioClient
*iface
,
1300 REFERENCE_TIME
*defperiod
, REFERENCE_TIME
*minperiod
)
1302 ACImpl
*This
= impl_from_IAudioClient(iface
);
1304 TRACE("(%p)->(%p, %p)\n", This
, defperiod
, minperiod
);
1306 if(!defperiod
&& !minperiod
)
1310 *defperiod
= DefaultPeriod
;
1312 *minperiod
= MinimumPeriod
;
1317 static snd_pcm_sframes_t
alsa_write_best_effort(snd_pcm_t
*handle
, BYTE
*buf
,
1318 snd_pcm_uframes_t frames
)
1320 snd_pcm_sframes_t written
;
1322 written
= snd_pcm_writei(handle
, buf
, frames
);
1326 if(written
== -EAGAIN
)
1330 WARN("writei failed, recovering: %ld (%s)\n", written
,
1331 snd_strerror(written
));
1333 ret
= wine_snd_pcm_recover(handle
, written
, 0);
1335 WARN("Could not recover: %d (%s)\n", ret
, snd_strerror(ret
));
1339 written
= snd_pcm_writei(handle
, buf
, frames
);
1345 static void alsa_write_data(ACImpl
*This
)
1347 snd_pcm_sframes_t written
;
1348 snd_pcm_uframes_t to_write
;
1350 This
->local_buffer
+ (This
->lcl_offs_frames
* This
->fmt
->nBlockAlign
);
1352 if(This
->lcl_offs_frames
+ This
->held_frames
> This
->bufsize_frames
)
1353 to_write
= This
->bufsize_frames
- This
->lcl_offs_frames
;
1355 to_write
= This
->held_frames
;
1357 written
= alsa_write_best_effort(This
->pcm_handle
, buf
, to_write
);
1359 WARN("Couldn't write: %ld (%s)\n", written
, snd_strerror(written
));
1363 This
->lcl_offs_frames
+= written
;
1364 This
->lcl_offs_frames
%= This
->bufsize_frames
;
1365 This
->held_frames
-= written
;
1367 if(written
< to_write
){
1368 /* ALSA buffer probably full */
1372 if(This
->held_frames
){
1373 /* wrapped and have some data back at the start to write */
1374 written
= alsa_write_best_effort(This
->pcm_handle
, This
->local_buffer
,
1377 WARN("Couldn't write: %ld (%s)\n", written
, snd_strerror(written
));
1381 This
->lcl_offs_frames
+= written
;
1382 This
->lcl_offs_frames
%= This
->bufsize_frames
;
1383 This
->held_frames
-= written
;
1387 static void alsa_read_data(ACImpl
*This
)
1389 snd_pcm_sframes_t pos
, readable
, nread
;
1391 pos
= (This
->held_frames
+ This
->lcl_offs_frames
) % This
->bufsize_frames
;
1392 readable
= This
->bufsize_frames
- pos
;
1394 nread
= snd_pcm_readi(This
->pcm_handle
,
1395 This
->local_buffer
+ pos
* This
->fmt
->nBlockAlign
, readable
);
1399 WARN("read failed, recovering: %ld (%s)\n", nread
, snd_strerror(nread
));
1401 ret
= wine_snd_pcm_recover(This
->pcm_handle
, nread
, 0);
1403 WARN("Recover failed: %d (%s)\n", ret
, snd_strerror(ret
));
1407 nread
= snd_pcm_readi(This
->pcm_handle
,
1408 This
->local_buffer
+ pos
* This
->fmt
->nBlockAlign
, readable
);
1410 WARN("read failed: %ld (%s)\n", nread
, snd_strerror(nread
));
1415 This
->held_frames
+= nread
;
1417 if(This
->held_frames
> This
->bufsize_frames
){
1418 WARN("Overflow of unread data\n");
1419 This
->lcl_offs_frames
+= This
->held_frames
;
1420 This
->lcl_offs_frames
%= This
->bufsize_frames
;
1421 This
->held_frames
= This
->bufsize_frames
;
1425 static void CALLBACK
alsa_push_buffer_data(void *user
, BOOLEAN timer
)
1427 ACImpl
*This
= user
;
1429 EnterCriticalSection(&This
->lock
);
1431 if(This
->dataflow
== eRender
&& This
->held_frames
)
1432 alsa_write_data(This
);
1433 else if(This
->dataflow
== eCapture
)
1434 alsa_read_data(This
);
1437 SetEvent(This
->event
);
1439 LeaveCriticalSection(&This
->lock
);
1442 static HRESULT
alsa_consider_start(ACImpl
*This
)
1444 snd_pcm_sframes_t avail
;
1447 avail
= snd_pcm_avail_update(This
->pcm_handle
);
1449 WARN("Unable to get avail_update: %ld (%s)\n", avail
,
1450 snd_strerror(avail
));
1454 if(This
->period_alsa
< This
->bufsize_alsa
- avail
){
1455 if((err
= snd_pcm_start(This
->pcm_handle
)) < 0){
1456 WARN("Start failed: %d (%s), state: %d\n", err
, snd_strerror(err
),
1457 snd_pcm_state(This
->pcm_handle
));
1467 static HRESULT WINAPI
AudioClient_Start(IAudioClient
*iface
)
1469 ACImpl
*This
= impl_from_IAudioClient(iface
);
1473 TRACE("(%p)\n", This
);
1475 EnterCriticalSection(&This
->lock
);
1478 LeaveCriticalSection(&This
->lock
);
1479 return AUDCLNT_E_NOT_INITIALIZED
;
1482 if((This
->flags
& AUDCLNT_STREAMFLAGS_EVENTCALLBACK
) && !This
->event
){
1483 LeaveCriticalSection(&This
->lock
);
1484 return AUDCLNT_E_EVENTHANDLE_NOT_SET
;
1488 LeaveCriticalSection(&This
->lock
);
1489 return AUDCLNT_E_NOT_STOPPED
;
1492 hr
= alsa_consider_start(This
);
1494 LeaveCriticalSection(&This
->lock
);
1498 period_ms
= This
->period_us
/ 1000;
1502 if(This
->dataflow
== eCapture
){
1503 /* dump any data that might be leftover in the ALSA capture buffer */
1504 snd_pcm_readi(This
->pcm_handle
, This
->local_buffer
,
1505 This
->bufsize_frames
);
1508 if(!CreateTimerQueueTimer(&This
->timer
, g_timer_q
, alsa_push_buffer_data
,
1509 This
, 0, period_ms
, WT_EXECUTEINTIMERTHREAD
)){
1510 LeaveCriticalSection(&This
->lock
);
1511 WARN("Unable to create timer: %u\n", GetLastError());
1515 This
->started
= TRUE
;
1517 LeaveCriticalSection(&This
->lock
);
1522 static HRESULT WINAPI
AudioClient_Stop(IAudioClient
*iface
)
1524 ACImpl
*This
= impl_from_IAudioClient(iface
);
1527 TRACE("(%p)\n", This
);
1529 EnterCriticalSection(&This
->lock
);
1532 LeaveCriticalSection(&This
->lock
);
1533 return AUDCLNT_E_NOT_INITIALIZED
;
1537 LeaveCriticalSection(&This
->lock
);
1541 DeleteTimerQueueTimer(g_timer_q
, This
->timer
, INVALID_HANDLE_VALUE
);
1543 if((err
= snd_pcm_drop(This
->pcm_handle
)) < 0){
1544 LeaveCriticalSection(&This
->lock
);
1545 WARN("Drop failed: %d (%s)\n", err
, snd_strerror(err
));
1549 if((err
= snd_pcm_prepare(This
->pcm_handle
)) < 0){
1550 LeaveCriticalSection(&This
->lock
);
1551 WARN("Prepare failed: %d (%s)\n", err
, snd_strerror(err
));
1555 This
->started
= FALSE
;
1557 LeaveCriticalSection(&This
->lock
);
1562 static HRESULT WINAPI
AudioClient_Reset(IAudioClient
*iface
)
1564 ACImpl
*This
= impl_from_IAudioClient(iface
);
1566 TRACE("(%p)\n", This
);
1568 EnterCriticalSection(&This
->lock
);
1571 LeaveCriticalSection(&This
->lock
);
1572 return AUDCLNT_E_NOT_INITIALIZED
;
1576 LeaveCriticalSection(&This
->lock
);
1577 return AUDCLNT_E_NOT_STOPPED
;
1580 This
->held_frames
= 0;
1581 This
->written_frames
= 0;
1582 This
->lcl_offs_frames
= 0;
1584 LeaveCriticalSection(&This
->lock
);
1589 static HRESULT WINAPI
AudioClient_SetEventHandle(IAudioClient
*iface
,
1592 ACImpl
*This
= impl_from_IAudioClient(iface
);
1594 TRACE("(%p)->(%p)\n", This
, event
);
1597 return E_INVALIDARG
;
1599 EnterCriticalSection(&This
->lock
);
1602 LeaveCriticalSection(&This
->lock
);
1603 return AUDCLNT_E_NOT_INITIALIZED
;
1606 if(!(This
->flags
& AUDCLNT_STREAMFLAGS_EVENTCALLBACK
)){
1607 LeaveCriticalSection(&This
->lock
);
1608 return AUDCLNT_E_EVENTHANDLE_NOT_EXPECTED
;
1611 This
->event
= event
;
1613 LeaveCriticalSection(&This
->lock
);
1618 static HRESULT WINAPI
AudioClient_GetService(IAudioClient
*iface
, REFIID riid
,
1621 ACImpl
*This
= impl_from_IAudioClient(iface
);
1623 TRACE("(%p)->(%s, %p)\n", This
, debugstr_guid(riid
), ppv
);
1629 EnterCriticalSection(&This
->lock
);
1632 LeaveCriticalSection(&This
->lock
);
1633 return AUDCLNT_E_NOT_INITIALIZED
;
1636 if(IsEqualIID(riid
, &IID_IAudioRenderClient
)){
1637 if(This
->dataflow
!= eRender
){
1638 LeaveCriticalSection(&This
->lock
);
1639 return AUDCLNT_E_WRONG_ENDPOINT_TYPE
;
1641 *ppv
= &This
->IAudioRenderClient_iface
;
1642 }else if(IsEqualIID(riid
, &IID_IAudioCaptureClient
)){
1643 if(This
->dataflow
!= eCapture
){
1644 LeaveCriticalSection(&This
->lock
);
1645 return AUDCLNT_E_WRONG_ENDPOINT_TYPE
;
1647 *ppv
= &This
->IAudioCaptureClient_iface
;
1648 }else if(IsEqualIID(riid
, &IID_IAudioClock
)){
1649 *ppv
= &This
->IAudioClock_iface
;
1650 }else if(IsEqualIID(riid
, &IID_IAudioStreamVolume
)){
1651 *ppv
= &This
->IAudioStreamVolume_iface
;
1652 }else if(IsEqualIID(riid
, &IID_IAudioSessionControl
)){
1653 if(!This
->session_wrapper
){
1654 This
->session_wrapper
= AudioSessionWrapper_Create(This
);
1655 if(!This
->session_wrapper
){
1656 LeaveCriticalSection(&This
->lock
);
1657 return E_OUTOFMEMORY
;
1661 *ppv
= &This
->session_wrapper
->IAudioSessionControl2_iface
;
1662 }else if(IsEqualIID(riid
, &IID_IChannelAudioVolume
)){
1663 if(!This
->session_wrapper
){
1664 This
->session_wrapper
= AudioSessionWrapper_Create(This
);
1665 if(!This
->session_wrapper
){
1666 LeaveCriticalSection(&This
->lock
);
1667 return E_OUTOFMEMORY
;
1671 *ppv
= &This
->session_wrapper
->IChannelAudioVolume_iface
;
1672 }else if(IsEqualIID(riid
, &IID_ISimpleAudioVolume
)){
1673 if(!This
->session_wrapper
){
1674 This
->session_wrapper
= AudioSessionWrapper_Create(This
);
1675 if(!This
->session_wrapper
){
1676 LeaveCriticalSection(&This
->lock
);
1677 return E_OUTOFMEMORY
;
1681 *ppv
= &This
->session_wrapper
->ISimpleAudioVolume_iface
;
1685 IUnknown_AddRef((IUnknown
*)*ppv
);
1686 LeaveCriticalSection(&This
->lock
);
1690 LeaveCriticalSection(&This
->lock
);
1692 FIXME("stub %s\n", debugstr_guid(riid
));
1693 return E_NOINTERFACE
;
1696 static const IAudioClientVtbl AudioClient_Vtbl
=
1698 AudioClient_QueryInterface
,
1700 AudioClient_Release
,
1701 AudioClient_Initialize
,
1702 AudioClient_GetBufferSize
,
1703 AudioClient_GetStreamLatency
,
1704 AudioClient_GetCurrentPadding
,
1705 AudioClient_IsFormatSupported
,
1706 AudioClient_GetMixFormat
,
1707 AudioClient_GetDevicePeriod
,
1711 AudioClient_SetEventHandle
,
1712 AudioClient_GetService
1715 static HRESULT WINAPI
AudioRenderClient_QueryInterface(
1716 IAudioRenderClient
*iface
, REFIID riid
, void **ppv
)
1718 TRACE("(%p)->(%s, %p)\n", iface
, debugstr_guid(riid
), ppv
);
1724 if(IsEqualIID(riid
, &IID_IUnknown
) ||
1725 IsEqualIID(riid
, &IID_IAudioRenderClient
))
1728 IUnknown_AddRef((IUnknown
*)*ppv
);
1732 WARN("Unknown interface %s\n", debugstr_guid(riid
));
1733 return E_NOINTERFACE
;
1736 static ULONG WINAPI
AudioRenderClient_AddRef(IAudioRenderClient
*iface
)
1738 ACImpl
*This
= impl_from_IAudioRenderClient(iface
);
1739 return AudioClient_AddRef(&This
->IAudioClient_iface
);
1742 static ULONG WINAPI
AudioRenderClient_Release(IAudioRenderClient
*iface
)
1744 ACImpl
*This
= impl_from_IAudioRenderClient(iface
);
1745 return AudioClient_Release(&This
->IAudioClient_iface
);
1748 static HRESULT WINAPI
AudioRenderClient_GetBuffer(IAudioRenderClient
*iface
,
1749 UINT32 frames
, BYTE
**data
)
1751 ACImpl
*This
= impl_from_IAudioRenderClient(iface
);
1756 TRACE("(%p)->(%u, %p)\n", This
, frames
, data
);
1761 EnterCriticalSection(&This
->lock
);
1763 if(This
->buf_state
!= NOT_LOCKED
){
1764 LeaveCriticalSection(&This
->lock
);
1765 return AUDCLNT_E_OUT_OF_ORDER
;
1769 This
->buf_state
= LOCKED_NORMAL
;
1770 LeaveCriticalSection(&This
->lock
);
1774 hr
= IAudioClient_GetCurrentPadding(&This
->IAudioClient_iface
, &pad
);
1776 LeaveCriticalSection(&This
->lock
);
1780 if(pad
+ frames
> This
->bufsize_frames
){
1781 LeaveCriticalSection(&This
->lock
);
1782 return AUDCLNT_E_BUFFER_TOO_LARGE
;
1786 (This
->lcl_offs_frames
+ This
->held_frames
) % This
->bufsize_frames
;
1787 if(write_pos
+ frames
> This
->bufsize_frames
){
1788 if(This
->tmp_buffer_frames
< frames
){
1789 if(This
->tmp_buffer
)
1790 This
->tmp_buffer
= HeapReAlloc(GetProcessHeap(), 0,
1791 This
->tmp_buffer
, frames
* This
->fmt
->nBlockAlign
);
1793 This
->tmp_buffer
= HeapAlloc(GetProcessHeap(), 0,
1794 frames
* This
->fmt
->nBlockAlign
);
1795 if(!This
->tmp_buffer
){
1796 LeaveCriticalSection(&This
->lock
);
1797 return E_OUTOFMEMORY
;
1799 This
->tmp_buffer_frames
= frames
;
1801 *data
= This
->tmp_buffer
;
1802 This
->buf_state
= LOCKED_WRAPPED
;
1804 *data
= This
->local_buffer
+
1805 This
->lcl_offs_frames
* This
->fmt
->nBlockAlign
;
1806 This
->buf_state
= LOCKED_NORMAL
;
1809 LeaveCriticalSection(&This
->lock
);
1814 static void alsa_wrap_buffer(ACImpl
*This
, BYTE
*buffer
, UINT32 written_bytes
)
1816 snd_pcm_uframes_t write_offs_frames
=
1817 (This
->lcl_offs_frames
+ This
->held_frames
) % This
->bufsize_frames
;
1818 UINT32 write_offs_bytes
= write_offs_frames
* This
->fmt
->nBlockAlign
;
1819 snd_pcm_uframes_t chunk_frames
= This
->bufsize_frames
- write_offs_frames
;
1820 UINT32 chunk_bytes
= chunk_frames
* This
->fmt
->nBlockAlign
;
1822 if(written_bytes
< chunk_bytes
){
1823 memcpy(This
->local_buffer
+ write_offs_bytes
, buffer
, written_bytes
);
1825 memcpy(This
->local_buffer
+ write_offs_bytes
, buffer
, chunk_bytes
);
1826 memcpy(This
->local_buffer
, buffer
+ chunk_bytes
,
1827 written_bytes
- chunk_bytes
);
1831 static HRESULT WINAPI
AudioRenderClient_ReleaseBuffer(
1832 IAudioRenderClient
*iface
, UINT32 written_frames
, DWORD flags
)
1834 ACImpl
*This
= impl_from_IAudioRenderClient(iface
);
1835 UINT32 written_bytes
= written_frames
* This
->fmt
->nBlockAlign
;
1839 TRACE("(%p)->(%u, %x)\n", This
, written_frames
, flags
);
1841 EnterCriticalSection(&This
->lock
);
1843 if(This
->buf_state
== NOT_LOCKED
|| !written_frames
){
1844 This
->buf_state
= NOT_LOCKED
;
1845 LeaveCriticalSection(&This
->lock
);
1846 return written_frames
? AUDCLNT_E_OUT_OF_ORDER
: S_OK
;
1849 if(This
->buf_state
== LOCKED_NORMAL
)
1850 buffer
= This
->local_buffer
+
1851 This
->lcl_offs_frames
* This
->fmt
->nBlockAlign
;
1853 buffer
= This
->tmp_buffer
;
1855 if(flags
& AUDCLNT_BUFFERFLAGS_SILENT
){
1856 WAVEFORMATEXTENSIBLE
*fmtex
= (WAVEFORMATEXTENSIBLE
*)This
->fmt
;
1857 if((This
->fmt
->wFormatTag
== WAVE_FORMAT_PCM
||
1858 (This
->fmt
->wFormatTag
== WAVE_FORMAT_EXTENSIBLE
&&
1859 IsEqualGUID(&fmtex
->SubFormat
, &KSDATAFORMAT_SUBTYPE_PCM
))) &&
1860 This
->fmt
->wBitsPerSample
== 8)
1861 memset(buffer
, 128, written_frames
* This
->fmt
->nBlockAlign
);
1863 memset(buffer
, 0, written_frames
* This
->fmt
->nBlockAlign
);
1866 if(This
->held_frames
){
1867 if(This
->buf_state
== LOCKED_WRAPPED
)
1868 alsa_wrap_buffer(This
, buffer
, written_bytes
);
1870 This
->held_frames
+= written_frames
;
1872 snd_pcm_sframes_t written
;
1874 written
= alsa_write_best_effort(This
->pcm_handle
, buffer
,
1877 LeaveCriticalSection(&This
->lock
);
1878 WARN("write failed: %ld (%s)\n", written
, snd_strerror(written
));
1882 if(written
< written_frames
){
1883 if(This
->buf_state
== LOCKED_WRAPPED
)
1884 alsa_wrap_buffer(This
,
1885 This
->tmp_buffer
+ written
* This
->fmt
->nBlockAlign
,
1886 written_frames
- written
);
1888 This
->held_frames
= written_frames
- written
;
1893 snd_pcm_state(This
->pcm_handle
) == SND_PCM_STATE_PREPARED
){
1894 hr
= alsa_consider_start(This
);
1896 LeaveCriticalSection(&This
->lock
);
1901 This
->written_frames
+= written_frames
;
1902 This
->buf_state
= NOT_LOCKED
;
1904 LeaveCriticalSection(&This
->lock
);
1909 static const IAudioRenderClientVtbl AudioRenderClient_Vtbl
= {
1910 AudioRenderClient_QueryInterface
,
1911 AudioRenderClient_AddRef
,
1912 AudioRenderClient_Release
,
1913 AudioRenderClient_GetBuffer
,
1914 AudioRenderClient_ReleaseBuffer
1917 static HRESULT WINAPI
AudioCaptureClient_QueryInterface(
1918 IAudioCaptureClient
*iface
, REFIID riid
, void **ppv
)
1920 TRACE("(%p)->(%s, %p)\n", iface
, debugstr_guid(riid
), ppv
);
1926 if(IsEqualIID(riid
, &IID_IUnknown
) ||
1927 IsEqualIID(riid
, &IID_IAudioCaptureClient
))
1930 IUnknown_AddRef((IUnknown
*)*ppv
);
1934 WARN("Unknown interface %s\n", debugstr_guid(riid
));
1935 return E_NOINTERFACE
;
1938 static ULONG WINAPI
AudioCaptureClient_AddRef(IAudioCaptureClient
*iface
)
1940 ACImpl
*This
= impl_from_IAudioCaptureClient(iface
);
1941 return IAudioClient_AddRef(&This
->IAudioClient_iface
);
1944 static ULONG WINAPI
AudioCaptureClient_Release(IAudioCaptureClient
*iface
)
1946 ACImpl
*This
= impl_from_IAudioCaptureClient(iface
);
1947 return IAudioClient_Release(&This
->IAudioClient_iface
);
1950 static HRESULT WINAPI
AudioCaptureClient_GetBuffer(IAudioCaptureClient
*iface
,
1951 BYTE
**data
, UINT32
*frames
, DWORD
*flags
, UINT64
*devpos
,
1954 ACImpl
*This
= impl_from_IAudioCaptureClient(iface
);
1957 TRACE("(%p)->(%p, %p, %p, %p, %p)\n", This
, data
, frames
, flags
,
1960 if(!data
|| !frames
|| !flags
)
1963 EnterCriticalSection(&This
->lock
);
1965 if(This
->buf_state
!= NOT_LOCKED
){
1966 LeaveCriticalSection(&This
->lock
);
1967 return AUDCLNT_E_OUT_OF_ORDER
;
1970 hr
= IAudioCaptureClient_GetNextPacketSize(iface
, frames
);
1972 LeaveCriticalSection(&This
->lock
);
1978 if(This
->lcl_offs_frames
+ *frames
> This
->bufsize_frames
){
1979 UINT32 chunk_bytes
, offs_bytes
, frames_bytes
;
1980 if(This
->tmp_buffer_frames
< *frames
){
1981 if(This
->tmp_buffer
)
1982 This
->tmp_buffer
= HeapReAlloc(GetProcessHeap(), 0,
1983 This
->tmp_buffer
, *frames
* This
->fmt
->nBlockAlign
);
1985 This
->tmp_buffer
= HeapAlloc(GetProcessHeap(), 0,
1986 *frames
* This
->fmt
->nBlockAlign
);
1987 if(!This
->tmp_buffer
){
1988 LeaveCriticalSection(&This
->lock
);
1989 return E_OUTOFMEMORY
;
1991 This
->tmp_buffer_frames
= *frames
;
1994 *data
= This
->tmp_buffer
;
1995 chunk_bytes
= (This
->bufsize_frames
- This
->lcl_offs_frames
) *
1996 This
->fmt
->nBlockAlign
;
1997 offs_bytes
= This
->lcl_offs_frames
* This
->fmt
->nBlockAlign
;
1998 frames_bytes
= *frames
* This
->fmt
->nBlockAlign
;
1999 memcpy(This
->tmp_buffer
, This
->local_buffer
+ offs_bytes
, chunk_bytes
);
2000 memcpy(This
->tmp_buffer
+ chunk_bytes
, This
->local_buffer
,
2001 frames_bytes
- chunk_bytes
);
2003 *data
= This
->local_buffer
+
2004 This
->lcl_offs_frames
* This
->fmt
->nBlockAlign
;
2006 This
->buf_state
= LOCKED_NORMAL
;
2008 if(devpos
|| qpcpos
)
2009 IAudioClock_GetPosition(&This
->IAudioClock_iface
, devpos
, qpcpos
);
2011 LeaveCriticalSection(&This
->lock
);
2013 return *frames
? S_OK
: AUDCLNT_S_BUFFER_EMPTY
;
2016 static HRESULT WINAPI
AudioCaptureClient_ReleaseBuffer(
2017 IAudioCaptureClient
*iface
, UINT32 done
)
2019 ACImpl
*This
= impl_from_IAudioCaptureClient(iface
);
2021 TRACE("(%p)->(%u)\n", This
, done
);
2023 EnterCriticalSection(&This
->lock
);
2025 if(This
->buf_state
== NOT_LOCKED
){
2026 LeaveCriticalSection(&This
->lock
);
2027 return AUDCLNT_E_OUT_OF_ORDER
;
2030 This
->held_frames
-= done
;
2031 This
->lcl_offs_frames
+= done
;
2032 This
->lcl_offs_frames
%= This
->bufsize_frames
;
2034 This
->buf_state
= NOT_LOCKED
;
2036 LeaveCriticalSection(&This
->lock
);
2041 static HRESULT WINAPI
AudioCaptureClient_GetNextPacketSize(
2042 IAudioCaptureClient
*iface
, UINT32
*frames
)
2044 ACImpl
*This
= impl_from_IAudioCaptureClient(iface
);
2046 TRACE("(%p)->(%p)\n", This
, frames
);
2048 return AudioClient_GetCurrentPadding(&This
->IAudioClient_iface
, frames
);
2051 static const IAudioCaptureClientVtbl AudioCaptureClient_Vtbl
=
2053 AudioCaptureClient_QueryInterface
,
2054 AudioCaptureClient_AddRef
,
2055 AudioCaptureClient_Release
,
2056 AudioCaptureClient_GetBuffer
,
2057 AudioCaptureClient_ReleaseBuffer
,
2058 AudioCaptureClient_GetNextPacketSize
2061 static HRESULT WINAPI
AudioClock_QueryInterface(IAudioClock
*iface
,
2062 REFIID riid
, void **ppv
)
2064 ACImpl
*This
= impl_from_IAudioClock(iface
);
2066 TRACE("(%p)->(%s, %p)\n", iface
, debugstr_guid(riid
), ppv
);
2072 if(IsEqualIID(riid
, &IID_IUnknown
) || IsEqualIID(riid
, &IID_IAudioClock
))
2074 else if(IsEqualIID(riid
, &IID_IAudioClock2
))
2075 *ppv
= &This
->IAudioClock2_iface
;
2077 IUnknown_AddRef((IUnknown
*)*ppv
);
2081 WARN("Unknown interface %s\n", debugstr_guid(riid
));
2082 return E_NOINTERFACE
;
2085 static ULONG WINAPI
AudioClock_AddRef(IAudioClock
*iface
)
2087 ACImpl
*This
= impl_from_IAudioClock(iface
);
2088 return IAudioClient_AddRef(&This
->IAudioClient_iface
);
2091 static ULONG WINAPI
AudioClock_Release(IAudioClock
*iface
)
2093 ACImpl
*This
= impl_from_IAudioClock(iface
);
2094 return IAudioClient_Release(&This
->IAudioClient_iface
);
2097 static HRESULT WINAPI
AudioClock_GetFrequency(IAudioClock
*iface
, UINT64
*freq
)
2099 ACImpl
*This
= impl_from_IAudioClock(iface
);
2101 TRACE("(%p)->(%p)\n", This
, freq
);
2103 *freq
= This
->fmt
->nSamplesPerSec
;
2108 static HRESULT WINAPI
AudioClock_GetPosition(IAudioClock
*iface
, UINT64
*pos
,
2111 ACImpl
*This
= impl_from_IAudioClock(iface
);
2115 TRACE("(%p)->(%p, %p)\n", This
, pos
, qpctime
);
2120 EnterCriticalSection(&This
->lock
);
2122 hr
= IAudioClient_GetCurrentPadding(&This
->IAudioClient_iface
, &pad
);
2124 LeaveCriticalSection(&This
->lock
);
2128 if(This
->dataflow
== eRender
)
2129 *pos
= This
->written_frames
- pad
;
2130 else if(This
->dataflow
== eCapture
)
2131 *pos
= This
->written_frames
+ pad
;
2133 LeaveCriticalSection(&This
->lock
);
2136 LARGE_INTEGER stamp
, freq
;
2137 QueryPerformanceCounter(&stamp
);
2138 QueryPerformanceFrequency(&freq
);
2139 *qpctime
= (stamp
.QuadPart
* (INT64
)10000000) / freq
.QuadPart
;
2145 static HRESULT WINAPI
AudioClock_GetCharacteristics(IAudioClock
*iface
,
2148 ACImpl
*This
= impl_from_IAudioClock(iface
);
2150 TRACE("(%p)->(%p)\n", This
, chars
);
2155 *chars
= AUDIOCLOCK_CHARACTERISTIC_FIXED_FREQ
;
2160 static const IAudioClockVtbl AudioClock_Vtbl
=
2162 AudioClock_QueryInterface
,
2165 AudioClock_GetFrequency
,
2166 AudioClock_GetPosition
,
2167 AudioClock_GetCharacteristics
2170 static HRESULT WINAPI
AudioClock2_QueryInterface(IAudioClock2
*iface
,
2171 REFIID riid
, void **ppv
)
2173 ACImpl
*This
= impl_from_IAudioClock2(iface
);
2174 return IAudioClock_QueryInterface(&This
->IAudioClock_iface
, riid
, ppv
);
2177 static ULONG WINAPI
AudioClock2_AddRef(IAudioClock2
*iface
)
2179 ACImpl
*This
= impl_from_IAudioClock2(iface
);
2180 return IAudioClient_AddRef(&This
->IAudioClient_iface
);
2183 static ULONG WINAPI
AudioClock2_Release(IAudioClock2
*iface
)
2185 ACImpl
*This
= impl_from_IAudioClock2(iface
);
2186 return IAudioClient_Release(&This
->IAudioClient_iface
);
2189 static HRESULT WINAPI
AudioClock2_GetDevicePosition(IAudioClock2
*iface
,
2190 UINT64
*pos
, UINT64
*qpctime
)
2192 ACImpl
*This
= impl_from_IAudioClock2(iface
);
2194 FIXME("(%p)->(%p, %p)\n", This
, pos
, qpctime
);
2199 static const IAudioClock2Vtbl AudioClock2_Vtbl
=
2201 AudioClock2_QueryInterface
,
2203 AudioClock2_Release
,
2204 AudioClock2_GetDevicePosition
2207 static AudioSessionWrapper
*AudioSessionWrapper_Create(ACImpl
*client
)
2209 AudioSessionWrapper
*ret
;
2211 ret
= HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY
,
2212 sizeof(AudioSessionWrapper
));
2216 ret
->IAudioSessionControl2_iface
.lpVtbl
= &AudioSessionControl2_Vtbl
;
2217 ret
->ISimpleAudioVolume_iface
.lpVtbl
= &SimpleAudioVolume_Vtbl
;
2218 ret
->IChannelAudioVolume_iface
.lpVtbl
= &ChannelAudioVolume_Vtbl
;
2220 ret
->client
= client
;
2221 ret
->session
= client
->session
;
2222 AudioClient_AddRef(&client
->IAudioClient_iface
);
2227 static HRESULT WINAPI
AudioSessionControl_QueryInterface(
2228 IAudioSessionControl2
*iface
, REFIID riid
, void **ppv
)
2230 TRACE("(%p)->(%s, %p)\n", iface
, debugstr_guid(riid
), ppv
);
2236 if(IsEqualIID(riid
, &IID_IUnknown
) ||
2237 IsEqualIID(riid
, &IID_IAudioSessionControl
) ||
2238 IsEqualIID(riid
, &IID_IAudioSessionControl2
))
2241 IUnknown_AddRef((IUnknown
*)*ppv
);
2245 WARN("Unknown interface %s\n", debugstr_guid(riid
));
2246 return E_NOINTERFACE
;
2249 static ULONG WINAPI
AudioSessionControl_AddRef(IAudioSessionControl2
*iface
)
2251 AudioSessionWrapper
*This
= impl_from_IAudioSessionControl2(iface
);
2253 ref
= InterlockedIncrement(&This
->ref
);
2254 TRACE("(%p) Refcount now %u\n", This
, ref
);
2258 static ULONG WINAPI
AudioSessionControl_Release(IAudioSessionControl2
*iface
)
2260 AudioSessionWrapper
*This
= impl_from_IAudioSessionControl2(iface
);
2262 ref
= InterlockedDecrement(&This
->ref
);
2263 TRACE("(%p) Refcount now %u\n", This
, ref
);
2265 EnterCriticalSection(&This
->client
->lock
);
2266 This
->client
->session_wrapper
= NULL
;
2267 LeaveCriticalSection(&This
->client
->lock
);
2268 AudioClient_Release(&This
->client
->IAudioClient_iface
);
2269 HeapFree(GetProcessHeap(), 0, This
);
2274 static HRESULT WINAPI
AudioSessionControl_GetState(IAudioSessionControl2
*iface
,
2275 AudioSessionState
*state
)
2277 AudioSessionWrapper
*This
= impl_from_IAudioSessionControl2(iface
);
2280 TRACE("(%p)->(%p)\n", This
, state
);
2283 return NULL_PTR_ERR
;
2285 EnterCriticalSection(&g_sessions_lock
);
2287 if(list_empty(&This
->session
->clients
)){
2288 *state
= AudioSessionStateExpired
;
2289 LeaveCriticalSection(&g_sessions_lock
);
2293 LIST_FOR_EACH_ENTRY(client
, &This
->session
->clients
, ACImpl
, entry
){
2294 EnterCriticalSection(&client
->lock
);
2295 if(client
->started
){
2296 *state
= AudioSessionStateActive
;
2297 LeaveCriticalSection(&client
->lock
);
2298 LeaveCriticalSection(&g_sessions_lock
);
2301 LeaveCriticalSection(&client
->lock
);
2304 LeaveCriticalSection(&g_sessions_lock
);
2306 *state
= AudioSessionStateInactive
;
2311 static HRESULT WINAPI
AudioSessionControl_GetDisplayName(
2312 IAudioSessionControl2
*iface
, WCHAR
**name
)
2314 AudioSessionWrapper
*This
= impl_from_IAudioSessionControl2(iface
);
2316 FIXME("(%p)->(%p) - stub\n", This
, name
);
2321 static HRESULT WINAPI
AudioSessionControl_SetDisplayName(
2322 IAudioSessionControl2
*iface
, const WCHAR
*name
, const GUID
*session
)
2324 AudioSessionWrapper
*This
= impl_from_IAudioSessionControl2(iface
);
2326 FIXME("(%p)->(%p, %s) - stub\n", This
, name
, debugstr_guid(session
));
2331 static HRESULT WINAPI
AudioSessionControl_GetIconPath(
2332 IAudioSessionControl2
*iface
, WCHAR
**path
)
2334 AudioSessionWrapper
*This
= impl_from_IAudioSessionControl2(iface
);
2336 FIXME("(%p)->(%p) - stub\n", This
, path
);
2341 static HRESULT WINAPI
AudioSessionControl_SetIconPath(
2342 IAudioSessionControl2
*iface
, const WCHAR
*path
, const GUID
*session
)
2344 AudioSessionWrapper
*This
= impl_from_IAudioSessionControl2(iface
);
2346 FIXME("(%p)->(%p, %s) - stub\n", This
, path
, debugstr_guid(session
));
2351 static HRESULT WINAPI
AudioSessionControl_GetGroupingParam(
2352 IAudioSessionControl2
*iface
, GUID
*group
)
2354 AudioSessionWrapper
*This
= impl_from_IAudioSessionControl2(iface
);
2356 FIXME("(%p)->(%p) - stub\n", This
, group
);
2361 static HRESULT WINAPI
AudioSessionControl_SetGroupingParam(
2362 IAudioSessionControl2
*iface
, GUID
*group
, const GUID
*session
)
2364 AudioSessionWrapper
*This
= impl_from_IAudioSessionControl2(iface
);
2366 FIXME("(%p)->(%s, %s) - stub\n", This
, debugstr_guid(group
),
2367 debugstr_guid(session
));
2372 static HRESULT WINAPI
AudioSessionControl_RegisterAudioSessionNotification(
2373 IAudioSessionControl2
*iface
, IAudioSessionEvents
*events
)
2375 AudioSessionWrapper
*This
= impl_from_IAudioSessionControl2(iface
);
2377 FIXME("(%p)->(%p) - stub\n", This
, events
);
2382 static HRESULT WINAPI
AudioSessionControl_UnregisterAudioSessionNotification(
2383 IAudioSessionControl2
*iface
, IAudioSessionEvents
*events
)
2385 AudioSessionWrapper
*This
= impl_from_IAudioSessionControl2(iface
);
2387 FIXME("(%p)->(%p) - stub\n", This
, events
);
2392 static HRESULT WINAPI
AudioSessionControl_GetSessionIdentifier(
2393 IAudioSessionControl2
*iface
, WCHAR
**id
)
2395 AudioSessionWrapper
*This
= impl_from_IAudioSessionControl2(iface
);
2397 FIXME("(%p)->(%p) - stub\n", This
, id
);
2402 static HRESULT WINAPI
AudioSessionControl_GetSessionInstanceIdentifier(
2403 IAudioSessionControl2
*iface
, WCHAR
**id
)
2405 AudioSessionWrapper
*This
= impl_from_IAudioSessionControl2(iface
);
2407 FIXME("(%p)->(%p) - stub\n", This
, id
);
2412 static HRESULT WINAPI
AudioSessionControl_GetProcessId(
2413 IAudioSessionControl2
*iface
, DWORD
*pid
)
2415 AudioSessionWrapper
*This
= impl_from_IAudioSessionControl2(iface
);
2417 TRACE("(%p)->(%p)\n", This
, pid
);
2422 *pid
= GetCurrentProcessId();
2427 static HRESULT WINAPI
AudioSessionControl_IsSystemSoundsSession(
2428 IAudioSessionControl2
*iface
)
2430 AudioSessionWrapper
*This
= impl_from_IAudioSessionControl2(iface
);
2432 TRACE("(%p)\n", This
);
2437 static HRESULT WINAPI
AudioSessionControl_SetDuckingPreference(
2438 IAudioSessionControl2
*iface
, BOOL optout
)
2440 AudioSessionWrapper
*This
= impl_from_IAudioSessionControl2(iface
);
2442 TRACE("(%p)->(%d)\n", This
, optout
);
2447 static const IAudioSessionControl2Vtbl AudioSessionControl2_Vtbl
=
2449 AudioSessionControl_QueryInterface
,
2450 AudioSessionControl_AddRef
,
2451 AudioSessionControl_Release
,
2452 AudioSessionControl_GetState
,
2453 AudioSessionControl_GetDisplayName
,
2454 AudioSessionControl_SetDisplayName
,
2455 AudioSessionControl_GetIconPath
,
2456 AudioSessionControl_SetIconPath
,
2457 AudioSessionControl_GetGroupingParam
,
2458 AudioSessionControl_SetGroupingParam
,
2459 AudioSessionControl_RegisterAudioSessionNotification
,
2460 AudioSessionControl_UnregisterAudioSessionNotification
,
2461 AudioSessionControl_GetSessionIdentifier
,
2462 AudioSessionControl_GetSessionInstanceIdentifier
,
2463 AudioSessionControl_GetProcessId
,
2464 AudioSessionControl_IsSystemSoundsSession
,
2465 AudioSessionControl_SetDuckingPreference
2468 static HRESULT WINAPI
SimpleAudioVolume_QueryInterface(
2469 ISimpleAudioVolume
*iface
, REFIID riid
, void **ppv
)
2471 TRACE("(%p)->(%s, %p)\n", iface
, debugstr_guid(riid
), ppv
);
2477 if(IsEqualIID(riid
, &IID_IUnknown
) ||
2478 IsEqualIID(riid
, &IID_ISimpleAudioVolume
))
2481 IUnknown_AddRef((IUnknown
*)*ppv
);
2485 WARN("Unknown interface %s\n", debugstr_guid(riid
));
2486 return E_NOINTERFACE
;
2489 static ULONG WINAPI
SimpleAudioVolume_AddRef(ISimpleAudioVolume
*iface
)
2491 AudioSessionWrapper
*This
= impl_from_ISimpleAudioVolume(iface
);
2492 return AudioSessionControl_AddRef(&This
->IAudioSessionControl2_iface
);
2495 static ULONG WINAPI
SimpleAudioVolume_Release(ISimpleAudioVolume
*iface
)
2497 AudioSessionWrapper
*This
= impl_from_ISimpleAudioVolume(iface
);
2498 return AudioSessionControl_Release(&This
->IAudioSessionControl2_iface
);
2501 static HRESULT WINAPI
SimpleAudioVolume_SetMasterVolume(
2502 ISimpleAudioVolume
*iface
, float level
, const GUID
*context
)
2504 AudioSessionWrapper
*This
= impl_from_ISimpleAudioVolume(iface
);
2505 AudioSession
*session
= This
->session
;
2507 TRACE("(%p)->(%f, %s)\n", session
, level
, wine_dbgstr_guid(context
));
2509 if(level
< 0.f
|| level
> 1.f
)
2510 return E_INVALIDARG
;
2513 FIXME("Notifications not supported yet\n");
2515 TRACE("ALSA does not support volume control\n");
2517 EnterCriticalSection(&session
->lock
);
2519 session
->master_vol
= level
;
2521 LeaveCriticalSection(&session
->lock
);
2526 static HRESULT WINAPI
SimpleAudioVolume_GetMasterVolume(
2527 ISimpleAudioVolume
*iface
, float *level
)
2529 AudioSessionWrapper
*This
= impl_from_ISimpleAudioVolume(iface
);
2530 AudioSession
*session
= This
->session
;
2532 TRACE("(%p)->(%p)\n", session
, level
);
2535 return NULL_PTR_ERR
;
2537 *level
= session
->master_vol
;
2542 static HRESULT WINAPI
SimpleAudioVolume_SetMute(ISimpleAudioVolume
*iface
,
2543 BOOL mute
, const GUID
*context
)
2545 AudioSessionWrapper
*This
= impl_from_ISimpleAudioVolume(iface
);
2546 AudioSession
*session
= This
->session
;
2548 FIXME("(%p)->(%u, %p) - stub\n", session
, mute
, context
);
2553 static HRESULT WINAPI
SimpleAudioVolume_GetMute(ISimpleAudioVolume
*iface
,
2556 AudioSessionWrapper
*This
= impl_from_ISimpleAudioVolume(iface
);
2557 AudioSession
*session
= This
->session
;
2559 FIXME("(%p)->(%p) - stub\n", session
, mute
);
2562 return NULL_PTR_ERR
;
2567 static const ISimpleAudioVolumeVtbl SimpleAudioVolume_Vtbl
=
2569 SimpleAudioVolume_QueryInterface
,
2570 SimpleAudioVolume_AddRef
,
2571 SimpleAudioVolume_Release
,
2572 SimpleAudioVolume_SetMasterVolume
,
2573 SimpleAudioVolume_GetMasterVolume
,
2574 SimpleAudioVolume_SetMute
,
2575 SimpleAudioVolume_GetMute
2578 static HRESULT WINAPI
AudioStreamVolume_QueryInterface(
2579 IAudioStreamVolume
*iface
, REFIID riid
, void **ppv
)
2581 TRACE("(%p)->(%s, %p)\n", iface
, debugstr_guid(riid
), ppv
);
2587 if(IsEqualIID(riid
, &IID_IUnknown
) ||
2588 IsEqualIID(riid
, &IID_IAudioStreamVolume
))
2591 IUnknown_AddRef((IUnknown
*)*ppv
);
2595 WARN("Unknown interface %s\n", debugstr_guid(riid
));
2596 return E_NOINTERFACE
;
2599 static ULONG WINAPI
AudioStreamVolume_AddRef(IAudioStreamVolume
*iface
)
2601 ACImpl
*This
= impl_from_IAudioStreamVolume(iface
);
2602 return IAudioClient_AddRef(&This
->IAudioClient_iface
);
2605 static ULONG WINAPI
AudioStreamVolume_Release(IAudioStreamVolume
*iface
)
2607 ACImpl
*This
= impl_from_IAudioStreamVolume(iface
);
2608 return IAudioClient_Release(&This
->IAudioClient_iface
);
2611 static HRESULT WINAPI
AudioStreamVolume_GetChannelCount(
2612 IAudioStreamVolume
*iface
, UINT32
*out
)
2614 ACImpl
*This
= impl_from_IAudioStreamVolume(iface
);
2616 TRACE("(%p)->(%p)\n", This
, out
);
2621 *out
= This
->fmt
->nChannels
;
2626 static HRESULT WINAPI
AudioStreamVolume_SetChannelVolume(
2627 IAudioStreamVolume
*iface
, UINT32 index
, float level
)
2629 ACImpl
*This
= impl_from_IAudioStreamVolume(iface
);
2631 TRACE("(%p)->(%d, %f)\n", This
, index
, level
);
2633 if(level
< 0.f
|| level
> 1.f
)
2634 return E_INVALIDARG
;
2636 if(index
>= This
->fmt
->nChannels
)
2637 return E_INVALIDARG
;
2639 TRACE("ALSA does not support volume control\n");
2641 EnterCriticalSection(&This
->lock
);
2643 This
->vols
[index
] = level
;
2645 LeaveCriticalSection(&This
->lock
);
2650 static HRESULT WINAPI
AudioStreamVolume_GetChannelVolume(
2651 IAudioStreamVolume
*iface
, UINT32 index
, float *level
)
2653 ACImpl
*This
= impl_from_IAudioStreamVolume(iface
);
2655 TRACE("(%p)->(%d, %p)\n", This
, index
, level
);
2660 if(index
>= This
->fmt
->nChannels
)
2661 return E_INVALIDARG
;
2663 *level
= This
->vols
[index
];
2668 static HRESULT WINAPI
AudioStreamVolume_SetAllVolumes(
2669 IAudioStreamVolume
*iface
, UINT32 count
, const float *levels
)
2671 ACImpl
*This
= impl_from_IAudioStreamVolume(iface
);
2674 TRACE("(%p)->(%d, %p)\n", This
, count
, levels
);
2679 if(count
!= This
->fmt
->nChannels
)
2680 return E_INVALIDARG
;
2682 TRACE("ALSA does not support volume control\n");
2684 EnterCriticalSection(&This
->lock
);
2686 for(i
= 0; i
< count
; ++i
)
2687 This
->vols
[i
] = levels
[i
];
2689 LeaveCriticalSection(&This
->lock
);
2694 static HRESULT WINAPI
AudioStreamVolume_GetAllVolumes(
2695 IAudioStreamVolume
*iface
, UINT32 count
, float *levels
)
2697 ACImpl
*This
= impl_from_IAudioStreamVolume(iface
);
2700 TRACE("(%p)->(%d, %p)\n", This
, count
, levels
);
2705 if(count
!= This
->fmt
->nChannels
)
2706 return E_INVALIDARG
;
2708 EnterCriticalSection(&This
->lock
);
2710 for(i
= 0; i
< count
; ++i
)
2711 levels
[i
] = This
->vols
[i
];
2713 LeaveCriticalSection(&This
->lock
);
2718 static const IAudioStreamVolumeVtbl AudioStreamVolume_Vtbl
=
2720 AudioStreamVolume_QueryInterface
,
2721 AudioStreamVolume_AddRef
,
2722 AudioStreamVolume_Release
,
2723 AudioStreamVolume_GetChannelCount
,
2724 AudioStreamVolume_SetChannelVolume
,
2725 AudioStreamVolume_GetChannelVolume
,
2726 AudioStreamVolume_SetAllVolumes
,
2727 AudioStreamVolume_GetAllVolumes
2730 static HRESULT WINAPI
ChannelAudioVolume_QueryInterface(
2731 IChannelAudioVolume
*iface
, REFIID riid
, void **ppv
)
2733 TRACE("(%p)->(%s, %p)\n", iface
, debugstr_guid(riid
), ppv
);
2739 if(IsEqualIID(riid
, &IID_IUnknown
) ||
2740 IsEqualIID(riid
, &IID_IChannelAudioVolume
))
2743 IUnknown_AddRef((IUnknown
*)*ppv
);
2747 WARN("Unknown interface %s\n", debugstr_guid(riid
));
2748 return E_NOINTERFACE
;
2751 static ULONG WINAPI
ChannelAudioVolume_AddRef(IChannelAudioVolume
*iface
)
2753 AudioSessionWrapper
*This
= impl_from_IChannelAudioVolume(iface
);
2754 return AudioSessionControl_AddRef(&This
->IAudioSessionControl2_iface
);
2757 static ULONG WINAPI
ChannelAudioVolume_Release(IChannelAudioVolume
*iface
)
2759 AudioSessionWrapper
*This
= impl_from_IChannelAudioVolume(iface
);
2760 return AudioSessionControl_Release(&This
->IAudioSessionControl2_iface
);
2763 static HRESULT WINAPI
ChannelAudioVolume_GetChannelCount(
2764 IChannelAudioVolume
*iface
, UINT32
*out
)
2766 AudioSessionWrapper
*This
= impl_from_IChannelAudioVolume(iface
);
2767 AudioSession
*session
= This
->session
;
2769 TRACE("(%p)->(%p)\n", session
, out
);
2772 return NULL_PTR_ERR
;
2774 *out
= session
->channel_count
;
2779 static HRESULT WINAPI
ChannelAudioVolume_SetChannelVolume(
2780 IChannelAudioVolume
*iface
, UINT32 index
, float level
,
2781 const GUID
*context
)
2783 AudioSessionWrapper
*This
= impl_from_IChannelAudioVolume(iface
);
2784 AudioSession
*session
= This
->session
;
2786 TRACE("(%p)->(%d, %f, %s)\n", session
, index
, level
,
2787 wine_dbgstr_guid(context
));
2789 if(level
< 0.f
|| level
> 1.f
)
2790 return E_INVALIDARG
;
2792 if(index
>= session
->channel_count
)
2793 return E_INVALIDARG
;
2796 FIXME("Notifications not supported yet\n");
2798 TRACE("ALSA does not support volume control\n");
2800 EnterCriticalSection(&session
->lock
);
2802 session
->channel_vols
[index
] = level
;
2804 LeaveCriticalSection(&session
->lock
);
2809 static HRESULT WINAPI
ChannelAudioVolume_GetChannelVolume(
2810 IChannelAudioVolume
*iface
, UINT32 index
, float *level
)
2812 AudioSessionWrapper
*This
= impl_from_IChannelAudioVolume(iface
);
2813 AudioSession
*session
= This
->session
;
2815 TRACE("(%p)->(%d, %p)\n", session
, index
, level
);
2818 return NULL_PTR_ERR
;
2820 if(index
>= session
->channel_count
)
2821 return E_INVALIDARG
;
2823 *level
= session
->channel_vols
[index
];
2828 static HRESULT WINAPI
ChannelAudioVolume_SetAllVolumes(
2829 IChannelAudioVolume
*iface
, UINT32 count
, const float *levels
,
2830 const GUID
*context
)
2832 AudioSessionWrapper
*This
= impl_from_IChannelAudioVolume(iface
);
2833 AudioSession
*session
= This
->session
;
2836 TRACE("(%p)->(%d, %p, %s)\n", session
, count
, levels
,
2837 wine_dbgstr_guid(context
));
2840 return NULL_PTR_ERR
;
2842 if(count
!= session
->channel_count
)
2843 return E_INVALIDARG
;
2846 FIXME("Notifications not supported yet\n");
2848 TRACE("ALSA does not support volume control\n");
2850 EnterCriticalSection(&session
->lock
);
2852 for(i
= 0; i
< count
; ++i
)
2853 session
->channel_vols
[i
] = levels
[i
];
2855 LeaveCriticalSection(&session
->lock
);
2860 static HRESULT WINAPI
ChannelAudioVolume_GetAllVolumes(
2861 IChannelAudioVolume
*iface
, UINT32 count
, float *levels
)
2863 AudioSessionWrapper
*This
= impl_from_IChannelAudioVolume(iface
);
2864 AudioSession
*session
= This
->session
;
2867 TRACE("(%p)->(%d, %p)\n", session
, count
, levels
);
2870 return NULL_PTR_ERR
;
2872 if(count
!= session
->channel_count
)
2873 return E_INVALIDARG
;
2875 for(i
= 0; i
< count
; ++i
)
2876 levels
[i
] = session
->channel_vols
[i
];
2881 static const IChannelAudioVolumeVtbl ChannelAudioVolume_Vtbl
=
2883 ChannelAudioVolume_QueryInterface
,
2884 ChannelAudioVolume_AddRef
,
2885 ChannelAudioVolume_Release
,
2886 ChannelAudioVolume_GetChannelCount
,
2887 ChannelAudioVolume_SetChannelVolume
,
2888 ChannelAudioVolume_GetChannelVolume
,
2889 ChannelAudioVolume_SetAllVolumes
,
2890 ChannelAudioVolume_GetAllVolumes