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 {
68 typedef struct _AudioSessionWrapper {
69 IAudioSessionControl2 IAudioSessionControl2_iface;
74 AudioSession *session;
75 } AudioSessionWrapper;
78 IAudioClient IAudioClient_iface;
79 IAudioRenderClient IAudioRenderClient_iface;
80 IAudioCaptureClient IAudioCaptureClient_iface;
81 ISimpleAudioVolume ISimpleAudioVolume_iface;
82 IAudioClock IAudioClock_iface;
83 IAudioClock2 IAudioClock2_iface;
87 snd_pcm_t *pcm_handle;
88 snd_pcm_uframes_t period_alsa, bufsize_alsa;
89 snd_pcm_hw_params_t *hw_params; /* does not hold state between calls */
96 AUDCLNT_SHAREMODE share;
99 BOOL initted, started;
100 UINT64 written_frames, held_frames, tmp_buffer_frames;
101 UINT32 bufsize_frames, period_us;
102 UINT32 lcl_offs_frames; /* offs into local_buffer where valid data starts */
105 BYTE *local_buffer, *tmp_buffer;
108 CRITICAL_SECTION lock;
110 AudioSession *session;
111 AudioSessionWrapper *session_wrapper;
118 LOCKED_NORMAL, /* public buffer piece is from local_buffer */
119 LOCKED_WRAPPED /* public buffer piece is wrapped around, in tmp_buffer */
122 static HANDLE g_timer_q;
124 static CRITICAL_SECTION g_sessions_lock;
125 static struct list g_sessions = LIST_INIT(g_sessions);
127 static const WCHAR defaultW[] = {'d','e','f','a','u','l','t',0};
129 static const IAudioClientVtbl AudioClient_Vtbl;
130 static const IAudioRenderClientVtbl AudioRenderClient_Vtbl;
131 static const IAudioCaptureClientVtbl AudioCaptureClient_Vtbl;
132 static const IAudioSessionControl2Vtbl AudioSessionControl2_Vtbl;
133 static const ISimpleAudioVolumeVtbl SimpleAudioVolume_Vtbl;
134 static const IAudioClockVtbl AudioClock_Vtbl;
135 static const IAudioClock2Vtbl AudioClock2_Vtbl;
137 int wine_snd_pcm_recover(snd_pcm_t *pcm, int err, int silent);
138 static AudioSessionWrapper *AudioSessionWrapper_Create(ACImpl *client);
140 static inline ACImpl *impl_from_IAudioClient(IAudioClient *iface)
142 return CONTAINING_RECORD(iface, ACImpl, IAudioClient_iface);
145 static inline ACImpl *impl_from_IAudioRenderClient(IAudioRenderClient *iface)
147 return CONTAINING_RECORD(iface, ACImpl, IAudioRenderClient_iface);
150 static inline ACImpl *impl_from_IAudioCaptureClient(IAudioCaptureClient *iface)
152 return CONTAINING_RECORD(iface, ACImpl, IAudioCaptureClient_iface);
155 static inline AudioSessionWrapper *impl_from_IAudioSessionControl2(IAudioSessionControl2 *iface)
157 return CONTAINING_RECORD(iface, AudioSessionWrapper, IAudioSessionControl2_iface);
160 static inline ACImpl *impl_from_ISimpleAudioVolume(ISimpleAudioVolume *iface)
162 return CONTAINING_RECORD(iface, ACImpl, ISimpleAudioVolume_iface);
165 static inline ACImpl *impl_from_IAudioClock(IAudioClock *iface)
167 return CONTAINING_RECORD(iface, ACImpl, IAudioClock_iface);
170 static inline ACImpl *impl_from_IAudioClock2(IAudioClock2 *iface)
172 return CONTAINING_RECORD(iface, ACImpl, IAudioClock2_iface);
175 BOOL WINAPI DllMain(HINSTANCE dll, DWORD reason, void *reserved)
177 if(reason == DLL_PROCESS_ATTACH){
178 g_timer_q = CreateTimerQueue();
182 InitializeCriticalSection(&g_sessions_lock);
188 HRESULT WINAPI AUDDRV_GetEndpointIDs(EDataFlow flow, WCHAR ***ids, void ***keys,
189 UINT *num, UINT *def_index)
191 TRACE("%d %p %p %p\n", flow, ids, num, def_index);
196 *ids = HeapAlloc(GetProcessHeap(), 0, sizeof(WCHAR *));
198 return E_OUTOFMEMORY;
200 (*ids)[0] = HeapAlloc(GetProcessHeap(), 0, sizeof(defaultW));
202 HeapFree(GetProcessHeap(), 0, *ids);
203 return E_OUTOFMEMORY;
206 lstrcpyW((*ids)[0], defaultW);
208 *keys = HeapAlloc(GetProcessHeap(), 0, sizeof(void *));
214 HRESULT WINAPI AUDDRV_GetAudioEndpoint(void *key, IMMDevice *dev,
215 EDataFlow dataflow, IAudioClient **out)
219 snd_pcm_stream_t stream;
221 TRACE("%p %p %d %p\n", key, dev, dataflow, out);
223 This = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, sizeof(ACImpl));
225 return E_OUTOFMEMORY;
227 This->IAudioClient_iface.lpVtbl = &AudioClient_Vtbl;
228 This->IAudioRenderClient_iface.lpVtbl = &AudioRenderClient_Vtbl;
229 This->IAudioCaptureClient_iface.lpVtbl = &AudioCaptureClient_Vtbl;
230 This->ISimpleAudioVolume_iface.lpVtbl = &SimpleAudioVolume_Vtbl;
231 This->IAudioClock_iface.lpVtbl = &AudioClock_Vtbl;
232 This->IAudioClock2_iface.lpVtbl = &AudioClock2_Vtbl;
234 if(dataflow == eRender)
235 stream = SND_PCM_STREAM_PLAYBACK;
236 else if(dataflow == eCapture)
237 stream = SND_PCM_STREAM_CAPTURE;
239 HeapFree(GetProcessHeap(), 0, This);
243 This->dataflow = dataflow;
244 if((err = snd_pcm_open(&This->pcm_handle, "default", stream,
245 SND_PCM_NONBLOCK)) < 0){
246 HeapFree(GetProcessHeap(), 0, This);
247 WARN("Unable to open PCM \"default\": %d (%s)\n", err,
252 This->hw_params = HeapAlloc(GetProcessHeap(), 0,
253 snd_pcm_hw_params_sizeof());
254 if(!This->hw_params){
255 HeapFree(GetProcessHeap(), 0, This);
256 snd_pcm_close(This->pcm_handle);
257 return E_OUTOFMEMORY;
260 InitializeCriticalSection(&This->lock);
263 IMMDevice_AddRef(This->parent);
265 *out = &This->IAudioClient_iface;
266 IAudioClient_AddRef(&This->IAudioClient_iface);
271 static HRESULT WINAPI AudioClient_QueryInterface(IAudioClient *iface,
272 REFIID riid, void **ppv)
274 TRACE("(%p)->(%s, %p)\n", iface, debugstr_guid(riid), ppv);
279 if(IsEqualIID(riid, &IID_IUnknown) || IsEqualIID(riid, &IID_IAudioClient))
282 IUnknown_AddRef((IUnknown*)*ppv);
285 WARN("Unknown interface %s\n", debugstr_guid(riid));
286 return E_NOINTERFACE;
289 static ULONG WINAPI AudioClient_AddRef(IAudioClient *iface)
291 ACImpl *This = impl_from_IAudioClient(iface);
293 ref = InterlockedIncrement(&This->ref);
294 TRACE("(%p) Refcount now %u\n", This, ref);
298 static ULONG WINAPI AudioClient_Release(IAudioClient *iface)
300 ACImpl *This = impl_from_IAudioClient(iface);
302 ref = InterlockedDecrement(&This->ref);
303 TRACE("(%p) Refcount now %u\n", This, ref);
305 IAudioClient_Stop(iface);
306 IMMDevice_Release(This->parent);
307 DeleteCriticalSection(&This->lock);
308 snd_pcm_drop(This->pcm_handle);
309 snd_pcm_close(This->pcm_handle);
311 EnterCriticalSection(&g_sessions_lock);
312 list_remove(&This->entry);
313 if(list_empty(&This->session->clients)){
314 list_remove(&This->session->entry);
315 HeapFree(GetProcessHeap(), 0, This->session);
317 LeaveCriticalSection(&g_sessions_lock);
319 HeapFree(GetProcessHeap(), 0, This->local_buffer);
320 HeapFree(GetProcessHeap(), 0, This->hw_params);
321 CoTaskMemFree(This->fmt);
322 HeapFree(GetProcessHeap(), 0, This);
327 static void dump_fmt(const WAVEFORMATEX *fmt)
329 TRACE("wFormatTag: 0x%x (", fmt->wFormatTag);
330 switch(fmt->wFormatTag){
331 case WAVE_FORMAT_PCM:
332 TRACE("WAVE_FORMAT_PCM");
334 case WAVE_FORMAT_IEEE_FLOAT:
335 TRACE("WAVE_FORMAT_IEEE_FLOAT");
337 case WAVE_FORMAT_EXTENSIBLE:
338 TRACE("WAVE_FORMAT_EXTENSIBLE");
346 TRACE("nChannels: %u\n", fmt->nChannels);
347 TRACE("nSamplesPerSec: %u\n", fmt->nSamplesPerSec);
348 TRACE("nAvgBytesPerSec: %u\n", fmt->nAvgBytesPerSec);
349 TRACE("nBlockAlign: %u\n", fmt->nBlockAlign);
350 TRACE("wBitsPerSample: %u\n", fmt->wBitsPerSample);
351 TRACE("cbSize: %u\n", fmt->cbSize);
353 if(fmt->wFormatTag == WAVE_FORMAT_EXTENSIBLE){
354 WAVEFORMATEXTENSIBLE *fmtex = (void*)fmt;
355 TRACE("dwChannelMask: %08x\n", fmtex->dwChannelMask);
356 TRACE("Samples: %04x\n", fmtex->Samples.wReserved);
357 TRACE("SubFormat: %s\n", wine_dbgstr_guid(&fmtex->SubFormat));
361 static WAVEFORMATEX *clone_format(const WAVEFORMATEX *fmt)
366 if(fmt->wFormatTag == WAVE_FORMAT_EXTENSIBLE)
367 size = sizeof(WAVEFORMATEXTENSIBLE);
369 size = sizeof(WAVEFORMATEX);
371 ret = CoTaskMemAlloc(size);
375 memcpy(ret, fmt, size);
377 ret->cbSize = size - sizeof(WAVEFORMATEX);
382 static AudioSession *create_session(const GUID *guid, EDataFlow flow)
386 ret = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, sizeof(AudioSession));
390 memcpy(&ret->guid, guid, sizeof(GUID));
392 ret->dataflow = flow;
394 list_init(&ret->clients);
396 list_add_head(&g_sessions, &ret->entry);
401 static HRESULT WINAPI AudioClient_Initialize(IAudioClient *iface,
402 AUDCLNT_SHAREMODE mode, DWORD flags, REFERENCE_TIME duration,
403 REFERENCE_TIME period, const WAVEFORMATEX *fmt,
404 const GUID *sessionguid)
406 ACImpl *This = impl_from_IAudioClient(iface);
407 snd_pcm_sw_params_t *sw_params = NULL;
408 snd_pcm_format_t format;
409 snd_pcm_uframes_t boundary;
410 const WAVEFORMATEXTENSIBLE *fmtex = (const WAVEFORMATEXTENSIBLE *)fmt;
411 unsigned int time_us, rate;
415 TRACE("(%p)->(%x, %x, %s, %s, %p, %s)\n", This, mode, flags,
416 wine_dbgstr_longlong(duration), wine_dbgstr_longlong(period), fmt, debugstr_guid(sessionguid));
421 if(mode != AUDCLNT_SHAREMODE_SHARED && mode != AUDCLNT_SHAREMODE_EXCLUSIVE)
422 return AUDCLNT_E_NOT_INITIALIZED;
424 if(flags & ~(AUDCLNT_STREAMFLAGS_CROSSPROCESS |
425 AUDCLNT_STREAMFLAGS_LOOPBACK |
426 AUDCLNT_STREAMFLAGS_EVENTCALLBACK |
427 AUDCLNT_STREAMFLAGS_NOPERSIST |
428 AUDCLNT_STREAMFLAGS_RATEADJUST |
429 AUDCLNT_SESSIONFLAGS_EXPIREWHENUNOWNED |
430 AUDCLNT_SESSIONFLAGS_DISPLAY_HIDE |
431 AUDCLNT_SESSIONFLAGS_DISPLAY_HIDEWHENEXPIRED)){
432 TRACE("Unknown flags: %08x\n", flags);
436 EnterCriticalSection(&This->lock);
439 LeaveCriticalSection(&This->lock);
440 return AUDCLNT_E_ALREADY_INITIALIZED;
445 if((err = snd_pcm_hw_params_any(This->pcm_handle, This->hw_params)) < 0){
446 WARN("Unable to get hw_params: %d (%s)\n", err, snd_strerror(err));
451 if((err = snd_pcm_hw_params_set_access(This->pcm_handle, This->hw_params,
452 SND_PCM_ACCESS_RW_INTERLEAVED)) < 0){
453 WARN("Unable to set access: %d (%s)\n", err, snd_strerror(err));
458 if(fmt->wFormatTag == WAVE_FORMAT_PCM ||
459 (fmt->wFormatTag == WAVE_FORMAT_EXTENSIBLE &&
460 IsEqualGUID(&fmtex->SubFormat, &KSDATAFORMAT_SUBTYPE_PCM))){
461 if(fmt->wBitsPerSample == 8)
462 format = SND_PCM_FORMAT_U8;
463 else if(fmt->wBitsPerSample == 16)
464 format = SND_PCM_FORMAT_S16_LE;
465 else if(fmt->wBitsPerSample == 24)
466 format = SND_PCM_FORMAT_S24_3LE;
467 else if(fmt->wBitsPerSample == 32)
468 format = SND_PCM_FORMAT_S32_LE;
470 WARN("Unsupported bit depth: %u\n", fmt->wBitsPerSample);
471 hr = AUDCLNT_E_UNSUPPORTED_FORMAT;
474 }else if(fmt->wFormatTag == WAVE_FORMAT_IEEE_FLOAT ||
475 (fmt->wFormatTag == WAVE_FORMAT_EXTENSIBLE &&
476 IsEqualGUID(&fmtex->SubFormat, &KSDATAFORMAT_SUBTYPE_IEEE_FLOAT))){
477 if(fmt->wBitsPerSample == 32)
478 format = SND_PCM_FORMAT_FLOAT_LE;
479 else if(fmt->wBitsPerSample == 64)
480 format = SND_PCM_FORMAT_FLOAT64_LE;
482 WARN("Unsupported float size: %u\n", fmt->wBitsPerSample);
483 hr = AUDCLNT_E_UNSUPPORTED_FORMAT;
487 WARN("Unknown wave format: %04x\n", fmt->wFormatTag);
488 hr = AUDCLNT_E_UNSUPPORTED_FORMAT;
492 if((err = snd_pcm_hw_params_set_format(This->pcm_handle, This->hw_params,
494 WARN("Unable to set ALSA format to %u: %d (%s)\n", format, err,
500 rate = fmt->nSamplesPerSec;
501 if((err = snd_pcm_hw_params_set_rate_near(This->pcm_handle, This->hw_params,
503 WARN("Unable to set rate to %u: %d (%s)\n", rate, err,
509 if((err = snd_pcm_hw_params_set_channels(This->pcm_handle, This->hw_params,
510 fmt->nChannels)) < 0){
511 WARN("Unable to set channels to %u: %d (%s)\n", fmt->nChannels, err,
517 time_us = MinimumPeriod / 10;
518 if((err = snd_pcm_hw_params_set_period_time_near(This->pcm_handle,
519 This->hw_params, &time_us, NULL)) < 0){
520 WARN("Unable to set max period time to %u: %d (%s)\n", time_us,
521 err, snd_strerror(err));
526 if((err = snd_pcm_hw_params(This->pcm_handle, This->hw_params)) < 0){
527 WARN("Unable to set hw params: %d (%s)\n", err, snd_strerror(err));
532 sw_params = HeapAlloc(GetProcessHeap(), 0, snd_pcm_sw_params_sizeof());
538 if((err = snd_pcm_sw_params_current(This->pcm_handle, sw_params)) < 0){
539 WARN("Unable to get sw_params: %d (%s)\n", err, snd_strerror(err));
544 This->bufsize_frames = ceil((duration / 10000000.) * fmt->nSamplesPerSec);
545 This->local_buffer = HeapAlloc(GetProcessHeap(), 0,
546 This->bufsize_frames * fmt->nBlockAlign);
547 if(!This->local_buffer){
551 if (fmt->wBitsPerSample == 8)
552 memset(This->local_buffer, 128, This->bufsize_frames * fmt->nBlockAlign);
554 memset(This->local_buffer, 0, This->bufsize_frames * fmt->nBlockAlign);
556 if((err = snd_pcm_sw_params_get_boundary(sw_params, &boundary)) < 0){
557 WARN("Unable to get boundary: %d (%s)\n", err, snd_strerror(err));
562 if((err = snd_pcm_sw_params_set_start_threshold(This->pcm_handle,
563 sw_params, boundary)) < 0){
564 WARN("Unable to set start threshold to %lx: %d (%s)\n", boundary, err,
570 if((err = snd_pcm_sw_params_set_stop_threshold(This->pcm_handle,
571 sw_params, boundary)) < 0){
572 WARN("Unable to set stop threshold to %lx: %d (%s)\n", boundary, err,
578 if((err = snd_pcm_sw_params_set_avail_min(This->pcm_handle,
580 WARN("Unable to set avail min to 0: %d (%s)\n", err, snd_strerror(err));
585 if((err = snd_pcm_sw_params(This->pcm_handle, sw_params)) < 0){
586 WARN("Unable to set sw params: %d (%s)\n", err, snd_strerror(err));
591 if((err = snd_pcm_prepare(This->pcm_handle)) < 0){
592 WARN("Unable to prepare device: %d (%s)\n", err, snd_strerror(err));
597 if((err = snd_pcm_hw_params_get_buffer_size(This->hw_params,
598 &This->bufsize_alsa)) < 0){
599 WARN("Unable to get buffer size: %d (%s)\n", err, snd_strerror(err));
604 if((err = snd_pcm_hw_params_get_period_size(This->hw_params,
605 &This->period_alsa, NULL)) < 0){
606 WARN("Unable to get period size: %d (%s)\n", err, snd_strerror(err));
611 if((err = snd_pcm_hw_params_get_period_time(This->hw_params,
612 &This->period_us, NULL)) < 0){
613 WARN("Unable to get period time: %d (%s)\n", err, snd_strerror(err));
618 This->fmt = clone_format(fmt);
627 EnterCriticalSection(&g_sessions_lock);
629 if(!sessionguid || IsEqualGUID(sessionguid, &GUID_NULL)){
630 This->session = create_session(&GUID_NULL, This->dataflow);
632 LeaveCriticalSection(&g_sessions_lock);
637 AudioSession *session;
639 LIST_FOR_EACH_ENTRY(session, &g_sessions, AudioSession, entry)
640 if(IsEqualGUID(sessionguid, &session->guid) &&
641 This->dataflow == session->dataflow)
642 This->session = session;
645 This->session = create_session(sessionguid, This->dataflow);
647 LeaveCriticalSection(&g_sessions_lock);
654 list_add_tail(&This->session->clients, &This->entry);
656 LeaveCriticalSection(&g_sessions_lock);
658 This->initted = TRUE;
661 HeapFree(GetProcessHeap(), 0, sw_params);
663 if(This->local_buffer){
664 HeapFree(GetProcessHeap(), 0, This->local_buffer);
665 This->local_buffer = NULL;
669 LeaveCriticalSection(&This->lock);
674 static HRESULT WINAPI AudioClient_GetBufferSize(IAudioClient *iface,
677 ACImpl *This = impl_from_IAudioClient(iface);
679 TRACE("(%p)->(%p)\n", This, out);
684 EnterCriticalSection(&This->lock);
687 LeaveCriticalSection(&This->lock);
688 return AUDCLNT_E_NOT_INITIALIZED;
691 *out = This->bufsize_frames;
693 LeaveCriticalSection(&This->lock);
698 static HRESULT WINAPI AudioClient_GetStreamLatency(IAudioClient *iface,
699 REFERENCE_TIME *latency)
701 ACImpl *This = impl_from_IAudioClient(iface);
703 TRACE("(%p)->(%p)\n", This, latency);
708 EnterCriticalSection(&This->lock);
711 LeaveCriticalSection(&This->lock);
712 return AUDCLNT_E_NOT_INITIALIZED;
715 LeaveCriticalSection(&This->lock);
722 static HRESULT WINAPI AudioClient_GetCurrentPadding(IAudioClient *iface,
725 ACImpl *This = impl_from_IAudioClient(iface);
727 TRACE("(%p)->(%p)\n", This, out);
732 EnterCriticalSection(&This->lock);
735 LeaveCriticalSection(&This->lock);
736 return AUDCLNT_E_NOT_INITIALIZED;
739 if(This->dataflow == eRender){
740 snd_pcm_sframes_t avail_frames;
742 avail_frames = snd_pcm_avail_update(This->pcm_handle);
744 if(This->bufsize_alsa < avail_frames){
745 WARN("Xrun detected\n");
746 *out = This->held_frames;
748 *out = This->bufsize_alsa - avail_frames + This->held_frames;
749 }else if(This->dataflow == eCapture){
750 *out = This->held_frames;
752 LeaveCriticalSection(&This->lock);
756 LeaveCriticalSection(&This->lock);
761 static DWORD get_channel_mask(unsigned int channels)
767 return SPEAKER_FRONT_CENTER;
769 return SPEAKER_FRONT_LEFT | SPEAKER_FRONT_RIGHT;
771 return SPEAKER_FRONT_LEFT | SPEAKER_FRONT_RIGHT |
772 SPEAKER_LOW_FREQUENCY;
774 return SPEAKER_FRONT_LEFT | SPEAKER_FRONT_RIGHT | SPEAKER_BACK_LEFT |
777 return SPEAKER_FRONT_LEFT | SPEAKER_FRONT_RIGHT | SPEAKER_BACK_LEFT |
778 SPEAKER_BACK_RIGHT | SPEAKER_LOW_FREQUENCY;
780 return SPEAKER_FRONT_LEFT | SPEAKER_FRONT_RIGHT | SPEAKER_BACK_LEFT |
781 SPEAKER_BACK_RIGHT | SPEAKER_LOW_FREQUENCY | SPEAKER_FRONT_CENTER;
783 return SPEAKER_FRONT_LEFT | SPEAKER_FRONT_RIGHT | SPEAKER_BACK_LEFT |
784 SPEAKER_BACK_RIGHT | SPEAKER_LOW_FREQUENCY | SPEAKER_FRONT_CENTER |
787 FIXME("Unknown speaker configuration: %u\n", channels);
791 static HRESULT WINAPI AudioClient_IsFormatSupported(IAudioClient *iface,
792 AUDCLNT_SHAREMODE mode, const WAVEFORMATEX *fmt,
795 ACImpl *This = impl_from_IAudioClient(iface);
796 snd_pcm_format_mask_t *formats = NULL;
798 WAVEFORMATEX *closest = NULL;
799 const WAVEFORMATEXTENSIBLE *fmtex = (const WAVEFORMATEXTENSIBLE *)fmt;
800 unsigned int max = 0, min = 0;
803 TRACE("(%p)->(%x, %p, %p)\n", This, mode, fmt, out);
805 if(!fmt || (mode == AUDCLNT_SHAREMODE_SHARED && !out))
808 if(mode != AUDCLNT_SHAREMODE_SHARED && mode != AUDCLNT_SHAREMODE_EXCLUSIVE)
811 if(fmt->wFormatTag == WAVE_FORMAT_EXTENSIBLE &&
812 fmt->cbSize < sizeof(WAVEFORMATEXTENSIBLE) - sizeof(WAVEFORMATEX))
817 EnterCriticalSection(&This->lock);
819 if((err = snd_pcm_hw_params_any(This->pcm_handle, This->hw_params)) < 0){
824 formats = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY,
825 snd_pcm_format_mask_sizeof());
831 snd_pcm_hw_params_get_format_mask(This->hw_params, formats);
833 if(fmt->wFormatTag == WAVE_FORMAT_PCM ||
834 (fmt->wFormatTag == WAVE_FORMAT_EXTENSIBLE &&
835 IsEqualGUID(&fmtex->SubFormat, &KSDATAFORMAT_SUBTYPE_PCM))){
836 switch(fmt->wBitsPerSample){
838 if(!snd_pcm_format_mask_test(formats, SND_PCM_FORMAT_U8)){
839 hr = AUDCLNT_E_UNSUPPORTED_FORMAT;
844 if(!snd_pcm_format_mask_test(formats, SND_PCM_FORMAT_S16_LE)){
845 hr = AUDCLNT_E_UNSUPPORTED_FORMAT;
850 if(!snd_pcm_format_mask_test(formats, SND_PCM_FORMAT_S24_3LE)){
851 hr = AUDCLNT_E_UNSUPPORTED_FORMAT;
856 if(!snd_pcm_format_mask_test(formats, SND_PCM_FORMAT_S32_LE)){
857 hr = AUDCLNT_E_UNSUPPORTED_FORMAT;
862 hr = AUDCLNT_E_UNSUPPORTED_FORMAT;
865 }else if(fmt->wFormatTag == WAVE_FORMAT_IEEE_FLOAT ||
866 (fmt->wFormatTag == WAVE_FORMAT_EXTENSIBLE &&
867 IsEqualGUID(&fmtex->SubFormat, &KSDATAFORMAT_SUBTYPE_IEEE_FLOAT))){
868 switch(fmt->wBitsPerSample){
870 if(!snd_pcm_format_mask_test(formats, SND_PCM_FORMAT_FLOAT_LE)){
871 hr = AUDCLNT_E_UNSUPPORTED_FORMAT;
876 if(!snd_pcm_format_mask_test(formats, SND_PCM_FORMAT_FLOAT64_LE)){
877 hr = AUDCLNT_E_UNSUPPORTED_FORMAT;
882 hr = AUDCLNT_E_UNSUPPORTED_FORMAT;
886 hr = AUDCLNT_E_UNSUPPORTED_FORMAT;
890 closest = clone_format(fmt);
896 if((err = snd_pcm_hw_params_get_rate_min(This->hw_params, &min, NULL)) < 0){
898 WARN("Unable to get min rate: %d (%s)\n", err, snd_strerror(err));
902 if((err = snd_pcm_hw_params_get_rate_max(This->hw_params, &max, NULL)) < 0){
904 WARN("Unable to get max rate: %d (%s)\n", err, snd_strerror(err));
908 if(fmt->nSamplesPerSec < min || fmt->nSamplesPerSec > max ||
909 (fmt->nSamplesPerSec != 48000 &&
910 fmt->nSamplesPerSec != 44100 &&
911 fmt->nSamplesPerSec != 22050 &&
912 fmt->nSamplesPerSec != 11025 &&
913 fmt->nSamplesPerSec != 8000)){
914 hr = AUDCLNT_E_UNSUPPORTED_FORMAT;
918 if((err = snd_pcm_hw_params_get_channels_min(This->hw_params, &min)) < 0){
920 WARN("Unable to get min channels: %d (%s)\n", err, snd_strerror(err));
924 if((err = snd_pcm_hw_params_get_channels_max(This->hw_params, &max)) < 0){
926 WARN("Unable to get max channels: %d (%s)\n", err, snd_strerror(err));
931 if(fmt->nChannels > max){
933 closest->nChannels = max;
934 }else if(fmt->nChannels < min){
936 closest->nChannels = min;
939 if(closest->wFormatTag == WAVE_FORMAT_EXTENSIBLE){
940 DWORD mask = get_channel_mask(closest->nChannels);
942 ((WAVEFORMATEXTENSIBLE*)closest)->dwChannelMask = mask;
944 if(fmt->wFormatTag == WAVE_FORMAT_EXTENSIBLE &&
945 fmtex->dwChannelMask != mask)
950 LeaveCriticalSection(&This->lock);
951 HeapFree(GetProcessHeap(), 0, formats);
953 if(hr == S_OK || !out){
954 CoTaskMemFree(closest);
958 closest->nBlockAlign =
959 closest->nChannels * closest->wBitsPerSample / 8;
960 closest->nAvgBytesPerSec =
961 closest->nBlockAlign * closest->nSamplesPerSec;
965 TRACE("returning: %08x\n", hr);
969 static HRESULT WINAPI AudioClient_GetMixFormat(IAudioClient *iface,
972 ACImpl *This = impl_from_IAudioClient(iface);
973 WAVEFORMATEXTENSIBLE *fmt;
974 snd_pcm_format_mask_t *formats;
975 unsigned int max_rate, max_channels;
979 TRACE("(%p)->(%p)\n", This, pwfx);
984 *pwfx = HeapAlloc(GetProcessHeap(), 0, sizeof(WAVEFORMATEXTENSIBLE));
986 return E_OUTOFMEMORY;
988 fmt = (WAVEFORMATEXTENSIBLE*)*pwfx;
990 formats = HeapAlloc(GetProcessHeap(), 0, snd_pcm_format_mask_sizeof());
992 HeapFree(GetProcessHeap(), 0, *pwfx);
993 return E_OUTOFMEMORY;
996 EnterCriticalSection(&This->lock);
998 if((err = snd_pcm_hw_params_any(This->pcm_handle, This->hw_params)) < 0){
999 WARN("Unable to get hw_params: %d (%s)\n", err, snd_strerror(err));
1004 snd_pcm_hw_params_get_format_mask(This->hw_params, formats);
1006 fmt->Format.wFormatTag = WAVE_FORMAT_EXTENSIBLE;
1007 if(snd_pcm_format_mask_test(formats, SND_PCM_FORMAT_FLOAT_LE)){
1008 fmt->Format.wBitsPerSample = 32;
1009 fmt->SubFormat = KSDATAFORMAT_SUBTYPE_IEEE_FLOAT;
1010 }else if(snd_pcm_format_mask_test(formats, SND_PCM_FORMAT_S16_LE)){
1011 fmt->Format.wBitsPerSample = 16;
1012 fmt->SubFormat = KSDATAFORMAT_SUBTYPE_PCM;
1013 }else if(snd_pcm_format_mask_test(formats, SND_PCM_FORMAT_U8)){
1014 fmt->Format.wBitsPerSample = 8;
1015 fmt->SubFormat = KSDATAFORMAT_SUBTYPE_PCM;
1016 }else if(snd_pcm_format_mask_test(formats, SND_PCM_FORMAT_S32_LE)){
1017 fmt->Format.wBitsPerSample = 32;
1018 fmt->SubFormat = KSDATAFORMAT_SUBTYPE_PCM;
1019 }else if(snd_pcm_format_mask_test(formats, SND_PCM_FORMAT_S24_3LE)){
1020 fmt->Format.wBitsPerSample = 24;
1021 fmt->SubFormat = KSDATAFORMAT_SUBTYPE_PCM;
1023 ERR("Didn't recognize any available ALSA formats\n");
1028 if((err = snd_pcm_hw_params_get_channels_max(This->hw_params,
1029 &max_channels)) < 0){
1030 WARN("Unable to get max channels: %d (%s)\n", err, snd_strerror(err));
1035 if(max_channels > 2){
1036 FIXME("Don't know what to do with %u channels, pretending there's "
1037 "only 2 channels\n", max_channels);
1038 fmt->Format.nChannels = 2;
1040 fmt->Format.nChannels = max_channels;
1042 fmt->dwChannelMask = get_channel_mask(fmt->Format.nChannels);
1044 if((err = snd_pcm_hw_params_get_rate_max(This->hw_params, &max_rate,
1046 WARN("Unable to get max rate: %d (%s)\n", err, snd_strerror(err));
1051 if(max_rate >= 48000)
1052 fmt->Format.nSamplesPerSec = 48000;
1053 else if(max_rate >= 44100)
1054 fmt->Format.nSamplesPerSec = 44100;
1055 else if(max_rate >= 22050)
1056 fmt->Format.nSamplesPerSec = 22050;
1057 else if(max_rate >= 11025)
1058 fmt->Format.nSamplesPerSec = 11025;
1059 else if(max_rate >= 8000)
1060 fmt->Format.nSamplesPerSec = 8000;
1062 ERR("Unknown max rate: %u\n", max_rate);
1067 fmt->Format.nBlockAlign = (fmt->Format.wBitsPerSample *
1068 fmt->Format.nChannels) / 8;
1069 fmt->Format.nAvgBytesPerSec = fmt->Format.nSamplesPerSec *
1070 fmt->Format.nBlockAlign;
1072 fmt->Samples.wValidBitsPerSample = fmt->Format.wBitsPerSample;
1073 fmt->Format.cbSize = sizeof(WAVEFORMATEXTENSIBLE) - sizeof(WAVEFORMATEX);
1075 dump_fmt((WAVEFORMATEX*)fmt);
1078 LeaveCriticalSection(&This->lock);
1080 HeapFree(GetProcessHeap(), 0, *pwfx);
1081 HeapFree(GetProcessHeap(), 0, formats);
1086 static HRESULT WINAPI AudioClient_GetDevicePeriod(IAudioClient *iface,
1087 REFERENCE_TIME *defperiod, REFERENCE_TIME *minperiod)
1089 ACImpl *This = impl_from_IAudioClient(iface);
1091 TRACE("(%p)->(%p, %p)\n", This, defperiod, minperiod);
1093 if(!defperiod && !minperiod)
1097 *defperiod = DefaultPeriod;
1099 *minperiod = MinimumPeriod;
1104 static snd_pcm_sframes_t alsa_write_best_effort(snd_pcm_t *handle, BYTE *buf,
1105 snd_pcm_uframes_t frames)
1107 snd_pcm_sframes_t written;
1109 written = snd_pcm_writei(handle, buf, frames);
1113 if(written == -EAGAIN)
1117 WARN("writei failed, recovering: %ld (%s)\n", written,
1118 snd_strerror(written));
1120 ret = wine_snd_pcm_recover(handle, written, 0);
1122 WARN("Could not recover: %d (%s)\n", ret, snd_strerror(ret));
1126 written = snd_pcm_writei(handle, buf, frames);
1132 static void alsa_write_data(ACImpl *This)
1134 snd_pcm_sframes_t written;
1135 snd_pcm_uframes_t to_write;
1137 This->local_buffer + (This->lcl_offs_frames * This->fmt->nBlockAlign);
1139 if(This->lcl_offs_frames + This->held_frames > This->bufsize_frames)
1140 to_write = This->bufsize_frames - This->lcl_offs_frames;
1142 to_write = This->held_frames;
1144 written = alsa_write_best_effort(This->pcm_handle, buf, to_write);
1146 WARN("Couldn't write: %ld (%s)\n", written, snd_strerror(written));
1150 This->lcl_offs_frames += written;
1151 This->lcl_offs_frames %= This->bufsize_frames;
1152 This->held_frames -= written;
1154 if(written < to_write){
1155 /* ALSA buffer probably full */
1159 if(This->held_frames){
1160 /* wrapped and have some data back at the start to write */
1161 written = alsa_write_best_effort(This->pcm_handle, This->local_buffer,
1164 WARN("Couldn't write: %ld (%s)\n", written, snd_strerror(written));
1168 This->lcl_offs_frames += written;
1169 This->lcl_offs_frames %= This->bufsize_frames;
1170 This->held_frames -= written;
1174 static void alsa_read_data(ACImpl *This)
1176 snd_pcm_sframes_t pos, readable, nread;
1178 pos = (This->held_frames + This->lcl_offs_frames) % This->bufsize_frames;
1179 readable = This->bufsize_frames - pos;
1181 nread = snd_pcm_readi(This->pcm_handle,
1182 This->local_buffer + pos * This->fmt->nBlockAlign, readable);
1186 WARN("read failed, recovering: %ld (%s)\n", nread, snd_strerror(nread));
1188 ret = wine_snd_pcm_recover(This->pcm_handle, nread, 0);
1190 WARN("Recover failed: %d (%s)\n", ret, snd_strerror(ret));
1194 nread = snd_pcm_readi(This->pcm_handle,
1195 This->local_buffer + pos * This->fmt->nBlockAlign, readable);
1197 WARN("read failed: %ld (%s)\n", nread, snd_strerror(nread));
1202 This->held_frames += nread;
1204 if(This->held_frames > This->bufsize_frames){
1205 WARN("Overflow of unread data\n");
1206 This->lcl_offs_frames += This->held_frames;
1207 This->lcl_offs_frames %= This->bufsize_frames;
1208 This->held_frames = This->bufsize_frames;
1212 static void CALLBACK alsa_push_buffer_data(void *user, BOOLEAN timer)
1214 ACImpl *This = user;
1216 EnterCriticalSection(&This->lock);
1218 if(This->dataflow == eRender && This->held_frames)
1219 alsa_write_data(This);
1220 else if(This->dataflow == eCapture)
1221 alsa_read_data(This);
1224 SetEvent(This->event);
1226 LeaveCriticalSection(&This->lock);
1229 static HRESULT alsa_consider_start(ACImpl *This)
1231 snd_pcm_sframes_t avail;
1234 avail = snd_pcm_avail_update(This->pcm_handle);
1236 WARN("Unable to get avail_update: %ld (%s)\n", avail,
1237 snd_strerror(avail));
1241 if(This->period_alsa < This->bufsize_alsa - avail){
1242 if((err = snd_pcm_start(This->pcm_handle)) < 0){
1243 WARN("Start failed: %d (%s), state: %d\n", err, snd_strerror(err),
1244 snd_pcm_state(This->pcm_handle));
1254 static HRESULT WINAPI AudioClient_Start(IAudioClient *iface)
1256 ACImpl *This = impl_from_IAudioClient(iface);
1260 TRACE("(%p)\n", This);
1262 EnterCriticalSection(&This->lock);
1265 LeaveCriticalSection(&This->lock);
1266 return AUDCLNT_E_NOT_INITIALIZED;
1269 if((This->flags & AUDCLNT_STREAMFLAGS_EVENTCALLBACK) && !This->event){
1270 LeaveCriticalSection(&This->lock);
1271 return AUDCLNT_E_EVENTHANDLE_NOT_SET;
1275 LeaveCriticalSection(&This->lock);
1276 return AUDCLNT_E_NOT_STOPPED;
1279 hr = alsa_consider_start(This);
1281 LeaveCriticalSection(&This->lock);
1285 period_ms = This->period_us / 1000;
1289 if(This->dataflow == eCapture){
1290 /* dump any data that might be leftover in the ALSA capture buffer */
1291 snd_pcm_readi(This->pcm_handle, This->local_buffer,
1292 This->bufsize_frames);
1295 if(!CreateTimerQueueTimer(&This->timer, g_timer_q, alsa_push_buffer_data,
1296 This, 0, period_ms, WT_EXECUTEINTIMERTHREAD)){
1297 LeaveCriticalSection(&This->lock);
1298 WARN("Unable to create timer: %u\n", GetLastError());
1302 This->started = TRUE;
1304 LeaveCriticalSection(&This->lock);
1309 static HRESULT WINAPI AudioClient_Stop(IAudioClient *iface)
1311 ACImpl *This = impl_from_IAudioClient(iface);
1314 TRACE("(%p)\n", This);
1316 EnterCriticalSection(&This->lock);
1319 LeaveCriticalSection(&This->lock);
1320 return AUDCLNT_E_NOT_INITIALIZED;
1324 LeaveCriticalSection(&This->lock);
1328 DeleteTimerQueueTimer(g_timer_q, This->timer, INVALID_HANDLE_VALUE);
1330 if((err = snd_pcm_drop(This->pcm_handle)) < 0){
1331 LeaveCriticalSection(&This->lock);
1332 WARN("Drop failed: %d (%s)\n", err, snd_strerror(err));
1336 if((err = snd_pcm_prepare(This->pcm_handle)) < 0){
1337 LeaveCriticalSection(&This->lock);
1338 WARN("Prepare failed: %d (%s)\n", err, snd_strerror(err));
1342 This->started = FALSE;
1344 LeaveCriticalSection(&This->lock);
1349 static HRESULT WINAPI AudioClient_Reset(IAudioClient *iface)
1351 ACImpl *This = impl_from_IAudioClient(iface);
1353 TRACE("(%p)\n", This);
1355 EnterCriticalSection(&This->lock);
1358 LeaveCriticalSection(&This->lock);
1359 return AUDCLNT_E_NOT_INITIALIZED;
1363 LeaveCriticalSection(&This->lock);
1364 return AUDCLNT_E_NOT_STOPPED;
1367 This->held_frames = 0;
1368 This->written_frames = 0;
1369 This->lcl_offs_frames = 0;
1371 LeaveCriticalSection(&This->lock);
1376 static HRESULT WINAPI AudioClient_SetEventHandle(IAudioClient *iface,
1379 ACImpl *This = impl_from_IAudioClient(iface);
1381 TRACE("(%p)->(%p)\n", This, event);
1384 return E_INVALIDARG;
1386 EnterCriticalSection(&This->lock);
1389 LeaveCriticalSection(&This->lock);
1390 return AUDCLNT_E_NOT_INITIALIZED;
1393 if(!(This->flags & AUDCLNT_STREAMFLAGS_EVENTCALLBACK)){
1394 LeaveCriticalSection(&This->lock);
1395 return AUDCLNT_E_EVENTHANDLE_NOT_EXPECTED;
1398 This->event = event;
1400 LeaveCriticalSection(&This->lock);
1405 static HRESULT WINAPI AudioClient_GetService(IAudioClient *iface, REFIID riid,
1408 ACImpl *This = impl_from_IAudioClient(iface);
1410 TRACE("(%p)->(%s, %p)\n", This, debugstr_guid(riid), ppv);
1416 EnterCriticalSection(&This->lock);
1419 LeaveCriticalSection(&This->lock);
1420 return AUDCLNT_E_NOT_INITIALIZED;
1423 if(IsEqualIID(riid, &IID_IAudioRenderClient)){
1424 if(This->dataflow != eRender){
1425 LeaveCriticalSection(&This->lock);
1426 return AUDCLNT_E_WRONG_ENDPOINT_TYPE;
1428 *ppv = &This->IAudioRenderClient_iface;
1429 }else if(IsEqualIID(riid, &IID_IAudioCaptureClient)){
1430 if(This->dataflow != eCapture){
1431 LeaveCriticalSection(&This->lock);
1432 return AUDCLNT_E_WRONG_ENDPOINT_TYPE;
1434 *ppv = &This->IAudioCaptureClient_iface;
1435 }else if(IsEqualIID(riid, &IID_IAudioSessionControl)){
1436 if(!This->session_wrapper){
1437 This->session_wrapper = AudioSessionWrapper_Create(This);
1438 if(!This->session_wrapper){
1439 LeaveCriticalSection(&This->lock);
1440 return E_OUTOFMEMORY;
1444 *ppv = &This->session_wrapper->IAudioSessionControl2_iface;
1445 }else if(IsEqualIID(riid, &IID_ISimpleAudioVolume)){
1446 *ppv = &This->ISimpleAudioVolume_iface;
1447 }else if(IsEqualIID(riid, &IID_IAudioClock)){
1448 *ppv = &This->IAudioClock_iface;
1452 IUnknown_AddRef((IUnknown*)*ppv);
1453 LeaveCriticalSection(&This->lock);
1457 LeaveCriticalSection(&This->lock);
1459 FIXME("stub %s\n", debugstr_guid(riid));
1460 return E_NOINTERFACE;
1463 static const IAudioClientVtbl AudioClient_Vtbl =
1465 AudioClient_QueryInterface,
1467 AudioClient_Release,
1468 AudioClient_Initialize,
1469 AudioClient_GetBufferSize,
1470 AudioClient_GetStreamLatency,
1471 AudioClient_GetCurrentPadding,
1472 AudioClient_IsFormatSupported,
1473 AudioClient_GetMixFormat,
1474 AudioClient_GetDevicePeriod,
1478 AudioClient_SetEventHandle,
1479 AudioClient_GetService
1482 static HRESULT WINAPI AudioRenderClient_QueryInterface(
1483 IAudioRenderClient *iface, REFIID riid, void **ppv)
1485 TRACE("(%p)->(%s, %p)\n", iface, debugstr_guid(riid), ppv);
1491 if(IsEqualIID(riid, &IID_IUnknown) ||
1492 IsEqualIID(riid, &IID_IAudioRenderClient))
1495 IUnknown_AddRef((IUnknown*)*ppv);
1499 WARN("Unknown interface %s\n", debugstr_guid(riid));
1500 return E_NOINTERFACE;
1503 static ULONG WINAPI AudioRenderClient_AddRef(IAudioRenderClient *iface)
1505 ACImpl *This = impl_from_IAudioRenderClient(iface);
1506 return AudioClient_AddRef(&This->IAudioClient_iface);
1509 static ULONG WINAPI AudioRenderClient_Release(IAudioRenderClient *iface)
1511 ACImpl *This = impl_from_IAudioRenderClient(iface);
1512 return AudioClient_Release(&This->IAudioClient_iface);
1515 static HRESULT WINAPI AudioRenderClient_GetBuffer(IAudioRenderClient *iface,
1516 UINT32 frames, BYTE **data)
1518 ACImpl *This = impl_from_IAudioRenderClient(iface);
1523 TRACE("(%p)->(%u, %p)\n", This, frames, data);
1528 EnterCriticalSection(&This->lock);
1530 if(This->buf_state != NOT_LOCKED){
1531 LeaveCriticalSection(&This->lock);
1532 return AUDCLNT_E_OUT_OF_ORDER;
1536 This->buf_state = LOCKED_NORMAL;
1537 LeaveCriticalSection(&This->lock);
1541 hr = IAudioClient_GetCurrentPadding(&This->IAudioClient_iface, &pad);
1543 LeaveCriticalSection(&This->lock);
1547 if(pad + frames > This->bufsize_frames){
1548 LeaveCriticalSection(&This->lock);
1549 return AUDCLNT_E_BUFFER_TOO_LARGE;
1553 (This->lcl_offs_frames + This->held_frames) % This->bufsize_frames;
1554 if(write_pos + frames > This->bufsize_frames){
1555 if(This->tmp_buffer_frames < frames){
1556 if(This->tmp_buffer)
1557 This->tmp_buffer = HeapReAlloc(GetProcessHeap(), 0,
1558 This->tmp_buffer, frames * This->fmt->nBlockAlign);
1560 This->tmp_buffer = HeapAlloc(GetProcessHeap(), 0,
1561 frames * This->fmt->nBlockAlign);
1562 if(!This->tmp_buffer){
1563 LeaveCriticalSection(&This->lock);
1564 return E_OUTOFMEMORY;
1566 This->tmp_buffer_frames = frames;
1568 *data = This->tmp_buffer;
1569 This->buf_state = LOCKED_WRAPPED;
1571 *data = This->local_buffer +
1572 This->lcl_offs_frames * This->fmt->nBlockAlign;
1573 This->buf_state = LOCKED_NORMAL;
1576 LeaveCriticalSection(&This->lock);
1581 static void alsa_wrap_buffer(ACImpl *This, BYTE *buffer, UINT32 written_bytes)
1583 snd_pcm_uframes_t write_offs_frames =
1584 (This->lcl_offs_frames + This->held_frames) % This->bufsize_frames;
1585 UINT32 write_offs_bytes = write_offs_frames * This->fmt->nBlockAlign;
1586 snd_pcm_uframes_t chunk_frames = This->bufsize_frames - write_offs_frames;
1587 UINT32 chunk_bytes = chunk_frames * This->fmt->nBlockAlign;
1589 if(written_bytes < chunk_bytes){
1590 memcpy(This->local_buffer + write_offs_bytes, buffer, written_bytes);
1592 memcpy(This->local_buffer + write_offs_bytes, buffer, chunk_bytes);
1593 memcpy(This->local_buffer, buffer + chunk_bytes,
1594 written_bytes - chunk_bytes);
1598 static HRESULT WINAPI AudioRenderClient_ReleaseBuffer(
1599 IAudioRenderClient *iface, UINT32 written_frames, DWORD flags)
1601 ACImpl *This = impl_from_IAudioRenderClient(iface);
1602 UINT32 written_bytes = written_frames * This->fmt->nBlockAlign;
1606 TRACE("(%p)->(%u, %x)\n", This, written_frames, flags);
1608 EnterCriticalSection(&This->lock);
1610 if(This->buf_state == NOT_LOCKED || !written_frames){
1611 This->buf_state = NOT_LOCKED;
1612 LeaveCriticalSection(&This->lock);
1613 return written_frames ? AUDCLNT_E_OUT_OF_ORDER : S_OK;
1616 if(This->buf_state == LOCKED_NORMAL)
1617 buffer = This->local_buffer +
1618 This->lcl_offs_frames * This->fmt->nBlockAlign;
1620 buffer = This->tmp_buffer;
1622 if(flags & AUDCLNT_BUFFERFLAGS_SILENT){
1623 WAVEFORMATEXTENSIBLE *fmtex = (WAVEFORMATEXTENSIBLE*)This->fmt;
1624 if((This->fmt->wFormatTag == WAVE_FORMAT_PCM ||
1625 (This->fmt->wFormatTag == WAVE_FORMAT_EXTENSIBLE &&
1626 IsEqualGUID(&fmtex->SubFormat, &KSDATAFORMAT_SUBTYPE_PCM))) &&
1627 This->fmt->wBitsPerSample == 8)
1628 memset(buffer, 128, written_frames * This->fmt->nBlockAlign);
1630 memset(buffer, 0, written_frames * This->fmt->nBlockAlign);
1633 if(This->held_frames){
1634 if(This->buf_state == LOCKED_WRAPPED)
1635 alsa_wrap_buffer(This, buffer, written_bytes);
1637 This->held_frames += written_frames;
1639 snd_pcm_sframes_t written;
1641 written = alsa_write_best_effort(This->pcm_handle, buffer,
1644 LeaveCriticalSection(&This->lock);
1645 WARN("write failed: %ld (%s)\n", written, snd_strerror(written));
1649 if(written < written_frames){
1650 if(This->buf_state == LOCKED_WRAPPED)
1651 alsa_wrap_buffer(This,
1652 This->tmp_buffer + written * This->fmt->nBlockAlign,
1653 written_frames - written);
1655 This->held_frames = written_frames - written;
1660 snd_pcm_state(This->pcm_handle) == SND_PCM_STATE_PREPARED){
1661 hr = alsa_consider_start(This);
1663 LeaveCriticalSection(&This->lock);
1668 This->written_frames += written_frames;
1669 This->buf_state = NOT_LOCKED;
1671 LeaveCriticalSection(&This->lock);
1676 static const IAudioRenderClientVtbl AudioRenderClient_Vtbl = {
1677 AudioRenderClient_QueryInterface,
1678 AudioRenderClient_AddRef,
1679 AudioRenderClient_Release,
1680 AudioRenderClient_GetBuffer,
1681 AudioRenderClient_ReleaseBuffer
1684 static HRESULT WINAPI AudioCaptureClient_QueryInterface(
1685 IAudioCaptureClient *iface, REFIID riid, void **ppv)
1687 TRACE("(%p)->(%s, %p)\n", iface, debugstr_guid(riid), ppv);
1693 if(IsEqualIID(riid, &IID_IUnknown) ||
1694 IsEqualIID(riid, &IID_IAudioCaptureClient))
1697 IUnknown_AddRef((IUnknown*)*ppv);
1701 WARN("Unknown interface %s\n", debugstr_guid(riid));
1702 return E_NOINTERFACE;
1705 static ULONG WINAPI AudioCaptureClient_AddRef(IAudioCaptureClient *iface)
1707 ACImpl *This = impl_from_IAudioCaptureClient(iface);
1708 return IAudioClient_AddRef(&This->IAudioClient_iface);
1711 static ULONG WINAPI AudioCaptureClient_Release(IAudioCaptureClient *iface)
1713 ACImpl *This = impl_from_IAudioCaptureClient(iface);
1714 return IAudioClient_Release(&This->IAudioClient_iface);
1717 static HRESULT WINAPI AudioCaptureClient_GetBuffer(IAudioCaptureClient *iface,
1718 BYTE **data, UINT32 *frames, DWORD *flags, UINT64 *devpos,
1721 ACImpl *This = impl_from_IAudioCaptureClient(iface);
1724 TRACE("(%p)->(%p, %p, %p, %p, %p)\n", This, data, frames, flags,
1727 if(!data || !frames || !flags)
1730 EnterCriticalSection(&This->lock);
1732 if(This->buf_state != NOT_LOCKED){
1733 LeaveCriticalSection(&This->lock);
1734 return AUDCLNT_E_OUT_OF_ORDER;
1737 hr = IAudioCaptureClient_GetNextPacketSize(iface, frames);
1739 LeaveCriticalSection(&This->lock);
1745 if(This->lcl_offs_frames + *frames > This->bufsize_frames){
1746 UINT32 chunk_bytes, offs_bytes, frames_bytes;
1747 if(This->tmp_buffer_frames < *frames){
1748 if(This->tmp_buffer)
1749 This->tmp_buffer = HeapReAlloc(GetProcessHeap(), 0,
1750 This->tmp_buffer, *frames * This->fmt->nBlockAlign);
1752 This->tmp_buffer = HeapAlloc(GetProcessHeap(), 0,
1753 *frames * This->fmt->nBlockAlign);
1754 if(!This->tmp_buffer){
1755 LeaveCriticalSection(&This->lock);
1756 return E_OUTOFMEMORY;
1758 This->tmp_buffer_frames = *frames;
1761 *data = This->tmp_buffer;
1762 chunk_bytes = (This->bufsize_frames - This->lcl_offs_frames) *
1763 This->fmt->nBlockAlign;
1764 offs_bytes = This->lcl_offs_frames * This->fmt->nBlockAlign;
1765 frames_bytes = *frames * This->fmt->nBlockAlign;
1766 memcpy(This->tmp_buffer, This->local_buffer + offs_bytes, chunk_bytes);
1767 memcpy(This->tmp_buffer + chunk_bytes, This->local_buffer,
1768 frames_bytes - chunk_bytes);
1770 *data = This->local_buffer +
1771 This->lcl_offs_frames * This->fmt->nBlockAlign;
1773 This->buf_state = LOCKED_NORMAL;
1775 if(devpos || qpcpos)
1776 IAudioClock_GetPosition(&This->IAudioClock_iface, devpos, qpcpos);
1778 LeaveCriticalSection(&This->lock);
1780 return *frames ? S_OK : AUDCLNT_S_BUFFER_EMPTY;
1783 static HRESULT WINAPI AudioCaptureClient_ReleaseBuffer(
1784 IAudioCaptureClient *iface, UINT32 done)
1786 ACImpl *This = impl_from_IAudioCaptureClient(iface);
1788 TRACE("(%p)->(%u)\n", This, done);
1790 EnterCriticalSection(&This->lock);
1792 if(This->buf_state == NOT_LOCKED){
1793 LeaveCriticalSection(&This->lock);
1794 return AUDCLNT_E_OUT_OF_ORDER;
1797 This->held_frames -= done;
1798 This->lcl_offs_frames += done;
1799 This->lcl_offs_frames %= This->bufsize_frames;
1801 This->buf_state = NOT_LOCKED;
1803 LeaveCriticalSection(&This->lock);
1808 static HRESULT WINAPI AudioCaptureClient_GetNextPacketSize(
1809 IAudioCaptureClient *iface, UINT32 *frames)
1811 ACImpl *This = impl_from_IAudioCaptureClient(iface);
1813 TRACE("(%p)->(%p)\n", This, frames);
1815 return AudioClient_GetCurrentPadding(&This->IAudioClient_iface, frames);
1818 static const IAudioCaptureClientVtbl AudioCaptureClient_Vtbl =
1820 AudioCaptureClient_QueryInterface,
1821 AudioCaptureClient_AddRef,
1822 AudioCaptureClient_Release,
1823 AudioCaptureClient_GetBuffer,
1824 AudioCaptureClient_ReleaseBuffer,
1825 AudioCaptureClient_GetNextPacketSize
1828 static HRESULT WINAPI AudioClock_QueryInterface(IAudioClock *iface,
1829 REFIID riid, void **ppv)
1831 ACImpl *This = impl_from_IAudioClock(iface);
1833 TRACE("(%p)->(%s, %p)\n", iface, debugstr_guid(riid), ppv);
1839 if(IsEqualIID(riid, &IID_IUnknown) || IsEqualIID(riid, &IID_IAudioClock))
1841 else if(IsEqualIID(riid, &IID_IAudioClock2))
1842 *ppv = &This->IAudioClock2_iface;
1844 IUnknown_AddRef((IUnknown*)*ppv);
1848 WARN("Unknown interface %s\n", debugstr_guid(riid));
1849 return E_NOINTERFACE;
1852 static ULONG WINAPI AudioClock_AddRef(IAudioClock *iface)
1854 ACImpl *This = impl_from_IAudioClock(iface);
1855 return IAudioClient_AddRef(&This->IAudioClient_iface);
1858 static ULONG WINAPI AudioClock_Release(IAudioClock *iface)
1860 ACImpl *This = impl_from_IAudioClock(iface);
1861 return IAudioClient_Release(&This->IAudioClient_iface);
1864 static HRESULT WINAPI AudioClock_GetFrequency(IAudioClock *iface, UINT64 *freq)
1866 ACImpl *This = impl_from_IAudioClock(iface);
1868 TRACE("(%p)->(%p)\n", This, freq);
1870 *freq = This->fmt->nSamplesPerSec;
1875 static HRESULT WINAPI AudioClock_GetPosition(IAudioClock *iface, UINT64 *pos,
1878 ACImpl *This = impl_from_IAudioClock(iface);
1882 TRACE("(%p)->(%p, %p)\n", This, pos, qpctime);
1887 EnterCriticalSection(&This->lock);
1889 hr = IAudioClient_GetCurrentPadding(&This->IAudioClient_iface, &pad);
1891 LeaveCriticalSection(&This->lock);
1895 if(This->dataflow == eRender)
1896 *pos = This->written_frames - pad;
1897 else if(This->dataflow == eCapture)
1898 *pos = This->written_frames + pad;
1900 LeaveCriticalSection(&This->lock);
1903 LARGE_INTEGER stamp, freq;
1904 QueryPerformanceCounter(&stamp);
1905 QueryPerformanceFrequency(&freq);
1906 *qpctime = (stamp.QuadPart * (INT64)10000000) / freq.QuadPart;
1912 static HRESULT WINAPI AudioClock_GetCharacteristics(IAudioClock *iface,
1915 ACImpl *This = impl_from_IAudioClock(iface);
1917 TRACE("(%p)->(%p)\n", This, chars);
1922 *chars = AUDIOCLOCK_CHARACTERISTIC_FIXED_FREQ;
1927 static const IAudioClockVtbl AudioClock_Vtbl =
1929 AudioClock_QueryInterface,
1932 AudioClock_GetFrequency,
1933 AudioClock_GetPosition,
1934 AudioClock_GetCharacteristics
1937 static HRESULT WINAPI AudioClock2_QueryInterface(IAudioClock2 *iface,
1938 REFIID riid, void **ppv)
1940 ACImpl *This = impl_from_IAudioClock2(iface);
1941 return IAudioClock_QueryInterface(&This->IAudioClock_iface, riid, ppv);
1944 static ULONG WINAPI AudioClock2_AddRef(IAudioClock2 *iface)
1946 ACImpl *This = impl_from_IAudioClock2(iface);
1947 return IAudioClient_AddRef(&This->IAudioClient_iface);
1950 static ULONG WINAPI AudioClock2_Release(IAudioClock2 *iface)
1952 ACImpl *This = impl_from_IAudioClock2(iface);
1953 return IAudioClient_Release(&This->IAudioClient_iface);
1956 static HRESULT WINAPI AudioClock2_GetDevicePosition(IAudioClock2 *iface,
1957 UINT64 *pos, UINT64 *qpctime)
1959 ACImpl *This = impl_from_IAudioClock2(iface);
1961 FIXME("(%p)->(%p, %p)\n", This, pos, qpctime);
1966 static const IAudioClock2Vtbl AudioClock2_Vtbl =
1968 AudioClock2_QueryInterface,
1970 AudioClock2_Release,
1971 AudioClock2_GetDevicePosition
1974 static AudioSessionWrapper *AudioSessionWrapper_Create(ACImpl *client)
1976 AudioSessionWrapper *ret;
1978 ret = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY,
1979 sizeof(AudioSessionWrapper));
1983 ret->IAudioSessionControl2_iface.lpVtbl = &AudioSessionControl2_Vtbl;
1985 ret->client = client;
1986 ret->session = client->session;
1987 AudioClient_AddRef(&client->IAudioClient_iface);
1992 static HRESULT WINAPI AudioSessionControl_QueryInterface(
1993 IAudioSessionControl2 *iface, REFIID riid, void **ppv)
1995 TRACE("(%p)->(%s, %p)\n", iface, debugstr_guid(riid), ppv);
2001 if(IsEqualIID(riid, &IID_IUnknown) ||
2002 IsEqualIID(riid, &IID_IAudioSessionControl) ||
2003 IsEqualIID(riid, &IID_IAudioSessionControl2))
2006 IUnknown_AddRef((IUnknown*)*ppv);
2010 WARN("Unknown interface %s\n", debugstr_guid(riid));
2011 return E_NOINTERFACE;
2014 static ULONG WINAPI AudioSessionControl_AddRef(IAudioSessionControl2 *iface)
2016 AudioSessionWrapper *This = impl_from_IAudioSessionControl2(iface);
2018 ref = InterlockedIncrement(&This->ref);
2019 TRACE("(%p) Refcount now %u\n", This, ref);
2023 static ULONG WINAPI AudioSessionControl_Release(IAudioSessionControl2 *iface)
2025 AudioSessionWrapper *This = impl_from_IAudioSessionControl2(iface);
2027 ref = InterlockedDecrement(&This->ref);
2028 TRACE("(%p) Refcount now %u\n", This, ref);
2030 EnterCriticalSection(&This->client->lock);
2031 This->client->session_wrapper = NULL;
2032 LeaveCriticalSection(&This->client->lock);
2033 AudioClient_Release(&This->client->IAudioClient_iface);
2034 HeapFree(GetProcessHeap(), 0, This);
2039 static HRESULT WINAPI AudioSessionControl_GetState(IAudioSessionControl2 *iface,
2040 AudioSessionState *state)
2042 AudioSessionWrapper *This = impl_from_IAudioSessionControl2(iface);
2045 TRACE("(%p)->(%p)\n", This, state);
2048 return NULL_PTR_ERR;
2050 EnterCriticalSection(&g_sessions_lock);
2052 if(list_empty(&This->session->clients)){
2053 *state = AudioSessionStateExpired;
2054 LeaveCriticalSection(&g_sessions_lock);
2058 LIST_FOR_EACH_ENTRY(client, &This->session->clients, ACImpl, entry){
2059 EnterCriticalSection(&client->lock);
2060 if(client->started){
2061 *state = AudioSessionStateActive;
2062 LeaveCriticalSection(&client->lock);
2063 LeaveCriticalSection(&g_sessions_lock);
2066 LeaveCriticalSection(&client->lock);
2069 LeaveCriticalSection(&g_sessions_lock);
2071 *state = AudioSessionStateInactive;
2076 static HRESULT WINAPI AudioSessionControl_GetDisplayName(
2077 IAudioSessionControl2 *iface, WCHAR **name)
2079 AudioSessionWrapper *This = impl_from_IAudioSessionControl2(iface);
2081 FIXME("(%p)->(%p) - stub\n", This, name);
2086 static HRESULT WINAPI AudioSessionControl_SetDisplayName(
2087 IAudioSessionControl2 *iface, const WCHAR *name, const GUID *session)
2089 AudioSessionWrapper *This = impl_from_IAudioSessionControl2(iface);
2091 FIXME("(%p)->(%p, %s) - stub\n", This, name, debugstr_guid(session));
2096 static HRESULT WINAPI AudioSessionControl_GetIconPath(
2097 IAudioSessionControl2 *iface, WCHAR **path)
2099 AudioSessionWrapper *This = impl_from_IAudioSessionControl2(iface);
2101 FIXME("(%p)->(%p) - stub\n", This, path);
2106 static HRESULT WINAPI AudioSessionControl_SetIconPath(
2107 IAudioSessionControl2 *iface, const WCHAR *path, const GUID *session)
2109 AudioSessionWrapper *This = impl_from_IAudioSessionControl2(iface);
2111 FIXME("(%p)->(%p, %s) - stub\n", This, path, debugstr_guid(session));
2116 static HRESULT WINAPI AudioSessionControl_GetGroupingParam(
2117 IAudioSessionControl2 *iface, GUID *group)
2119 AudioSessionWrapper *This = impl_from_IAudioSessionControl2(iface);
2121 FIXME("(%p)->(%p) - stub\n", This, group);
2126 static HRESULT WINAPI AudioSessionControl_SetGroupingParam(
2127 IAudioSessionControl2 *iface, GUID *group, const GUID *session)
2129 AudioSessionWrapper *This = impl_from_IAudioSessionControl2(iface);
2131 FIXME("(%p)->(%s, %s) - stub\n", This, debugstr_guid(group),
2132 debugstr_guid(session));
2137 static HRESULT WINAPI AudioSessionControl_RegisterAudioSessionNotification(
2138 IAudioSessionControl2 *iface, IAudioSessionEvents *events)
2140 AudioSessionWrapper *This = impl_from_IAudioSessionControl2(iface);
2142 FIXME("(%p)->(%p) - stub\n", This, events);
2147 static HRESULT WINAPI AudioSessionControl_UnregisterAudioSessionNotification(
2148 IAudioSessionControl2 *iface, IAudioSessionEvents *events)
2150 AudioSessionWrapper *This = impl_from_IAudioSessionControl2(iface);
2152 FIXME("(%p)->(%p) - stub\n", This, events);
2157 static HRESULT WINAPI AudioSessionControl_GetSessionIdentifier(
2158 IAudioSessionControl2 *iface, WCHAR **id)
2160 AudioSessionWrapper *This = impl_from_IAudioSessionControl2(iface);
2162 FIXME("(%p)->(%p) - stub\n", This, id);
2167 static HRESULT WINAPI AudioSessionControl_GetSessionInstanceIdentifier(
2168 IAudioSessionControl2 *iface, WCHAR **id)
2170 AudioSessionWrapper *This = impl_from_IAudioSessionControl2(iface);
2172 FIXME("(%p)->(%p) - stub\n", This, id);
2177 static HRESULT WINAPI AudioSessionControl_GetProcessId(
2178 IAudioSessionControl2 *iface, DWORD *pid)
2180 AudioSessionWrapper *This = impl_from_IAudioSessionControl2(iface);
2182 TRACE("(%p)->(%p)\n", This, pid);
2187 *pid = GetCurrentProcessId();
2192 static HRESULT WINAPI AudioSessionControl_IsSystemSoundsSession(
2193 IAudioSessionControl2 *iface)
2195 AudioSessionWrapper *This = impl_from_IAudioSessionControl2(iface);
2197 TRACE("(%p)\n", This);
2202 static HRESULT WINAPI AudioSessionControl_SetDuckingPreference(
2203 IAudioSessionControl2 *iface, BOOL optout)
2205 AudioSessionWrapper *This = impl_from_IAudioSessionControl2(iface);
2207 TRACE("(%p)->(%d)\n", This, optout);
2212 static const IAudioSessionControl2Vtbl AudioSessionControl2_Vtbl =
2214 AudioSessionControl_QueryInterface,
2215 AudioSessionControl_AddRef,
2216 AudioSessionControl_Release,
2217 AudioSessionControl_GetState,
2218 AudioSessionControl_GetDisplayName,
2219 AudioSessionControl_SetDisplayName,
2220 AudioSessionControl_GetIconPath,
2221 AudioSessionControl_SetIconPath,
2222 AudioSessionControl_GetGroupingParam,
2223 AudioSessionControl_SetGroupingParam,
2224 AudioSessionControl_RegisterAudioSessionNotification,
2225 AudioSessionControl_UnregisterAudioSessionNotification,
2226 AudioSessionControl_GetSessionIdentifier,
2227 AudioSessionControl_GetSessionInstanceIdentifier,
2228 AudioSessionControl_GetProcessId,
2229 AudioSessionControl_IsSystemSoundsSession,
2230 AudioSessionControl_SetDuckingPreference
2233 static HRESULT WINAPI SimpleAudioVolume_QueryInterface(
2234 ISimpleAudioVolume *iface, REFIID riid, void **ppv)
2236 TRACE("(%p)->(%s, %p)\n", iface, debugstr_guid(riid), ppv);
2242 if(IsEqualIID(riid, &IID_IUnknown) ||
2243 IsEqualIID(riid, &IID_ISimpleAudioVolume))
2246 IUnknown_AddRef((IUnknown*)*ppv);
2250 WARN("Unknown interface %s\n", debugstr_guid(riid));
2251 return E_NOINTERFACE;
2254 static ULONG WINAPI SimpleAudioVolume_AddRef(ISimpleAudioVolume *iface)
2256 ACImpl *This = impl_from_ISimpleAudioVolume(iface);
2257 return IAudioClient_AddRef(&This->IAudioClient_iface);
2260 static ULONG WINAPI SimpleAudioVolume_Release(ISimpleAudioVolume *iface)
2262 ACImpl *This = impl_from_ISimpleAudioVolume(iface);
2263 return IAudioClient_Release(&This->IAudioClient_iface);
2266 static HRESULT WINAPI SimpleAudioVolume_SetMasterVolume(
2267 ISimpleAudioVolume *iface, float level, const GUID *context)
2269 ACImpl *This = impl_from_ISimpleAudioVolume(iface);
2271 FIXME("(%p)->(%f, %p) - stub\n", This, level, context);
2276 static HRESULT WINAPI SimpleAudioVolume_GetMasterVolume(
2277 ISimpleAudioVolume *iface, float *level)
2279 ACImpl *This = impl_from_ISimpleAudioVolume(iface);
2281 FIXME("(%p)->(%p) - stub\n", This, level);
2286 static HRESULT WINAPI SimpleAudioVolume_SetMute(ISimpleAudioVolume *iface,
2287 BOOL mute, const GUID *context)
2289 ACImpl *This = impl_from_ISimpleAudioVolume(iface);
2291 FIXME("(%p)->(%u, %p) - stub\n", This, mute, context);
2296 static HRESULT WINAPI SimpleAudioVolume_GetMute(ISimpleAudioVolume *iface,
2299 ACImpl *This = impl_from_ISimpleAudioVolume(iface);
2301 FIXME("(%p)->(%p) - stub\n", This, mute);
2306 static const ISimpleAudioVolumeVtbl SimpleAudioVolume_Vtbl =
2308 SimpleAudioVolume_QueryInterface,
2309 SimpleAudioVolume_AddRef,
2310 SimpleAudioVolume_Release,
2311 SimpleAudioVolume_SetMasterVolume,
2312 SimpleAudioVolume_GetMasterVolume,
2313 SimpleAudioVolume_SetMute,
2314 SimpleAudioVolume_GetMute