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 {
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 period_alsa, bufsize_alsa;
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 UINT64 written_frames, held_frames, tmp_buffer_frames;
112 UINT32 bufsize_frames, period_us;
113 UINT32 lcl_offs_frames; /* offs into local_buffer where valid data starts */
116 BYTE *local_buffer, *tmp_buffer;
119 CRITICAL_SECTION lock;
121 AudioSession *session;
122 AudioSessionWrapper *session_wrapper;
129 LOCKED_NORMAL, /* public buffer piece is from local_buffer */
130 LOCKED_WRAPPED /* public buffer piece is wrapped around, in tmp_buffer */
133 typedef struct _SessionMgr {
134 IAudioSessionManager2 IAudioSessionManager2_iface;
141 static HANDLE g_timer_q;
143 static CRITICAL_SECTION g_sessions_lock;
144 static struct list g_sessions = LIST_INIT(g_sessions);
146 static const WCHAR defaultW[] = {'d','e','f','a','u','l','t',0};
147 static const char defname[] = "default";
149 static const IAudioClientVtbl AudioClient_Vtbl;
150 static const IAudioRenderClientVtbl AudioRenderClient_Vtbl;
151 static const IAudioCaptureClientVtbl AudioCaptureClient_Vtbl;
152 static const IAudioSessionControl2Vtbl AudioSessionControl2_Vtbl;
153 static const ISimpleAudioVolumeVtbl SimpleAudioVolume_Vtbl;
154 static const IAudioClockVtbl AudioClock_Vtbl;
155 static const IAudioClock2Vtbl AudioClock2_Vtbl;
156 static const IAudioStreamVolumeVtbl AudioStreamVolume_Vtbl;
157 static const IChannelAudioVolumeVtbl ChannelAudioVolume_Vtbl;
158 static const IAudioSessionManager2Vtbl AudioSessionManager2_Vtbl;
160 int wine_snd_pcm_recover(snd_pcm_t *pcm, int err, int silent);
161 static AudioSessionWrapper *AudioSessionWrapper_Create(ACImpl *client);
163 static inline ACImpl *impl_from_IAudioClient(IAudioClient *iface)
165 return CONTAINING_RECORD(iface, ACImpl, IAudioClient_iface);
168 static inline ACImpl *impl_from_IAudioRenderClient(IAudioRenderClient *iface)
170 return CONTAINING_RECORD(iface, ACImpl, IAudioRenderClient_iface);
173 static inline ACImpl *impl_from_IAudioCaptureClient(IAudioCaptureClient *iface)
175 return CONTAINING_RECORD(iface, ACImpl, IAudioCaptureClient_iface);
178 static inline AudioSessionWrapper *impl_from_IAudioSessionControl2(IAudioSessionControl2 *iface)
180 return CONTAINING_RECORD(iface, AudioSessionWrapper, IAudioSessionControl2_iface);
183 static inline AudioSessionWrapper *impl_from_ISimpleAudioVolume(ISimpleAudioVolume *iface)
185 return CONTAINING_RECORD(iface, AudioSessionWrapper, ISimpleAudioVolume_iface);
188 static inline AudioSessionWrapper *impl_from_IChannelAudioVolume(IChannelAudioVolume *iface)
190 return CONTAINING_RECORD(iface, AudioSessionWrapper, IChannelAudioVolume_iface);
193 static inline ACImpl *impl_from_IAudioClock(IAudioClock *iface)
195 return CONTAINING_RECORD(iface, ACImpl, IAudioClock_iface);
198 static inline ACImpl *impl_from_IAudioClock2(IAudioClock2 *iface)
200 return CONTAINING_RECORD(iface, ACImpl, IAudioClock2_iface);
203 static inline ACImpl *impl_from_IAudioStreamVolume(IAudioStreamVolume *iface)
205 return CONTAINING_RECORD(iface, ACImpl, IAudioStreamVolume_iface);
208 static inline SessionMgr *impl_from_IAudioSessionManager2(IAudioSessionManager2 *iface)
210 return CONTAINING_RECORD(iface, SessionMgr, IAudioSessionManager2_iface);
213 BOOL WINAPI DllMain(HINSTANCE dll, DWORD reason, void *reserved)
215 if(reason == DLL_PROCESS_ATTACH){
216 g_timer_q = CreateTimerQueue();
220 InitializeCriticalSection(&g_sessions_lock);
226 static HRESULT alsa_get_card_devices(EDataFlow flow, WCHAR **ids, char **keys,
227 UINT *num, snd_ctl_t *ctl, int card, const WCHAR *cardnameW)
229 static const WCHAR dashW[] = {' ','-',' ',0};
231 snd_pcm_info_t *info;
233 info = HeapAlloc(GetProcessHeap(), 0, snd_pcm_info_sizeof());
235 return E_OUTOFMEMORY;
237 snd_pcm_info_set_subdevice(info, 0);
238 snd_pcm_info_set_stream(info,
239 flow == eRender ? SND_PCM_STREAM_PLAYBACK : SND_PCM_STREAM_CAPTURE);
242 for(err = snd_ctl_pcm_next_device(ctl, &device); device != -1 && err >= 0;
243 err = snd_ctl_pcm_next_device(ctl, &device)){
246 snd_pcm_info_set_device(info, device);
248 if((err = snd_ctl_pcm_info(ctl, info)) < 0){
250 /* This device doesn't have the right stream direction */
253 WARN("Failed to get info for card %d, device %d: %d (%s)\n",
254 card, device, err, snd_strerror(err));
261 devname = snd_pcm_info_get_name(info);
263 WARN("Unable to get device name for card %d, device %d\n", card,
268 cardlen = lstrlenW(cardnameW);
269 len = MultiByteToWideChar(CP_UNIXCP, 0, devname, -1, NULL, 0);
270 len += lstrlenW(dashW);
272 ids[*num] = HeapAlloc(GetProcessHeap(), 0, len * sizeof(WCHAR));
274 HeapFree(GetProcessHeap(), 0, info);
275 return E_OUTOFMEMORY;
277 memcpy(ids[*num], cardnameW, cardlen * sizeof(WCHAR));
278 memcpy(ids[*num] + cardlen, dashW, lstrlenW(dashW) * sizeof(WCHAR));
279 cardlen += lstrlenW(dashW);
280 MultiByteToWideChar(CP_UNIXCP, 0, devname, -1, ids[*num] + cardlen,
283 keys[*num] = HeapAlloc(GetProcessHeap(), 0, 32);
285 HeapFree(GetProcessHeap(), 0, info);
286 HeapFree(GetProcessHeap(), 0, ids[*num]);
287 return E_OUTOFMEMORY;
289 sprintf(keys[*num], "hw:%d,%d", card, device);
295 HeapFree(GetProcessHeap(), 0, info);
298 WARN("Got a failure during device enumeration on card %d: %d (%s)\n",
299 card, err, snd_strerror(err));
304 static HRESULT alsa_enum_devices(EDataFlow flow, WCHAR **ids, char **keys,
311 for(err = snd_card_next(&card); card != -1 && err >= 0;
312 err = snd_card_next(&card)){
314 const char *cardname;
319 sprintf(cardpath, "hw:%u", card);
321 if((err = snd_ctl_open(&ctl, cardpath, 0)) < 0){
322 WARN("Unable to open ctl for ALSA device %s: %d (%s)\n", cardpath,
323 err, snd_strerror(err));
327 if((err = snd_card_get_name(card, (char **)&cardname)) < 0){
328 WARN("Unable to get card name for ALSA device %s: %d (%s)\n",
329 cardpath, err, snd_strerror(err));
330 /* FIXME: Should be localized */
331 cardname = "Unknown soundcard";
334 len = MultiByteToWideChar(CP_UNIXCP, 0, cardname, -1, NULL, 0);
335 cardnameW = HeapAlloc(GetProcessHeap(), 0, len * sizeof(WCHAR));
338 return E_OUTOFMEMORY;
340 MultiByteToWideChar(CP_UNIXCP, 0, cardname, -1, cardnameW, len);
342 alsa_get_card_devices(flow, ids, keys, num, ctl, card, cardnameW);
344 HeapFree(GetProcessHeap(), 0, cardnameW);
350 WARN("Got a failure during card enumeration: %d (%s)\n",
351 err, snd_strerror(err));
356 HRESULT WINAPI AUDDRV_GetEndpointIDs(EDataFlow flow, WCHAR ***ids, char ***keys,
357 UINT *num, UINT *def_index)
361 TRACE("%d %p %p %p %p\n", flow, ids, keys, num, def_index);
363 hr = alsa_enum_devices(flow, NULL, NULL, num);
367 *ids = HeapAlloc(GetProcessHeap(), 0, (*num + 1) * sizeof(WCHAR *));
368 *keys = HeapAlloc(GetProcessHeap(), 0, (*num + 1) * sizeof(char *));
370 HeapFree(GetProcessHeap(), 0, *ids);
371 HeapFree(GetProcessHeap(), 0, *keys);
372 return E_OUTOFMEMORY;
375 (*ids)[0] = HeapAlloc(GetProcessHeap(), 0, sizeof(defaultW));
376 memcpy((*ids)[0], defaultW, sizeof(defaultW));
377 (*keys)[0] = HeapAlloc(GetProcessHeap(), 0, sizeof(defname));
378 memcpy((*keys)[0], defname, sizeof(defname));
381 hr = alsa_enum_devices(flow, (*ids) + 1, (*keys) + 1, num);
384 for(i = 0; i < *num; ++i){
385 HeapFree(GetProcessHeap(), 0, (*ids)[i]);
386 HeapFree(GetProcessHeap(), 0, (*keys)[i]);
388 HeapFree(GetProcessHeap(), 0, *ids);
389 HeapFree(GetProcessHeap(), 0, *keys);
390 return E_OUTOFMEMORY;
393 ++(*num); /* for default device */
398 HRESULT WINAPI AUDDRV_GetAudioEndpoint(const char *key, IMMDevice *dev,
399 EDataFlow dataflow, IAudioClient **out)
403 snd_pcm_stream_t stream;
405 TRACE("\"%s\" %p %d %p\n", key, dev, dataflow, out);
407 This = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, sizeof(ACImpl));
409 return E_OUTOFMEMORY;
411 This->IAudioClient_iface.lpVtbl = &AudioClient_Vtbl;
412 This->IAudioRenderClient_iface.lpVtbl = &AudioRenderClient_Vtbl;
413 This->IAudioCaptureClient_iface.lpVtbl = &AudioCaptureClient_Vtbl;
414 This->IAudioClock_iface.lpVtbl = &AudioClock_Vtbl;
415 This->IAudioClock2_iface.lpVtbl = &AudioClock2_Vtbl;
416 This->IAudioStreamVolume_iface.lpVtbl = &AudioStreamVolume_Vtbl;
418 if(dataflow == eRender)
419 stream = SND_PCM_STREAM_PLAYBACK;
420 else if(dataflow == eCapture)
421 stream = SND_PCM_STREAM_CAPTURE;
423 HeapFree(GetProcessHeap(), 0, This);
427 This->dataflow = dataflow;
428 if((err = snd_pcm_open(&This->pcm_handle, key, stream,
429 SND_PCM_NONBLOCK)) < 0){
430 HeapFree(GetProcessHeap(), 0, This);
431 WARN("Unable to open PCM \"%s\": %d (%s)\n", key, err,
436 This->hw_params = HeapAlloc(GetProcessHeap(), 0,
437 snd_pcm_hw_params_sizeof());
438 if(!This->hw_params){
439 HeapFree(GetProcessHeap(), 0, This);
440 snd_pcm_close(This->pcm_handle);
441 return E_OUTOFMEMORY;
444 InitializeCriticalSection(&This->lock);
447 IMMDevice_AddRef(This->parent);
449 *out = &This->IAudioClient_iface;
450 IAudioClient_AddRef(&This->IAudioClient_iface);
455 static HRESULT WINAPI AudioClient_QueryInterface(IAudioClient *iface,
456 REFIID riid, void **ppv)
458 TRACE("(%p)->(%s, %p)\n", iface, debugstr_guid(riid), ppv);
463 if(IsEqualIID(riid, &IID_IUnknown) || IsEqualIID(riid, &IID_IAudioClient))
466 IUnknown_AddRef((IUnknown*)*ppv);
469 WARN("Unknown interface %s\n", debugstr_guid(riid));
470 return E_NOINTERFACE;
473 static ULONG WINAPI AudioClient_AddRef(IAudioClient *iface)
475 ACImpl *This = impl_from_IAudioClient(iface);
477 ref = InterlockedIncrement(&This->ref);
478 TRACE("(%p) Refcount now %u\n", This, ref);
482 static ULONG WINAPI AudioClient_Release(IAudioClient *iface)
484 ACImpl *This = impl_from_IAudioClient(iface);
486 ref = InterlockedDecrement(&This->ref);
487 TRACE("(%p) Refcount now %u\n", This, ref);
489 IAudioClient_Stop(iface);
490 IMMDevice_Release(This->parent);
491 DeleteCriticalSection(&This->lock);
492 snd_pcm_drop(This->pcm_handle);
493 snd_pcm_close(This->pcm_handle);
495 EnterCriticalSection(&g_sessions_lock);
496 list_remove(&This->entry);
497 LeaveCriticalSection(&g_sessions_lock);
499 HeapFree(GetProcessHeap(), 0, This->vols);
500 HeapFree(GetProcessHeap(), 0, This->local_buffer);
501 HeapFree(GetProcessHeap(), 0, This->tmp_buffer);
502 HeapFree(GetProcessHeap(), 0, This->hw_params);
503 CoTaskMemFree(This->fmt);
504 HeapFree(GetProcessHeap(), 0, This);
509 static void dump_fmt(const WAVEFORMATEX *fmt)
511 TRACE("wFormatTag: 0x%x (", fmt->wFormatTag);
512 switch(fmt->wFormatTag){
513 case WAVE_FORMAT_PCM:
514 TRACE("WAVE_FORMAT_PCM");
516 case WAVE_FORMAT_IEEE_FLOAT:
517 TRACE("WAVE_FORMAT_IEEE_FLOAT");
519 case WAVE_FORMAT_EXTENSIBLE:
520 TRACE("WAVE_FORMAT_EXTENSIBLE");
528 TRACE("nChannels: %u\n", fmt->nChannels);
529 TRACE("nSamplesPerSec: %u\n", fmt->nSamplesPerSec);
530 TRACE("nAvgBytesPerSec: %u\n", fmt->nAvgBytesPerSec);
531 TRACE("nBlockAlign: %u\n", fmt->nBlockAlign);
532 TRACE("wBitsPerSample: %u\n", fmt->wBitsPerSample);
533 TRACE("cbSize: %u\n", fmt->cbSize);
535 if(fmt->wFormatTag == WAVE_FORMAT_EXTENSIBLE){
536 WAVEFORMATEXTENSIBLE *fmtex = (void*)fmt;
537 TRACE("dwChannelMask: %08x\n", fmtex->dwChannelMask);
538 TRACE("Samples: %04x\n", fmtex->Samples.wReserved);
539 TRACE("SubFormat: %s\n", wine_dbgstr_guid(&fmtex->SubFormat));
543 static WAVEFORMATEX *clone_format(const WAVEFORMATEX *fmt)
548 if(fmt->wFormatTag == WAVE_FORMAT_EXTENSIBLE)
549 size = sizeof(WAVEFORMATEXTENSIBLE);
551 size = sizeof(WAVEFORMATEX);
553 ret = CoTaskMemAlloc(size);
557 memcpy(ret, fmt, size);
559 ret->cbSize = size - sizeof(WAVEFORMATEX);
564 static void session_init_vols(AudioSession *session, UINT channels)
566 if(session->channel_count < channels){
569 if(session->channel_vols)
570 session->channel_vols = HeapReAlloc(GetProcessHeap(), 0,
571 session->channel_vols, sizeof(float) * channels);
573 session->channel_vols = HeapAlloc(GetProcessHeap(), 0,
574 sizeof(float) * channels);
575 if(!session->channel_vols)
578 for(i = session->channel_count; i < channels; ++i)
579 session->channel_vols[i] = 1.f;
581 session->channel_count = channels;
585 static AudioSession *create_session(const GUID *guid, IMMDevice *device,
590 ret = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, sizeof(AudioSession));
594 memcpy(&ret->guid, guid, sizeof(GUID));
596 ret->device = device;
598 list_init(&ret->clients);
600 list_add_head(&g_sessions, &ret->entry);
602 InitializeCriticalSection(&ret->lock);
604 session_init_vols(ret, num_channels);
606 ret->master_vol = 1.f;
611 /* if channels == 0, then this will return or create a session with
612 * matching dataflow and GUID. otherwise, channels must also match */
613 static HRESULT get_audio_session(const GUID *sessionguid,
614 IMMDevice *device, UINT channels, AudioSession **out)
616 AudioSession *session;
618 if(!sessionguid || IsEqualGUID(sessionguid, &GUID_NULL)){
619 *out = create_session(&GUID_NULL, device, channels);
621 return E_OUTOFMEMORY;
627 LIST_FOR_EACH_ENTRY(session, &g_sessions, AudioSession, entry){
628 if(session->device == device &&
629 IsEqualGUID(sessionguid, &session->guid)){
630 session_init_vols(session, channels);
637 *out = create_session(sessionguid, device, channels);
639 return E_OUTOFMEMORY;
645 static HRESULT WINAPI AudioClient_Initialize(IAudioClient *iface,
646 AUDCLNT_SHAREMODE mode, DWORD flags, REFERENCE_TIME duration,
647 REFERENCE_TIME period, const WAVEFORMATEX *fmt,
648 const GUID *sessionguid)
650 ACImpl *This = impl_from_IAudioClient(iface);
651 snd_pcm_sw_params_t *sw_params = NULL;
652 snd_pcm_format_t format;
653 snd_pcm_uframes_t boundary;
654 const WAVEFORMATEXTENSIBLE *fmtex = (const WAVEFORMATEXTENSIBLE *)fmt;
655 unsigned int time_us, rate;
659 TRACE("(%p)->(%x, %x, %s, %s, %p, %s)\n", This, mode, flags,
660 wine_dbgstr_longlong(duration), wine_dbgstr_longlong(period), fmt, debugstr_guid(sessionguid));
665 if(mode != AUDCLNT_SHAREMODE_SHARED && mode != AUDCLNT_SHAREMODE_EXCLUSIVE)
666 return AUDCLNT_E_NOT_INITIALIZED;
668 if(flags & ~(AUDCLNT_STREAMFLAGS_CROSSPROCESS |
669 AUDCLNT_STREAMFLAGS_LOOPBACK |
670 AUDCLNT_STREAMFLAGS_EVENTCALLBACK |
671 AUDCLNT_STREAMFLAGS_NOPERSIST |
672 AUDCLNT_STREAMFLAGS_RATEADJUST |
673 AUDCLNT_SESSIONFLAGS_EXPIREWHENUNOWNED |
674 AUDCLNT_SESSIONFLAGS_DISPLAY_HIDE |
675 AUDCLNT_SESSIONFLAGS_DISPLAY_HIDEWHENEXPIRED)){
676 TRACE("Unknown flags: %08x\n", flags);
680 EnterCriticalSection(&This->lock);
683 LeaveCriticalSection(&This->lock);
684 return AUDCLNT_E_ALREADY_INITIALIZED;
689 if((err = snd_pcm_hw_params_any(This->pcm_handle, This->hw_params)) < 0){
690 WARN("Unable to get hw_params: %d (%s)\n", err, snd_strerror(err));
695 if((err = snd_pcm_hw_params_set_access(This->pcm_handle, This->hw_params,
696 SND_PCM_ACCESS_RW_INTERLEAVED)) < 0){
697 WARN("Unable to set access: %d (%s)\n", err, snd_strerror(err));
702 if(fmt->wFormatTag == WAVE_FORMAT_PCM ||
703 (fmt->wFormatTag == WAVE_FORMAT_EXTENSIBLE &&
704 IsEqualGUID(&fmtex->SubFormat, &KSDATAFORMAT_SUBTYPE_PCM))){
705 if(fmt->wBitsPerSample == 8)
706 format = SND_PCM_FORMAT_U8;
707 else if(fmt->wBitsPerSample == 16)
708 format = SND_PCM_FORMAT_S16_LE;
709 else if(fmt->wBitsPerSample == 24)
710 format = SND_PCM_FORMAT_S24_3LE;
711 else if(fmt->wBitsPerSample == 32)
712 format = SND_PCM_FORMAT_S32_LE;
714 WARN("Unsupported bit depth: %u\n", fmt->wBitsPerSample);
715 hr = AUDCLNT_E_UNSUPPORTED_FORMAT;
718 }else if(fmt->wFormatTag == WAVE_FORMAT_IEEE_FLOAT ||
719 (fmt->wFormatTag == WAVE_FORMAT_EXTENSIBLE &&
720 IsEqualGUID(&fmtex->SubFormat, &KSDATAFORMAT_SUBTYPE_IEEE_FLOAT))){
721 if(fmt->wBitsPerSample == 32)
722 format = SND_PCM_FORMAT_FLOAT_LE;
723 else if(fmt->wBitsPerSample == 64)
724 format = SND_PCM_FORMAT_FLOAT64_LE;
726 WARN("Unsupported float size: %u\n", fmt->wBitsPerSample);
727 hr = AUDCLNT_E_UNSUPPORTED_FORMAT;
731 WARN("Unknown wave format: %04x\n", fmt->wFormatTag);
732 hr = AUDCLNT_E_UNSUPPORTED_FORMAT;
736 if((err = snd_pcm_hw_params_set_format(This->pcm_handle, This->hw_params,
738 WARN("Unable to set ALSA format to %u: %d (%s)\n", format, err,
744 This->alsa_format = format;
746 rate = fmt->nSamplesPerSec;
747 if((err = snd_pcm_hw_params_set_rate_near(This->pcm_handle, This->hw_params,
749 WARN("Unable to set rate to %u: %d (%s)\n", rate, err,
755 if((err = snd_pcm_hw_params_set_channels(This->pcm_handle, This->hw_params,
756 fmt->nChannels)) < 0){
757 WARN("Unable to set channels to %u: %d (%s)\n", fmt->nChannels, err,
763 time_us = MinimumPeriod / 10;
764 if((err = snd_pcm_hw_params_set_period_time_near(This->pcm_handle,
765 This->hw_params, &time_us, NULL)) < 0){
766 WARN("Unable to set max period time to %u: %d (%s)\n", time_us,
767 err, snd_strerror(err));
772 if((err = snd_pcm_hw_params(This->pcm_handle, This->hw_params)) < 0){
773 WARN("Unable to set hw params: %d (%s)\n", err, snd_strerror(err));
778 sw_params = HeapAlloc(GetProcessHeap(), 0, snd_pcm_sw_params_sizeof());
784 if((err = snd_pcm_sw_params_current(This->pcm_handle, sw_params)) < 0){
785 WARN("Unable to get sw_params: %d (%s)\n", err, snd_strerror(err));
790 This->bufsize_frames = ceil((duration / 10000000.) * fmt->nSamplesPerSec);
791 This->local_buffer = HeapAlloc(GetProcessHeap(), 0,
792 This->bufsize_frames * fmt->nBlockAlign);
793 if(!This->local_buffer){
797 if (fmt->wBitsPerSample == 8)
798 memset(This->local_buffer, 128, This->bufsize_frames * fmt->nBlockAlign);
800 memset(This->local_buffer, 0, This->bufsize_frames * fmt->nBlockAlign);
802 if((err = snd_pcm_sw_params_get_boundary(sw_params, &boundary)) < 0){
803 WARN("Unable to get boundary: %d (%s)\n", err, snd_strerror(err));
808 if((err = snd_pcm_sw_params_set_start_threshold(This->pcm_handle,
809 sw_params, boundary)) < 0){
810 WARN("Unable to set start threshold to %lx: %d (%s)\n", boundary, err,
816 if((err = snd_pcm_sw_params_set_stop_threshold(This->pcm_handle,
817 sw_params, boundary)) < 0){
818 WARN("Unable to set stop threshold to %lx: %d (%s)\n", boundary, err,
824 if((err = snd_pcm_sw_params_set_avail_min(This->pcm_handle,
826 WARN("Unable to set avail min to 0: %d (%s)\n", err, snd_strerror(err));
831 if((err = snd_pcm_sw_params(This->pcm_handle, sw_params)) < 0){
832 WARN("Unable to set sw params: %d (%s)\n", err, snd_strerror(err));
837 if((err = snd_pcm_prepare(This->pcm_handle)) < 0){
838 WARN("Unable to prepare device: %d (%s)\n", err, snd_strerror(err));
843 if((err = snd_pcm_hw_params_get_buffer_size(This->hw_params,
844 &This->bufsize_alsa)) < 0){
845 WARN("Unable to get buffer size: %d (%s)\n", err, snd_strerror(err));
850 if((err = snd_pcm_hw_params_get_period_size(This->hw_params,
851 &This->period_alsa, NULL)) < 0){
852 WARN("Unable to get period size: %d (%s)\n", err, snd_strerror(err));
857 if((err = snd_pcm_hw_params_get_period_time(This->hw_params,
858 &This->period_us, NULL)) < 0){
859 WARN("Unable to get period time: %d (%s)\n", err, snd_strerror(err));
864 This->fmt = clone_format(fmt);
870 This->vols = HeapAlloc(GetProcessHeap(), 0, fmt->nChannels * sizeof(float));
876 for(i = 0; i < fmt->nChannels; ++i)
882 EnterCriticalSection(&g_sessions_lock);
884 hr = get_audio_session(sessionguid, This->parent, fmt->nChannels,
887 LeaveCriticalSection(&g_sessions_lock);
891 list_add_tail(&This->session->clients, &This->entry);
893 LeaveCriticalSection(&g_sessions_lock);
895 This->initted = TRUE;
898 HeapFree(GetProcessHeap(), 0, sw_params);
900 if(This->local_buffer){
901 HeapFree(GetProcessHeap(), 0, This->local_buffer);
902 This->local_buffer = NULL;
905 CoTaskMemFree(This->fmt);
909 HeapFree(GetProcessHeap(), 0, This->vols);
914 LeaveCriticalSection(&This->lock);
919 static HRESULT WINAPI AudioClient_GetBufferSize(IAudioClient *iface,
922 ACImpl *This = impl_from_IAudioClient(iface);
924 TRACE("(%p)->(%p)\n", This, out);
929 EnterCriticalSection(&This->lock);
932 LeaveCriticalSection(&This->lock);
933 return AUDCLNT_E_NOT_INITIALIZED;
936 *out = This->bufsize_frames;
938 LeaveCriticalSection(&This->lock);
943 static HRESULT WINAPI AudioClient_GetStreamLatency(IAudioClient *iface,
944 REFERENCE_TIME *latency)
946 ACImpl *This = impl_from_IAudioClient(iface);
948 TRACE("(%p)->(%p)\n", This, latency);
953 EnterCriticalSection(&This->lock);
956 LeaveCriticalSection(&This->lock);
957 return AUDCLNT_E_NOT_INITIALIZED;
960 LeaveCriticalSection(&This->lock);
967 static HRESULT WINAPI AudioClient_GetCurrentPadding(IAudioClient *iface,
970 ACImpl *This = impl_from_IAudioClient(iface);
972 TRACE("(%p)->(%p)\n", This, out);
977 EnterCriticalSection(&This->lock);
980 LeaveCriticalSection(&This->lock);
981 return AUDCLNT_E_NOT_INITIALIZED;
984 if(This->dataflow == eRender){
985 snd_pcm_sframes_t avail_frames;
987 avail_frames = snd_pcm_avail_update(This->pcm_handle);
989 if(This->bufsize_alsa < avail_frames){
990 WARN("Xrun detected\n");
991 *out = This->held_frames;
993 *out = This->bufsize_alsa - avail_frames + This->held_frames;
994 }else if(This->dataflow == eCapture){
995 *out = This->held_frames;
997 LeaveCriticalSection(&This->lock);
1001 LeaveCriticalSection(&This->lock);
1006 static DWORD get_channel_mask(unsigned int channels)
1012 return SPEAKER_FRONT_CENTER;
1014 return SPEAKER_FRONT_LEFT | SPEAKER_FRONT_RIGHT;
1016 return SPEAKER_FRONT_LEFT | SPEAKER_FRONT_RIGHT |
1017 SPEAKER_LOW_FREQUENCY;
1019 return SPEAKER_FRONT_LEFT | SPEAKER_FRONT_RIGHT | SPEAKER_BACK_LEFT |
1022 return SPEAKER_FRONT_LEFT | SPEAKER_FRONT_RIGHT | SPEAKER_BACK_LEFT |
1023 SPEAKER_BACK_RIGHT | SPEAKER_LOW_FREQUENCY;
1025 return SPEAKER_FRONT_LEFT | SPEAKER_FRONT_RIGHT | SPEAKER_BACK_LEFT |
1026 SPEAKER_BACK_RIGHT | SPEAKER_LOW_FREQUENCY | SPEAKER_FRONT_CENTER;
1028 return SPEAKER_FRONT_LEFT | SPEAKER_FRONT_RIGHT | SPEAKER_BACK_LEFT |
1029 SPEAKER_BACK_RIGHT | SPEAKER_LOW_FREQUENCY | SPEAKER_FRONT_CENTER |
1030 SPEAKER_BACK_CENTER;
1032 FIXME("Unknown speaker configuration: %u\n", channels);
1036 static HRESULT WINAPI AudioClient_IsFormatSupported(IAudioClient *iface,
1037 AUDCLNT_SHAREMODE mode, const WAVEFORMATEX *fmt,
1040 ACImpl *This = impl_from_IAudioClient(iface);
1041 snd_pcm_format_mask_t *formats = NULL;
1043 WAVEFORMATEX *closest = NULL;
1044 const WAVEFORMATEXTENSIBLE *fmtex = (const WAVEFORMATEXTENSIBLE *)fmt;
1045 unsigned int max = 0, min = 0;
1048 TRACE("(%p)->(%x, %p, %p)\n", This, mode, fmt, out);
1050 if(!fmt || (mode == AUDCLNT_SHAREMODE_SHARED && !out))
1053 if(mode != AUDCLNT_SHAREMODE_SHARED && mode != AUDCLNT_SHAREMODE_EXCLUSIVE)
1054 return E_INVALIDARG;
1056 if(fmt->wFormatTag == WAVE_FORMAT_EXTENSIBLE &&
1057 fmt->cbSize < sizeof(WAVEFORMATEXTENSIBLE) - sizeof(WAVEFORMATEX))
1058 return E_INVALIDARG;
1062 EnterCriticalSection(&This->lock);
1064 if((err = snd_pcm_hw_params_any(This->pcm_handle, This->hw_params)) < 0){
1069 formats = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY,
1070 snd_pcm_format_mask_sizeof());
1076 snd_pcm_hw_params_get_format_mask(This->hw_params, formats);
1078 if(fmt->wFormatTag == WAVE_FORMAT_PCM ||
1079 (fmt->wFormatTag == WAVE_FORMAT_EXTENSIBLE &&
1080 IsEqualGUID(&fmtex->SubFormat, &KSDATAFORMAT_SUBTYPE_PCM))){
1081 switch(fmt->wBitsPerSample){
1083 if(!snd_pcm_format_mask_test(formats, SND_PCM_FORMAT_U8)){
1084 hr = AUDCLNT_E_UNSUPPORTED_FORMAT;
1089 if(!snd_pcm_format_mask_test(formats, SND_PCM_FORMAT_S16_LE)){
1090 hr = AUDCLNT_E_UNSUPPORTED_FORMAT;
1095 if(!snd_pcm_format_mask_test(formats, SND_PCM_FORMAT_S24_3LE)){
1096 hr = AUDCLNT_E_UNSUPPORTED_FORMAT;
1101 if(!snd_pcm_format_mask_test(formats, SND_PCM_FORMAT_S32_LE)){
1102 hr = AUDCLNT_E_UNSUPPORTED_FORMAT;
1107 hr = AUDCLNT_E_UNSUPPORTED_FORMAT;
1110 }else if(fmt->wFormatTag == WAVE_FORMAT_IEEE_FLOAT ||
1111 (fmt->wFormatTag == WAVE_FORMAT_EXTENSIBLE &&
1112 IsEqualGUID(&fmtex->SubFormat, &KSDATAFORMAT_SUBTYPE_IEEE_FLOAT))){
1113 switch(fmt->wBitsPerSample){
1115 if(!snd_pcm_format_mask_test(formats, SND_PCM_FORMAT_FLOAT_LE)){
1116 hr = AUDCLNT_E_UNSUPPORTED_FORMAT;
1121 if(!snd_pcm_format_mask_test(formats, SND_PCM_FORMAT_FLOAT64_LE)){
1122 hr = AUDCLNT_E_UNSUPPORTED_FORMAT;
1127 hr = AUDCLNT_E_UNSUPPORTED_FORMAT;
1131 hr = AUDCLNT_E_UNSUPPORTED_FORMAT;
1135 closest = clone_format(fmt);
1141 if((err = snd_pcm_hw_params_get_rate_min(This->hw_params, &min, NULL)) < 0){
1143 WARN("Unable to get min rate: %d (%s)\n", err, snd_strerror(err));
1147 if((err = snd_pcm_hw_params_get_rate_max(This->hw_params, &max, NULL)) < 0){
1149 WARN("Unable to get max rate: %d (%s)\n", err, snd_strerror(err));
1153 if(fmt->nSamplesPerSec < min || fmt->nSamplesPerSec > max){
1154 hr = AUDCLNT_E_UNSUPPORTED_FORMAT;
1158 if((err = snd_pcm_hw_params_get_channels_min(This->hw_params, &min)) < 0){
1160 WARN("Unable to get min channels: %d (%s)\n", err, snd_strerror(err));
1164 if((err = snd_pcm_hw_params_get_channels_max(This->hw_params, &max)) < 0){
1166 WARN("Unable to get max channels: %d (%s)\n", err, snd_strerror(err));
1171 if(fmt->nChannels > max){
1173 closest->nChannels = max;
1174 }else if(fmt->nChannels < min){
1176 closest->nChannels = min;
1179 if(closest->wFormatTag == WAVE_FORMAT_EXTENSIBLE){
1180 DWORD mask = get_channel_mask(closest->nChannels);
1182 ((WAVEFORMATEXTENSIBLE*)closest)->dwChannelMask = mask;
1184 if(fmt->wFormatTag == WAVE_FORMAT_EXTENSIBLE &&
1185 fmtex->dwChannelMask != mask)
1190 LeaveCriticalSection(&This->lock);
1191 HeapFree(GetProcessHeap(), 0, formats);
1193 if(hr == S_OK || !out){
1194 CoTaskMemFree(closest);
1198 closest->nBlockAlign =
1199 closest->nChannels * closest->wBitsPerSample / 8;
1200 closest->nAvgBytesPerSec =
1201 closest->nBlockAlign * closest->nSamplesPerSec;
1205 TRACE("returning: %08x\n", hr);
1209 static HRESULT WINAPI AudioClient_GetMixFormat(IAudioClient *iface,
1210 WAVEFORMATEX **pwfx)
1212 ACImpl *This = impl_from_IAudioClient(iface);
1213 WAVEFORMATEXTENSIBLE *fmt;
1214 snd_pcm_format_mask_t *formats;
1215 unsigned int max_rate, max_channels;
1219 TRACE("(%p)->(%p)\n", This, pwfx);
1225 fmt = CoTaskMemAlloc(sizeof(WAVEFORMATEXTENSIBLE));
1227 return E_OUTOFMEMORY;
1229 formats = HeapAlloc(GetProcessHeap(), 0, snd_pcm_format_mask_sizeof());
1232 return E_OUTOFMEMORY;
1235 EnterCriticalSection(&This->lock);
1237 if((err = snd_pcm_hw_params_any(This->pcm_handle, This->hw_params)) < 0){
1238 WARN("Unable to get hw_params: %d (%s)\n", err, snd_strerror(err));
1243 snd_pcm_hw_params_get_format_mask(This->hw_params, formats);
1245 fmt->Format.wFormatTag = WAVE_FORMAT_EXTENSIBLE;
1246 if(snd_pcm_format_mask_test(formats, SND_PCM_FORMAT_FLOAT_LE)){
1247 fmt->Format.wBitsPerSample = 32;
1248 fmt->SubFormat = KSDATAFORMAT_SUBTYPE_IEEE_FLOAT;
1249 }else if(snd_pcm_format_mask_test(formats, SND_PCM_FORMAT_S16_LE)){
1250 fmt->Format.wBitsPerSample = 16;
1251 fmt->SubFormat = KSDATAFORMAT_SUBTYPE_PCM;
1252 }else if(snd_pcm_format_mask_test(formats, SND_PCM_FORMAT_U8)){
1253 fmt->Format.wBitsPerSample = 8;
1254 fmt->SubFormat = KSDATAFORMAT_SUBTYPE_PCM;
1255 }else if(snd_pcm_format_mask_test(formats, SND_PCM_FORMAT_S32_LE)){
1256 fmt->Format.wBitsPerSample = 32;
1257 fmt->SubFormat = KSDATAFORMAT_SUBTYPE_PCM;
1258 }else if(snd_pcm_format_mask_test(formats, SND_PCM_FORMAT_S24_3LE)){
1259 fmt->Format.wBitsPerSample = 24;
1260 fmt->SubFormat = KSDATAFORMAT_SUBTYPE_PCM;
1262 ERR("Didn't recognize any available ALSA formats\n");
1267 if((err = snd_pcm_hw_params_get_channels_max(This->hw_params,
1268 &max_channels)) < 0){
1269 WARN("Unable to get max channels: %d (%s)\n", err, snd_strerror(err));
1274 if(max_channels > 2){
1275 FIXME("Don't know what to do with %u channels, pretending there's "
1276 "only 2 channels\n", max_channels);
1277 fmt->Format.nChannels = 2;
1279 fmt->Format.nChannels = max_channels;
1281 fmt->dwChannelMask = get_channel_mask(fmt->Format.nChannels);
1283 if((err = snd_pcm_hw_params_get_rate_max(This->hw_params, &max_rate,
1285 WARN("Unable to get max rate: %d (%s)\n", err, snd_strerror(err));
1290 if(max_rate >= 48000)
1291 fmt->Format.nSamplesPerSec = 48000;
1292 else if(max_rate >= 44100)
1293 fmt->Format.nSamplesPerSec = 44100;
1294 else if(max_rate >= 22050)
1295 fmt->Format.nSamplesPerSec = 22050;
1296 else if(max_rate >= 11025)
1297 fmt->Format.nSamplesPerSec = 11025;
1298 else if(max_rate >= 8000)
1299 fmt->Format.nSamplesPerSec = 8000;
1301 ERR("Unknown max rate: %u\n", max_rate);
1306 fmt->Format.nBlockAlign = (fmt->Format.wBitsPerSample *
1307 fmt->Format.nChannels) / 8;
1308 fmt->Format.nAvgBytesPerSec = fmt->Format.nSamplesPerSec *
1309 fmt->Format.nBlockAlign;
1311 fmt->Samples.wValidBitsPerSample = fmt->Format.wBitsPerSample;
1312 fmt->Format.cbSize = sizeof(WAVEFORMATEXTENSIBLE) - sizeof(WAVEFORMATEX);
1314 dump_fmt((WAVEFORMATEX*)fmt);
1315 *pwfx = (WAVEFORMATEX*)fmt;
1318 LeaveCriticalSection(&This->lock);
1321 HeapFree(GetProcessHeap(), 0, formats);
1326 static HRESULT WINAPI AudioClient_GetDevicePeriod(IAudioClient *iface,
1327 REFERENCE_TIME *defperiod, REFERENCE_TIME *minperiod)
1329 ACImpl *This = impl_from_IAudioClient(iface);
1331 TRACE("(%p)->(%p, %p)\n", This, defperiod, minperiod);
1333 if(!defperiod && !minperiod)
1337 *defperiod = DefaultPeriod;
1339 *minperiod = MinimumPeriod;
1344 static snd_pcm_sframes_t alsa_write_best_effort(snd_pcm_t *handle, BYTE *buf,
1345 snd_pcm_uframes_t frames, ACImpl *This)
1347 snd_pcm_sframes_t written;
1349 if(This->session->mute){
1351 if((err = snd_pcm_format_set_silence(This->alsa_format, buf,
1352 frames * This->fmt->nChannels)) < 0)
1353 WARN("Setting buffer to silence failed: %d (%s)\n", err,
1357 written = snd_pcm_writei(handle, buf, frames);
1361 if(written == -EAGAIN)
1365 WARN("writei failed, recovering: %ld (%s)\n", written,
1366 snd_strerror(written));
1368 ret = wine_snd_pcm_recover(handle, written, 0);
1370 WARN("Could not recover: %d (%s)\n", ret, snd_strerror(ret));
1374 written = snd_pcm_writei(handle, buf, frames);
1380 static void alsa_write_data(ACImpl *This)
1382 snd_pcm_sframes_t written;
1383 snd_pcm_uframes_t to_write;
1385 This->local_buffer + (This->lcl_offs_frames * This->fmt->nBlockAlign);
1387 if(This->lcl_offs_frames + This->held_frames > This->bufsize_frames)
1388 to_write = This->bufsize_frames - This->lcl_offs_frames;
1390 to_write = This->held_frames;
1392 written = alsa_write_best_effort(This->pcm_handle, buf, to_write, This);
1394 WARN("Couldn't write: %ld (%s)\n", written, snd_strerror(written));
1398 This->lcl_offs_frames += written;
1399 This->lcl_offs_frames %= This->bufsize_frames;
1400 This->held_frames -= written;
1402 if(written < to_write){
1403 /* ALSA buffer probably full */
1407 if(This->held_frames){
1408 /* wrapped and have some data back at the start to write */
1409 written = alsa_write_best_effort(This->pcm_handle, This->local_buffer,
1410 This->held_frames, This);
1412 WARN("Couldn't write: %ld (%s)\n", written, snd_strerror(written));
1416 This->lcl_offs_frames += written;
1417 This->lcl_offs_frames %= This->bufsize_frames;
1418 This->held_frames -= written;
1422 static void alsa_read_data(ACImpl *This)
1424 snd_pcm_sframes_t pos, readable, nread;
1426 pos = (This->held_frames + This->lcl_offs_frames) % This->bufsize_frames;
1427 readable = This->bufsize_frames - pos;
1429 nread = snd_pcm_readi(This->pcm_handle,
1430 This->local_buffer + pos * This->fmt->nBlockAlign, readable);
1434 WARN("read failed, recovering: %ld (%s)\n", nread, snd_strerror(nread));
1436 ret = wine_snd_pcm_recover(This->pcm_handle, nread, 0);
1438 WARN("Recover failed: %d (%s)\n", ret, snd_strerror(ret));
1442 nread = snd_pcm_readi(This->pcm_handle,
1443 This->local_buffer + pos * This->fmt->nBlockAlign, readable);
1445 WARN("read failed: %ld (%s)\n", nread, snd_strerror(nread));
1450 if(This->session->mute){
1452 if((err = snd_pcm_format_set_silence(This->alsa_format,
1453 This->local_buffer + pos * This->fmt->nBlockAlign,
1455 WARN("Setting buffer to silence failed: %d (%s)\n", err,
1459 This->held_frames += nread;
1461 if(This->held_frames > This->bufsize_frames){
1462 WARN("Overflow of unread data\n");
1463 This->lcl_offs_frames += This->held_frames;
1464 This->lcl_offs_frames %= This->bufsize_frames;
1465 This->held_frames = This->bufsize_frames;
1469 static void CALLBACK alsa_push_buffer_data(void *user, BOOLEAN timer)
1471 ACImpl *This = user;
1473 EnterCriticalSection(&This->lock);
1476 if(This->dataflow == eRender && This->held_frames)
1477 alsa_write_data(This);
1478 else if(This->dataflow == eCapture)
1479 alsa_read_data(This);
1482 SetEvent(This->event);
1485 LeaveCriticalSection(&This->lock);
1488 static HRESULT alsa_consider_start(ACImpl *This)
1490 snd_pcm_sframes_t avail;
1493 avail = snd_pcm_avail_update(This->pcm_handle);
1495 WARN("Unable to get avail_update: %ld (%s)\n", avail,
1496 snd_strerror(avail));
1500 if(This->period_alsa < This->bufsize_alsa - avail){
1501 if((err = snd_pcm_start(This->pcm_handle)) < 0){
1502 WARN("Start failed: %d (%s), state: %d\n", err, snd_strerror(err),
1503 snd_pcm_state(This->pcm_handle));
1513 static HRESULT WINAPI AudioClient_Start(IAudioClient *iface)
1515 ACImpl *This = impl_from_IAudioClient(iface);
1519 TRACE("(%p)\n", This);
1521 EnterCriticalSection(&This->lock);
1524 LeaveCriticalSection(&This->lock);
1525 return AUDCLNT_E_NOT_INITIALIZED;
1528 if((This->flags & AUDCLNT_STREAMFLAGS_EVENTCALLBACK) && !This->event){
1529 LeaveCriticalSection(&This->lock);
1530 return AUDCLNT_E_EVENTHANDLE_NOT_SET;
1534 LeaveCriticalSection(&This->lock);
1535 return AUDCLNT_E_NOT_STOPPED;
1538 hr = alsa_consider_start(This);
1540 LeaveCriticalSection(&This->lock);
1544 period_ms = This->period_us / 1000;
1548 if(This->dataflow == eCapture){
1549 /* dump any data that might be leftover in the ALSA capture buffer */
1550 snd_pcm_readi(This->pcm_handle, This->local_buffer,
1551 This->bufsize_frames);
1554 if(!CreateTimerQueueTimer(&This->timer, g_timer_q, alsa_push_buffer_data,
1555 This, 0, period_ms, WT_EXECUTEINTIMERTHREAD)){
1556 LeaveCriticalSection(&This->lock);
1557 WARN("Unable to create timer: %u\n", GetLastError());
1561 This->started = TRUE;
1563 LeaveCriticalSection(&This->lock);
1568 static HRESULT WINAPI AudioClient_Stop(IAudioClient *iface)
1570 ACImpl *This = impl_from_IAudioClient(iface);
1575 TRACE("(%p)\n", This);
1577 EnterCriticalSection(&This->lock);
1580 LeaveCriticalSection(&This->lock);
1581 return AUDCLNT_E_NOT_INITIALIZED;
1585 LeaveCriticalSection(&This->lock);
1589 event = CreateEventW(NULL, TRUE, FALSE, NULL);
1590 wait = !DeleteTimerQueueTimer(g_timer_q, This->timer, event);
1592 WARN("DeleteTimerQueueTimer error %u\n", GetLastError());
1593 wait = wait && GetLastError() == ERROR_IO_PENDING;
1595 if((err = snd_pcm_drop(This->pcm_handle)) < 0){
1596 LeaveCriticalSection(&This->lock);
1597 WARN("Drop failed: %d (%s)\n", err, snd_strerror(err));
1601 if((err = snd_pcm_prepare(This->pcm_handle)) < 0){
1602 LeaveCriticalSection(&This->lock);
1603 WARN("Prepare failed: %d (%s)\n", err, snd_strerror(err));
1607 This->started = FALSE;
1609 LeaveCriticalSection(&This->lock);
1612 WaitForSingleObject(event, INFINITE);
1618 static HRESULT WINAPI AudioClient_Reset(IAudioClient *iface)
1620 ACImpl *This = impl_from_IAudioClient(iface);
1622 TRACE("(%p)\n", This);
1624 EnterCriticalSection(&This->lock);
1627 LeaveCriticalSection(&This->lock);
1628 return AUDCLNT_E_NOT_INITIALIZED;
1632 LeaveCriticalSection(&This->lock);
1633 return AUDCLNT_E_NOT_STOPPED;
1636 This->held_frames = 0;
1637 This->written_frames = 0;
1638 This->lcl_offs_frames = 0;
1640 LeaveCriticalSection(&This->lock);
1645 static HRESULT WINAPI AudioClient_SetEventHandle(IAudioClient *iface,
1648 ACImpl *This = impl_from_IAudioClient(iface);
1650 TRACE("(%p)->(%p)\n", This, event);
1653 return E_INVALIDARG;
1655 EnterCriticalSection(&This->lock);
1658 LeaveCriticalSection(&This->lock);
1659 return AUDCLNT_E_NOT_INITIALIZED;
1662 if(!(This->flags & AUDCLNT_STREAMFLAGS_EVENTCALLBACK)){
1663 LeaveCriticalSection(&This->lock);
1664 return AUDCLNT_E_EVENTHANDLE_NOT_EXPECTED;
1667 This->event = event;
1669 LeaveCriticalSection(&This->lock);
1674 static HRESULT WINAPI AudioClient_GetService(IAudioClient *iface, REFIID riid,
1677 ACImpl *This = impl_from_IAudioClient(iface);
1679 TRACE("(%p)->(%s, %p)\n", This, debugstr_guid(riid), ppv);
1685 EnterCriticalSection(&This->lock);
1688 LeaveCriticalSection(&This->lock);
1689 return AUDCLNT_E_NOT_INITIALIZED;
1692 if(IsEqualIID(riid, &IID_IAudioRenderClient)){
1693 if(This->dataflow != eRender){
1694 LeaveCriticalSection(&This->lock);
1695 return AUDCLNT_E_WRONG_ENDPOINT_TYPE;
1697 IAudioRenderClient_AddRef(&This->IAudioRenderClient_iface);
1698 *ppv = &This->IAudioRenderClient_iface;
1699 }else if(IsEqualIID(riid, &IID_IAudioCaptureClient)){
1700 if(This->dataflow != eCapture){
1701 LeaveCriticalSection(&This->lock);
1702 return AUDCLNT_E_WRONG_ENDPOINT_TYPE;
1704 IAudioCaptureClient_AddRef(&This->IAudioCaptureClient_iface);
1705 *ppv = &This->IAudioCaptureClient_iface;
1706 }else if(IsEqualIID(riid, &IID_IAudioClock)){
1707 IAudioClock_AddRef(&This->IAudioClock_iface);
1708 *ppv = &This->IAudioClock_iface;
1709 }else if(IsEqualIID(riid, &IID_IAudioStreamVolume)){
1710 IAudioStreamVolume_AddRef(&This->IAudioStreamVolume_iface);
1711 *ppv = &This->IAudioStreamVolume_iface;
1712 }else if(IsEqualIID(riid, &IID_IAudioSessionControl)){
1713 if(!This->session_wrapper){
1714 This->session_wrapper = AudioSessionWrapper_Create(This);
1715 if(!This->session_wrapper){
1716 LeaveCriticalSection(&This->lock);
1717 return E_OUTOFMEMORY;
1720 IAudioSessionControl2_AddRef(&This->session_wrapper->IAudioSessionControl2_iface);
1722 *ppv = &This->session_wrapper->IAudioSessionControl2_iface;
1723 }else if(IsEqualIID(riid, &IID_IChannelAudioVolume)){
1724 if(!This->session_wrapper){
1725 This->session_wrapper = AudioSessionWrapper_Create(This);
1726 if(!This->session_wrapper){
1727 LeaveCriticalSection(&This->lock);
1728 return E_OUTOFMEMORY;
1731 IChannelAudioVolume_AddRef(&This->session_wrapper->IChannelAudioVolume_iface);
1733 *ppv = &This->session_wrapper->IChannelAudioVolume_iface;
1734 }else if(IsEqualIID(riid, &IID_ISimpleAudioVolume)){
1735 if(!This->session_wrapper){
1736 This->session_wrapper = AudioSessionWrapper_Create(This);
1737 if(!This->session_wrapper){
1738 LeaveCriticalSection(&This->lock);
1739 return E_OUTOFMEMORY;
1742 ISimpleAudioVolume_AddRef(&This->session_wrapper->ISimpleAudioVolume_iface);
1744 *ppv = &This->session_wrapper->ISimpleAudioVolume_iface;
1748 LeaveCriticalSection(&This->lock);
1752 LeaveCriticalSection(&This->lock);
1754 FIXME("stub %s\n", debugstr_guid(riid));
1755 return E_NOINTERFACE;
1758 static const IAudioClientVtbl AudioClient_Vtbl =
1760 AudioClient_QueryInterface,
1762 AudioClient_Release,
1763 AudioClient_Initialize,
1764 AudioClient_GetBufferSize,
1765 AudioClient_GetStreamLatency,
1766 AudioClient_GetCurrentPadding,
1767 AudioClient_IsFormatSupported,
1768 AudioClient_GetMixFormat,
1769 AudioClient_GetDevicePeriod,
1773 AudioClient_SetEventHandle,
1774 AudioClient_GetService
1777 static HRESULT WINAPI AudioRenderClient_QueryInterface(
1778 IAudioRenderClient *iface, REFIID riid, void **ppv)
1780 TRACE("(%p)->(%s, %p)\n", iface, debugstr_guid(riid), ppv);
1786 if(IsEqualIID(riid, &IID_IUnknown) ||
1787 IsEqualIID(riid, &IID_IAudioRenderClient))
1790 IUnknown_AddRef((IUnknown*)*ppv);
1794 WARN("Unknown interface %s\n", debugstr_guid(riid));
1795 return E_NOINTERFACE;
1798 static ULONG WINAPI AudioRenderClient_AddRef(IAudioRenderClient *iface)
1800 ACImpl *This = impl_from_IAudioRenderClient(iface);
1801 return AudioClient_AddRef(&This->IAudioClient_iface);
1804 static ULONG WINAPI AudioRenderClient_Release(IAudioRenderClient *iface)
1806 ACImpl *This = impl_from_IAudioRenderClient(iface);
1807 return AudioClient_Release(&This->IAudioClient_iface);
1810 static HRESULT WINAPI AudioRenderClient_GetBuffer(IAudioRenderClient *iface,
1811 UINT32 frames, BYTE **data)
1813 ACImpl *This = impl_from_IAudioRenderClient(iface);
1818 TRACE("(%p)->(%u, %p)\n", This, frames, data);
1823 EnterCriticalSection(&This->lock);
1825 if(This->buf_state != NOT_LOCKED){
1826 LeaveCriticalSection(&This->lock);
1827 return AUDCLNT_E_OUT_OF_ORDER;
1831 This->buf_state = LOCKED_NORMAL;
1832 LeaveCriticalSection(&This->lock);
1836 hr = IAudioClient_GetCurrentPadding(&This->IAudioClient_iface, &pad);
1838 LeaveCriticalSection(&This->lock);
1842 if(pad + frames > This->bufsize_frames){
1843 LeaveCriticalSection(&This->lock);
1844 return AUDCLNT_E_BUFFER_TOO_LARGE;
1848 (This->lcl_offs_frames + This->held_frames) % This->bufsize_frames;
1849 if(write_pos + frames > This->bufsize_frames){
1850 if(This->tmp_buffer_frames < frames){
1851 if(This->tmp_buffer)
1852 This->tmp_buffer = HeapReAlloc(GetProcessHeap(), 0,
1853 This->tmp_buffer, frames * This->fmt->nBlockAlign);
1855 This->tmp_buffer = HeapAlloc(GetProcessHeap(), 0,
1856 frames * This->fmt->nBlockAlign);
1857 if(!This->tmp_buffer){
1858 LeaveCriticalSection(&This->lock);
1859 return E_OUTOFMEMORY;
1861 This->tmp_buffer_frames = frames;
1863 *data = This->tmp_buffer;
1864 This->buf_state = LOCKED_WRAPPED;
1866 *data = This->local_buffer +
1867 This->lcl_offs_frames * This->fmt->nBlockAlign;
1868 This->buf_state = LOCKED_NORMAL;
1871 LeaveCriticalSection(&This->lock);
1876 static void alsa_wrap_buffer(ACImpl *This, BYTE *buffer, UINT32 written_bytes)
1878 snd_pcm_uframes_t write_offs_frames =
1879 (This->lcl_offs_frames + This->held_frames) % This->bufsize_frames;
1880 UINT32 write_offs_bytes = write_offs_frames * This->fmt->nBlockAlign;
1881 snd_pcm_uframes_t chunk_frames = This->bufsize_frames - write_offs_frames;
1882 UINT32 chunk_bytes = chunk_frames * This->fmt->nBlockAlign;
1884 if(written_bytes < chunk_bytes){
1885 memcpy(This->local_buffer + write_offs_bytes, buffer, written_bytes);
1887 memcpy(This->local_buffer + write_offs_bytes, buffer, chunk_bytes);
1888 memcpy(This->local_buffer, buffer + chunk_bytes,
1889 written_bytes - chunk_bytes);
1893 static HRESULT WINAPI AudioRenderClient_ReleaseBuffer(
1894 IAudioRenderClient *iface, UINT32 written_frames, DWORD flags)
1896 ACImpl *This = impl_from_IAudioRenderClient(iface);
1897 UINT32 written_bytes = written_frames * This->fmt->nBlockAlign;
1901 TRACE("(%p)->(%u, %x)\n", This, written_frames, flags);
1903 EnterCriticalSection(&This->lock);
1905 if(This->buf_state == NOT_LOCKED || !written_frames){
1906 This->buf_state = NOT_LOCKED;
1907 LeaveCriticalSection(&This->lock);
1908 return written_frames ? AUDCLNT_E_OUT_OF_ORDER : S_OK;
1911 if(This->buf_state == LOCKED_NORMAL)
1912 buffer = This->local_buffer +
1913 This->lcl_offs_frames * This->fmt->nBlockAlign;
1915 buffer = This->tmp_buffer;
1917 if(flags & AUDCLNT_BUFFERFLAGS_SILENT){
1918 WAVEFORMATEXTENSIBLE *fmtex = (WAVEFORMATEXTENSIBLE*)This->fmt;
1919 if((This->fmt->wFormatTag == WAVE_FORMAT_PCM ||
1920 (This->fmt->wFormatTag == WAVE_FORMAT_EXTENSIBLE &&
1921 IsEqualGUID(&fmtex->SubFormat, &KSDATAFORMAT_SUBTYPE_PCM))) &&
1922 This->fmt->wBitsPerSample == 8)
1923 memset(buffer, 128, written_frames * This->fmt->nBlockAlign);
1925 memset(buffer, 0, written_frames * This->fmt->nBlockAlign);
1928 if(This->held_frames){
1929 if(This->buf_state == LOCKED_WRAPPED)
1930 alsa_wrap_buffer(This, buffer, written_bytes);
1932 This->held_frames += written_frames;
1934 snd_pcm_sframes_t written;
1936 written = alsa_write_best_effort(This->pcm_handle, buffer,
1937 written_frames, This);
1939 LeaveCriticalSection(&This->lock);
1940 WARN("write failed: %ld (%s)\n", written, snd_strerror(written));
1944 if(written < written_frames){
1945 if(This->buf_state == LOCKED_WRAPPED)
1946 alsa_wrap_buffer(This,
1947 This->tmp_buffer + written * This->fmt->nBlockAlign,
1948 written_frames - written);
1950 This->held_frames = written_frames - written;
1955 snd_pcm_state(This->pcm_handle) == SND_PCM_STATE_PREPARED){
1956 hr = alsa_consider_start(This);
1958 LeaveCriticalSection(&This->lock);
1963 This->written_frames += written_frames;
1964 This->buf_state = NOT_LOCKED;
1966 LeaveCriticalSection(&This->lock);
1971 static const IAudioRenderClientVtbl AudioRenderClient_Vtbl = {
1972 AudioRenderClient_QueryInterface,
1973 AudioRenderClient_AddRef,
1974 AudioRenderClient_Release,
1975 AudioRenderClient_GetBuffer,
1976 AudioRenderClient_ReleaseBuffer
1979 static HRESULT WINAPI AudioCaptureClient_QueryInterface(
1980 IAudioCaptureClient *iface, REFIID riid, void **ppv)
1982 TRACE("(%p)->(%s, %p)\n", iface, debugstr_guid(riid), ppv);
1988 if(IsEqualIID(riid, &IID_IUnknown) ||
1989 IsEqualIID(riid, &IID_IAudioCaptureClient))
1992 IUnknown_AddRef((IUnknown*)*ppv);
1996 WARN("Unknown interface %s\n", debugstr_guid(riid));
1997 return E_NOINTERFACE;
2000 static ULONG WINAPI AudioCaptureClient_AddRef(IAudioCaptureClient *iface)
2002 ACImpl *This = impl_from_IAudioCaptureClient(iface);
2003 return IAudioClient_AddRef(&This->IAudioClient_iface);
2006 static ULONG WINAPI AudioCaptureClient_Release(IAudioCaptureClient *iface)
2008 ACImpl *This = impl_from_IAudioCaptureClient(iface);
2009 return IAudioClient_Release(&This->IAudioClient_iface);
2012 static HRESULT WINAPI AudioCaptureClient_GetBuffer(IAudioCaptureClient *iface,
2013 BYTE **data, UINT32 *frames, DWORD *flags, UINT64 *devpos,
2016 ACImpl *This = impl_from_IAudioCaptureClient(iface);
2019 TRACE("(%p)->(%p, %p, %p, %p, %p)\n", This, data, frames, flags,
2022 if(!data || !frames || !flags)
2025 EnterCriticalSection(&This->lock);
2027 if(This->buf_state != NOT_LOCKED){
2028 LeaveCriticalSection(&This->lock);
2029 return AUDCLNT_E_OUT_OF_ORDER;
2032 hr = IAudioCaptureClient_GetNextPacketSize(iface, frames);
2034 LeaveCriticalSection(&This->lock);
2040 if(This->lcl_offs_frames + *frames > This->bufsize_frames){
2041 UINT32 chunk_bytes, offs_bytes, frames_bytes;
2042 if(This->tmp_buffer_frames < *frames){
2043 if(This->tmp_buffer)
2044 This->tmp_buffer = HeapReAlloc(GetProcessHeap(), 0,
2045 This->tmp_buffer, *frames * This->fmt->nBlockAlign);
2047 This->tmp_buffer = HeapAlloc(GetProcessHeap(), 0,
2048 *frames * This->fmt->nBlockAlign);
2049 if(!This->tmp_buffer){
2050 LeaveCriticalSection(&This->lock);
2051 return E_OUTOFMEMORY;
2053 This->tmp_buffer_frames = *frames;
2056 *data = This->tmp_buffer;
2057 chunk_bytes = (This->bufsize_frames - This->lcl_offs_frames) *
2058 This->fmt->nBlockAlign;
2059 offs_bytes = This->lcl_offs_frames * This->fmt->nBlockAlign;
2060 frames_bytes = *frames * This->fmt->nBlockAlign;
2061 memcpy(This->tmp_buffer, This->local_buffer + offs_bytes, chunk_bytes);
2062 memcpy(This->tmp_buffer + chunk_bytes, This->local_buffer,
2063 frames_bytes - chunk_bytes);
2065 *data = This->local_buffer +
2066 This->lcl_offs_frames * This->fmt->nBlockAlign;
2068 This->buf_state = LOCKED_NORMAL;
2070 if(devpos || qpcpos)
2071 IAudioClock_GetPosition(&This->IAudioClock_iface, devpos, qpcpos);
2073 LeaveCriticalSection(&This->lock);
2075 return *frames ? S_OK : AUDCLNT_S_BUFFER_EMPTY;
2078 static HRESULT WINAPI AudioCaptureClient_ReleaseBuffer(
2079 IAudioCaptureClient *iface, UINT32 done)
2081 ACImpl *This = impl_from_IAudioCaptureClient(iface);
2083 TRACE("(%p)->(%u)\n", This, done);
2085 EnterCriticalSection(&This->lock);
2087 if(This->buf_state == NOT_LOCKED){
2088 LeaveCriticalSection(&This->lock);
2089 return AUDCLNT_E_OUT_OF_ORDER;
2092 This->held_frames -= done;
2093 This->lcl_offs_frames += done;
2094 This->lcl_offs_frames %= This->bufsize_frames;
2096 This->buf_state = NOT_LOCKED;
2098 LeaveCriticalSection(&This->lock);
2103 static HRESULT WINAPI AudioCaptureClient_GetNextPacketSize(
2104 IAudioCaptureClient *iface, UINT32 *frames)
2106 ACImpl *This = impl_from_IAudioCaptureClient(iface);
2108 TRACE("(%p)->(%p)\n", This, frames);
2110 return AudioClient_GetCurrentPadding(&This->IAudioClient_iface, frames);
2113 static const IAudioCaptureClientVtbl AudioCaptureClient_Vtbl =
2115 AudioCaptureClient_QueryInterface,
2116 AudioCaptureClient_AddRef,
2117 AudioCaptureClient_Release,
2118 AudioCaptureClient_GetBuffer,
2119 AudioCaptureClient_ReleaseBuffer,
2120 AudioCaptureClient_GetNextPacketSize
2123 static HRESULT WINAPI AudioClock_QueryInterface(IAudioClock *iface,
2124 REFIID riid, void **ppv)
2126 ACImpl *This = impl_from_IAudioClock(iface);
2128 TRACE("(%p)->(%s, %p)\n", iface, debugstr_guid(riid), ppv);
2134 if(IsEqualIID(riid, &IID_IUnknown) || IsEqualIID(riid, &IID_IAudioClock))
2136 else if(IsEqualIID(riid, &IID_IAudioClock2))
2137 *ppv = &This->IAudioClock2_iface;
2139 IUnknown_AddRef((IUnknown*)*ppv);
2143 WARN("Unknown interface %s\n", debugstr_guid(riid));
2144 return E_NOINTERFACE;
2147 static ULONG WINAPI AudioClock_AddRef(IAudioClock *iface)
2149 ACImpl *This = impl_from_IAudioClock(iface);
2150 return IAudioClient_AddRef(&This->IAudioClient_iface);
2153 static ULONG WINAPI AudioClock_Release(IAudioClock *iface)
2155 ACImpl *This = impl_from_IAudioClock(iface);
2156 return IAudioClient_Release(&This->IAudioClient_iface);
2159 static HRESULT WINAPI AudioClock_GetFrequency(IAudioClock *iface, UINT64 *freq)
2161 ACImpl *This = impl_from_IAudioClock(iface);
2163 TRACE("(%p)->(%p)\n", This, freq);
2165 *freq = This->fmt->nSamplesPerSec;
2170 static HRESULT WINAPI AudioClock_GetPosition(IAudioClock *iface, UINT64 *pos,
2173 ACImpl *This = impl_from_IAudioClock(iface);
2177 TRACE("(%p)->(%p, %p)\n", This, pos, qpctime);
2182 EnterCriticalSection(&This->lock);
2184 hr = IAudioClient_GetCurrentPadding(&This->IAudioClient_iface, &pad);
2186 LeaveCriticalSection(&This->lock);
2190 if(This->dataflow == eRender)
2191 *pos = This->written_frames - pad;
2192 else if(This->dataflow == eCapture)
2193 *pos = This->written_frames + pad;
2195 LeaveCriticalSection(&This->lock);
2198 LARGE_INTEGER stamp, freq;
2199 QueryPerformanceCounter(&stamp);
2200 QueryPerformanceFrequency(&freq);
2201 *qpctime = (stamp.QuadPart * (INT64)10000000) / freq.QuadPart;
2207 static HRESULT WINAPI AudioClock_GetCharacteristics(IAudioClock *iface,
2210 ACImpl *This = impl_from_IAudioClock(iface);
2212 TRACE("(%p)->(%p)\n", This, chars);
2217 *chars = AUDIOCLOCK_CHARACTERISTIC_FIXED_FREQ;
2222 static const IAudioClockVtbl AudioClock_Vtbl =
2224 AudioClock_QueryInterface,
2227 AudioClock_GetFrequency,
2228 AudioClock_GetPosition,
2229 AudioClock_GetCharacteristics
2232 static HRESULT WINAPI AudioClock2_QueryInterface(IAudioClock2 *iface,
2233 REFIID riid, void **ppv)
2235 ACImpl *This = impl_from_IAudioClock2(iface);
2236 return IAudioClock_QueryInterface(&This->IAudioClock_iface, riid, ppv);
2239 static ULONG WINAPI AudioClock2_AddRef(IAudioClock2 *iface)
2241 ACImpl *This = impl_from_IAudioClock2(iface);
2242 return IAudioClient_AddRef(&This->IAudioClient_iface);
2245 static ULONG WINAPI AudioClock2_Release(IAudioClock2 *iface)
2247 ACImpl *This = impl_from_IAudioClock2(iface);
2248 return IAudioClient_Release(&This->IAudioClient_iface);
2251 static HRESULT WINAPI AudioClock2_GetDevicePosition(IAudioClock2 *iface,
2252 UINT64 *pos, UINT64 *qpctime)
2254 ACImpl *This = impl_from_IAudioClock2(iface);
2256 FIXME("(%p)->(%p, %p)\n", This, pos, qpctime);
2261 static const IAudioClock2Vtbl AudioClock2_Vtbl =
2263 AudioClock2_QueryInterface,
2265 AudioClock2_Release,
2266 AudioClock2_GetDevicePosition
2269 static AudioSessionWrapper *AudioSessionWrapper_Create(ACImpl *client)
2271 AudioSessionWrapper *ret;
2273 ret = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY,
2274 sizeof(AudioSessionWrapper));
2278 ret->IAudioSessionControl2_iface.lpVtbl = &AudioSessionControl2_Vtbl;
2279 ret->ISimpleAudioVolume_iface.lpVtbl = &SimpleAudioVolume_Vtbl;
2280 ret->IChannelAudioVolume_iface.lpVtbl = &ChannelAudioVolume_Vtbl;
2284 ret->client = client;
2286 ret->session = client->session;
2287 AudioClient_AddRef(&client->IAudioClient_iface);
2293 static HRESULT WINAPI AudioSessionControl_QueryInterface(
2294 IAudioSessionControl2 *iface, REFIID riid, void **ppv)
2296 TRACE("(%p)->(%s, %p)\n", iface, debugstr_guid(riid), ppv);
2302 if(IsEqualIID(riid, &IID_IUnknown) ||
2303 IsEqualIID(riid, &IID_IAudioSessionControl) ||
2304 IsEqualIID(riid, &IID_IAudioSessionControl2))
2307 IUnknown_AddRef((IUnknown*)*ppv);
2311 WARN("Unknown interface %s\n", debugstr_guid(riid));
2312 return E_NOINTERFACE;
2315 static ULONG WINAPI AudioSessionControl_AddRef(IAudioSessionControl2 *iface)
2317 AudioSessionWrapper *This = impl_from_IAudioSessionControl2(iface);
2319 ref = InterlockedIncrement(&This->ref);
2320 TRACE("(%p) Refcount now %u\n", This, ref);
2324 static ULONG WINAPI AudioSessionControl_Release(IAudioSessionControl2 *iface)
2326 AudioSessionWrapper *This = impl_from_IAudioSessionControl2(iface);
2328 ref = InterlockedDecrement(&This->ref);
2329 TRACE("(%p) Refcount now %u\n", This, ref);
2332 EnterCriticalSection(&This->client->lock);
2333 This->client->session_wrapper = NULL;
2334 LeaveCriticalSection(&This->client->lock);
2335 AudioClient_Release(&This->client->IAudioClient_iface);
2337 HeapFree(GetProcessHeap(), 0, This);
2342 static HRESULT WINAPI AudioSessionControl_GetState(IAudioSessionControl2 *iface,
2343 AudioSessionState *state)
2345 AudioSessionWrapper *This = impl_from_IAudioSessionControl2(iface);
2348 TRACE("(%p)->(%p)\n", This, state);
2351 return NULL_PTR_ERR;
2353 EnterCriticalSection(&g_sessions_lock);
2355 if(list_empty(&This->session->clients)){
2356 *state = AudioSessionStateExpired;
2357 LeaveCriticalSection(&g_sessions_lock);
2361 LIST_FOR_EACH_ENTRY(client, &This->session->clients, ACImpl, entry){
2362 EnterCriticalSection(&client->lock);
2363 if(client->started){
2364 *state = AudioSessionStateActive;
2365 LeaveCriticalSection(&client->lock);
2366 LeaveCriticalSection(&g_sessions_lock);
2369 LeaveCriticalSection(&client->lock);
2372 LeaveCriticalSection(&g_sessions_lock);
2374 *state = AudioSessionStateInactive;
2379 static HRESULT WINAPI AudioSessionControl_GetDisplayName(
2380 IAudioSessionControl2 *iface, WCHAR **name)
2382 AudioSessionWrapper *This = impl_from_IAudioSessionControl2(iface);
2384 FIXME("(%p)->(%p) - stub\n", This, name);
2389 static HRESULT WINAPI AudioSessionControl_SetDisplayName(
2390 IAudioSessionControl2 *iface, const WCHAR *name, const GUID *session)
2392 AudioSessionWrapper *This = impl_from_IAudioSessionControl2(iface);
2394 FIXME("(%p)->(%p, %s) - stub\n", This, name, debugstr_guid(session));
2399 static HRESULT WINAPI AudioSessionControl_GetIconPath(
2400 IAudioSessionControl2 *iface, WCHAR **path)
2402 AudioSessionWrapper *This = impl_from_IAudioSessionControl2(iface);
2404 FIXME("(%p)->(%p) - stub\n", This, path);
2409 static HRESULT WINAPI AudioSessionControl_SetIconPath(
2410 IAudioSessionControl2 *iface, const WCHAR *path, const GUID *session)
2412 AudioSessionWrapper *This = impl_from_IAudioSessionControl2(iface);
2414 FIXME("(%p)->(%p, %s) - stub\n", This, path, debugstr_guid(session));
2419 static HRESULT WINAPI AudioSessionControl_GetGroupingParam(
2420 IAudioSessionControl2 *iface, GUID *group)
2422 AudioSessionWrapper *This = impl_from_IAudioSessionControl2(iface);
2424 FIXME("(%p)->(%p) - stub\n", This, group);
2429 static HRESULT WINAPI AudioSessionControl_SetGroupingParam(
2430 IAudioSessionControl2 *iface, GUID *group, const GUID *session)
2432 AudioSessionWrapper *This = impl_from_IAudioSessionControl2(iface);
2434 FIXME("(%p)->(%s, %s) - stub\n", This, debugstr_guid(group),
2435 debugstr_guid(session));
2440 static HRESULT WINAPI AudioSessionControl_RegisterAudioSessionNotification(
2441 IAudioSessionControl2 *iface, IAudioSessionEvents *events)
2443 AudioSessionWrapper *This = impl_from_IAudioSessionControl2(iface);
2445 FIXME("(%p)->(%p) - stub\n", This, events);
2450 static HRESULT WINAPI AudioSessionControl_UnregisterAudioSessionNotification(
2451 IAudioSessionControl2 *iface, IAudioSessionEvents *events)
2453 AudioSessionWrapper *This = impl_from_IAudioSessionControl2(iface);
2455 FIXME("(%p)->(%p) - stub\n", This, events);
2460 static HRESULT WINAPI AudioSessionControl_GetSessionIdentifier(
2461 IAudioSessionControl2 *iface, WCHAR **id)
2463 AudioSessionWrapper *This = impl_from_IAudioSessionControl2(iface);
2465 FIXME("(%p)->(%p) - stub\n", This, id);
2470 static HRESULT WINAPI AudioSessionControl_GetSessionInstanceIdentifier(
2471 IAudioSessionControl2 *iface, WCHAR **id)
2473 AudioSessionWrapper *This = impl_from_IAudioSessionControl2(iface);
2475 FIXME("(%p)->(%p) - stub\n", This, id);
2480 static HRESULT WINAPI AudioSessionControl_GetProcessId(
2481 IAudioSessionControl2 *iface, DWORD *pid)
2483 AudioSessionWrapper *This = impl_from_IAudioSessionControl2(iface);
2485 TRACE("(%p)->(%p)\n", This, pid);
2490 *pid = GetCurrentProcessId();
2495 static HRESULT WINAPI AudioSessionControl_IsSystemSoundsSession(
2496 IAudioSessionControl2 *iface)
2498 AudioSessionWrapper *This = impl_from_IAudioSessionControl2(iface);
2500 TRACE("(%p)\n", This);
2505 static HRESULT WINAPI AudioSessionControl_SetDuckingPreference(
2506 IAudioSessionControl2 *iface, BOOL optout)
2508 AudioSessionWrapper *This = impl_from_IAudioSessionControl2(iface);
2510 TRACE("(%p)->(%d)\n", This, optout);
2515 static const IAudioSessionControl2Vtbl AudioSessionControl2_Vtbl =
2517 AudioSessionControl_QueryInterface,
2518 AudioSessionControl_AddRef,
2519 AudioSessionControl_Release,
2520 AudioSessionControl_GetState,
2521 AudioSessionControl_GetDisplayName,
2522 AudioSessionControl_SetDisplayName,
2523 AudioSessionControl_GetIconPath,
2524 AudioSessionControl_SetIconPath,
2525 AudioSessionControl_GetGroupingParam,
2526 AudioSessionControl_SetGroupingParam,
2527 AudioSessionControl_RegisterAudioSessionNotification,
2528 AudioSessionControl_UnregisterAudioSessionNotification,
2529 AudioSessionControl_GetSessionIdentifier,
2530 AudioSessionControl_GetSessionInstanceIdentifier,
2531 AudioSessionControl_GetProcessId,
2532 AudioSessionControl_IsSystemSoundsSession,
2533 AudioSessionControl_SetDuckingPreference
2536 static HRESULT WINAPI SimpleAudioVolume_QueryInterface(
2537 ISimpleAudioVolume *iface, REFIID riid, void **ppv)
2539 TRACE("(%p)->(%s, %p)\n", iface, debugstr_guid(riid), ppv);
2545 if(IsEqualIID(riid, &IID_IUnknown) ||
2546 IsEqualIID(riid, &IID_ISimpleAudioVolume))
2549 IUnknown_AddRef((IUnknown*)*ppv);
2553 WARN("Unknown interface %s\n", debugstr_guid(riid));
2554 return E_NOINTERFACE;
2557 static ULONG WINAPI SimpleAudioVolume_AddRef(ISimpleAudioVolume *iface)
2559 AudioSessionWrapper *This = impl_from_ISimpleAudioVolume(iface);
2560 return AudioSessionControl_AddRef(&This->IAudioSessionControl2_iface);
2563 static ULONG WINAPI SimpleAudioVolume_Release(ISimpleAudioVolume *iface)
2565 AudioSessionWrapper *This = impl_from_ISimpleAudioVolume(iface);
2566 return AudioSessionControl_Release(&This->IAudioSessionControl2_iface);
2569 static HRESULT WINAPI SimpleAudioVolume_SetMasterVolume(
2570 ISimpleAudioVolume *iface, float level, const GUID *context)
2572 AudioSessionWrapper *This = impl_from_ISimpleAudioVolume(iface);
2573 AudioSession *session = This->session;
2575 TRACE("(%p)->(%f, %s)\n", session, level, wine_dbgstr_guid(context));
2577 if(level < 0.f || level > 1.f)
2578 return E_INVALIDARG;
2581 FIXME("Notifications not supported yet\n");
2583 TRACE("ALSA does not support volume control\n");
2585 EnterCriticalSection(&session->lock);
2587 session->master_vol = level;
2589 LeaveCriticalSection(&session->lock);
2594 static HRESULT WINAPI SimpleAudioVolume_GetMasterVolume(
2595 ISimpleAudioVolume *iface, float *level)
2597 AudioSessionWrapper *This = impl_from_ISimpleAudioVolume(iface);
2598 AudioSession *session = This->session;
2600 TRACE("(%p)->(%p)\n", session, level);
2603 return NULL_PTR_ERR;
2605 *level = session->master_vol;
2610 static HRESULT WINAPI SimpleAudioVolume_SetMute(ISimpleAudioVolume *iface,
2611 BOOL mute, const GUID *context)
2613 AudioSessionWrapper *This = impl_from_ISimpleAudioVolume(iface);
2614 AudioSession *session = This->session;
2616 TRACE("(%p)->(%u, %p)\n", session, mute, context);
2619 FIXME("Notifications not supported yet\n");
2621 session->mute = mute;
2626 static HRESULT WINAPI SimpleAudioVolume_GetMute(ISimpleAudioVolume *iface,
2629 AudioSessionWrapper *This = impl_from_ISimpleAudioVolume(iface);
2630 AudioSession *session = This->session;
2632 TRACE("(%p)->(%p)\n", session, mute);
2635 return NULL_PTR_ERR;
2637 *mute = session->mute;
2642 static const ISimpleAudioVolumeVtbl SimpleAudioVolume_Vtbl =
2644 SimpleAudioVolume_QueryInterface,
2645 SimpleAudioVolume_AddRef,
2646 SimpleAudioVolume_Release,
2647 SimpleAudioVolume_SetMasterVolume,
2648 SimpleAudioVolume_GetMasterVolume,
2649 SimpleAudioVolume_SetMute,
2650 SimpleAudioVolume_GetMute
2653 static HRESULT WINAPI AudioStreamVolume_QueryInterface(
2654 IAudioStreamVolume *iface, REFIID riid, void **ppv)
2656 TRACE("(%p)->(%s, %p)\n", iface, debugstr_guid(riid), ppv);
2662 if(IsEqualIID(riid, &IID_IUnknown) ||
2663 IsEqualIID(riid, &IID_IAudioStreamVolume))
2666 IUnknown_AddRef((IUnknown*)*ppv);
2670 WARN("Unknown interface %s\n", debugstr_guid(riid));
2671 return E_NOINTERFACE;
2674 static ULONG WINAPI AudioStreamVolume_AddRef(IAudioStreamVolume *iface)
2676 ACImpl *This = impl_from_IAudioStreamVolume(iface);
2677 return IAudioClient_AddRef(&This->IAudioClient_iface);
2680 static ULONG WINAPI AudioStreamVolume_Release(IAudioStreamVolume *iface)
2682 ACImpl *This = impl_from_IAudioStreamVolume(iface);
2683 return IAudioClient_Release(&This->IAudioClient_iface);
2686 static HRESULT WINAPI AudioStreamVolume_GetChannelCount(
2687 IAudioStreamVolume *iface, UINT32 *out)
2689 ACImpl *This = impl_from_IAudioStreamVolume(iface);
2691 TRACE("(%p)->(%p)\n", This, out);
2696 *out = This->fmt->nChannels;
2701 static HRESULT WINAPI AudioStreamVolume_SetChannelVolume(
2702 IAudioStreamVolume *iface, UINT32 index, float level)
2704 ACImpl *This = impl_from_IAudioStreamVolume(iface);
2706 TRACE("(%p)->(%d, %f)\n", This, index, level);
2708 if(level < 0.f || level > 1.f)
2709 return E_INVALIDARG;
2711 if(index >= This->fmt->nChannels)
2712 return E_INVALIDARG;
2714 TRACE("ALSA does not support volume control\n");
2716 EnterCriticalSection(&This->lock);
2718 This->vols[index] = level;
2720 LeaveCriticalSection(&This->lock);
2725 static HRESULT WINAPI AudioStreamVolume_GetChannelVolume(
2726 IAudioStreamVolume *iface, UINT32 index, float *level)
2728 ACImpl *This = impl_from_IAudioStreamVolume(iface);
2730 TRACE("(%p)->(%d, %p)\n", This, index, level);
2735 if(index >= This->fmt->nChannels)
2736 return E_INVALIDARG;
2738 *level = This->vols[index];
2743 static HRESULT WINAPI AudioStreamVolume_SetAllVolumes(
2744 IAudioStreamVolume *iface, UINT32 count, const float *levels)
2746 ACImpl *This = impl_from_IAudioStreamVolume(iface);
2749 TRACE("(%p)->(%d, %p)\n", This, count, levels);
2754 if(count != This->fmt->nChannels)
2755 return E_INVALIDARG;
2757 TRACE("ALSA does not support volume control\n");
2759 EnterCriticalSection(&This->lock);
2761 for(i = 0; i < count; ++i)
2762 This->vols[i] = levels[i];
2764 LeaveCriticalSection(&This->lock);
2769 static HRESULT WINAPI AudioStreamVolume_GetAllVolumes(
2770 IAudioStreamVolume *iface, UINT32 count, float *levels)
2772 ACImpl *This = impl_from_IAudioStreamVolume(iface);
2775 TRACE("(%p)->(%d, %p)\n", This, count, levels);
2780 if(count != This->fmt->nChannels)
2781 return E_INVALIDARG;
2783 EnterCriticalSection(&This->lock);
2785 for(i = 0; i < count; ++i)
2786 levels[i] = This->vols[i];
2788 LeaveCriticalSection(&This->lock);
2793 static const IAudioStreamVolumeVtbl AudioStreamVolume_Vtbl =
2795 AudioStreamVolume_QueryInterface,
2796 AudioStreamVolume_AddRef,
2797 AudioStreamVolume_Release,
2798 AudioStreamVolume_GetChannelCount,
2799 AudioStreamVolume_SetChannelVolume,
2800 AudioStreamVolume_GetChannelVolume,
2801 AudioStreamVolume_SetAllVolumes,
2802 AudioStreamVolume_GetAllVolumes
2805 static HRESULT WINAPI ChannelAudioVolume_QueryInterface(
2806 IChannelAudioVolume *iface, REFIID riid, void **ppv)
2808 TRACE("(%p)->(%s, %p)\n", iface, debugstr_guid(riid), ppv);
2814 if(IsEqualIID(riid, &IID_IUnknown) ||
2815 IsEqualIID(riid, &IID_IChannelAudioVolume))
2818 IUnknown_AddRef((IUnknown*)*ppv);
2822 WARN("Unknown interface %s\n", debugstr_guid(riid));
2823 return E_NOINTERFACE;
2826 static ULONG WINAPI ChannelAudioVolume_AddRef(IChannelAudioVolume *iface)
2828 AudioSessionWrapper *This = impl_from_IChannelAudioVolume(iface);
2829 return AudioSessionControl_AddRef(&This->IAudioSessionControl2_iface);
2832 static ULONG WINAPI ChannelAudioVolume_Release(IChannelAudioVolume *iface)
2834 AudioSessionWrapper *This = impl_from_IChannelAudioVolume(iface);
2835 return AudioSessionControl_Release(&This->IAudioSessionControl2_iface);
2838 static HRESULT WINAPI ChannelAudioVolume_GetChannelCount(
2839 IChannelAudioVolume *iface, UINT32 *out)
2841 AudioSessionWrapper *This = impl_from_IChannelAudioVolume(iface);
2842 AudioSession *session = This->session;
2844 TRACE("(%p)->(%p)\n", session, out);
2847 return NULL_PTR_ERR;
2849 *out = session->channel_count;
2854 static HRESULT WINAPI ChannelAudioVolume_SetChannelVolume(
2855 IChannelAudioVolume *iface, UINT32 index, float level,
2856 const GUID *context)
2858 AudioSessionWrapper *This = impl_from_IChannelAudioVolume(iface);
2859 AudioSession *session = This->session;
2861 TRACE("(%p)->(%d, %f, %s)\n", session, index, level,
2862 wine_dbgstr_guid(context));
2864 if(level < 0.f || level > 1.f)
2865 return E_INVALIDARG;
2867 if(index >= session->channel_count)
2868 return E_INVALIDARG;
2871 FIXME("Notifications not supported yet\n");
2873 TRACE("ALSA does not support volume control\n");
2875 EnterCriticalSection(&session->lock);
2877 session->channel_vols[index] = level;
2879 LeaveCriticalSection(&session->lock);
2884 static HRESULT WINAPI ChannelAudioVolume_GetChannelVolume(
2885 IChannelAudioVolume *iface, UINT32 index, float *level)
2887 AudioSessionWrapper *This = impl_from_IChannelAudioVolume(iface);
2888 AudioSession *session = This->session;
2890 TRACE("(%p)->(%d, %p)\n", session, index, level);
2893 return NULL_PTR_ERR;
2895 if(index >= session->channel_count)
2896 return E_INVALIDARG;
2898 *level = session->channel_vols[index];
2903 static HRESULT WINAPI ChannelAudioVolume_SetAllVolumes(
2904 IChannelAudioVolume *iface, UINT32 count, const float *levels,
2905 const GUID *context)
2907 AudioSessionWrapper *This = impl_from_IChannelAudioVolume(iface);
2908 AudioSession *session = This->session;
2911 TRACE("(%p)->(%d, %p, %s)\n", session, count, levels,
2912 wine_dbgstr_guid(context));
2915 return NULL_PTR_ERR;
2917 if(count != session->channel_count)
2918 return E_INVALIDARG;
2921 FIXME("Notifications not supported yet\n");
2923 TRACE("ALSA does not support volume control\n");
2925 EnterCriticalSection(&session->lock);
2927 for(i = 0; i < count; ++i)
2928 session->channel_vols[i] = levels[i];
2930 LeaveCriticalSection(&session->lock);
2935 static HRESULT WINAPI ChannelAudioVolume_GetAllVolumes(
2936 IChannelAudioVolume *iface, UINT32 count, float *levels)
2938 AudioSessionWrapper *This = impl_from_IChannelAudioVolume(iface);
2939 AudioSession *session = This->session;
2942 TRACE("(%p)->(%d, %p)\n", session, count, levels);
2945 return NULL_PTR_ERR;
2947 if(count != session->channel_count)
2948 return E_INVALIDARG;
2950 for(i = 0; i < count; ++i)
2951 levels[i] = session->channel_vols[i];
2956 static const IChannelAudioVolumeVtbl ChannelAudioVolume_Vtbl =
2958 ChannelAudioVolume_QueryInterface,
2959 ChannelAudioVolume_AddRef,
2960 ChannelAudioVolume_Release,
2961 ChannelAudioVolume_GetChannelCount,
2962 ChannelAudioVolume_SetChannelVolume,
2963 ChannelAudioVolume_GetChannelVolume,
2964 ChannelAudioVolume_SetAllVolumes,
2965 ChannelAudioVolume_GetAllVolumes
2968 HRESULT WINAPI AudioSessionManager_QueryInterface(IAudioSessionManager2 *iface,
2969 REFIID riid, void **ppv)
2971 TRACE("(%p)->(%s, %p)\n", iface, debugstr_guid(riid), ppv);
2977 if(IsEqualIID(riid, &IID_IUnknown) ||
2978 IsEqualIID(riid, &IID_IAudioSessionManager) ||
2979 IsEqualIID(riid, &IID_IAudioSessionManager2))
2982 IUnknown_AddRef((IUnknown*)*ppv);
2986 WARN("Unknown interface %s\n", debugstr_guid(riid));
2987 return E_NOINTERFACE;
2990 ULONG WINAPI AudioSessionManager_AddRef(IAudioSessionManager2 *iface)
2992 SessionMgr *This = impl_from_IAudioSessionManager2(iface);
2994 ref = InterlockedIncrement(&This->ref);
2995 TRACE("(%p) Refcount now %u\n", This, ref);
2999 ULONG WINAPI AudioSessionManager_Release(IAudioSessionManager2 *iface)
3001 SessionMgr *This = impl_from_IAudioSessionManager2(iface);
3003 ref = InterlockedDecrement(&This->ref);
3004 TRACE("(%p) Refcount now %u\n", This, ref);
3006 HeapFree(GetProcessHeap(), 0, This);
3010 HRESULT WINAPI AudioSessionManager_GetAudioSessionControl(
3011 IAudioSessionManager2 *iface, const GUID *session_guid, DWORD flags,
3012 IAudioSessionControl **out)
3014 SessionMgr *This = impl_from_IAudioSessionManager2(iface);
3015 AudioSession *session;
3016 AudioSessionWrapper *wrapper;
3019 TRACE("(%p)->(%s, %x, %p)\n", This, debugstr_guid(session_guid),
3022 hr = get_audio_session(session_guid, This->device, 0, &session);
3026 wrapper = AudioSessionWrapper_Create(NULL);
3028 return E_OUTOFMEMORY;
3030 wrapper->session = session;
3032 *out = (IAudioSessionControl*)&wrapper->IAudioSessionControl2_iface;
3037 HRESULT WINAPI AudioSessionManager_GetSimpleAudioVolume(
3038 IAudioSessionManager2 *iface, const GUID *session_guid, DWORD flags,
3039 ISimpleAudioVolume **out)
3041 SessionMgr *This = impl_from_IAudioSessionManager2(iface);
3042 AudioSession *session;
3043 AudioSessionWrapper *wrapper;
3046 TRACE("(%p)->(%s, %x, %p)\n", This, debugstr_guid(session_guid),
3049 hr = get_audio_session(session_guid, This->device, 0, &session);
3053 wrapper = AudioSessionWrapper_Create(NULL);
3055 return E_OUTOFMEMORY;
3057 wrapper->session = session;
3059 *out = &wrapper->ISimpleAudioVolume_iface;
3064 HRESULT WINAPI AudioSessionManager_GetSessionEnumerator(
3065 IAudioSessionManager2 *iface, IAudioSessionEnumerator **out)
3067 SessionMgr *This = impl_from_IAudioSessionManager2(iface);
3068 FIXME("(%p)->(%p) - stub\n", This, out);
3072 HRESULT WINAPI AudioSessionManager_RegisterSessionNotification(
3073 IAudioSessionManager2 *iface, IAudioSessionNotification *notification)
3075 SessionMgr *This = impl_from_IAudioSessionManager2(iface);
3076 FIXME("(%p)->(%p) - stub\n", This, notification);
3080 HRESULT WINAPI AudioSessionManager_UnregisterSessionNotification(
3081 IAudioSessionManager2 *iface, IAudioSessionNotification *notification)
3083 SessionMgr *This = impl_from_IAudioSessionManager2(iface);
3084 FIXME("(%p)->(%p) - stub\n", This, notification);
3088 HRESULT WINAPI AudioSessionManager_RegisterDuckNotification(
3089 IAudioSessionManager2 *iface, const WCHAR *session_id,
3090 IAudioVolumeDuckNotification *notification)
3092 SessionMgr *This = impl_from_IAudioSessionManager2(iface);
3093 FIXME("(%p)->(%p) - stub\n", This, notification);
3097 HRESULT WINAPI AudioSessionManager_UnregisterDuckNotification(
3098 IAudioSessionManager2 *iface,
3099 IAudioVolumeDuckNotification *notification)
3101 SessionMgr *This = impl_from_IAudioSessionManager2(iface);
3102 FIXME("(%p)->(%p) - stub\n", This, notification);
3106 static const IAudioSessionManager2Vtbl AudioSessionManager2_Vtbl =
3108 AudioSessionManager_QueryInterface,
3109 AudioSessionManager_AddRef,
3110 AudioSessionManager_Release,
3111 AudioSessionManager_GetAudioSessionControl,
3112 AudioSessionManager_GetSimpleAudioVolume,
3113 AudioSessionManager_GetSessionEnumerator,
3114 AudioSessionManager_RegisterSessionNotification,
3115 AudioSessionManager_UnregisterSessionNotification,
3116 AudioSessionManager_RegisterDuckNotification,
3117 AudioSessionManager_UnregisterDuckNotification
3120 HRESULT WINAPI AUDDRV_GetAudioSessionManager(IMMDevice *device,
3121 IAudioSessionManager2 **out)
3125 This = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, sizeof(SessionMgr));
3127 return E_OUTOFMEMORY;
3129 This->IAudioSessionManager2_iface.lpVtbl = &AudioSessionManager2_Vtbl;
3130 This->device = device;
3133 *out = &This->IAudioSessionManager2_iface;