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;
232 snd_pcm_stream_t stream = (flow == eRender ? SND_PCM_STREAM_PLAYBACK :
233 SND_PCM_STREAM_CAPTURE);
235 info = HeapAlloc(GetProcessHeap(), 0, snd_pcm_info_sizeof());
237 return E_OUTOFMEMORY;
239 snd_pcm_info_set_subdevice(info, 0);
240 snd_pcm_info_set_stream(info, stream);
243 for(err = snd_ctl_pcm_next_device(ctl, &device); device != -1 && err >= 0;
244 err = snd_ctl_pcm_next_device(ctl, &device)){
249 snd_pcm_info_set_device(info, device);
251 if((err = snd_ctl_pcm_info(ctl, info)) < 0){
253 /* This device doesn't have the right stream direction */
256 WARN("Failed to get info for card %d, device %d: %d (%s)\n",
257 card, device, err, snd_strerror(err));
261 sprintf(devnode, "hw:%d,%d", card, device);
262 if((err = snd_pcm_open(&handle, devnode, stream, SND_PCM_NONBLOCK)) < 0){
263 WARN("The device \"%s\" failed to open, pretending it doesn't exist: %d (%s)\n",
264 devnode, err, snd_strerror(err));
268 snd_pcm_close(handle);
273 devname = snd_pcm_info_get_name(info);
275 WARN("Unable to get device name for card %d, device %d\n", card,
280 cardlen = lstrlenW(cardnameW);
281 len = MultiByteToWideChar(CP_UNIXCP, 0, devname, -1, NULL, 0);
282 len += lstrlenW(dashW);
284 ids[*num] = HeapAlloc(GetProcessHeap(), 0, len * sizeof(WCHAR));
286 HeapFree(GetProcessHeap(), 0, info);
287 return E_OUTOFMEMORY;
289 memcpy(ids[*num], cardnameW, cardlen * sizeof(WCHAR));
290 memcpy(ids[*num] + cardlen, dashW, lstrlenW(dashW) * sizeof(WCHAR));
291 cardlen += lstrlenW(dashW);
292 MultiByteToWideChar(CP_UNIXCP, 0, devname, -1, ids[*num] + cardlen,
295 keys[*num] = HeapAlloc(GetProcessHeap(), 0, 32);
297 HeapFree(GetProcessHeap(), 0, info);
298 HeapFree(GetProcessHeap(), 0, ids[*num]);
299 return E_OUTOFMEMORY;
301 memcpy(keys[*num], devnode, sizeof(devnode));
307 HeapFree(GetProcessHeap(), 0, info);
310 WARN("Got a failure during device enumeration on card %d: %d (%s)\n",
311 card, err, snd_strerror(err));
316 static HRESULT alsa_enum_devices(EDataFlow flow, WCHAR **ids, char **keys,
323 for(err = snd_card_next(&card); card != -1 && err >= 0;
324 err = snd_card_next(&card)){
326 const char *cardname;
331 sprintf(cardpath, "hw:%u", card);
333 if((err = snd_ctl_open(&ctl, cardpath, 0)) < 0){
334 WARN("Unable to open ctl for ALSA device %s: %d (%s)\n", cardpath,
335 err, snd_strerror(err));
339 if((err = snd_card_get_name(card, (char **)&cardname)) < 0){
340 WARN("Unable to get card name for ALSA device %s: %d (%s)\n",
341 cardpath, err, snd_strerror(err));
342 /* FIXME: Should be localized */
343 cardname = "Unknown soundcard";
346 len = MultiByteToWideChar(CP_UNIXCP, 0, cardname, -1, NULL, 0);
347 cardnameW = HeapAlloc(GetProcessHeap(), 0, len * sizeof(WCHAR));
350 return E_OUTOFMEMORY;
352 MultiByteToWideChar(CP_UNIXCP, 0, cardname, -1, cardnameW, len);
354 alsa_get_card_devices(flow, ids, keys, num, ctl, card, cardnameW);
356 HeapFree(GetProcessHeap(), 0, cardnameW);
362 WARN("Got a failure during card enumeration: %d (%s)\n",
363 err, snd_strerror(err));
368 HRESULT WINAPI AUDDRV_GetEndpointIDs(EDataFlow flow, WCHAR ***ids, char ***keys,
369 UINT *num, UINT *def_index)
373 TRACE("%d %p %p %p %p\n", flow, ids, keys, num, def_index);
375 hr = alsa_enum_devices(flow, NULL, NULL, num);
379 *ids = HeapAlloc(GetProcessHeap(), 0, (*num + 1) * sizeof(WCHAR *));
380 *keys = HeapAlloc(GetProcessHeap(), 0, (*num + 1) * sizeof(char *));
382 HeapFree(GetProcessHeap(), 0, *ids);
383 HeapFree(GetProcessHeap(), 0, *keys);
384 return E_OUTOFMEMORY;
387 (*ids)[0] = HeapAlloc(GetProcessHeap(), 0, sizeof(defaultW));
388 memcpy((*ids)[0], defaultW, sizeof(defaultW));
389 (*keys)[0] = HeapAlloc(GetProcessHeap(), 0, sizeof(defname));
390 memcpy((*keys)[0], defname, sizeof(defname));
393 hr = alsa_enum_devices(flow, (*ids) + 1, (*keys) + 1, num);
396 for(i = 0; i < *num; ++i){
397 HeapFree(GetProcessHeap(), 0, (*ids)[i]);
398 HeapFree(GetProcessHeap(), 0, (*keys)[i]);
400 HeapFree(GetProcessHeap(), 0, *ids);
401 HeapFree(GetProcessHeap(), 0, *keys);
402 return E_OUTOFMEMORY;
405 ++(*num); /* for default device */
410 HRESULT WINAPI AUDDRV_GetAudioEndpoint(const char *key, IMMDevice *dev,
411 EDataFlow dataflow, IAudioClient **out)
415 snd_pcm_stream_t stream;
417 TRACE("\"%s\" %p %d %p\n", key, dev, dataflow, out);
419 This = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, sizeof(ACImpl));
421 return E_OUTOFMEMORY;
423 This->IAudioClient_iface.lpVtbl = &AudioClient_Vtbl;
424 This->IAudioRenderClient_iface.lpVtbl = &AudioRenderClient_Vtbl;
425 This->IAudioCaptureClient_iface.lpVtbl = &AudioCaptureClient_Vtbl;
426 This->IAudioClock_iface.lpVtbl = &AudioClock_Vtbl;
427 This->IAudioClock2_iface.lpVtbl = &AudioClock2_Vtbl;
428 This->IAudioStreamVolume_iface.lpVtbl = &AudioStreamVolume_Vtbl;
430 if(dataflow == eRender)
431 stream = SND_PCM_STREAM_PLAYBACK;
432 else if(dataflow == eCapture)
433 stream = SND_PCM_STREAM_CAPTURE;
435 HeapFree(GetProcessHeap(), 0, This);
439 This->dataflow = dataflow;
440 if((err = snd_pcm_open(&This->pcm_handle, key, stream,
441 SND_PCM_NONBLOCK)) < 0){
442 HeapFree(GetProcessHeap(), 0, This);
443 WARN("Unable to open PCM \"%s\": %d (%s)\n", key, err,
448 This->hw_params = HeapAlloc(GetProcessHeap(), 0,
449 snd_pcm_hw_params_sizeof());
450 if(!This->hw_params){
451 HeapFree(GetProcessHeap(), 0, This);
452 snd_pcm_close(This->pcm_handle);
453 return E_OUTOFMEMORY;
456 InitializeCriticalSection(&This->lock);
459 IMMDevice_AddRef(This->parent);
461 *out = &This->IAudioClient_iface;
462 IAudioClient_AddRef(&This->IAudioClient_iface);
467 static HRESULT WINAPI AudioClient_QueryInterface(IAudioClient *iface,
468 REFIID riid, void **ppv)
470 TRACE("(%p)->(%s, %p)\n", iface, debugstr_guid(riid), ppv);
475 if(IsEqualIID(riid, &IID_IUnknown) || IsEqualIID(riid, &IID_IAudioClient))
478 IUnknown_AddRef((IUnknown*)*ppv);
481 WARN("Unknown interface %s\n", debugstr_guid(riid));
482 return E_NOINTERFACE;
485 static ULONG WINAPI AudioClient_AddRef(IAudioClient *iface)
487 ACImpl *This = impl_from_IAudioClient(iface);
489 ref = InterlockedIncrement(&This->ref);
490 TRACE("(%p) Refcount now %u\n", This, ref);
494 static ULONG WINAPI AudioClient_Release(IAudioClient *iface)
496 ACImpl *This = impl_from_IAudioClient(iface);
498 ref = InterlockedDecrement(&This->ref);
499 TRACE("(%p) Refcount now %u\n", This, ref);
501 IAudioClient_Stop(iface);
502 IMMDevice_Release(This->parent);
503 DeleteCriticalSection(&This->lock);
504 snd_pcm_drop(This->pcm_handle);
505 snd_pcm_close(This->pcm_handle);
507 EnterCriticalSection(&g_sessions_lock);
508 list_remove(&This->entry);
509 LeaveCriticalSection(&g_sessions_lock);
511 HeapFree(GetProcessHeap(), 0, This->vols);
512 HeapFree(GetProcessHeap(), 0, This->local_buffer);
513 HeapFree(GetProcessHeap(), 0, This->tmp_buffer);
514 HeapFree(GetProcessHeap(), 0, This->hw_params);
515 CoTaskMemFree(This->fmt);
516 HeapFree(GetProcessHeap(), 0, This);
521 static void dump_fmt(const WAVEFORMATEX *fmt)
523 TRACE("wFormatTag: 0x%x (", fmt->wFormatTag);
524 switch(fmt->wFormatTag){
525 case WAVE_FORMAT_PCM:
526 TRACE("WAVE_FORMAT_PCM");
528 case WAVE_FORMAT_IEEE_FLOAT:
529 TRACE("WAVE_FORMAT_IEEE_FLOAT");
531 case WAVE_FORMAT_EXTENSIBLE:
532 TRACE("WAVE_FORMAT_EXTENSIBLE");
540 TRACE("nChannels: %u\n", fmt->nChannels);
541 TRACE("nSamplesPerSec: %u\n", fmt->nSamplesPerSec);
542 TRACE("nAvgBytesPerSec: %u\n", fmt->nAvgBytesPerSec);
543 TRACE("nBlockAlign: %u\n", fmt->nBlockAlign);
544 TRACE("wBitsPerSample: %u\n", fmt->wBitsPerSample);
545 TRACE("cbSize: %u\n", fmt->cbSize);
547 if(fmt->wFormatTag == WAVE_FORMAT_EXTENSIBLE){
548 WAVEFORMATEXTENSIBLE *fmtex = (void*)fmt;
549 TRACE("dwChannelMask: %08x\n", fmtex->dwChannelMask);
550 TRACE("Samples: %04x\n", fmtex->Samples.wReserved);
551 TRACE("SubFormat: %s\n", wine_dbgstr_guid(&fmtex->SubFormat));
555 static WAVEFORMATEX *clone_format(const WAVEFORMATEX *fmt)
560 if(fmt->wFormatTag == WAVE_FORMAT_EXTENSIBLE)
561 size = sizeof(WAVEFORMATEXTENSIBLE);
563 size = sizeof(WAVEFORMATEX);
565 ret = CoTaskMemAlloc(size);
569 memcpy(ret, fmt, size);
571 ret->cbSize = size - sizeof(WAVEFORMATEX);
576 static void session_init_vols(AudioSession *session, UINT channels)
578 if(session->channel_count < channels){
581 if(session->channel_vols)
582 session->channel_vols = HeapReAlloc(GetProcessHeap(), 0,
583 session->channel_vols, sizeof(float) * channels);
585 session->channel_vols = HeapAlloc(GetProcessHeap(), 0,
586 sizeof(float) * channels);
587 if(!session->channel_vols)
590 for(i = session->channel_count; i < channels; ++i)
591 session->channel_vols[i] = 1.f;
593 session->channel_count = channels;
597 static AudioSession *create_session(const GUID *guid, IMMDevice *device,
602 ret = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, sizeof(AudioSession));
606 memcpy(&ret->guid, guid, sizeof(GUID));
608 ret->device = device;
610 list_init(&ret->clients);
612 list_add_head(&g_sessions, &ret->entry);
614 InitializeCriticalSection(&ret->lock);
616 session_init_vols(ret, num_channels);
618 ret->master_vol = 1.f;
623 /* if channels == 0, then this will return or create a session with
624 * matching dataflow and GUID. otherwise, channels must also match */
625 static HRESULT get_audio_session(const GUID *sessionguid,
626 IMMDevice *device, UINT channels, AudioSession **out)
628 AudioSession *session;
630 if(!sessionguid || IsEqualGUID(sessionguid, &GUID_NULL)){
631 *out = create_session(&GUID_NULL, device, channels);
633 return E_OUTOFMEMORY;
639 LIST_FOR_EACH_ENTRY(session, &g_sessions, AudioSession, entry){
640 if(session->device == device &&
641 IsEqualGUID(sessionguid, &session->guid)){
642 session_init_vols(session, channels);
649 *out = create_session(sessionguid, device, channels);
651 return E_OUTOFMEMORY;
657 static HRESULT WINAPI AudioClient_Initialize(IAudioClient *iface,
658 AUDCLNT_SHAREMODE mode, DWORD flags, REFERENCE_TIME duration,
659 REFERENCE_TIME period, const WAVEFORMATEX *fmt,
660 const GUID *sessionguid)
662 ACImpl *This = impl_from_IAudioClient(iface);
663 snd_pcm_sw_params_t *sw_params = NULL;
664 snd_pcm_format_t format;
665 snd_pcm_uframes_t boundary;
666 const WAVEFORMATEXTENSIBLE *fmtex = (const WAVEFORMATEXTENSIBLE *)fmt;
667 unsigned int time_us, rate;
671 TRACE("(%p)->(%x, %x, %s, %s, %p, %s)\n", This, mode, flags,
672 wine_dbgstr_longlong(duration), wine_dbgstr_longlong(period), fmt, debugstr_guid(sessionguid));
677 if(mode != AUDCLNT_SHAREMODE_SHARED && mode != AUDCLNT_SHAREMODE_EXCLUSIVE)
678 return AUDCLNT_E_NOT_INITIALIZED;
680 if(flags & ~(AUDCLNT_STREAMFLAGS_CROSSPROCESS |
681 AUDCLNT_STREAMFLAGS_LOOPBACK |
682 AUDCLNT_STREAMFLAGS_EVENTCALLBACK |
683 AUDCLNT_STREAMFLAGS_NOPERSIST |
684 AUDCLNT_STREAMFLAGS_RATEADJUST |
685 AUDCLNT_SESSIONFLAGS_EXPIREWHENUNOWNED |
686 AUDCLNT_SESSIONFLAGS_DISPLAY_HIDE |
687 AUDCLNT_SESSIONFLAGS_DISPLAY_HIDEWHENEXPIRED)){
688 TRACE("Unknown flags: %08x\n", flags);
692 EnterCriticalSection(&This->lock);
695 LeaveCriticalSection(&This->lock);
696 return AUDCLNT_E_ALREADY_INITIALIZED;
701 if((err = snd_pcm_hw_params_any(This->pcm_handle, This->hw_params)) < 0){
702 WARN("Unable to get hw_params: %d (%s)\n", err, snd_strerror(err));
707 if((err = snd_pcm_hw_params_set_access(This->pcm_handle, This->hw_params,
708 SND_PCM_ACCESS_RW_INTERLEAVED)) < 0){
709 WARN("Unable to set access: %d (%s)\n", err, snd_strerror(err));
714 if(fmt->wFormatTag == WAVE_FORMAT_PCM ||
715 (fmt->wFormatTag == WAVE_FORMAT_EXTENSIBLE &&
716 IsEqualGUID(&fmtex->SubFormat, &KSDATAFORMAT_SUBTYPE_PCM))){
717 if(fmt->wBitsPerSample == 8)
718 format = SND_PCM_FORMAT_U8;
719 else if(fmt->wBitsPerSample == 16)
720 format = SND_PCM_FORMAT_S16_LE;
721 else if(fmt->wBitsPerSample == 24)
722 format = SND_PCM_FORMAT_S24_3LE;
723 else if(fmt->wBitsPerSample == 32)
724 format = SND_PCM_FORMAT_S32_LE;
726 WARN("Unsupported bit depth: %u\n", fmt->wBitsPerSample);
727 hr = AUDCLNT_E_UNSUPPORTED_FORMAT;
730 }else if(fmt->wFormatTag == WAVE_FORMAT_IEEE_FLOAT ||
731 (fmt->wFormatTag == WAVE_FORMAT_EXTENSIBLE &&
732 IsEqualGUID(&fmtex->SubFormat, &KSDATAFORMAT_SUBTYPE_IEEE_FLOAT))){
733 if(fmt->wBitsPerSample == 32)
734 format = SND_PCM_FORMAT_FLOAT_LE;
735 else if(fmt->wBitsPerSample == 64)
736 format = SND_PCM_FORMAT_FLOAT64_LE;
738 WARN("Unsupported float size: %u\n", fmt->wBitsPerSample);
739 hr = AUDCLNT_E_UNSUPPORTED_FORMAT;
743 WARN("Unknown wave format: %04x\n", fmt->wFormatTag);
744 hr = AUDCLNT_E_UNSUPPORTED_FORMAT;
748 if((err = snd_pcm_hw_params_set_format(This->pcm_handle, This->hw_params,
750 WARN("Unable to set ALSA format to %u: %d (%s)\n", format, err,
756 This->alsa_format = format;
758 rate = fmt->nSamplesPerSec;
759 if((err = snd_pcm_hw_params_set_rate_near(This->pcm_handle, This->hw_params,
761 WARN("Unable to set rate to %u: %d (%s)\n", rate, err,
767 if((err = snd_pcm_hw_params_set_channels(This->pcm_handle, This->hw_params,
768 fmt->nChannels)) < 0){
769 WARN("Unable to set channels to %u: %d (%s)\n", fmt->nChannels, err,
775 time_us = MinimumPeriod / 10;
776 if((err = snd_pcm_hw_params_set_period_time_near(This->pcm_handle,
777 This->hw_params, &time_us, NULL)) < 0){
778 WARN("Unable to set max period time to %u: %d (%s)\n", time_us,
779 err, snd_strerror(err));
784 if((err = snd_pcm_hw_params(This->pcm_handle, This->hw_params)) < 0){
785 WARN("Unable to set hw params: %d (%s)\n", err, snd_strerror(err));
790 sw_params = HeapAlloc(GetProcessHeap(), 0, snd_pcm_sw_params_sizeof());
796 if((err = snd_pcm_sw_params_current(This->pcm_handle, sw_params)) < 0){
797 WARN("Unable to get sw_params: %d (%s)\n", err, snd_strerror(err));
803 duration = 300000; /* 0.03s */
804 This->bufsize_frames = ceil((duration / 10000000.) * fmt->nSamplesPerSec);
805 This->local_buffer = HeapAlloc(GetProcessHeap(), 0,
806 This->bufsize_frames * fmt->nBlockAlign);
807 if(!This->local_buffer){
811 if (fmt->wBitsPerSample == 8)
812 memset(This->local_buffer, 128, This->bufsize_frames * fmt->nBlockAlign);
814 memset(This->local_buffer, 0, This->bufsize_frames * fmt->nBlockAlign);
816 if((err = snd_pcm_sw_params_get_boundary(sw_params, &boundary)) < 0){
817 WARN("Unable to get boundary: %d (%s)\n", err, snd_strerror(err));
822 if((err = snd_pcm_sw_params_set_start_threshold(This->pcm_handle,
823 sw_params, boundary)) < 0){
824 WARN("Unable to set start threshold to %lx: %d (%s)\n", boundary, err,
830 if((err = snd_pcm_sw_params_set_stop_threshold(This->pcm_handle,
831 sw_params, boundary)) < 0){
832 WARN("Unable to set stop threshold to %lx: %d (%s)\n", boundary, err,
838 if((err = snd_pcm_sw_params_set_avail_min(This->pcm_handle,
840 WARN("Unable to set avail min to 0: %d (%s)\n", err, snd_strerror(err));
845 if((err = snd_pcm_sw_params(This->pcm_handle, sw_params)) < 0){
846 WARN("Unable to set sw params: %d (%s)\n", err, snd_strerror(err));
851 if((err = snd_pcm_prepare(This->pcm_handle)) < 0){
852 WARN("Unable to prepare device: %d (%s)\n", err, snd_strerror(err));
857 if((err = snd_pcm_hw_params_get_buffer_size(This->hw_params,
858 &This->bufsize_alsa)) < 0){
859 WARN("Unable to get buffer size: %d (%s)\n", err, snd_strerror(err));
864 if((err = snd_pcm_hw_params_get_period_size(This->hw_params,
865 &This->period_alsa, NULL)) < 0){
866 WARN("Unable to get period size: %d (%s)\n", err, snd_strerror(err));
871 if((err = snd_pcm_hw_params_get_period_time(This->hw_params,
872 &This->period_us, NULL)) < 0){
873 WARN("Unable to get period time: %d (%s)\n", err, snd_strerror(err));
878 This->fmt = clone_format(fmt);
884 This->vols = HeapAlloc(GetProcessHeap(), 0, fmt->nChannels * sizeof(float));
890 for(i = 0; i < fmt->nChannels; ++i)
896 EnterCriticalSection(&g_sessions_lock);
898 hr = get_audio_session(sessionguid, This->parent, fmt->nChannels,
901 LeaveCriticalSection(&g_sessions_lock);
905 list_add_tail(&This->session->clients, &This->entry);
907 LeaveCriticalSection(&g_sessions_lock);
909 This->initted = TRUE;
912 HeapFree(GetProcessHeap(), 0, sw_params);
914 if(This->local_buffer){
915 HeapFree(GetProcessHeap(), 0, This->local_buffer);
916 This->local_buffer = NULL;
919 CoTaskMemFree(This->fmt);
923 HeapFree(GetProcessHeap(), 0, This->vols);
928 LeaveCriticalSection(&This->lock);
933 static HRESULT WINAPI AudioClient_GetBufferSize(IAudioClient *iface,
936 ACImpl *This = impl_from_IAudioClient(iface);
938 TRACE("(%p)->(%p)\n", This, out);
943 EnterCriticalSection(&This->lock);
946 LeaveCriticalSection(&This->lock);
947 return AUDCLNT_E_NOT_INITIALIZED;
950 *out = This->bufsize_frames;
952 LeaveCriticalSection(&This->lock);
957 static HRESULT WINAPI AudioClient_GetStreamLatency(IAudioClient *iface,
958 REFERENCE_TIME *latency)
960 ACImpl *This = impl_from_IAudioClient(iface);
962 TRACE("(%p)->(%p)\n", This, latency);
967 EnterCriticalSection(&This->lock);
970 LeaveCriticalSection(&This->lock);
971 return AUDCLNT_E_NOT_INITIALIZED;
974 LeaveCriticalSection(&This->lock);
981 static HRESULT WINAPI AudioClient_GetCurrentPadding(IAudioClient *iface,
984 ACImpl *This = impl_from_IAudioClient(iface);
986 TRACE("(%p)->(%p)\n", This, out);
991 EnterCriticalSection(&This->lock);
994 LeaveCriticalSection(&This->lock);
995 return AUDCLNT_E_NOT_INITIALIZED;
998 if(This->dataflow == eRender){
999 snd_pcm_sframes_t avail_frames;
1001 avail_frames = snd_pcm_avail_update(This->pcm_handle);
1003 if(This->bufsize_alsa < avail_frames){
1004 WARN("Xrun detected\n");
1005 *out = This->held_frames;
1007 *out = This->bufsize_alsa - avail_frames + This->held_frames;
1008 }else if(This->dataflow == eCapture){
1009 *out = This->held_frames;
1011 LeaveCriticalSection(&This->lock);
1012 return E_UNEXPECTED;
1015 LeaveCriticalSection(&This->lock);
1020 static DWORD get_channel_mask(unsigned int channels)
1026 return SPEAKER_FRONT_CENTER;
1028 return SPEAKER_FRONT_LEFT | SPEAKER_FRONT_RIGHT;
1030 return SPEAKER_FRONT_LEFT | SPEAKER_FRONT_RIGHT |
1031 SPEAKER_LOW_FREQUENCY;
1033 return SPEAKER_FRONT_LEFT | SPEAKER_FRONT_RIGHT | SPEAKER_BACK_LEFT |
1036 return SPEAKER_FRONT_LEFT | SPEAKER_FRONT_RIGHT | SPEAKER_BACK_LEFT |
1037 SPEAKER_BACK_RIGHT | SPEAKER_LOW_FREQUENCY;
1039 return SPEAKER_FRONT_LEFT | SPEAKER_FRONT_RIGHT | SPEAKER_BACK_LEFT |
1040 SPEAKER_BACK_RIGHT | SPEAKER_LOW_FREQUENCY | SPEAKER_FRONT_CENTER;
1042 return SPEAKER_FRONT_LEFT | SPEAKER_FRONT_RIGHT | SPEAKER_BACK_LEFT |
1043 SPEAKER_BACK_RIGHT | SPEAKER_LOW_FREQUENCY | SPEAKER_FRONT_CENTER |
1044 SPEAKER_BACK_CENTER;
1046 FIXME("Unknown speaker configuration: %u\n", channels);
1050 static HRESULT WINAPI AudioClient_IsFormatSupported(IAudioClient *iface,
1051 AUDCLNT_SHAREMODE mode, const WAVEFORMATEX *fmt,
1054 ACImpl *This = impl_from_IAudioClient(iface);
1055 snd_pcm_format_mask_t *formats = NULL;
1057 WAVEFORMATEX *closest = NULL;
1058 const WAVEFORMATEXTENSIBLE *fmtex = (const WAVEFORMATEXTENSIBLE *)fmt;
1059 unsigned int max = 0, min = 0;
1062 TRACE("(%p)->(%x, %p, %p)\n", This, mode, fmt, out);
1064 if(!fmt || (mode == AUDCLNT_SHAREMODE_SHARED && !out))
1067 if(mode != AUDCLNT_SHAREMODE_SHARED && mode != AUDCLNT_SHAREMODE_EXCLUSIVE)
1068 return E_INVALIDARG;
1070 if(fmt->wFormatTag == WAVE_FORMAT_EXTENSIBLE &&
1071 fmt->cbSize < sizeof(WAVEFORMATEXTENSIBLE) - sizeof(WAVEFORMATEX))
1072 return E_INVALIDARG;
1076 EnterCriticalSection(&This->lock);
1078 if((err = snd_pcm_hw_params_any(This->pcm_handle, This->hw_params)) < 0){
1083 formats = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY,
1084 snd_pcm_format_mask_sizeof());
1090 snd_pcm_hw_params_get_format_mask(This->hw_params, formats);
1092 if(fmt->wFormatTag == WAVE_FORMAT_PCM ||
1093 (fmt->wFormatTag == WAVE_FORMAT_EXTENSIBLE &&
1094 IsEqualGUID(&fmtex->SubFormat, &KSDATAFORMAT_SUBTYPE_PCM))){
1095 switch(fmt->wBitsPerSample){
1097 if(!snd_pcm_format_mask_test(formats, SND_PCM_FORMAT_U8)){
1098 hr = AUDCLNT_E_UNSUPPORTED_FORMAT;
1103 if(!snd_pcm_format_mask_test(formats, SND_PCM_FORMAT_S16_LE)){
1104 hr = AUDCLNT_E_UNSUPPORTED_FORMAT;
1109 if(!snd_pcm_format_mask_test(formats, SND_PCM_FORMAT_S24_3LE)){
1110 hr = AUDCLNT_E_UNSUPPORTED_FORMAT;
1115 if(!snd_pcm_format_mask_test(formats, SND_PCM_FORMAT_S32_LE)){
1116 hr = AUDCLNT_E_UNSUPPORTED_FORMAT;
1121 hr = AUDCLNT_E_UNSUPPORTED_FORMAT;
1124 }else if(fmt->wFormatTag == WAVE_FORMAT_IEEE_FLOAT ||
1125 (fmt->wFormatTag == WAVE_FORMAT_EXTENSIBLE &&
1126 IsEqualGUID(&fmtex->SubFormat, &KSDATAFORMAT_SUBTYPE_IEEE_FLOAT))){
1127 switch(fmt->wBitsPerSample){
1129 if(!snd_pcm_format_mask_test(formats, SND_PCM_FORMAT_FLOAT_LE)){
1130 hr = AUDCLNT_E_UNSUPPORTED_FORMAT;
1135 if(!snd_pcm_format_mask_test(formats, SND_PCM_FORMAT_FLOAT64_LE)){
1136 hr = AUDCLNT_E_UNSUPPORTED_FORMAT;
1141 hr = AUDCLNT_E_UNSUPPORTED_FORMAT;
1145 hr = AUDCLNT_E_UNSUPPORTED_FORMAT;
1149 closest = clone_format(fmt);
1155 if((err = snd_pcm_hw_params_get_rate_min(This->hw_params, &min, NULL)) < 0){
1157 WARN("Unable to get min rate: %d (%s)\n", err, snd_strerror(err));
1161 if((err = snd_pcm_hw_params_get_rate_max(This->hw_params, &max, NULL)) < 0){
1163 WARN("Unable to get max rate: %d (%s)\n", err, snd_strerror(err));
1167 if(fmt->nSamplesPerSec < min || fmt->nSamplesPerSec > max){
1168 hr = AUDCLNT_E_UNSUPPORTED_FORMAT;
1172 if((err = snd_pcm_hw_params_get_channels_min(This->hw_params, &min)) < 0){
1174 WARN("Unable to get min channels: %d (%s)\n", err, snd_strerror(err));
1178 if((err = snd_pcm_hw_params_get_channels_max(This->hw_params, &max)) < 0){
1180 WARN("Unable to get max channels: %d (%s)\n", err, snd_strerror(err));
1185 if(fmt->nChannels > max){
1187 closest->nChannels = max;
1188 }else if(fmt->nChannels < min){
1190 closest->nChannels = min;
1193 if(closest->wFormatTag == WAVE_FORMAT_EXTENSIBLE){
1194 DWORD mask = get_channel_mask(closest->nChannels);
1196 ((WAVEFORMATEXTENSIBLE*)closest)->dwChannelMask = mask;
1198 if(fmt->wFormatTag == WAVE_FORMAT_EXTENSIBLE &&
1199 fmtex->dwChannelMask != mask)
1204 LeaveCriticalSection(&This->lock);
1205 HeapFree(GetProcessHeap(), 0, formats);
1207 if(hr == S_OK || !out){
1208 CoTaskMemFree(closest);
1212 closest->nBlockAlign =
1213 closest->nChannels * closest->wBitsPerSample / 8;
1214 closest->nAvgBytesPerSec =
1215 closest->nBlockAlign * closest->nSamplesPerSec;
1219 TRACE("returning: %08x\n", hr);
1223 static HRESULT WINAPI AudioClient_GetMixFormat(IAudioClient *iface,
1224 WAVEFORMATEX **pwfx)
1226 ACImpl *This = impl_from_IAudioClient(iface);
1227 WAVEFORMATEXTENSIBLE *fmt;
1228 snd_pcm_format_mask_t *formats;
1229 unsigned int max_rate, max_channels;
1233 TRACE("(%p)->(%p)\n", This, pwfx);
1239 fmt = CoTaskMemAlloc(sizeof(WAVEFORMATEXTENSIBLE));
1241 return E_OUTOFMEMORY;
1243 formats = HeapAlloc(GetProcessHeap(), 0, snd_pcm_format_mask_sizeof());
1246 return E_OUTOFMEMORY;
1249 EnterCriticalSection(&This->lock);
1251 if((err = snd_pcm_hw_params_any(This->pcm_handle, This->hw_params)) < 0){
1252 WARN("Unable to get hw_params: %d (%s)\n", err, snd_strerror(err));
1257 snd_pcm_hw_params_get_format_mask(This->hw_params, formats);
1259 fmt->Format.wFormatTag = WAVE_FORMAT_EXTENSIBLE;
1260 if(snd_pcm_format_mask_test(formats, SND_PCM_FORMAT_FLOAT_LE)){
1261 fmt->Format.wBitsPerSample = 32;
1262 fmt->SubFormat = KSDATAFORMAT_SUBTYPE_IEEE_FLOAT;
1263 }else if(snd_pcm_format_mask_test(formats, SND_PCM_FORMAT_S16_LE)){
1264 fmt->Format.wBitsPerSample = 16;
1265 fmt->SubFormat = KSDATAFORMAT_SUBTYPE_PCM;
1266 }else if(snd_pcm_format_mask_test(formats, SND_PCM_FORMAT_U8)){
1267 fmt->Format.wBitsPerSample = 8;
1268 fmt->SubFormat = KSDATAFORMAT_SUBTYPE_PCM;
1269 }else if(snd_pcm_format_mask_test(formats, SND_PCM_FORMAT_S32_LE)){
1270 fmt->Format.wBitsPerSample = 32;
1271 fmt->SubFormat = KSDATAFORMAT_SUBTYPE_PCM;
1272 }else if(snd_pcm_format_mask_test(formats, SND_PCM_FORMAT_S24_3LE)){
1273 fmt->Format.wBitsPerSample = 24;
1274 fmt->SubFormat = KSDATAFORMAT_SUBTYPE_PCM;
1276 ERR("Didn't recognize any available ALSA formats\n");
1281 if((err = snd_pcm_hw_params_get_channels_max(This->hw_params,
1282 &max_channels)) < 0){
1283 WARN("Unable to get max channels: %d (%s)\n", err, snd_strerror(err));
1288 if(max_channels > 2){
1289 FIXME("Don't know what to do with %u channels, pretending there's "
1290 "only 2 channels\n", max_channels);
1291 fmt->Format.nChannels = 2;
1293 fmt->Format.nChannels = max_channels;
1295 fmt->dwChannelMask = get_channel_mask(fmt->Format.nChannels);
1297 if((err = snd_pcm_hw_params_get_rate_max(This->hw_params, &max_rate,
1299 WARN("Unable to get max rate: %d (%s)\n", err, snd_strerror(err));
1304 if(max_rate >= 48000)
1305 fmt->Format.nSamplesPerSec = 48000;
1306 else if(max_rate >= 44100)
1307 fmt->Format.nSamplesPerSec = 44100;
1308 else if(max_rate >= 22050)
1309 fmt->Format.nSamplesPerSec = 22050;
1310 else if(max_rate >= 11025)
1311 fmt->Format.nSamplesPerSec = 11025;
1312 else if(max_rate >= 8000)
1313 fmt->Format.nSamplesPerSec = 8000;
1315 ERR("Unknown max rate: %u\n", max_rate);
1320 fmt->Format.nBlockAlign = (fmt->Format.wBitsPerSample *
1321 fmt->Format.nChannels) / 8;
1322 fmt->Format.nAvgBytesPerSec = fmt->Format.nSamplesPerSec *
1323 fmt->Format.nBlockAlign;
1325 fmt->Samples.wValidBitsPerSample = fmt->Format.wBitsPerSample;
1326 fmt->Format.cbSize = sizeof(WAVEFORMATEXTENSIBLE) - sizeof(WAVEFORMATEX);
1328 dump_fmt((WAVEFORMATEX*)fmt);
1329 *pwfx = (WAVEFORMATEX*)fmt;
1332 LeaveCriticalSection(&This->lock);
1335 HeapFree(GetProcessHeap(), 0, formats);
1340 static HRESULT WINAPI AudioClient_GetDevicePeriod(IAudioClient *iface,
1341 REFERENCE_TIME *defperiod, REFERENCE_TIME *minperiod)
1343 ACImpl *This = impl_from_IAudioClient(iface);
1345 TRACE("(%p)->(%p, %p)\n", This, defperiod, minperiod);
1347 if(!defperiod && !minperiod)
1351 *defperiod = DefaultPeriod;
1353 *minperiod = MinimumPeriod;
1358 static snd_pcm_sframes_t alsa_write_best_effort(snd_pcm_t *handle, BYTE *buf,
1359 snd_pcm_uframes_t frames, ACImpl *This)
1361 snd_pcm_sframes_t written;
1363 if(This->session->mute){
1365 if((err = snd_pcm_format_set_silence(This->alsa_format, buf,
1366 frames * This->fmt->nChannels)) < 0)
1367 WARN("Setting buffer to silence failed: %d (%s)\n", err,
1371 written = snd_pcm_writei(handle, buf, frames);
1375 if(written == -EAGAIN)
1379 WARN("writei failed, recovering: %ld (%s)\n", written,
1380 snd_strerror(written));
1382 ret = wine_snd_pcm_recover(handle, written, 0);
1384 WARN("Could not recover: %d (%s)\n", ret, snd_strerror(ret));
1388 written = snd_pcm_writei(handle, buf, frames);
1394 static void alsa_write_data(ACImpl *This)
1396 snd_pcm_sframes_t written;
1397 snd_pcm_uframes_t to_write;
1399 This->local_buffer + (This->lcl_offs_frames * This->fmt->nBlockAlign);
1401 if(This->lcl_offs_frames + This->held_frames > This->bufsize_frames)
1402 to_write = This->bufsize_frames - This->lcl_offs_frames;
1404 to_write = This->held_frames;
1406 written = alsa_write_best_effort(This->pcm_handle, buf, to_write, This);
1408 WARN("Couldn't write: %ld (%s)\n", written, snd_strerror(written));
1412 This->lcl_offs_frames += written;
1413 This->lcl_offs_frames %= This->bufsize_frames;
1414 This->held_frames -= written;
1416 if(written < to_write){
1417 /* ALSA buffer probably full */
1421 if(This->held_frames){
1422 /* wrapped and have some data back at the start to write */
1423 written = alsa_write_best_effort(This->pcm_handle, This->local_buffer,
1424 This->held_frames, This);
1426 WARN("Couldn't write: %ld (%s)\n", written, snd_strerror(written));
1430 This->lcl_offs_frames += written;
1431 This->lcl_offs_frames %= This->bufsize_frames;
1432 This->held_frames -= written;
1436 static void alsa_read_data(ACImpl *This)
1438 snd_pcm_sframes_t pos, readable, nread;
1440 pos = (This->held_frames + This->lcl_offs_frames) % This->bufsize_frames;
1441 readable = This->bufsize_frames - pos;
1443 nread = snd_pcm_readi(This->pcm_handle,
1444 This->local_buffer + pos * This->fmt->nBlockAlign, readable);
1448 WARN("read failed, recovering: %ld (%s)\n", nread, snd_strerror(nread));
1450 ret = wine_snd_pcm_recover(This->pcm_handle, nread, 0);
1452 WARN("Recover failed: %d (%s)\n", ret, snd_strerror(ret));
1456 nread = snd_pcm_readi(This->pcm_handle,
1457 This->local_buffer + pos * This->fmt->nBlockAlign, readable);
1459 WARN("read failed: %ld (%s)\n", nread, snd_strerror(nread));
1464 if(This->session->mute){
1466 if((err = snd_pcm_format_set_silence(This->alsa_format,
1467 This->local_buffer + pos * This->fmt->nBlockAlign,
1469 WARN("Setting buffer to silence failed: %d (%s)\n", err,
1473 This->held_frames += nread;
1475 if(This->held_frames > This->bufsize_frames){
1476 WARN("Overflow of unread data\n");
1477 This->lcl_offs_frames += This->held_frames;
1478 This->lcl_offs_frames %= This->bufsize_frames;
1479 This->held_frames = This->bufsize_frames;
1483 static void CALLBACK alsa_push_buffer_data(void *user, BOOLEAN timer)
1485 ACImpl *This = user;
1487 EnterCriticalSection(&This->lock);
1490 if(This->dataflow == eRender && This->held_frames)
1491 alsa_write_data(This);
1492 else if(This->dataflow == eCapture)
1493 alsa_read_data(This);
1496 SetEvent(This->event);
1499 LeaveCriticalSection(&This->lock);
1502 static HRESULT alsa_consider_start(ACImpl *This)
1504 snd_pcm_sframes_t avail;
1507 avail = snd_pcm_avail_update(This->pcm_handle);
1509 WARN("Unable to get avail_update: %ld (%s)\n", avail,
1510 snd_strerror(avail));
1514 if(This->period_alsa < This->bufsize_alsa - avail){
1515 if((err = snd_pcm_start(This->pcm_handle)) < 0){
1516 WARN("Start failed: %d (%s), state: %d\n", err, snd_strerror(err),
1517 snd_pcm_state(This->pcm_handle));
1527 static HRESULT WINAPI AudioClient_Start(IAudioClient *iface)
1529 ACImpl *This = impl_from_IAudioClient(iface);
1533 TRACE("(%p)\n", This);
1535 EnterCriticalSection(&This->lock);
1538 LeaveCriticalSection(&This->lock);
1539 return AUDCLNT_E_NOT_INITIALIZED;
1542 if((This->flags & AUDCLNT_STREAMFLAGS_EVENTCALLBACK) && !This->event){
1543 LeaveCriticalSection(&This->lock);
1544 return AUDCLNT_E_EVENTHANDLE_NOT_SET;
1548 LeaveCriticalSection(&This->lock);
1549 return AUDCLNT_E_NOT_STOPPED;
1552 hr = alsa_consider_start(This);
1554 LeaveCriticalSection(&This->lock);
1558 period_ms = This->period_us / 1000;
1562 if(This->dataflow == eCapture){
1563 /* dump any data that might be leftover in the ALSA capture buffer */
1564 snd_pcm_readi(This->pcm_handle, This->local_buffer,
1565 This->bufsize_frames);
1568 if(!CreateTimerQueueTimer(&This->timer, g_timer_q, alsa_push_buffer_data,
1569 This, 0, period_ms, WT_EXECUTEINTIMERTHREAD)){
1570 LeaveCriticalSection(&This->lock);
1571 WARN("Unable to create timer: %u\n", GetLastError());
1575 This->started = TRUE;
1577 LeaveCriticalSection(&This->lock);
1582 static HRESULT WINAPI AudioClient_Stop(IAudioClient *iface)
1584 ACImpl *This = impl_from_IAudioClient(iface);
1589 TRACE("(%p)\n", This);
1591 EnterCriticalSection(&This->lock);
1594 LeaveCriticalSection(&This->lock);
1595 return AUDCLNT_E_NOT_INITIALIZED;
1599 LeaveCriticalSection(&This->lock);
1603 event = CreateEventW(NULL, TRUE, FALSE, NULL);
1604 wait = !DeleteTimerQueueTimer(g_timer_q, This->timer, event);
1606 WARN("DeleteTimerQueueTimer error %u\n", GetLastError());
1607 wait = wait && GetLastError() == ERROR_IO_PENDING;
1609 if((err = snd_pcm_drop(This->pcm_handle)) < 0){
1610 LeaveCriticalSection(&This->lock);
1611 WARN("Drop failed: %d (%s)\n", err, snd_strerror(err));
1615 if((err = snd_pcm_prepare(This->pcm_handle)) < 0){
1616 LeaveCriticalSection(&This->lock);
1617 WARN("Prepare failed: %d (%s)\n", err, snd_strerror(err));
1621 This->started = FALSE;
1623 LeaveCriticalSection(&This->lock);
1626 WaitForSingleObject(event, INFINITE);
1632 static HRESULT WINAPI AudioClient_Reset(IAudioClient *iface)
1634 ACImpl *This = impl_from_IAudioClient(iface);
1636 TRACE("(%p)\n", This);
1638 EnterCriticalSection(&This->lock);
1641 LeaveCriticalSection(&This->lock);
1642 return AUDCLNT_E_NOT_INITIALIZED;
1646 LeaveCriticalSection(&This->lock);
1647 return AUDCLNT_E_NOT_STOPPED;
1650 This->held_frames = 0;
1651 This->written_frames = 0;
1652 This->lcl_offs_frames = 0;
1654 LeaveCriticalSection(&This->lock);
1659 static HRESULT WINAPI AudioClient_SetEventHandle(IAudioClient *iface,
1662 ACImpl *This = impl_from_IAudioClient(iface);
1664 TRACE("(%p)->(%p)\n", This, event);
1667 return E_INVALIDARG;
1669 EnterCriticalSection(&This->lock);
1672 LeaveCriticalSection(&This->lock);
1673 return AUDCLNT_E_NOT_INITIALIZED;
1676 if(!(This->flags & AUDCLNT_STREAMFLAGS_EVENTCALLBACK)){
1677 LeaveCriticalSection(&This->lock);
1678 return AUDCLNT_E_EVENTHANDLE_NOT_EXPECTED;
1681 This->event = event;
1683 LeaveCriticalSection(&This->lock);
1688 static HRESULT WINAPI AudioClient_GetService(IAudioClient *iface, REFIID riid,
1691 ACImpl *This = impl_from_IAudioClient(iface);
1693 TRACE("(%p)->(%s, %p)\n", This, debugstr_guid(riid), ppv);
1699 EnterCriticalSection(&This->lock);
1702 LeaveCriticalSection(&This->lock);
1703 return AUDCLNT_E_NOT_INITIALIZED;
1706 if(IsEqualIID(riid, &IID_IAudioRenderClient)){
1707 if(This->dataflow != eRender){
1708 LeaveCriticalSection(&This->lock);
1709 return AUDCLNT_E_WRONG_ENDPOINT_TYPE;
1711 IAudioRenderClient_AddRef(&This->IAudioRenderClient_iface);
1712 *ppv = &This->IAudioRenderClient_iface;
1713 }else if(IsEqualIID(riid, &IID_IAudioCaptureClient)){
1714 if(This->dataflow != eCapture){
1715 LeaveCriticalSection(&This->lock);
1716 return AUDCLNT_E_WRONG_ENDPOINT_TYPE;
1718 IAudioCaptureClient_AddRef(&This->IAudioCaptureClient_iface);
1719 *ppv = &This->IAudioCaptureClient_iface;
1720 }else if(IsEqualIID(riid, &IID_IAudioClock)){
1721 IAudioClock_AddRef(&This->IAudioClock_iface);
1722 *ppv = &This->IAudioClock_iface;
1723 }else if(IsEqualIID(riid, &IID_IAudioStreamVolume)){
1724 IAudioStreamVolume_AddRef(&This->IAudioStreamVolume_iface);
1725 *ppv = &This->IAudioStreamVolume_iface;
1726 }else if(IsEqualIID(riid, &IID_IAudioSessionControl)){
1727 if(!This->session_wrapper){
1728 This->session_wrapper = AudioSessionWrapper_Create(This);
1729 if(!This->session_wrapper){
1730 LeaveCriticalSection(&This->lock);
1731 return E_OUTOFMEMORY;
1734 IAudioSessionControl2_AddRef(&This->session_wrapper->IAudioSessionControl2_iface);
1736 *ppv = &This->session_wrapper->IAudioSessionControl2_iface;
1737 }else if(IsEqualIID(riid, &IID_IChannelAudioVolume)){
1738 if(!This->session_wrapper){
1739 This->session_wrapper = AudioSessionWrapper_Create(This);
1740 if(!This->session_wrapper){
1741 LeaveCriticalSection(&This->lock);
1742 return E_OUTOFMEMORY;
1745 IChannelAudioVolume_AddRef(&This->session_wrapper->IChannelAudioVolume_iface);
1747 *ppv = &This->session_wrapper->IChannelAudioVolume_iface;
1748 }else if(IsEqualIID(riid, &IID_ISimpleAudioVolume)){
1749 if(!This->session_wrapper){
1750 This->session_wrapper = AudioSessionWrapper_Create(This);
1751 if(!This->session_wrapper){
1752 LeaveCriticalSection(&This->lock);
1753 return E_OUTOFMEMORY;
1756 ISimpleAudioVolume_AddRef(&This->session_wrapper->ISimpleAudioVolume_iface);
1758 *ppv = &This->session_wrapper->ISimpleAudioVolume_iface;
1762 LeaveCriticalSection(&This->lock);
1766 LeaveCriticalSection(&This->lock);
1768 FIXME("stub %s\n", debugstr_guid(riid));
1769 return E_NOINTERFACE;
1772 static const IAudioClientVtbl AudioClient_Vtbl =
1774 AudioClient_QueryInterface,
1776 AudioClient_Release,
1777 AudioClient_Initialize,
1778 AudioClient_GetBufferSize,
1779 AudioClient_GetStreamLatency,
1780 AudioClient_GetCurrentPadding,
1781 AudioClient_IsFormatSupported,
1782 AudioClient_GetMixFormat,
1783 AudioClient_GetDevicePeriod,
1787 AudioClient_SetEventHandle,
1788 AudioClient_GetService
1791 static HRESULT WINAPI AudioRenderClient_QueryInterface(
1792 IAudioRenderClient *iface, REFIID riid, void **ppv)
1794 TRACE("(%p)->(%s, %p)\n", iface, debugstr_guid(riid), ppv);
1800 if(IsEqualIID(riid, &IID_IUnknown) ||
1801 IsEqualIID(riid, &IID_IAudioRenderClient))
1804 IUnknown_AddRef((IUnknown*)*ppv);
1808 WARN("Unknown interface %s\n", debugstr_guid(riid));
1809 return E_NOINTERFACE;
1812 static ULONG WINAPI AudioRenderClient_AddRef(IAudioRenderClient *iface)
1814 ACImpl *This = impl_from_IAudioRenderClient(iface);
1815 return AudioClient_AddRef(&This->IAudioClient_iface);
1818 static ULONG WINAPI AudioRenderClient_Release(IAudioRenderClient *iface)
1820 ACImpl *This = impl_from_IAudioRenderClient(iface);
1821 return AudioClient_Release(&This->IAudioClient_iface);
1824 static HRESULT WINAPI AudioRenderClient_GetBuffer(IAudioRenderClient *iface,
1825 UINT32 frames, BYTE **data)
1827 ACImpl *This = impl_from_IAudioRenderClient(iface);
1832 TRACE("(%p)->(%u, %p)\n", This, frames, data);
1837 EnterCriticalSection(&This->lock);
1839 if(This->buf_state != NOT_LOCKED){
1840 LeaveCriticalSection(&This->lock);
1841 return AUDCLNT_E_OUT_OF_ORDER;
1845 This->buf_state = LOCKED_NORMAL;
1846 LeaveCriticalSection(&This->lock);
1850 hr = IAudioClient_GetCurrentPadding(&This->IAudioClient_iface, &pad);
1852 LeaveCriticalSection(&This->lock);
1856 if(pad + frames > This->bufsize_frames){
1857 LeaveCriticalSection(&This->lock);
1858 return AUDCLNT_E_BUFFER_TOO_LARGE;
1862 (This->lcl_offs_frames + This->held_frames) % This->bufsize_frames;
1863 if(write_pos + frames > This->bufsize_frames){
1864 if(This->tmp_buffer_frames < frames){
1865 if(This->tmp_buffer)
1866 This->tmp_buffer = HeapReAlloc(GetProcessHeap(), 0,
1867 This->tmp_buffer, frames * This->fmt->nBlockAlign);
1869 This->tmp_buffer = HeapAlloc(GetProcessHeap(), 0,
1870 frames * This->fmt->nBlockAlign);
1871 if(!This->tmp_buffer){
1872 LeaveCriticalSection(&This->lock);
1873 return E_OUTOFMEMORY;
1875 This->tmp_buffer_frames = frames;
1877 *data = This->tmp_buffer;
1878 This->buf_state = LOCKED_WRAPPED;
1880 *data = This->local_buffer +
1881 This->lcl_offs_frames * This->fmt->nBlockAlign;
1882 This->buf_state = LOCKED_NORMAL;
1885 LeaveCriticalSection(&This->lock);
1890 static void alsa_wrap_buffer(ACImpl *This, BYTE *buffer, UINT32 written_bytes)
1892 snd_pcm_uframes_t write_offs_frames =
1893 (This->lcl_offs_frames + This->held_frames) % This->bufsize_frames;
1894 UINT32 write_offs_bytes = write_offs_frames * This->fmt->nBlockAlign;
1895 snd_pcm_uframes_t chunk_frames = This->bufsize_frames - write_offs_frames;
1896 UINT32 chunk_bytes = chunk_frames * This->fmt->nBlockAlign;
1898 if(written_bytes < chunk_bytes){
1899 memcpy(This->local_buffer + write_offs_bytes, buffer, written_bytes);
1901 memcpy(This->local_buffer + write_offs_bytes, buffer, chunk_bytes);
1902 memcpy(This->local_buffer, buffer + chunk_bytes,
1903 written_bytes - chunk_bytes);
1907 static HRESULT WINAPI AudioRenderClient_ReleaseBuffer(
1908 IAudioRenderClient *iface, UINT32 written_frames, DWORD flags)
1910 ACImpl *This = impl_from_IAudioRenderClient(iface);
1911 UINT32 written_bytes = written_frames * This->fmt->nBlockAlign;
1915 TRACE("(%p)->(%u, %x)\n", This, written_frames, flags);
1917 EnterCriticalSection(&This->lock);
1919 if(This->buf_state == NOT_LOCKED || !written_frames){
1920 This->buf_state = NOT_LOCKED;
1921 LeaveCriticalSection(&This->lock);
1922 return written_frames ? AUDCLNT_E_OUT_OF_ORDER : S_OK;
1925 if(This->buf_state == LOCKED_NORMAL)
1926 buffer = This->local_buffer +
1927 This->lcl_offs_frames * This->fmt->nBlockAlign;
1929 buffer = This->tmp_buffer;
1931 if(flags & AUDCLNT_BUFFERFLAGS_SILENT){
1932 WAVEFORMATEXTENSIBLE *fmtex = (WAVEFORMATEXTENSIBLE*)This->fmt;
1933 if((This->fmt->wFormatTag == WAVE_FORMAT_PCM ||
1934 (This->fmt->wFormatTag == WAVE_FORMAT_EXTENSIBLE &&
1935 IsEqualGUID(&fmtex->SubFormat, &KSDATAFORMAT_SUBTYPE_PCM))) &&
1936 This->fmt->wBitsPerSample == 8)
1937 memset(buffer, 128, written_frames * This->fmt->nBlockAlign);
1939 memset(buffer, 0, written_frames * This->fmt->nBlockAlign);
1942 if(This->held_frames){
1943 if(This->buf_state == LOCKED_WRAPPED)
1944 alsa_wrap_buffer(This, buffer, written_bytes);
1946 This->held_frames += written_frames;
1948 snd_pcm_sframes_t written;
1950 written = alsa_write_best_effort(This->pcm_handle, buffer,
1951 written_frames, This);
1953 LeaveCriticalSection(&This->lock);
1954 WARN("write failed: %ld (%s)\n", written, snd_strerror(written));
1958 if(written < written_frames){
1959 if(This->buf_state == LOCKED_WRAPPED)
1960 alsa_wrap_buffer(This,
1961 This->tmp_buffer + written * This->fmt->nBlockAlign,
1962 written_frames - written);
1964 This->held_frames = written_frames - written;
1969 snd_pcm_state(This->pcm_handle) == SND_PCM_STATE_PREPARED){
1970 hr = alsa_consider_start(This);
1972 LeaveCriticalSection(&This->lock);
1977 This->written_frames += written_frames;
1978 This->buf_state = NOT_LOCKED;
1980 LeaveCriticalSection(&This->lock);
1985 static const IAudioRenderClientVtbl AudioRenderClient_Vtbl = {
1986 AudioRenderClient_QueryInterface,
1987 AudioRenderClient_AddRef,
1988 AudioRenderClient_Release,
1989 AudioRenderClient_GetBuffer,
1990 AudioRenderClient_ReleaseBuffer
1993 static HRESULT WINAPI AudioCaptureClient_QueryInterface(
1994 IAudioCaptureClient *iface, REFIID riid, void **ppv)
1996 TRACE("(%p)->(%s, %p)\n", iface, debugstr_guid(riid), ppv);
2002 if(IsEqualIID(riid, &IID_IUnknown) ||
2003 IsEqualIID(riid, &IID_IAudioCaptureClient))
2006 IUnknown_AddRef((IUnknown*)*ppv);
2010 WARN("Unknown interface %s\n", debugstr_guid(riid));
2011 return E_NOINTERFACE;
2014 static ULONG WINAPI AudioCaptureClient_AddRef(IAudioCaptureClient *iface)
2016 ACImpl *This = impl_from_IAudioCaptureClient(iface);
2017 return IAudioClient_AddRef(&This->IAudioClient_iface);
2020 static ULONG WINAPI AudioCaptureClient_Release(IAudioCaptureClient *iface)
2022 ACImpl *This = impl_from_IAudioCaptureClient(iface);
2023 return IAudioClient_Release(&This->IAudioClient_iface);
2026 static HRESULT WINAPI AudioCaptureClient_GetBuffer(IAudioCaptureClient *iface,
2027 BYTE **data, UINT32 *frames, DWORD *flags, UINT64 *devpos,
2030 ACImpl *This = impl_from_IAudioCaptureClient(iface);
2033 TRACE("(%p)->(%p, %p, %p, %p, %p)\n", This, data, frames, flags,
2036 if(!data || !frames || !flags)
2039 EnterCriticalSection(&This->lock);
2041 if(This->buf_state != NOT_LOCKED){
2042 LeaveCriticalSection(&This->lock);
2043 return AUDCLNT_E_OUT_OF_ORDER;
2046 hr = IAudioCaptureClient_GetNextPacketSize(iface, frames);
2048 LeaveCriticalSection(&This->lock);
2054 if(This->lcl_offs_frames + *frames > This->bufsize_frames){
2055 UINT32 chunk_bytes, offs_bytes, frames_bytes;
2056 if(This->tmp_buffer_frames < *frames){
2057 if(This->tmp_buffer)
2058 This->tmp_buffer = HeapReAlloc(GetProcessHeap(), 0,
2059 This->tmp_buffer, *frames * This->fmt->nBlockAlign);
2061 This->tmp_buffer = HeapAlloc(GetProcessHeap(), 0,
2062 *frames * This->fmt->nBlockAlign);
2063 if(!This->tmp_buffer){
2064 LeaveCriticalSection(&This->lock);
2065 return E_OUTOFMEMORY;
2067 This->tmp_buffer_frames = *frames;
2070 *data = This->tmp_buffer;
2071 chunk_bytes = (This->bufsize_frames - This->lcl_offs_frames) *
2072 This->fmt->nBlockAlign;
2073 offs_bytes = This->lcl_offs_frames * This->fmt->nBlockAlign;
2074 frames_bytes = *frames * This->fmt->nBlockAlign;
2075 memcpy(This->tmp_buffer, This->local_buffer + offs_bytes, chunk_bytes);
2076 memcpy(This->tmp_buffer + chunk_bytes, This->local_buffer,
2077 frames_bytes - chunk_bytes);
2079 *data = This->local_buffer +
2080 This->lcl_offs_frames * This->fmt->nBlockAlign;
2082 This->buf_state = LOCKED_NORMAL;
2084 if(devpos || qpcpos)
2085 IAudioClock_GetPosition(&This->IAudioClock_iface, devpos, qpcpos);
2087 LeaveCriticalSection(&This->lock);
2089 return *frames ? S_OK : AUDCLNT_S_BUFFER_EMPTY;
2092 static HRESULT WINAPI AudioCaptureClient_ReleaseBuffer(
2093 IAudioCaptureClient *iface, UINT32 done)
2095 ACImpl *This = impl_from_IAudioCaptureClient(iface);
2097 TRACE("(%p)->(%u)\n", This, done);
2099 EnterCriticalSection(&This->lock);
2101 if(This->buf_state == NOT_LOCKED){
2102 LeaveCriticalSection(&This->lock);
2103 return AUDCLNT_E_OUT_OF_ORDER;
2106 This->held_frames -= done;
2107 This->lcl_offs_frames += done;
2108 This->lcl_offs_frames %= This->bufsize_frames;
2110 This->buf_state = NOT_LOCKED;
2112 LeaveCriticalSection(&This->lock);
2117 static HRESULT WINAPI AudioCaptureClient_GetNextPacketSize(
2118 IAudioCaptureClient *iface, UINT32 *frames)
2120 ACImpl *This = impl_from_IAudioCaptureClient(iface);
2122 TRACE("(%p)->(%p)\n", This, frames);
2124 return AudioClient_GetCurrentPadding(&This->IAudioClient_iface, frames);
2127 static const IAudioCaptureClientVtbl AudioCaptureClient_Vtbl =
2129 AudioCaptureClient_QueryInterface,
2130 AudioCaptureClient_AddRef,
2131 AudioCaptureClient_Release,
2132 AudioCaptureClient_GetBuffer,
2133 AudioCaptureClient_ReleaseBuffer,
2134 AudioCaptureClient_GetNextPacketSize
2137 static HRESULT WINAPI AudioClock_QueryInterface(IAudioClock *iface,
2138 REFIID riid, void **ppv)
2140 ACImpl *This = impl_from_IAudioClock(iface);
2142 TRACE("(%p)->(%s, %p)\n", iface, debugstr_guid(riid), ppv);
2148 if(IsEqualIID(riid, &IID_IUnknown) || IsEqualIID(riid, &IID_IAudioClock))
2150 else if(IsEqualIID(riid, &IID_IAudioClock2))
2151 *ppv = &This->IAudioClock2_iface;
2153 IUnknown_AddRef((IUnknown*)*ppv);
2157 WARN("Unknown interface %s\n", debugstr_guid(riid));
2158 return E_NOINTERFACE;
2161 static ULONG WINAPI AudioClock_AddRef(IAudioClock *iface)
2163 ACImpl *This = impl_from_IAudioClock(iface);
2164 return IAudioClient_AddRef(&This->IAudioClient_iface);
2167 static ULONG WINAPI AudioClock_Release(IAudioClock *iface)
2169 ACImpl *This = impl_from_IAudioClock(iface);
2170 return IAudioClient_Release(&This->IAudioClient_iface);
2173 static HRESULT WINAPI AudioClock_GetFrequency(IAudioClock *iface, UINT64 *freq)
2175 ACImpl *This = impl_from_IAudioClock(iface);
2177 TRACE("(%p)->(%p)\n", This, freq);
2179 *freq = This->fmt->nSamplesPerSec;
2184 static HRESULT WINAPI AudioClock_GetPosition(IAudioClock *iface, UINT64 *pos,
2187 ACImpl *This = impl_from_IAudioClock(iface);
2191 TRACE("(%p)->(%p, %p)\n", This, pos, qpctime);
2196 EnterCriticalSection(&This->lock);
2198 hr = IAudioClient_GetCurrentPadding(&This->IAudioClient_iface, &pad);
2200 LeaveCriticalSection(&This->lock);
2204 if(This->dataflow == eRender)
2205 *pos = This->written_frames - pad;
2206 else if(This->dataflow == eCapture)
2207 *pos = This->written_frames + pad;
2209 LeaveCriticalSection(&This->lock);
2212 LARGE_INTEGER stamp, freq;
2213 QueryPerformanceCounter(&stamp);
2214 QueryPerformanceFrequency(&freq);
2215 *qpctime = (stamp.QuadPart * (INT64)10000000) / freq.QuadPart;
2221 static HRESULT WINAPI AudioClock_GetCharacteristics(IAudioClock *iface,
2224 ACImpl *This = impl_from_IAudioClock(iface);
2226 TRACE("(%p)->(%p)\n", This, chars);
2231 *chars = AUDIOCLOCK_CHARACTERISTIC_FIXED_FREQ;
2236 static const IAudioClockVtbl AudioClock_Vtbl =
2238 AudioClock_QueryInterface,
2241 AudioClock_GetFrequency,
2242 AudioClock_GetPosition,
2243 AudioClock_GetCharacteristics
2246 static HRESULT WINAPI AudioClock2_QueryInterface(IAudioClock2 *iface,
2247 REFIID riid, void **ppv)
2249 ACImpl *This = impl_from_IAudioClock2(iface);
2250 return IAudioClock_QueryInterface(&This->IAudioClock_iface, riid, ppv);
2253 static ULONG WINAPI AudioClock2_AddRef(IAudioClock2 *iface)
2255 ACImpl *This = impl_from_IAudioClock2(iface);
2256 return IAudioClient_AddRef(&This->IAudioClient_iface);
2259 static ULONG WINAPI AudioClock2_Release(IAudioClock2 *iface)
2261 ACImpl *This = impl_from_IAudioClock2(iface);
2262 return IAudioClient_Release(&This->IAudioClient_iface);
2265 static HRESULT WINAPI AudioClock2_GetDevicePosition(IAudioClock2 *iface,
2266 UINT64 *pos, UINT64 *qpctime)
2268 ACImpl *This = impl_from_IAudioClock2(iface);
2270 FIXME("(%p)->(%p, %p)\n", This, pos, qpctime);
2275 static const IAudioClock2Vtbl AudioClock2_Vtbl =
2277 AudioClock2_QueryInterface,
2279 AudioClock2_Release,
2280 AudioClock2_GetDevicePosition
2283 static AudioSessionWrapper *AudioSessionWrapper_Create(ACImpl *client)
2285 AudioSessionWrapper *ret;
2287 ret = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY,
2288 sizeof(AudioSessionWrapper));
2292 ret->IAudioSessionControl2_iface.lpVtbl = &AudioSessionControl2_Vtbl;
2293 ret->ISimpleAudioVolume_iface.lpVtbl = &SimpleAudioVolume_Vtbl;
2294 ret->IChannelAudioVolume_iface.lpVtbl = &ChannelAudioVolume_Vtbl;
2298 ret->client = client;
2300 ret->session = client->session;
2301 AudioClient_AddRef(&client->IAudioClient_iface);
2307 static HRESULT WINAPI AudioSessionControl_QueryInterface(
2308 IAudioSessionControl2 *iface, REFIID riid, void **ppv)
2310 TRACE("(%p)->(%s, %p)\n", iface, debugstr_guid(riid), ppv);
2316 if(IsEqualIID(riid, &IID_IUnknown) ||
2317 IsEqualIID(riid, &IID_IAudioSessionControl) ||
2318 IsEqualIID(riid, &IID_IAudioSessionControl2))
2321 IUnknown_AddRef((IUnknown*)*ppv);
2325 WARN("Unknown interface %s\n", debugstr_guid(riid));
2326 return E_NOINTERFACE;
2329 static ULONG WINAPI AudioSessionControl_AddRef(IAudioSessionControl2 *iface)
2331 AudioSessionWrapper *This = impl_from_IAudioSessionControl2(iface);
2333 ref = InterlockedIncrement(&This->ref);
2334 TRACE("(%p) Refcount now %u\n", This, ref);
2338 static ULONG WINAPI AudioSessionControl_Release(IAudioSessionControl2 *iface)
2340 AudioSessionWrapper *This = impl_from_IAudioSessionControl2(iface);
2342 ref = InterlockedDecrement(&This->ref);
2343 TRACE("(%p) Refcount now %u\n", This, ref);
2346 EnterCriticalSection(&This->client->lock);
2347 This->client->session_wrapper = NULL;
2348 LeaveCriticalSection(&This->client->lock);
2349 AudioClient_Release(&This->client->IAudioClient_iface);
2351 HeapFree(GetProcessHeap(), 0, This);
2356 static HRESULT WINAPI AudioSessionControl_GetState(IAudioSessionControl2 *iface,
2357 AudioSessionState *state)
2359 AudioSessionWrapper *This = impl_from_IAudioSessionControl2(iface);
2362 TRACE("(%p)->(%p)\n", This, state);
2365 return NULL_PTR_ERR;
2367 EnterCriticalSection(&g_sessions_lock);
2369 if(list_empty(&This->session->clients)){
2370 *state = AudioSessionStateExpired;
2371 LeaveCriticalSection(&g_sessions_lock);
2375 LIST_FOR_EACH_ENTRY(client, &This->session->clients, ACImpl, entry){
2376 EnterCriticalSection(&client->lock);
2377 if(client->started){
2378 *state = AudioSessionStateActive;
2379 LeaveCriticalSection(&client->lock);
2380 LeaveCriticalSection(&g_sessions_lock);
2383 LeaveCriticalSection(&client->lock);
2386 LeaveCriticalSection(&g_sessions_lock);
2388 *state = AudioSessionStateInactive;
2393 static HRESULT WINAPI AudioSessionControl_GetDisplayName(
2394 IAudioSessionControl2 *iface, WCHAR **name)
2396 AudioSessionWrapper *This = impl_from_IAudioSessionControl2(iface);
2398 FIXME("(%p)->(%p) - stub\n", This, name);
2403 static HRESULT WINAPI AudioSessionControl_SetDisplayName(
2404 IAudioSessionControl2 *iface, const WCHAR *name, const GUID *session)
2406 AudioSessionWrapper *This = impl_from_IAudioSessionControl2(iface);
2408 FIXME("(%p)->(%p, %s) - stub\n", This, name, debugstr_guid(session));
2413 static HRESULT WINAPI AudioSessionControl_GetIconPath(
2414 IAudioSessionControl2 *iface, WCHAR **path)
2416 AudioSessionWrapper *This = impl_from_IAudioSessionControl2(iface);
2418 FIXME("(%p)->(%p) - stub\n", This, path);
2423 static HRESULT WINAPI AudioSessionControl_SetIconPath(
2424 IAudioSessionControl2 *iface, const WCHAR *path, const GUID *session)
2426 AudioSessionWrapper *This = impl_from_IAudioSessionControl2(iface);
2428 FIXME("(%p)->(%p, %s) - stub\n", This, path, debugstr_guid(session));
2433 static HRESULT WINAPI AudioSessionControl_GetGroupingParam(
2434 IAudioSessionControl2 *iface, GUID *group)
2436 AudioSessionWrapper *This = impl_from_IAudioSessionControl2(iface);
2438 FIXME("(%p)->(%p) - stub\n", This, group);
2443 static HRESULT WINAPI AudioSessionControl_SetGroupingParam(
2444 IAudioSessionControl2 *iface, const GUID *group, const GUID *session)
2446 AudioSessionWrapper *This = impl_from_IAudioSessionControl2(iface);
2448 FIXME("(%p)->(%s, %s) - stub\n", This, debugstr_guid(group),
2449 debugstr_guid(session));
2454 static HRESULT WINAPI AudioSessionControl_RegisterAudioSessionNotification(
2455 IAudioSessionControl2 *iface, IAudioSessionEvents *events)
2457 AudioSessionWrapper *This = impl_from_IAudioSessionControl2(iface);
2459 FIXME("(%p)->(%p) - stub\n", This, events);
2464 static HRESULT WINAPI AudioSessionControl_UnregisterAudioSessionNotification(
2465 IAudioSessionControl2 *iface, IAudioSessionEvents *events)
2467 AudioSessionWrapper *This = impl_from_IAudioSessionControl2(iface);
2469 FIXME("(%p)->(%p) - stub\n", This, events);
2474 static HRESULT WINAPI AudioSessionControl_GetSessionIdentifier(
2475 IAudioSessionControl2 *iface, WCHAR **id)
2477 AudioSessionWrapper *This = impl_from_IAudioSessionControl2(iface);
2479 FIXME("(%p)->(%p) - stub\n", This, id);
2484 static HRESULT WINAPI AudioSessionControl_GetSessionInstanceIdentifier(
2485 IAudioSessionControl2 *iface, WCHAR **id)
2487 AudioSessionWrapper *This = impl_from_IAudioSessionControl2(iface);
2489 FIXME("(%p)->(%p) - stub\n", This, id);
2494 static HRESULT WINAPI AudioSessionControl_GetProcessId(
2495 IAudioSessionControl2 *iface, DWORD *pid)
2497 AudioSessionWrapper *This = impl_from_IAudioSessionControl2(iface);
2499 TRACE("(%p)->(%p)\n", This, pid);
2504 *pid = GetCurrentProcessId();
2509 static HRESULT WINAPI AudioSessionControl_IsSystemSoundsSession(
2510 IAudioSessionControl2 *iface)
2512 AudioSessionWrapper *This = impl_from_IAudioSessionControl2(iface);
2514 TRACE("(%p)\n", This);
2519 static HRESULT WINAPI AudioSessionControl_SetDuckingPreference(
2520 IAudioSessionControl2 *iface, BOOL optout)
2522 AudioSessionWrapper *This = impl_from_IAudioSessionControl2(iface);
2524 TRACE("(%p)->(%d)\n", This, optout);
2529 static const IAudioSessionControl2Vtbl AudioSessionControl2_Vtbl =
2531 AudioSessionControl_QueryInterface,
2532 AudioSessionControl_AddRef,
2533 AudioSessionControl_Release,
2534 AudioSessionControl_GetState,
2535 AudioSessionControl_GetDisplayName,
2536 AudioSessionControl_SetDisplayName,
2537 AudioSessionControl_GetIconPath,
2538 AudioSessionControl_SetIconPath,
2539 AudioSessionControl_GetGroupingParam,
2540 AudioSessionControl_SetGroupingParam,
2541 AudioSessionControl_RegisterAudioSessionNotification,
2542 AudioSessionControl_UnregisterAudioSessionNotification,
2543 AudioSessionControl_GetSessionIdentifier,
2544 AudioSessionControl_GetSessionInstanceIdentifier,
2545 AudioSessionControl_GetProcessId,
2546 AudioSessionControl_IsSystemSoundsSession,
2547 AudioSessionControl_SetDuckingPreference
2550 static HRESULT WINAPI SimpleAudioVolume_QueryInterface(
2551 ISimpleAudioVolume *iface, REFIID riid, void **ppv)
2553 TRACE("(%p)->(%s, %p)\n", iface, debugstr_guid(riid), ppv);
2559 if(IsEqualIID(riid, &IID_IUnknown) ||
2560 IsEqualIID(riid, &IID_ISimpleAudioVolume))
2563 IUnknown_AddRef((IUnknown*)*ppv);
2567 WARN("Unknown interface %s\n", debugstr_guid(riid));
2568 return E_NOINTERFACE;
2571 static ULONG WINAPI SimpleAudioVolume_AddRef(ISimpleAudioVolume *iface)
2573 AudioSessionWrapper *This = impl_from_ISimpleAudioVolume(iface);
2574 return AudioSessionControl_AddRef(&This->IAudioSessionControl2_iface);
2577 static ULONG WINAPI SimpleAudioVolume_Release(ISimpleAudioVolume *iface)
2579 AudioSessionWrapper *This = impl_from_ISimpleAudioVolume(iface);
2580 return AudioSessionControl_Release(&This->IAudioSessionControl2_iface);
2583 static HRESULT WINAPI SimpleAudioVolume_SetMasterVolume(
2584 ISimpleAudioVolume *iface, float level, const GUID *context)
2586 AudioSessionWrapper *This = impl_from_ISimpleAudioVolume(iface);
2587 AudioSession *session = This->session;
2589 TRACE("(%p)->(%f, %s)\n", session, level, wine_dbgstr_guid(context));
2591 if(level < 0.f || level > 1.f)
2592 return E_INVALIDARG;
2595 FIXME("Notifications not supported yet\n");
2597 TRACE("ALSA does not support volume control\n");
2599 EnterCriticalSection(&session->lock);
2601 session->master_vol = level;
2603 LeaveCriticalSection(&session->lock);
2608 static HRESULT WINAPI SimpleAudioVolume_GetMasterVolume(
2609 ISimpleAudioVolume *iface, float *level)
2611 AudioSessionWrapper *This = impl_from_ISimpleAudioVolume(iface);
2612 AudioSession *session = This->session;
2614 TRACE("(%p)->(%p)\n", session, level);
2617 return NULL_PTR_ERR;
2619 *level = session->master_vol;
2624 static HRESULT WINAPI SimpleAudioVolume_SetMute(ISimpleAudioVolume *iface,
2625 BOOL mute, const GUID *context)
2627 AudioSessionWrapper *This = impl_from_ISimpleAudioVolume(iface);
2628 AudioSession *session = This->session;
2630 TRACE("(%p)->(%u, %p)\n", session, mute, context);
2633 FIXME("Notifications not supported yet\n");
2635 session->mute = mute;
2640 static HRESULT WINAPI SimpleAudioVolume_GetMute(ISimpleAudioVolume *iface,
2643 AudioSessionWrapper *This = impl_from_ISimpleAudioVolume(iface);
2644 AudioSession *session = This->session;
2646 TRACE("(%p)->(%p)\n", session, mute);
2649 return NULL_PTR_ERR;
2651 *mute = session->mute;
2656 static const ISimpleAudioVolumeVtbl SimpleAudioVolume_Vtbl =
2658 SimpleAudioVolume_QueryInterface,
2659 SimpleAudioVolume_AddRef,
2660 SimpleAudioVolume_Release,
2661 SimpleAudioVolume_SetMasterVolume,
2662 SimpleAudioVolume_GetMasterVolume,
2663 SimpleAudioVolume_SetMute,
2664 SimpleAudioVolume_GetMute
2667 static HRESULT WINAPI AudioStreamVolume_QueryInterface(
2668 IAudioStreamVolume *iface, REFIID riid, void **ppv)
2670 TRACE("(%p)->(%s, %p)\n", iface, debugstr_guid(riid), ppv);
2676 if(IsEqualIID(riid, &IID_IUnknown) ||
2677 IsEqualIID(riid, &IID_IAudioStreamVolume))
2680 IUnknown_AddRef((IUnknown*)*ppv);
2684 WARN("Unknown interface %s\n", debugstr_guid(riid));
2685 return E_NOINTERFACE;
2688 static ULONG WINAPI AudioStreamVolume_AddRef(IAudioStreamVolume *iface)
2690 ACImpl *This = impl_from_IAudioStreamVolume(iface);
2691 return IAudioClient_AddRef(&This->IAudioClient_iface);
2694 static ULONG WINAPI AudioStreamVolume_Release(IAudioStreamVolume *iface)
2696 ACImpl *This = impl_from_IAudioStreamVolume(iface);
2697 return IAudioClient_Release(&This->IAudioClient_iface);
2700 static HRESULT WINAPI AudioStreamVolume_GetChannelCount(
2701 IAudioStreamVolume *iface, UINT32 *out)
2703 ACImpl *This = impl_from_IAudioStreamVolume(iface);
2705 TRACE("(%p)->(%p)\n", This, out);
2710 *out = This->fmt->nChannels;
2715 static HRESULT WINAPI AudioStreamVolume_SetChannelVolume(
2716 IAudioStreamVolume *iface, UINT32 index, float level)
2718 ACImpl *This = impl_from_IAudioStreamVolume(iface);
2720 TRACE("(%p)->(%d, %f)\n", This, index, level);
2722 if(level < 0.f || level > 1.f)
2723 return E_INVALIDARG;
2725 if(index >= This->fmt->nChannels)
2726 return E_INVALIDARG;
2728 TRACE("ALSA does not support volume control\n");
2730 EnterCriticalSection(&This->lock);
2732 This->vols[index] = level;
2734 LeaveCriticalSection(&This->lock);
2739 static HRESULT WINAPI AudioStreamVolume_GetChannelVolume(
2740 IAudioStreamVolume *iface, UINT32 index, float *level)
2742 ACImpl *This = impl_from_IAudioStreamVolume(iface);
2744 TRACE("(%p)->(%d, %p)\n", This, index, level);
2749 if(index >= This->fmt->nChannels)
2750 return E_INVALIDARG;
2752 *level = This->vols[index];
2757 static HRESULT WINAPI AudioStreamVolume_SetAllVolumes(
2758 IAudioStreamVolume *iface, UINT32 count, const float *levels)
2760 ACImpl *This = impl_from_IAudioStreamVolume(iface);
2763 TRACE("(%p)->(%d, %p)\n", This, count, levels);
2768 if(count != This->fmt->nChannels)
2769 return E_INVALIDARG;
2771 TRACE("ALSA does not support volume control\n");
2773 EnterCriticalSection(&This->lock);
2775 for(i = 0; i < count; ++i)
2776 This->vols[i] = levels[i];
2778 LeaveCriticalSection(&This->lock);
2783 static HRESULT WINAPI AudioStreamVolume_GetAllVolumes(
2784 IAudioStreamVolume *iface, UINT32 count, float *levels)
2786 ACImpl *This = impl_from_IAudioStreamVolume(iface);
2789 TRACE("(%p)->(%d, %p)\n", This, count, levels);
2794 if(count != This->fmt->nChannels)
2795 return E_INVALIDARG;
2797 EnterCriticalSection(&This->lock);
2799 for(i = 0; i < count; ++i)
2800 levels[i] = This->vols[i];
2802 LeaveCriticalSection(&This->lock);
2807 static const IAudioStreamVolumeVtbl AudioStreamVolume_Vtbl =
2809 AudioStreamVolume_QueryInterface,
2810 AudioStreamVolume_AddRef,
2811 AudioStreamVolume_Release,
2812 AudioStreamVolume_GetChannelCount,
2813 AudioStreamVolume_SetChannelVolume,
2814 AudioStreamVolume_GetChannelVolume,
2815 AudioStreamVolume_SetAllVolumes,
2816 AudioStreamVolume_GetAllVolumes
2819 static HRESULT WINAPI ChannelAudioVolume_QueryInterface(
2820 IChannelAudioVolume *iface, REFIID riid, void **ppv)
2822 TRACE("(%p)->(%s, %p)\n", iface, debugstr_guid(riid), ppv);
2828 if(IsEqualIID(riid, &IID_IUnknown) ||
2829 IsEqualIID(riid, &IID_IChannelAudioVolume))
2832 IUnknown_AddRef((IUnknown*)*ppv);
2836 WARN("Unknown interface %s\n", debugstr_guid(riid));
2837 return E_NOINTERFACE;
2840 static ULONG WINAPI ChannelAudioVolume_AddRef(IChannelAudioVolume *iface)
2842 AudioSessionWrapper *This = impl_from_IChannelAudioVolume(iface);
2843 return AudioSessionControl_AddRef(&This->IAudioSessionControl2_iface);
2846 static ULONG WINAPI ChannelAudioVolume_Release(IChannelAudioVolume *iface)
2848 AudioSessionWrapper *This = impl_from_IChannelAudioVolume(iface);
2849 return AudioSessionControl_Release(&This->IAudioSessionControl2_iface);
2852 static HRESULT WINAPI ChannelAudioVolume_GetChannelCount(
2853 IChannelAudioVolume *iface, UINT32 *out)
2855 AudioSessionWrapper *This = impl_from_IChannelAudioVolume(iface);
2856 AudioSession *session = This->session;
2858 TRACE("(%p)->(%p)\n", session, out);
2861 return NULL_PTR_ERR;
2863 *out = session->channel_count;
2868 static HRESULT WINAPI ChannelAudioVolume_SetChannelVolume(
2869 IChannelAudioVolume *iface, UINT32 index, float level,
2870 const GUID *context)
2872 AudioSessionWrapper *This = impl_from_IChannelAudioVolume(iface);
2873 AudioSession *session = This->session;
2875 TRACE("(%p)->(%d, %f, %s)\n", session, index, level,
2876 wine_dbgstr_guid(context));
2878 if(level < 0.f || level > 1.f)
2879 return E_INVALIDARG;
2881 if(index >= session->channel_count)
2882 return E_INVALIDARG;
2885 FIXME("Notifications not supported yet\n");
2887 TRACE("ALSA does not support volume control\n");
2889 EnterCriticalSection(&session->lock);
2891 session->channel_vols[index] = level;
2893 LeaveCriticalSection(&session->lock);
2898 static HRESULT WINAPI ChannelAudioVolume_GetChannelVolume(
2899 IChannelAudioVolume *iface, UINT32 index, float *level)
2901 AudioSessionWrapper *This = impl_from_IChannelAudioVolume(iface);
2902 AudioSession *session = This->session;
2904 TRACE("(%p)->(%d, %p)\n", session, index, level);
2907 return NULL_PTR_ERR;
2909 if(index >= session->channel_count)
2910 return E_INVALIDARG;
2912 *level = session->channel_vols[index];
2917 static HRESULT WINAPI ChannelAudioVolume_SetAllVolumes(
2918 IChannelAudioVolume *iface, UINT32 count, const float *levels,
2919 const GUID *context)
2921 AudioSessionWrapper *This = impl_from_IChannelAudioVolume(iface);
2922 AudioSession *session = This->session;
2925 TRACE("(%p)->(%d, %p, %s)\n", session, count, levels,
2926 wine_dbgstr_guid(context));
2929 return NULL_PTR_ERR;
2931 if(count != session->channel_count)
2932 return E_INVALIDARG;
2935 FIXME("Notifications not supported yet\n");
2937 TRACE("ALSA does not support volume control\n");
2939 EnterCriticalSection(&session->lock);
2941 for(i = 0; i < count; ++i)
2942 session->channel_vols[i] = levels[i];
2944 LeaveCriticalSection(&session->lock);
2949 static HRESULT WINAPI ChannelAudioVolume_GetAllVolumes(
2950 IChannelAudioVolume *iface, UINT32 count, float *levels)
2952 AudioSessionWrapper *This = impl_from_IChannelAudioVolume(iface);
2953 AudioSession *session = This->session;
2956 TRACE("(%p)->(%d, %p)\n", session, count, levels);
2959 return NULL_PTR_ERR;
2961 if(count != session->channel_count)
2962 return E_INVALIDARG;
2964 for(i = 0; i < count; ++i)
2965 levels[i] = session->channel_vols[i];
2970 static const IChannelAudioVolumeVtbl ChannelAudioVolume_Vtbl =
2972 ChannelAudioVolume_QueryInterface,
2973 ChannelAudioVolume_AddRef,
2974 ChannelAudioVolume_Release,
2975 ChannelAudioVolume_GetChannelCount,
2976 ChannelAudioVolume_SetChannelVolume,
2977 ChannelAudioVolume_GetChannelVolume,
2978 ChannelAudioVolume_SetAllVolumes,
2979 ChannelAudioVolume_GetAllVolumes
2982 static HRESULT WINAPI AudioSessionManager_QueryInterface(IAudioSessionManager2 *iface,
2983 REFIID riid, void **ppv)
2985 TRACE("(%p)->(%s, %p)\n", iface, debugstr_guid(riid), ppv);
2991 if(IsEqualIID(riid, &IID_IUnknown) ||
2992 IsEqualIID(riid, &IID_IAudioSessionManager) ||
2993 IsEqualIID(riid, &IID_IAudioSessionManager2))
2996 IUnknown_AddRef((IUnknown*)*ppv);
3000 WARN("Unknown interface %s\n", debugstr_guid(riid));
3001 return E_NOINTERFACE;
3004 static ULONG WINAPI AudioSessionManager_AddRef(IAudioSessionManager2 *iface)
3006 SessionMgr *This = impl_from_IAudioSessionManager2(iface);
3008 ref = InterlockedIncrement(&This->ref);
3009 TRACE("(%p) Refcount now %u\n", This, ref);
3013 static ULONG WINAPI AudioSessionManager_Release(IAudioSessionManager2 *iface)
3015 SessionMgr *This = impl_from_IAudioSessionManager2(iface);
3017 ref = InterlockedDecrement(&This->ref);
3018 TRACE("(%p) Refcount now %u\n", This, ref);
3020 HeapFree(GetProcessHeap(), 0, This);
3024 static HRESULT WINAPI AudioSessionManager_GetAudioSessionControl(
3025 IAudioSessionManager2 *iface, const GUID *session_guid, DWORD flags,
3026 IAudioSessionControl **out)
3028 SessionMgr *This = impl_from_IAudioSessionManager2(iface);
3029 AudioSession *session;
3030 AudioSessionWrapper *wrapper;
3033 TRACE("(%p)->(%s, %x, %p)\n", This, debugstr_guid(session_guid),
3036 hr = get_audio_session(session_guid, This->device, 0, &session);
3040 wrapper = AudioSessionWrapper_Create(NULL);
3042 return E_OUTOFMEMORY;
3044 wrapper->session = session;
3046 *out = (IAudioSessionControl*)&wrapper->IAudioSessionControl2_iface;
3051 static HRESULT WINAPI AudioSessionManager_GetSimpleAudioVolume(
3052 IAudioSessionManager2 *iface, const GUID *session_guid, DWORD flags,
3053 ISimpleAudioVolume **out)
3055 SessionMgr *This = impl_from_IAudioSessionManager2(iface);
3056 AudioSession *session;
3057 AudioSessionWrapper *wrapper;
3060 TRACE("(%p)->(%s, %x, %p)\n", This, debugstr_guid(session_guid),
3063 hr = get_audio_session(session_guid, This->device, 0, &session);
3067 wrapper = AudioSessionWrapper_Create(NULL);
3069 return E_OUTOFMEMORY;
3071 wrapper->session = session;
3073 *out = &wrapper->ISimpleAudioVolume_iface;
3078 static HRESULT WINAPI AudioSessionManager_GetSessionEnumerator(
3079 IAudioSessionManager2 *iface, IAudioSessionEnumerator **out)
3081 SessionMgr *This = impl_from_IAudioSessionManager2(iface);
3082 FIXME("(%p)->(%p) - stub\n", This, out);
3086 static HRESULT WINAPI AudioSessionManager_RegisterSessionNotification(
3087 IAudioSessionManager2 *iface, IAudioSessionNotification *notification)
3089 SessionMgr *This = impl_from_IAudioSessionManager2(iface);
3090 FIXME("(%p)->(%p) - stub\n", This, notification);
3094 static HRESULT WINAPI AudioSessionManager_UnregisterSessionNotification(
3095 IAudioSessionManager2 *iface, IAudioSessionNotification *notification)
3097 SessionMgr *This = impl_from_IAudioSessionManager2(iface);
3098 FIXME("(%p)->(%p) - stub\n", This, notification);
3102 static HRESULT WINAPI AudioSessionManager_RegisterDuckNotification(
3103 IAudioSessionManager2 *iface, const WCHAR *session_id,
3104 IAudioVolumeDuckNotification *notification)
3106 SessionMgr *This = impl_from_IAudioSessionManager2(iface);
3107 FIXME("(%p)->(%p) - stub\n", This, notification);
3111 static HRESULT WINAPI AudioSessionManager_UnregisterDuckNotification(
3112 IAudioSessionManager2 *iface,
3113 IAudioVolumeDuckNotification *notification)
3115 SessionMgr *This = impl_from_IAudioSessionManager2(iface);
3116 FIXME("(%p)->(%p) - stub\n", This, notification);
3120 static const IAudioSessionManager2Vtbl AudioSessionManager2_Vtbl =
3122 AudioSessionManager_QueryInterface,
3123 AudioSessionManager_AddRef,
3124 AudioSessionManager_Release,
3125 AudioSessionManager_GetAudioSessionControl,
3126 AudioSessionManager_GetSimpleAudioVolume,
3127 AudioSessionManager_GetSessionEnumerator,
3128 AudioSessionManager_RegisterSessionNotification,
3129 AudioSessionManager_UnregisterSessionNotification,
3130 AudioSessionManager_RegisterDuckNotification,
3131 AudioSessionManager_UnregisterDuckNotification
3134 HRESULT WINAPI AUDDRV_GetAudioSessionManager(IMMDevice *device,
3135 IAudioSessionManager2 **out)
3139 This = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, sizeof(SessionMgr));
3141 return E_OUTOFMEMORY;
3143 This->IAudioSessionManager2_iface.lpVtbl = &AudioSessionManager2_Vtbl;
3144 This->device = device;
3147 *out = &This->IAudioSessionManager2_iface;