gdi32: Update pixel colours when the colour table changes.
[wine/multimedia.git] / dlls / winealsa.drv / mmdevdrv.c
blob20958bf2031cfd91a6e5cb052c609380a0d28f54
1 /*
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
21 #define COBJMACROS
22 #include "config.h"
24 #include <stdarg.h>
25 #include <math.h>
27 #include "windef.h"
28 #include "winbase.h"
29 #include "winnls.h"
30 #include "winreg.h"
31 #include "wine/debug.h"
32 #include "wine/unicode.h"
33 #include "wine/list.h"
35 #include "ole2.h"
36 #include "mmdeviceapi.h"
37 #include "devpkey.h"
38 #include "dshow.h"
39 #include "dsound.h"
40 #include "endpointvolume.h"
42 #include "initguid.h"
43 #include "audioclient.h"
44 #include "audiopolicy.h"
45 #include "dsdriver.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;
56 struct ACImpl;
57 typedef struct ACImpl ACImpl;
59 typedef struct _AudioSession {
60 GUID guid;
61 struct list clients;
63 EDataFlow dataflow;
65 float master_vol;
66 UINT32 channel_count;
67 float *channel_vols;
69 CRITICAL_SECTION lock;
71 struct list entry;
72 } AudioSession;
74 typedef struct _AudioSessionWrapper {
75 IAudioSessionControl2 IAudioSessionControl2_iface;
76 IChannelAudioVolume IChannelAudioVolume_iface;
77 ISimpleAudioVolume ISimpleAudioVolume_iface;
79 LONG ref;
81 ACImpl *client;
82 AudioSession *session;
83 } AudioSessionWrapper;
85 struct ACImpl {
86 IAudioClient IAudioClient_iface;
87 IAudioRenderClient IAudioRenderClient_iface;
88 IAudioCaptureClient IAudioCaptureClient_iface;
89 IAudioClock IAudioClock_iface;
90 IAudioClock2 IAudioClock2_iface;
91 IAudioStreamVolume IAudioStreamVolume_iface;
93 LONG ref;
95 snd_pcm_t *pcm_handle;
96 snd_pcm_uframes_t period_alsa, bufsize_alsa;
97 snd_pcm_hw_params_t *hw_params; /* does not hold state between calls */
99 IMMDevice *parent;
101 EDataFlow dataflow;
102 WAVEFORMATEX *fmt;
103 DWORD flags;
104 AUDCLNT_SHAREMODE share;
105 HANDLE event;
106 float *vols;
108 BOOL initted, started;
109 UINT64 written_frames, held_frames, tmp_buffer_frames;
110 UINT32 bufsize_frames, period_us;
111 UINT32 lcl_offs_frames; /* offs into local_buffer where valid data starts */
113 HANDLE timer;
114 BYTE *local_buffer, *tmp_buffer;
115 int buf_state;
117 CRITICAL_SECTION lock;
119 AudioSession *session;
120 AudioSessionWrapper *session_wrapper;
122 struct list entry;
125 enum BufferStates {
126 NOT_LOCKED = 0,
127 LOCKED_NORMAL, /* public buffer piece is from local_buffer */
128 LOCKED_WRAPPED /* public buffer piece is wrapped around, in tmp_buffer */
131 static HANDLE g_timer_q;
133 static CRITICAL_SECTION g_sessions_lock;
134 static struct list g_sessions = LIST_INIT(g_sessions);
136 static const WCHAR defaultW[] = {'d','e','f','a','u','l','t',0};
137 static const char defname[] = "default";
139 static const IAudioClientVtbl AudioClient_Vtbl;
140 static const IAudioRenderClientVtbl AudioRenderClient_Vtbl;
141 static const IAudioCaptureClientVtbl AudioCaptureClient_Vtbl;
142 static const IAudioSessionControl2Vtbl AudioSessionControl2_Vtbl;
143 static const ISimpleAudioVolumeVtbl SimpleAudioVolume_Vtbl;
144 static const IAudioClockVtbl AudioClock_Vtbl;
145 static const IAudioClock2Vtbl AudioClock2_Vtbl;
146 static const IAudioStreamVolumeVtbl AudioStreamVolume_Vtbl;
147 static const IChannelAudioVolumeVtbl ChannelAudioVolume_Vtbl;
149 int wine_snd_pcm_recover(snd_pcm_t *pcm, int err, int silent);
150 static AudioSessionWrapper *AudioSessionWrapper_Create(ACImpl *client);
152 static inline ACImpl *impl_from_IAudioClient(IAudioClient *iface)
154 return CONTAINING_RECORD(iface, ACImpl, IAudioClient_iface);
157 static inline ACImpl *impl_from_IAudioRenderClient(IAudioRenderClient *iface)
159 return CONTAINING_RECORD(iface, ACImpl, IAudioRenderClient_iface);
162 static inline ACImpl *impl_from_IAudioCaptureClient(IAudioCaptureClient *iface)
164 return CONTAINING_RECORD(iface, ACImpl, IAudioCaptureClient_iface);
167 static inline AudioSessionWrapper *impl_from_IAudioSessionControl2(IAudioSessionControl2 *iface)
169 return CONTAINING_RECORD(iface, AudioSessionWrapper, IAudioSessionControl2_iface);
172 static inline AudioSessionWrapper *impl_from_ISimpleAudioVolume(ISimpleAudioVolume *iface)
174 return CONTAINING_RECORD(iface, AudioSessionWrapper, ISimpleAudioVolume_iface);
177 static inline AudioSessionWrapper *impl_from_IChannelAudioVolume(IChannelAudioVolume *iface)
179 return CONTAINING_RECORD(iface, AudioSessionWrapper, IChannelAudioVolume_iface);
182 static inline ACImpl *impl_from_IAudioClock(IAudioClock *iface)
184 return CONTAINING_RECORD(iface, ACImpl, IAudioClock_iface);
187 static inline ACImpl *impl_from_IAudioClock2(IAudioClock2 *iface)
189 return CONTAINING_RECORD(iface, ACImpl, IAudioClock2_iface);
192 static inline ACImpl *impl_from_IAudioStreamVolume(IAudioStreamVolume *iface)
194 return CONTAINING_RECORD(iface, ACImpl, IAudioStreamVolume_iface);
197 BOOL WINAPI DllMain(HINSTANCE dll, DWORD reason, void *reserved)
199 if(reason == DLL_PROCESS_ATTACH){
200 g_timer_q = CreateTimerQueue();
201 if(!g_timer_q)
202 return FALSE;
204 InitializeCriticalSection(&g_sessions_lock);
207 return TRUE;
210 static HRESULT alsa_get_card_devices(EDataFlow flow, WCHAR **ids, char **keys,
211 UINT *num, snd_ctl_t *ctl, int card, const WCHAR *cardnameW)
213 static const WCHAR dashW[] = {' ','-',' ',0};
214 int err, device;
215 snd_pcm_info_t *info;
217 info = HeapAlloc(GetProcessHeap(), 0, snd_pcm_info_sizeof());
218 if(!info)
219 return E_OUTOFMEMORY;
221 snd_pcm_info_set_subdevice(info, 0);
222 snd_pcm_info_set_stream(info,
223 flow == eRender ? SND_PCM_STREAM_PLAYBACK : SND_PCM_STREAM_CAPTURE);
225 device = -1;
226 for(err = snd_ctl_pcm_next_device(ctl, &device); device != -1 && err >= 0;
227 err = snd_ctl_pcm_next_device(ctl, &device)){
228 const char *devname;
230 snd_pcm_info_set_device(info, device);
232 if((err = snd_ctl_pcm_info(ctl, info)) < 0){
233 if(err == -ENOENT)
234 /* This device doesn't have the right stream direction */
235 continue;
237 WARN("Failed to get info for card %d, device %d: %d (%s)\n",
238 card, device, err, snd_strerror(err));
239 continue;
242 if(ids && keys){
243 DWORD len, cardlen;
245 devname = snd_pcm_info_get_name(info);
246 if(!devname){
247 WARN("Unable to get device name for card %d, device %d\n", card,
248 device);
249 continue;
252 cardlen = lstrlenW(cardnameW);
253 len = MultiByteToWideChar(CP_UNIXCP, 0, devname, -1, NULL, 0);
254 len += lstrlenW(dashW);
255 len += cardlen;
256 ids[*num] = HeapAlloc(GetProcessHeap(), 0, len * sizeof(WCHAR));
257 if(!ids[*num]){
258 HeapFree(GetProcessHeap(), 0, info);
259 return E_OUTOFMEMORY;
261 memcpy(ids[*num], cardnameW, cardlen * sizeof(WCHAR));
262 memcpy(ids[*num] + cardlen, dashW, lstrlenW(dashW) * sizeof(WCHAR));
263 cardlen += lstrlenW(dashW);
264 MultiByteToWideChar(CP_UNIXCP, 0, devname, -1, ids[*num] + cardlen,
265 len - cardlen);
267 keys[*num] = HeapAlloc(GetProcessHeap(), 0, 32);
268 if(!keys[*num]){
269 HeapFree(GetProcessHeap(), 0, info);
270 HeapFree(GetProcessHeap(), 0, ids[*num]);
271 return E_OUTOFMEMORY;
273 sprintf(keys[*num], "hw:%d,%d", card, device);
276 ++(*num);
279 HeapFree(GetProcessHeap(), 0, info);
281 if(err != 0)
282 WARN("Got a failure during device enumeration on card %d: %d (%s)\n",
283 card, err, snd_strerror(err));
285 return S_OK;
288 static HRESULT alsa_enum_devices(EDataFlow flow, WCHAR **ids, char **keys,
289 UINT *num)
291 int err, card;
293 card = -1;
294 *num = 0;
295 for(err = snd_card_next(&card); card != -1 && err >= 0;
296 err = snd_card_next(&card)){
297 char cardpath[64];
298 const char *cardname;
299 WCHAR *cardnameW;
300 snd_ctl_t *ctl;
301 DWORD len;
303 sprintf(cardpath, "hw:%u", card);
305 if((err = snd_ctl_open(&ctl, cardpath, 0)) < 0){
306 WARN("Unable to open ctl for ALSA device %s: %d (%s)\n", cardpath,
307 err, snd_strerror(err));
308 continue;
311 if((err = snd_card_get_name(card, (char **)&cardname)) < 0){
312 WARN("Unable to get card name for ALSA device %s: %d (%s)\n",
313 cardpath, err, snd_strerror(err));
314 /* FIXME: Should be localized */
315 cardname = "Unknown soundcard";
318 len = MultiByteToWideChar(CP_UNIXCP, 0, cardname, -1, NULL, 0);
319 cardnameW = HeapAlloc(GetProcessHeap(), 0, len * sizeof(WCHAR));
320 if(!cardnameW){
321 snd_ctl_close(ctl);
322 return E_OUTOFMEMORY;
324 MultiByteToWideChar(CP_UNIXCP, 0, cardname, -1, cardnameW, len);
326 alsa_get_card_devices(flow, ids, keys, num, ctl, card, cardnameW);
328 HeapFree(GetProcessHeap(), 0, cardnameW);
330 snd_ctl_close(ctl);
333 if(err != 0)
334 WARN("Got a failure during card enumeration: %d (%s)\n",
335 err, snd_strerror(err));
337 return S_OK;
340 HRESULT WINAPI AUDDRV_GetEndpointIDs(EDataFlow flow, WCHAR ***ids, char ***keys,
341 UINT *num, UINT *def_index)
343 HRESULT hr;
345 TRACE("%d %p %p %p %p\n", flow, ids, keys, num, def_index);
347 hr = alsa_enum_devices(flow, NULL, NULL, num);
348 if(FAILED(hr))
349 return hr;
351 *ids = HeapAlloc(GetProcessHeap(), 0, (*num + 1) * sizeof(WCHAR *));
352 *keys = HeapAlloc(GetProcessHeap(), 0, (*num + 1) * sizeof(char *));
353 if(!*ids || !*keys){
354 HeapFree(GetProcessHeap(), 0, *ids);
355 HeapFree(GetProcessHeap(), 0, *keys);
356 return E_OUTOFMEMORY;
359 (*ids)[0] = HeapAlloc(GetProcessHeap(), 0, sizeof(defaultW));
360 memcpy((*ids)[0], defaultW, sizeof(defaultW));
361 (*keys)[0] = HeapAlloc(GetProcessHeap(), 0, sizeof(defname));
362 memcpy((*keys)[0], defname, sizeof(defname));
363 *def_index = 0;
365 hr = alsa_enum_devices(flow, (*ids) + 1, (*keys) + 1, num);
366 if(FAILED(hr)){
367 int i;
368 for(i = 0; i < *num; ++i){
369 HeapFree(GetProcessHeap(), 0, (*ids)[i]);
370 HeapFree(GetProcessHeap(), 0, (*keys)[i]);
372 HeapFree(GetProcessHeap(), 0, *ids);
373 HeapFree(GetProcessHeap(), 0, *keys);
374 return E_OUTOFMEMORY;
377 ++(*num); /* for default device */
379 return S_OK;
382 HRESULT WINAPI AUDDRV_GetAudioEndpoint(const char *key, IMMDevice *dev,
383 EDataFlow dataflow, IAudioClient **out)
385 ACImpl *This;
386 int err;
387 snd_pcm_stream_t stream;
389 TRACE("\"%s\" %p %d %p\n", key, dev, dataflow, out);
391 This = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, sizeof(ACImpl));
392 if(!This)
393 return E_OUTOFMEMORY;
395 This->IAudioClient_iface.lpVtbl = &AudioClient_Vtbl;
396 This->IAudioRenderClient_iface.lpVtbl = &AudioRenderClient_Vtbl;
397 This->IAudioCaptureClient_iface.lpVtbl = &AudioCaptureClient_Vtbl;
398 This->IAudioClock_iface.lpVtbl = &AudioClock_Vtbl;
399 This->IAudioClock2_iface.lpVtbl = &AudioClock2_Vtbl;
400 This->IAudioStreamVolume_iface.lpVtbl = &AudioStreamVolume_Vtbl;
402 if(dataflow == eRender)
403 stream = SND_PCM_STREAM_PLAYBACK;
404 else if(dataflow == eCapture)
405 stream = SND_PCM_STREAM_CAPTURE;
406 else{
407 HeapFree(GetProcessHeap(), 0, This);
408 return E_UNEXPECTED;
411 This->dataflow = dataflow;
412 if((err = snd_pcm_open(&This->pcm_handle, key, stream,
413 SND_PCM_NONBLOCK)) < 0){
414 HeapFree(GetProcessHeap(), 0, This);
415 WARN("Unable to open PCM \"%s\": %d (%s)\n", key, err,
416 snd_strerror(err));
417 return E_FAIL;
420 This->hw_params = HeapAlloc(GetProcessHeap(), 0,
421 snd_pcm_hw_params_sizeof());
422 if(!This->hw_params){
423 HeapFree(GetProcessHeap(), 0, This);
424 snd_pcm_close(This->pcm_handle);
425 return E_OUTOFMEMORY;
428 InitializeCriticalSection(&This->lock);
430 This->parent = dev;
431 IMMDevice_AddRef(This->parent);
433 *out = &This->IAudioClient_iface;
434 IAudioClient_AddRef(&This->IAudioClient_iface);
436 return S_OK;
439 static HRESULT WINAPI AudioClient_QueryInterface(IAudioClient *iface,
440 REFIID riid, void **ppv)
442 TRACE("(%p)->(%s, %p)\n", iface, debugstr_guid(riid), ppv);
444 if(!ppv)
445 return E_POINTER;
446 *ppv = NULL;
447 if(IsEqualIID(riid, &IID_IUnknown) || IsEqualIID(riid, &IID_IAudioClient))
448 *ppv = iface;
449 if(*ppv){
450 IUnknown_AddRef((IUnknown*)*ppv);
451 return S_OK;
453 WARN("Unknown interface %s\n", debugstr_guid(riid));
454 return E_NOINTERFACE;
457 static ULONG WINAPI AudioClient_AddRef(IAudioClient *iface)
459 ACImpl *This = impl_from_IAudioClient(iface);
460 ULONG ref;
461 ref = InterlockedIncrement(&This->ref);
462 TRACE("(%p) Refcount now %u\n", This, ref);
463 return ref;
466 static ULONG WINAPI AudioClient_Release(IAudioClient *iface)
468 ACImpl *This = impl_from_IAudioClient(iface);
469 ULONG ref;
470 ref = InterlockedDecrement(&This->ref);
471 TRACE("(%p) Refcount now %u\n", This, ref);
472 if(!ref){
473 IAudioClient_Stop(iface);
474 IMMDevice_Release(This->parent);
475 DeleteCriticalSection(&This->lock);
476 snd_pcm_drop(This->pcm_handle);
477 snd_pcm_close(This->pcm_handle);
478 if(This->initted){
479 EnterCriticalSection(&g_sessions_lock);
480 list_remove(&This->entry);
481 if(list_empty(&This->session->clients)){
482 list_remove(&This->session->entry);
483 DeleteCriticalSection(&This->session->lock);
484 HeapFree(GetProcessHeap(), 0, This->session->channel_vols);
485 HeapFree(GetProcessHeap(), 0, This->session);
487 LeaveCriticalSection(&g_sessions_lock);
489 HeapFree(GetProcessHeap(), 0, This->vols);
490 HeapFree(GetProcessHeap(), 0, This->local_buffer);
491 HeapFree(GetProcessHeap(), 0, This->hw_params);
492 CoTaskMemFree(This->fmt);
493 HeapFree(GetProcessHeap(), 0, This);
495 return ref;
498 static void dump_fmt(const WAVEFORMATEX *fmt)
500 TRACE("wFormatTag: 0x%x (", fmt->wFormatTag);
501 switch(fmt->wFormatTag){
502 case WAVE_FORMAT_PCM:
503 TRACE("WAVE_FORMAT_PCM");
504 break;
505 case WAVE_FORMAT_IEEE_FLOAT:
506 TRACE("WAVE_FORMAT_IEEE_FLOAT");
507 break;
508 case WAVE_FORMAT_EXTENSIBLE:
509 TRACE("WAVE_FORMAT_EXTENSIBLE");
510 break;
511 default:
512 TRACE("Unknown");
513 break;
515 TRACE(")\n");
517 TRACE("nChannels: %u\n", fmt->nChannels);
518 TRACE("nSamplesPerSec: %u\n", fmt->nSamplesPerSec);
519 TRACE("nAvgBytesPerSec: %u\n", fmt->nAvgBytesPerSec);
520 TRACE("nBlockAlign: %u\n", fmt->nBlockAlign);
521 TRACE("wBitsPerSample: %u\n", fmt->wBitsPerSample);
522 TRACE("cbSize: %u\n", fmt->cbSize);
524 if(fmt->wFormatTag == WAVE_FORMAT_EXTENSIBLE){
525 WAVEFORMATEXTENSIBLE *fmtex = (void*)fmt;
526 TRACE("dwChannelMask: %08x\n", fmtex->dwChannelMask);
527 TRACE("Samples: %04x\n", fmtex->Samples.wReserved);
528 TRACE("SubFormat: %s\n", wine_dbgstr_guid(&fmtex->SubFormat));
532 static WAVEFORMATEX *clone_format(const WAVEFORMATEX *fmt)
534 WAVEFORMATEX *ret;
535 size_t size;
537 if(fmt->wFormatTag == WAVE_FORMAT_EXTENSIBLE)
538 size = sizeof(WAVEFORMATEXTENSIBLE);
539 else
540 size = sizeof(WAVEFORMATEX);
542 ret = CoTaskMemAlloc(size);
543 if(!ret)
544 return NULL;
546 memcpy(ret, fmt, size);
548 ret->cbSize = size - sizeof(WAVEFORMATEX);
550 return ret;
553 static AudioSession *create_session(const GUID *guid, EDataFlow flow,
554 int num_channels)
556 AudioSession *ret;
558 ret = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, sizeof(AudioSession));
559 if(!ret)
560 return NULL;
562 memcpy(&ret->guid, guid, sizeof(GUID));
564 ret->dataflow = flow;
566 list_init(&ret->clients);
568 list_add_head(&g_sessions, &ret->entry);
570 InitializeCriticalSection(&ret->lock);
572 ret->channel_count = num_channels;
573 ret->channel_vols = HeapAlloc(GetProcessHeap(), 0,
574 sizeof(float) * num_channels);
575 if(!ret->channel_vols){
576 HeapFree(GetProcessHeap(), 0, ret);
577 return NULL;
580 for(; num_channels > 0; --num_channels)
581 ret->channel_vols[num_channels - 1] = 1.f;
583 ret->master_vol = 1.f;
585 return ret;
588 static HRESULT WINAPI AudioClient_Initialize(IAudioClient *iface,
589 AUDCLNT_SHAREMODE mode, DWORD flags, REFERENCE_TIME duration,
590 REFERENCE_TIME period, const WAVEFORMATEX *fmt,
591 const GUID *sessionguid)
593 ACImpl *This = impl_from_IAudioClient(iface);
594 snd_pcm_sw_params_t *sw_params = NULL;
595 snd_pcm_format_t format;
596 snd_pcm_uframes_t boundary;
597 const WAVEFORMATEXTENSIBLE *fmtex = (const WAVEFORMATEXTENSIBLE *)fmt;
598 unsigned int time_us, rate;
599 int err, i;
600 HRESULT hr = S_OK;
602 TRACE("(%p)->(%x, %x, %s, %s, %p, %s)\n", This, mode, flags,
603 wine_dbgstr_longlong(duration), wine_dbgstr_longlong(period), fmt, debugstr_guid(sessionguid));
605 if(!fmt)
606 return E_POINTER;
608 if(mode != AUDCLNT_SHAREMODE_SHARED && mode != AUDCLNT_SHAREMODE_EXCLUSIVE)
609 return AUDCLNT_E_NOT_INITIALIZED;
611 if(flags & ~(AUDCLNT_STREAMFLAGS_CROSSPROCESS |
612 AUDCLNT_STREAMFLAGS_LOOPBACK |
613 AUDCLNT_STREAMFLAGS_EVENTCALLBACK |
614 AUDCLNT_STREAMFLAGS_NOPERSIST |
615 AUDCLNT_STREAMFLAGS_RATEADJUST |
616 AUDCLNT_SESSIONFLAGS_EXPIREWHENUNOWNED |
617 AUDCLNT_SESSIONFLAGS_DISPLAY_HIDE |
618 AUDCLNT_SESSIONFLAGS_DISPLAY_HIDEWHENEXPIRED)){
619 TRACE("Unknown flags: %08x\n", flags);
620 return E_INVALIDARG;
623 EnterCriticalSection(&This->lock);
625 if(This->initted){
626 LeaveCriticalSection(&This->lock);
627 return AUDCLNT_E_ALREADY_INITIALIZED;
630 dump_fmt(fmt);
632 if((err = snd_pcm_hw_params_any(This->pcm_handle, This->hw_params)) < 0){
633 WARN("Unable to get hw_params: %d (%s)\n", err, snd_strerror(err));
634 hr = E_FAIL;
635 goto exit;
638 if((err = snd_pcm_hw_params_set_access(This->pcm_handle, This->hw_params,
639 SND_PCM_ACCESS_RW_INTERLEAVED)) < 0){
640 WARN("Unable to set access: %d (%s)\n", err, snd_strerror(err));
641 hr = E_FAIL;
642 goto exit;
645 if(fmt->wFormatTag == WAVE_FORMAT_PCM ||
646 (fmt->wFormatTag == WAVE_FORMAT_EXTENSIBLE &&
647 IsEqualGUID(&fmtex->SubFormat, &KSDATAFORMAT_SUBTYPE_PCM))){
648 if(fmt->wBitsPerSample == 8)
649 format = SND_PCM_FORMAT_U8;
650 else if(fmt->wBitsPerSample == 16)
651 format = SND_PCM_FORMAT_S16_LE;
652 else if(fmt->wBitsPerSample == 24)
653 format = SND_PCM_FORMAT_S24_3LE;
654 else if(fmt->wBitsPerSample == 32)
655 format = SND_PCM_FORMAT_S32_LE;
656 else{
657 WARN("Unsupported bit depth: %u\n", fmt->wBitsPerSample);
658 hr = AUDCLNT_E_UNSUPPORTED_FORMAT;
659 goto exit;
661 }else if(fmt->wFormatTag == WAVE_FORMAT_IEEE_FLOAT ||
662 (fmt->wFormatTag == WAVE_FORMAT_EXTENSIBLE &&
663 IsEqualGUID(&fmtex->SubFormat, &KSDATAFORMAT_SUBTYPE_IEEE_FLOAT))){
664 if(fmt->wBitsPerSample == 32)
665 format = SND_PCM_FORMAT_FLOAT_LE;
666 else if(fmt->wBitsPerSample == 64)
667 format = SND_PCM_FORMAT_FLOAT64_LE;
668 else{
669 WARN("Unsupported float size: %u\n", fmt->wBitsPerSample);
670 hr = AUDCLNT_E_UNSUPPORTED_FORMAT;
671 goto exit;
673 }else{
674 WARN("Unknown wave format: %04x\n", fmt->wFormatTag);
675 hr = AUDCLNT_E_UNSUPPORTED_FORMAT;
676 goto exit;
679 if((err = snd_pcm_hw_params_set_format(This->pcm_handle, This->hw_params,
680 format)) < 0){
681 WARN("Unable to set ALSA format to %u: %d (%s)\n", format, err,
682 snd_strerror(err));
683 hr = E_FAIL;
684 goto exit;
687 rate = fmt->nSamplesPerSec;
688 if((err = snd_pcm_hw_params_set_rate_near(This->pcm_handle, This->hw_params,
689 &rate, NULL)) < 0){
690 WARN("Unable to set rate to %u: %d (%s)\n", rate, err,
691 snd_strerror(err));
692 hr = E_FAIL;
693 goto exit;
696 if((err = snd_pcm_hw_params_set_channels(This->pcm_handle, This->hw_params,
697 fmt->nChannels)) < 0){
698 WARN("Unable to set channels to %u: %d (%s)\n", fmt->nChannels, err,
699 snd_strerror(err));
700 hr = E_FAIL;
701 goto exit;
704 time_us = MinimumPeriod / 10;
705 if((err = snd_pcm_hw_params_set_period_time_near(This->pcm_handle,
706 This->hw_params, &time_us, NULL)) < 0){
707 WARN("Unable to set max period time to %u: %d (%s)\n", time_us,
708 err, snd_strerror(err));
709 hr = E_FAIL;
710 goto exit;
713 if((err = snd_pcm_hw_params(This->pcm_handle, This->hw_params)) < 0){
714 WARN("Unable to set hw params: %d (%s)\n", err, snd_strerror(err));
715 hr = E_FAIL;
716 goto exit;
719 sw_params = HeapAlloc(GetProcessHeap(), 0, snd_pcm_sw_params_sizeof());
720 if(!sw_params){
721 hr = E_OUTOFMEMORY;
722 goto exit;
725 if((err = snd_pcm_sw_params_current(This->pcm_handle, sw_params)) < 0){
726 WARN("Unable to get sw_params: %d (%s)\n", err, snd_strerror(err));
727 hr = E_FAIL;
728 goto exit;
731 This->bufsize_frames = ceil((duration / 10000000.) * fmt->nSamplesPerSec);
732 This->local_buffer = HeapAlloc(GetProcessHeap(), 0,
733 This->bufsize_frames * fmt->nBlockAlign);
734 if(!This->local_buffer){
735 hr = E_OUTOFMEMORY;
736 goto exit;
738 if (fmt->wBitsPerSample == 8)
739 memset(This->local_buffer, 128, This->bufsize_frames * fmt->nBlockAlign);
740 else
741 memset(This->local_buffer, 0, This->bufsize_frames * fmt->nBlockAlign);
743 if((err = snd_pcm_sw_params_get_boundary(sw_params, &boundary)) < 0){
744 WARN("Unable to get boundary: %d (%s)\n", err, snd_strerror(err));
745 hr = E_FAIL;
746 goto exit;
749 if((err = snd_pcm_sw_params_set_start_threshold(This->pcm_handle,
750 sw_params, boundary)) < 0){
751 WARN("Unable to set start threshold to %lx: %d (%s)\n", boundary, err,
752 snd_strerror(err));
753 hr = E_FAIL;
754 goto exit;
757 if((err = snd_pcm_sw_params_set_stop_threshold(This->pcm_handle,
758 sw_params, boundary)) < 0){
759 WARN("Unable to set stop threshold to %lx: %d (%s)\n", boundary, err,
760 snd_strerror(err));
761 hr = E_FAIL;
762 goto exit;
765 if((err = snd_pcm_sw_params_set_avail_min(This->pcm_handle,
766 sw_params, 0)) < 0){
767 WARN("Unable to set avail min to 0: %d (%s)\n", err, snd_strerror(err));
768 hr = E_FAIL;
769 goto exit;
772 if((err = snd_pcm_sw_params(This->pcm_handle, sw_params)) < 0){
773 WARN("Unable to set sw params: %d (%s)\n", err, snd_strerror(err));
774 hr = E_FAIL;
775 goto exit;
778 if((err = snd_pcm_prepare(This->pcm_handle)) < 0){
779 WARN("Unable to prepare device: %d (%s)\n", err, snd_strerror(err));
780 hr = E_FAIL;
781 goto exit;
784 if((err = snd_pcm_hw_params_get_buffer_size(This->hw_params,
785 &This->bufsize_alsa)) < 0){
786 WARN("Unable to get buffer size: %d (%s)\n", err, snd_strerror(err));
787 hr = E_FAIL;
788 goto exit;
791 if((err = snd_pcm_hw_params_get_period_size(This->hw_params,
792 &This->period_alsa, NULL)) < 0){
793 WARN("Unable to get period size: %d (%s)\n", err, snd_strerror(err));
794 hr = E_FAIL;
795 goto exit;
798 if((err = snd_pcm_hw_params_get_period_time(This->hw_params,
799 &This->period_us, NULL)) < 0){
800 WARN("Unable to get period time: %d (%s)\n", err, snd_strerror(err));
801 hr = E_FAIL;
802 goto exit;
805 This->fmt = clone_format(fmt);
806 if(!This->fmt){
807 hr = E_OUTOFMEMORY;
808 goto exit;
811 This->vols = HeapAlloc(GetProcessHeap(), 0, fmt->nChannels * sizeof(float));
812 if(!This->vols){
813 hr = E_OUTOFMEMORY;
814 goto exit;
817 for(i = 0; i < fmt->nChannels; ++i)
818 This->vols[i] = 1.f;
820 This->share = mode;
821 This->flags = flags;
823 EnterCriticalSection(&g_sessions_lock);
825 if(!sessionguid || IsEqualGUID(sessionguid, &GUID_NULL)){
826 This->session = create_session(&GUID_NULL, This->dataflow,
827 fmt->nChannels);
828 if(!This->session){
829 LeaveCriticalSection(&g_sessions_lock);
830 hr = E_OUTOFMEMORY;
831 goto exit;
833 }else{
834 AudioSession *session;
836 LIST_FOR_EACH_ENTRY(session, &g_sessions, AudioSession, entry){
837 if(IsEqualGUID(sessionguid, &session->guid) &&
838 This->dataflow == session->dataflow){
839 if(session->channel_count != fmt->nChannels){
840 LeaveCriticalSection(&g_sessions_lock);
841 hr = E_INVALIDARG;
842 goto exit;
844 This->session = session;
848 if(!This->session){
849 This->session = create_session(sessionguid, This->dataflow,
850 fmt->nChannels);
851 if(!This->session){
852 LeaveCriticalSection(&g_sessions_lock);
853 hr = E_OUTOFMEMORY;
854 goto exit;
859 list_add_tail(&This->session->clients, &This->entry);
861 LeaveCriticalSection(&g_sessions_lock);
863 This->initted = TRUE;
865 exit:
866 HeapFree(GetProcessHeap(), 0, sw_params);
867 if(FAILED(hr)){
868 if(This->local_buffer){
869 HeapFree(GetProcessHeap(), 0, This->local_buffer);
870 This->local_buffer = NULL;
872 if(This->fmt){
873 HeapFree(GetProcessHeap(), 0, This->fmt);
874 This->fmt = NULL;
876 if(This->vols){
877 HeapFree(GetProcessHeap(), 0, This->vols);
878 This->vols = NULL;
882 LeaveCriticalSection(&This->lock);
884 return hr;
887 static HRESULT WINAPI AudioClient_GetBufferSize(IAudioClient *iface,
888 UINT32 *out)
890 ACImpl *This = impl_from_IAudioClient(iface);
892 TRACE("(%p)->(%p)\n", This, out);
894 if(!out)
895 return E_POINTER;
897 EnterCriticalSection(&This->lock);
899 if(!This->initted){
900 LeaveCriticalSection(&This->lock);
901 return AUDCLNT_E_NOT_INITIALIZED;
904 *out = This->bufsize_frames;
906 LeaveCriticalSection(&This->lock);
908 return S_OK;
911 static HRESULT WINAPI AudioClient_GetStreamLatency(IAudioClient *iface,
912 REFERENCE_TIME *latency)
914 ACImpl *This = impl_from_IAudioClient(iface);
916 TRACE("(%p)->(%p)\n", This, latency);
918 if(!latency)
919 return E_POINTER;
921 EnterCriticalSection(&This->lock);
923 if(!This->initted){
924 LeaveCriticalSection(&This->lock);
925 return AUDCLNT_E_NOT_INITIALIZED;
928 LeaveCriticalSection(&This->lock);
930 *latency = 500000;
932 return S_OK;
935 static HRESULT WINAPI AudioClient_GetCurrentPadding(IAudioClient *iface,
936 UINT32 *out)
938 ACImpl *This = impl_from_IAudioClient(iface);
940 TRACE("(%p)->(%p)\n", This, out);
942 if(!out)
943 return E_POINTER;
945 EnterCriticalSection(&This->lock);
947 if(!This->initted){
948 LeaveCriticalSection(&This->lock);
949 return AUDCLNT_E_NOT_INITIALIZED;
952 if(This->dataflow == eRender){
953 snd_pcm_sframes_t avail_frames;
955 avail_frames = snd_pcm_avail_update(This->pcm_handle);
957 if(This->bufsize_alsa < avail_frames){
958 WARN("Xrun detected\n");
959 *out = This->held_frames;
960 }else
961 *out = This->bufsize_alsa - avail_frames + This->held_frames;
962 }else if(This->dataflow == eCapture){
963 *out = This->held_frames;
964 }else{
965 LeaveCriticalSection(&This->lock);
966 return E_UNEXPECTED;
969 LeaveCriticalSection(&This->lock);
971 return S_OK;
974 static DWORD get_channel_mask(unsigned int channels)
976 switch(channels){
977 case 0:
978 return 0;
979 case 1:
980 return SPEAKER_FRONT_CENTER;
981 case 2:
982 return SPEAKER_FRONT_LEFT | SPEAKER_FRONT_RIGHT;
983 case 3:
984 return SPEAKER_FRONT_LEFT | SPEAKER_FRONT_RIGHT |
985 SPEAKER_LOW_FREQUENCY;
986 case 4:
987 return SPEAKER_FRONT_LEFT | SPEAKER_FRONT_RIGHT | SPEAKER_BACK_LEFT |
988 SPEAKER_BACK_RIGHT;
989 case 5:
990 return SPEAKER_FRONT_LEFT | SPEAKER_FRONT_RIGHT | SPEAKER_BACK_LEFT |
991 SPEAKER_BACK_RIGHT | SPEAKER_LOW_FREQUENCY;
992 case 6:
993 return SPEAKER_FRONT_LEFT | SPEAKER_FRONT_RIGHT | SPEAKER_BACK_LEFT |
994 SPEAKER_BACK_RIGHT | SPEAKER_LOW_FREQUENCY | SPEAKER_FRONT_CENTER;
995 case 7:
996 return SPEAKER_FRONT_LEFT | SPEAKER_FRONT_RIGHT | SPEAKER_BACK_LEFT |
997 SPEAKER_BACK_RIGHT | SPEAKER_LOW_FREQUENCY | SPEAKER_FRONT_CENTER |
998 SPEAKER_BACK_CENTER;
1000 FIXME("Unknown speaker configuration: %u\n", channels);
1001 return 0;
1004 static HRESULT WINAPI AudioClient_IsFormatSupported(IAudioClient *iface,
1005 AUDCLNT_SHAREMODE mode, const WAVEFORMATEX *fmt,
1006 WAVEFORMATEX **out)
1008 ACImpl *This = impl_from_IAudioClient(iface);
1009 snd_pcm_format_mask_t *formats = NULL;
1010 HRESULT hr = S_OK;
1011 WAVEFORMATEX *closest = NULL;
1012 const WAVEFORMATEXTENSIBLE *fmtex = (const WAVEFORMATEXTENSIBLE *)fmt;
1013 unsigned int max = 0, min = 0;
1014 int err;
1016 TRACE("(%p)->(%x, %p, %p)\n", This, mode, fmt, out);
1018 if(!fmt || (mode == AUDCLNT_SHAREMODE_SHARED && !out))
1019 return E_POINTER;
1021 if(mode != AUDCLNT_SHAREMODE_SHARED && mode != AUDCLNT_SHAREMODE_EXCLUSIVE)
1022 return E_INVALIDARG;
1024 if(fmt->wFormatTag == WAVE_FORMAT_EXTENSIBLE &&
1025 fmt->cbSize < sizeof(WAVEFORMATEXTENSIBLE) - sizeof(WAVEFORMATEX))
1026 return E_INVALIDARG;
1028 dump_fmt(fmt);
1030 EnterCriticalSection(&This->lock);
1032 if((err = snd_pcm_hw_params_any(This->pcm_handle, This->hw_params)) < 0){
1033 hr = E_FAIL;
1034 goto exit;
1037 formats = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY,
1038 snd_pcm_format_mask_sizeof());
1039 if(!formats){
1040 hr = E_OUTOFMEMORY;
1041 goto exit;
1044 snd_pcm_hw_params_get_format_mask(This->hw_params, formats);
1046 if(fmt->wFormatTag == WAVE_FORMAT_PCM ||
1047 (fmt->wFormatTag == WAVE_FORMAT_EXTENSIBLE &&
1048 IsEqualGUID(&fmtex->SubFormat, &KSDATAFORMAT_SUBTYPE_PCM))){
1049 switch(fmt->wBitsPerSample){
1050 case 8:
1051 if(!snd_pcm_format_mask_test(formats, SND_PCM_FORMAT_U8)){
1052 hr = AUDCLNT_E_UNSUPPORTED_FORMAT;
1053 goto exit;
1055 break;
1056 case 16:
1057 if(!snd_pcm_format_mask_test(formats, SND_PCM_FORMAT_S16_LE)){
1058 hr = AUDCLNT_E_UNSUPPORTED_FORMAT;
1059 goto exit;
1061 break;
1062 case 24:
1063 if(!snd_pcm_format_mask_test(formats, SND_PCM_FORMAT_S24_3LE)){
1064 hr = AUDCLNT_E_UNSUPPORTED_FORMAT;
1065 goto exit;
1067 break;
1068 case 32:
1069 if(!snd_pcm_format_mask_test(formats, SND_PCM_FORMAT_S32_LE)){
1070 hr = AUDCLNT_E_UNSUPPORTED_FORMAT;
1071 goto exit;
1073 break;
1074 default:
1075 hr = AUDCLNT_E_UNSUPPORTED_FORMAT;
1076 goto exit;
1078 }else if(fmt->wFormatTag == WAVE_FORMAT_IEEE_FLOAT ||
1079 (fmt->wFormatTag == WAVE_FORMAT_EXTENSIBLE &&
1080 IsEqualGUID(&fmtex->SubFormat, &KSDATAFORMAT_SUBTYPE_IEEE_FLOAT))){
1081 switch(fmt->wBitsPerSample){
1082 case 32:
1083 if(!snd_pcm_format_mask_test(formats, SND_PCM_FORMAT_FLOAT_LE)){
1084 hr = AUDCLNT_E_UNSUPPORTED_FORMAT;
1085 goto exit;
1087 break;
1088 case 64:
1089 if(!snd_pcm_format_mask_test(formats, SND_PCM_FORMAT_FLOAT64_LE)){
1090 hr = AUDCLNT_E_UNSUPPORTED_FORMAT;
1091 goto exit;
1093 break;
1094 default:
1095 hr = AUDCLNT_E_UNSUPPORTED_FORMAT;
1096 goto exit;
1098 }else{
1099 hr = AUDCLNT_E_UNSUPPORTED_FORMAT;
1100 goto exit;
1103 closest = clone_format(fmt);
1104 if(!closest){
1105 hr = E_OUTOFMEMORY;
1106 goto exit;
1109 if((err = snd_pcm_hw_params_get_rate_min(This->hw_params, &min, NULL)) < 0){
1110 hr = E_FAIL;
1111 WARN("Unable to get min rate: %d (%s)\n", err, snd_strerror(err));
1112 goto exit;
1115 if((err = snd_pcm_hw_params_get_rate_max(This->hw_params, &max, NULL)) < 0){
1116 hr = E_FAIL;
1117 WARN("Unable to get max rate: %d (%s)\n", err, snd_strerror(err));
1118 goto exit;
1121 if(fmt->nSamplesPerSec < min || fmt->nSamplesPerSec > max ||
1122 (fmt->nSamplesPerSec != 48000 &&
1123 fmt->nSamplesPerSec != 44100 &&
1124 fmt->nSamplesPerSec != 22050 &&
1125 fmt->nSamplesPerSec != 11025 &&
1126 fmt->nSamplesPerSec != 8000)){
1127 hr = AUDCLNT_E_UNSUPPORTED_FORMAT;
1128 goto exit;
1131 if((err = snd_pcm_hw_params_get_channels_min(This->hw_params, &min)) < 0){
1132 hr = E_FAIL;
1133 WARN("Unable to get min channels: %d (%s)\n", err, snd_strerror(err));
1134 goto exit;
1137 if((err = snd_pcm_hw_params_get_channels_max(This->hw_params, &max)) < 0){
1138 hr = E_FAIL;
1139 WARN("Unable to get max channels: %d (%s)\n", err, snd_strerror(err));
1140 goto exit;
1142 if(max > 7)
1143 max = 2;
1144 if(fmt->nChannels > max){
1145 hr = S_FALSE;
1146 closest->nChannels = max;
1147 }else if(fmt->nChannels < min){
1148 hr = S_FALSE;
1149 closest->nChannels = min;
1152 if(closest->wFormatTag == WAVE_FORMAT_EXTENSIBLE){
1153 DWORD mask = get_channel_mask(closest->nChannels);
1155 ((WAVEFORMATEXTENSIBLE*)closest)->dwChannelMask = mask;
1157 if(fmt->wFormatTag == WAVE_FORMAT_EXTENSIBLE &&
1158 fmtex->dwChannelMask != mask)
1159 hr = S_FALSE;
1162 exit:
1163 LeaveCriticalSection(&This->lock);
1164 HeapFree(GetProcessHeap(), 0, formats);
1166 if(hr == S_OK || !out){
1167 CoTaskMemFree(closest);
1168 if(out)
1169 *out = NULL;
1170 }else if(closest){
1171 closest->nBlockAlign =
1172 closest->nChannels * closest->wBitsPerSample / 8;
1173 closest->nAvgBytesPerSec =
1174 closest->nBlockAlign * closest->nSamplesPerSec;
1175 *out = closest;
1178 TRACE("returning: %08x\n", hr);
1179 return hr;
1182 static HRESULT WINAPI AudioClient_GetMixFormat(IAudioClient *iface,
1183 WAVEFORMATEX **pwfx)
1185 ACImpl *This = impl_from_IAudioClient(iface);
1186 WAVEFORMATEXTENSIBLE *fmt;
1187 snd_pcm_format_mask_t *formats;
1188 unsigned int max_rate, max_channels;
1189 int err;
1190 HRESULT hr = S_OK;
1192 TRACE("(%p)->(%p)\n", This, pwfx);
1194 if(!pwfx)
1195 return E_POINTER;
1197 *pwfx = HeapAlloc(GetProcessHeap(), 0, sizeof(WAVEFORMATEXTENSIBLE));
1198 if(!*pwfx)
1199 return E_OUTOFMEMORY;
1201 fmt = (WAVEFORMATEXTENSIBLE*)*pwfx;
1203 formats = HeapAlloc(GetProcessHeap(), 0, snd_pcm_format_mask_sizeof());
1204 if(!formats){
1205 HeapFree(GetProcessHeap(), 0, *pwfx);
1206 return E_OUTOFMEMORY;
1209 EnterCriticalSection(&This->lock);
1211 if((err = snd_pcm_hw_params_any(This->pcm_handle, This->hw_params)) < 0){
1212 WARN("Unable to get hw_params: %d (%s)\n", err, snd_strerror(err));
1213 hr = E_FAIL;
1214 goto exit;
1217 snd_pcm_hw_params_get_format_mask(This->hw_params, formats);
1219 fmt->Format.wFormatTag = WAVE_FORMAT_EXTENSIBLE;
1220 if(snd_pcm_format_mask_test(formats, SND_PCM_FORMAT_FLOAT_LE)){
1221 fmt->Format.wBitsPerSample = 32;
1222 fmt->SubFormat = KSDATAFORMAT_SUBTYPE_IEEE_FLOAT;
1223 }else if(snd_pcm_format_mask_test(formats, SND_PCM_FORMAT_S16_LE)){
1224 fmt->Format.wBitsPerSample = 16;
1225 fmt->SubFormat = KSDATAFORMAT_SUBTYPE_PCM;
1226 }else if(snd_pcm_format_mask_test(formats, SND_PCM_FORMAT_U8)){
1227 fmt->Format.wBitsPerSample = 8;
1228 fmt->SubFormat = KSDATAFORMAT_SUBTYPE_PCM;
1229 }else if(snd_pcm_format_mask_test(formats, SND_PCM_FORMAT_S32_LE)){
1230 fmt->Format.wBitsPerSample = 32;
1231 fmt->SubFormat = KSDATAFORMAT_SUBTYPE_PCM;
1232 }else if(snd_pcm_format_mask_test(formats, SND_PCM_FORMAT_S24_3LE)){
1233 fmt->Format.wBitsPerSample = 24;
1234 fmt->SubFormat = KSDATAFORMAT_SUBTYPE_PCM;
1235 }else{
1236 ERR("Didn't recognize any available ALSA formats\n");
1237 hr = E_FAIL;
1238 goto exit;
1241 if((err = snd_pcm_hw_params_get_channels_max(This->hw_params,
1242 &max_channels)) < 0){
1243 WARN("Unable to get max channels: %d (%s)\n", err, snd_strerror(err));
1244 hr = E_FAIL;
1245 goto exit;
1248 if(max_channels > 2){
1249 FIXME("Don't know what to do with %u channels, pretending there's "
1250 "only 2 channels\n", max_channels);
1251 fmt->Format.nChannels = 2;
1252 }else
1253 fmt->Format.nChannels = max_channels;
1255 fmt->dwChannelMask = get_channel_mask(fmt->Format.nChannels);
1257 if((err = snd_pcm_hw_params_get_rate_max(This->hw_params, &max_rate,
1258 NULL)) < 0){
1259 WARN("Unable to get max rate: %d (%s)\n", err, snd_strerror(err));
1260 hr = E_FAIL;
1261 goto exit;
1264 if(max_rate >= 48000)
1265 fmt->Format.nSamplesPerSec = 48000;
1266 else if(max_rate >= 44100)
1267 fmt->Format.nSamplesPerSec = 44100;
1268 else if(max_rate >= 22050)
1269 fmt->Format.nSamplesPerSec = 22050;
1270 else if(max_rate >= 11025)
1271 fmt->Format.nSamplesPerSec = 11025;
1272 else if(max_rate >= 8000)
1273 fmt->Format.nSamplesPerSec = 8000;
1274 else{
1275 ERR("Unknown max rate: %u\n", max_rate);
1276 hr = E_FAIL;
1277 goto exit;
1280 fmt->Format.nBlockAlign = (fmt->Format.wBitsPerSample *
1281 fmt->Format.nChannels) / 8;
1282 fmt->Format.nAvgBytesPerSec = fmt->Format.nSamplesPerSec *
1283 fmt->Format.nBlockAlign;
1285 fmt->Samples.wValidBitsPerSample = fmt->Format.wBitsPerSample;
1286 fmt->Format.cbSize = sizeof(WAVEFORMATEXTENSIBLE) - sizeof(WAVEFORMATEX);
1288 dump_fmt((WAVEFORMATEX*)fmt);
1290 exit:
1291 LeaveCriticalSection(&This->lock);
1292 if(FAILED(hr))
1293 HeapFree(GetProcessHeap(), 0, *pwfx);
1294 HeapFree(GetProcessHeap(), 0, formats);
1296 return hr;
1299 static HRESULT WINAPI AudioClient_GetDevicePeriod(IAudioClient *iface,
1300 REFERENCE_TIME *defperiod, REFERENCE_TIME *minperiod)
1302 ACImpl *This = impl_from_IAudioClient(iface);
1304 TRACE("(%p)->(%p, %p)\n", This, defperiod, minperiod);
1306 if(!defperiod && !minperiod)
1307 return E_POINTER;
1309 if(defperiod)
1310 *defperiod = DefaultPeriod;
1311 if(minperiod)
1312 *minperiod = MinimumPeriod;
1314 return S_OK;
1317 static snd_pcm_sframes_t alsa_write_best_effort(snd_pcm_t *handle, BYTE *buf,
1318 snd_pcm_uframes_t frames)
1320 snd_pcm_sframes_t written;
1322 written = snd_pcm_writei(handle, buf, frames);
1323 if(written < 0){
1324 int ret;
1326 if(written == -EAGAIN)
1327 /* buffer full */
1328 return 0;
1330 WARN("writei failed, recovering: %ld (%s)\n", written,
1331 snd_strerror(written));
1333 ret = wine_snd_pcm_recover(handle, written, 0);
1334 if(ret < 0){
1335 WARN("Could not recover: %d (%s)\n", ret, snd_strerror(ret));
1336 return ret;
1339 written = snd_pcm_writei(handle, buf, frames);
1342 return written;
1345 static void alsa_write_data(ACImpl *This)
1347 snd_pcm_sframes_t written;
1348 snd_pcm_uframes_t to_write;
1349 BYTE *buf =
1350 This->local_buffer + (This->lcl_offs_frames * This->fmt->nBlockAlign);
1352 if(This->lcl_offs_frames + This->held_frames > This->bufsize_frames)
1353 to_write = This->bufsize_frames - This->lcl_offs_frames;
1354 else
1355 to_write = This->held_frames;
1357 written = alsa_write_best_effort(This->pcm_handle, buf, to_write);
1358 if(written < 0){
1359 WARN("Couldn't write: %ld (%s)\n", written, snd_strerror(written));
1360 return;
1363 This->lcl_offs_frames += written;
1364 This->lcl_offs_frames %= This->bufsize_frames;
1365 This->held_frames -= written;
1367 if(written < to_write){
1368 /* ALSA buffer probably full */
1369 return;
1372 if(This->held_frames){
1373 /* wrapped and have some data back at the start to write */
1374 written = alsa_write_best_effort(This->pcm_handle, This->local_buffer,
1375 This->held_frames);
1376 if(written < 0){
1377 WARN("Couldn't write: %ld (%s)\n", written, snd_strerror(written));
1378 return;
1381 This->lcl_offs_frames += written;
1382 This->lcl_offs_frames %= This->bufsize_frames;
1383 This->held_frames -= written;
1387 static void alsa_read_data(ACImpl *This)
1389 snd_pcm_sframes_t pos, readable, nread;
1391 pos = (This->held_frames + This->lcl_offs_frames) % This->bufsize_frames;
1392 readable = This->bufsize_frames - pos;
1394 nread = snd_pcm_readi(This->pcm_handle,
1395 This->local_buffer + pos * This->fmt->nBlockAlign, readable);
1396 if(nread < 0){
1397 int ret;
1399 WARN("read failed, recovering: %ld (%s)\n", nread, snd_strerror(nread));
1401 ret = wine_snd_pcm_recover(This->pcm_handle, nread, 0);
1402 if(ret < 0){
1403 WARN("Recover failed: %d (%s)\n", ret, snd_strerror(ret));
1404 return;
1407 nread = snd_pcm_readi(This->pcm_handle,
1408 This->local_buffer + pos * This->fmt->nBlockAlign, readable);
1409 if(nread < 0){
1410 WARN("read failed: %ld (%s)\n", nread, snd_strerror(nread));
1411 return;
1415 This->held_frames += nread;
1417 if(This->held_frames > This->bufsize_frames){
1418 WARN("Overflow of unread data\n");
1419 This->lcl_offs_frames += This->held_frames;
1420 This->lcl_offs_frames %= This->bufsize_frames;
1421 This->held_frames = This->bufsize_frames;
1425 static void CALLBACK alsa_push_buffer_data(void *user, BOOLEAN timer)
1427 ACImpl *This = user;
1429 EnterCriticalSection(&This->lock);
1431 if(This->dataflow == eRender && This->held_frames)
1432 alsa_write_data(This);
1433 else if(This->dataflow == eCapture)
1434 alsa_read_data(This);
1436 if(This->event)
1437 SetEvent(This->event);
1439 LeaveCriticalSection(&This->lock);
1442 static HRESULT alsa_consider_start(ACImpl *This)
1444 snd_pcm_sframes_t avail;
1445 int err;
1447 avail = snd_pcm_avail_update(This->pcm_handle);
1448 if(avail < 0){
1449 WARN("Unable to get avail_update: %ld (%s)\n", avail,
1450 snd_strerror(avail));
1451 return E_FAIL;
1454 if(This->period_alsa < This->bufsize_alsa - avail){
1455 if((err = snd_pcm_start(This->pcm_handle)) < 0){
1456 WARN("Start failed: %d (%s), state: %d\n", err, snd_strerror(err),
1457 snd_pcm_state(This->pcm_handle));
1458 return E_FAIL;
1461 return S_OK;
1464 return S_FALSE;
1467 static HRESULT WINAPI AudioClient_Start(IAudioClient *iface)
1469 ACImpl *This = impl_from_IAudioClient(iface);
1470 DWORD period_ms;
1471 HRESULT hr;
1473 TRACE("(%p)\n", This);
1475 EnterCriticalSection(&This->lock);
1477 if(!This->initted){
1478 LeaveCriticalSection(&This->lock);
1479 return AUDCLNT_E_NOT_INITIALIZED;
1482 if((This->flags & AUDCLNT_STREAMFLAGS_EVENTCALLBACK) && !This->event){
1483 LeaveCriticalSection(&This->lock);
1484 return AUDCLNT_E_EVENTHANDLE_NOT_SET;
1487 if(This->started){
1488 LeaveCriticalSection(&This->lock);
1489 return AUDCLNT_E_NOT_STOPPED;
1492 hr = alsa_consider_start(This);
1493 if(FAILED(hr)){
1494 LeaveCriticalSection(&This->lock);
1495 return hr;
1498 period_ms = This->period_us / 1000;
1499 if(!period_ms)
1500 period_ms = 10;
1502 if(This->dataflow == eCapture){
1503 /* dump any data that might be leftover in the ALSA capture buffer */
1504 snd_pcm_readi(This->pcm_handle, This->local_buffer,
1505 This->bufsize_frames);
1508 if(!CreateTimerQueueTimer(&This->timer, g_timer_q, alsa_push_buffer_data,
1509 This, 0, period_ms, WT_EXECUTEINTIMERTHREAD)){
1510 LeaveCriticalSection(&This->lock);
1511 WARN("Unable to create timer: %u\n", GetLastError());
1512 return E_FAIL;
1515 This->started = TRUE;
1517 LeaveCriticalSection(&This->lock);
1519 return S_OK;
1522 static HRESULT WINAPI AudioClient_Stop(IAudioClient *iface)
1524 ACImpl *This = impl_from_IAudioClient(iface);
1525 int err;
1527 TRACE("(%p)\n", This);
1529 EnterCriticalSection(&This->lock);
1531 if(!This->initted){
1532 LeaveCriticalSection(&This->lock);
1533 return AUDCLNT_E_NOT_INITIALIZED;
1536 if(!This->started){
1537 LeaveCriticalSection(&This->lock);
1538 return S_FALSE;
1541 DeleteTimerQueueTimer(g_timer_q, This->timer, INVALID_HANDLE_VALUE);
1543 if((err = snd_pcm_drop(This->pcm_handle)) < 0){
1544 LeaveCriticalSection(&This->lock);
1545 WARN("Drop failed: %d (%s)\n", err, snd_strerror(err));
1546 return E_FAIL;
1549 if((err = snd_pcm_prepare(This->pcm_handle)) < 0){
1550 LeaveCriticalSection(&This->lock);
1551 WARN("Prepare failed: %d (%s)\n", err, snd_strerror(err));
1552 return E_FAIL;
1555 This->started = FALSE;
1557 LeaveCriticalSection(&This->lock);
1559 return S_OK;
1562 static HRESULT WINAPI AudioClient_Reset(IAudioClient *iface)
1564 ACImpl *This = impl_from_IAudioClient(iface);
1566 TRACE("(%p)\n", This);
1568 EnterCriticalSection(&This->lock);
1570 if(!This->initted){
1571 LeaveCriticalSection(&This->lock);
1572 return AUDCLNT_E_NOT_INITIALIZED;
1575 if(This->started){
1576 LeaveCriticalSection(&This->lock);
1577 return AUDCLNT_E_NOT_STOPPED;
1580 This->held_frames = 0;
1581 This->written_frames = 0;
1582 This->lcl_offs_frames = 0;
1584 LeaveCriticalSection(&This->lock);
1586 return S_OK;
1589 static HRESULT WINAPI AudioClient_SetEventHandle(IAudioClient *iface,
1590 HANDLE event)
1592 ACImpl *This = impl_from_IAudioClient(iface);
1594 TRACE("(%p)->(%p)\n", This, event);
1596 if(!event)
1597 return E_INVALIDARG;
1599 EnterCriticalSection(&This->lock);
1601 if(!This->initted){
1602 LeaveCriticalSection(&This->lock);
1603 return AUDCLNT_E_NOT_INITIALIZED;
1606 if(!(This->flags & AUDCLNT_STREAMFLAGS_EVENTCALLBACK)){
1607 LeaveCriticalSection(&This->lock);
1608 return AUDCLNT_E_EVENTHANDLE_NOT_EXPECTED;
1611 This->event = event;
1613 LeaveCriticalSection(&This->lock);
1615 return S_OK;
1618 static HRESULT WINAPI AudioClient_GetService(IAudioClient *iface, REFIID riid,
1619 void **ppv)
1621 ACImpl *This = impl_from_IAudioClient(iface);
1623 TRACE("(%p)->(%s, %p)\n", This, debugstr_guid(riid), ppv);
1625 if(!ppv)
1626 return E_POINTER;
1627 *ppv = NULL;
1629 EnterCriticalSection(&This->lock);
1631 if(!This->initted){
1632 LeaveCriticalSection(&This->lock);
1633 return AUDCLNT_E_NOT_INITIALIZED;
1636 if(IsEqualIID(riid, &IID_IAudioRenderClient)){
1637 if(This->dataflow != eRender){
1638 LeaveCriticalSection(&This->lock);
1639 return AUDCLNT_E_WRONG_ENDPOINT_TYPE;
1641 *ppv = &This->IAudioRenderClient_iface;
1642 }else if(IsEqualIID(riid, &IID_IAudioCaptureClient)){
1643 if(This->dataflow != eCapture){
1644 LeaveCriticalSection(&This->lock);
1645 return AUDCLNT_E_WRONG_ENDPOINT_TYPE;
1647 *ppv = &This->IAudioCaptureClient_iface;
1648 }else if(IsEqualIID(riid, &IID_IAudioClock)){
1649 *ppv = &This->IAudioClock_iface;
1650 }else if(IsEqualIID(riid, &IID_IAudioStreamVolume)){
1651 *ppv = &This->IAudioStreamVolume_iface;
1652 }else if(IsEqualIID(riid, &IID_IAudioSessionControl)){
1653 if(!This->session_wrapper){
1654 This->session_wrapper = AudioSessionWrapper_Create(This);
1655 if(!This->session_wrapper){
1656 LeaveCriticalSection(&This->lock);
1657 return E_OUTOFMEMORY;
1661 *ppv = &This->session_wrapper->IAudioSessionControl2_iface;
1662 }else if(IsEqualIID(riid, &IID_IChannelAudioVolume)){
1663 if(!This->session_wrapper){
1664 This->session_wrapper = AudioSessionWrapper_Create(This);
1665 if(!This->session_wrapper){
1666 LeaveCriticalSection(&This->lock);
1667 return E_OUTOFMEMORY;
1671 *ppv = &This->session_wrapper->IChannelAudioVolume_iface;
1672 }else if(IsEqualIID(riid, &IID_ISimpleAudioVolume)){
1673 if(!This->session_wrapper){
1674 This->session_wrapper = AudioSessionWrapper_Create(This);
1675 if(!This->session_wrapper){
1676 LeaveCriticalSection(&This->lock);
1677 return E_OUTOFMEMORY;
1681 *ppv = &This->session_wrapper->ISimpleAudioVolume_iface;
1684 if(*ppv){
1685 IUnknown_AddRef((IUnknown*)*ppv);
1686 LeaveCriticalSection(&This->lock);
1687 return S_OK;
1690 LeaveCriticalSection(&This->lock);
1692 FIXME("stub %s\n", debugstr_guid(riid));
1693 return E_NOINTERFACE;
1696 static const IAudioClientVtbl AudioClient_Vtbl =
1698 AudioClient_QueryInterface,
1699 AudioClient_AddRef,
1700 AudioClient_Release,
1701 AudioClient_Initialize,
1702 AudioClient_GetBufferSize,
1703 AudioClient_GetStreamLatency,
1704 AudioClient_GetCurrentPadding,
1705 AudioClient_IsFormatSupported,
1706 AudioClient_GetMixFormat,
1707 AudioClient_GetDevicePeriod,
1708 AudioClient_Start,
1709 AudioClient_Stop,
1710 AudioClient_Reset,
1711 AudioClient_SetEventHandle,
1712 AudioClient_GetService
1715 static HRESULT WINAPI AudioRenderClient_QueryInterface(
1716 IAudioRenderClient *iface, REFIID riid, void **ppv)
1718 TRACE("(%p)->(%s, %p)\n", iface, debugstr_guid(riid), ppv);
1720 if(!ppv)
1721 return E_POINTER;
1722 *ppv = NULL;
1724 if(IsEqualIID(riid, &IID_IUnknown) ||
1725 IsEqualIID(riid, &IID_IAudioRenderClient))
1726 *ppv = iface;
1727 if(*ppv){
1728 IUnknown_AddRef((IUnknown*)*ppv);
1729 return S_OK;
1732 WARN("Unknown interface %s\n", debugstr_guid(riid));
1733 return E_NOINTERFACE;
1736 static ULONG WINAPI AudioRenderClient_AddRef(IAudioRenderClient *iface)
1738 ACImpl *This = impl_from_IAudioRenderClient(iface);
1739 return AudioClient_AddRef(&This->IAudioClient_iface);
1742 static ULONG WINAPI AudioRenderClient_Release(IAudioRenderClient *iface)
1744 ACImpl *This = impl_from_IAudioRenderClient(iface);
1745 return AudioClient_Release(&This->IAudioClient_iface);
1748 static HRESULT WINAPI AudioRenderClient_GetBuffer(IAudioRenderClient *iface,
1749 UINT32 frames, BYTE **data)
1751 ACImpl *This = impl_from_IAudioRenderClient(iface);
1752 UINT32 write_pos;
1753 UINT32 pad;
1754 HRESULT hr;
1756 TRACE("(%p)->(%u, %p)\n", This, frames, data);
1758 if(!data)
1759 return E_POINTER;
1761 EnterCriticalSection(&This->lock);
1763 if(This->buf_state != NOT_LOCKED){
1764 LeaveCriticalSection(&This->lock);
1765 return AUDCLNT_E_OUT_OF_ORDER;
1768 if(!frames){
1769 This->buf_state = LOCKED_NORMAL;
1770 LeaveCriticalSection(&This->lock);
1771 return S_OK;
1774 hr = IAudioClient_GetCurrentPadding(&This->IAudioClient_iface, &pad);
1775 if(FAILED(hr)){
1776 LeaveCriticalSection(&This->lock);
1777 return hr;
1780 if(pad + frames > This->bufsize_frames){
1781 LeaveCriticalSection(&This->lock);
1782 return AUDCLNT_E_BUFFER_TOO_LARGE;
1785 write_pos =
1786 (This->lcl_offs_frames + This->held_frames) % This->bufsize_frames;
1787 if(write_pos + frames > This->bufsize_frames){
1788 if(This->tmp_buffer_frames < frames){
1789 if(This->tmp_buffer)
1790 This->tmp_buffer = HeapReAlloc(GetProcessHeap(), 0,
1791 This->tmp_buffer, frames * This->fmt->nBlockAlign);
1792 else
1793 This->tmp_buffer = HeapAlloc(GetProcessHeap(), 0,
1794 frames * This->fmt->nBlockAlign);
1795 if(!This->tmp_buffer){
1796 LeaveCriticalSection(&This->lock);
1797 return E_OUTOFMEMORY;
1799 This->tmp_buffer_frames = frames;
1801 *data = This->tmp_buffer;
1802 This->buf_state = LOCKED_WRAPPED;
1803 }else{
1804 *data = This->local_buffer +
1805 This->lcl_offs_frames * This->fmt->nBlockAlign;
1806 This->buf_state = LOCKED_NORMAL;
1809 LeaveCriticalSection(&This->lock);
1811 return S_OK;
1814 static void alsa_wrap_buffer(ACImpl *This, BYTE *buffer, UINT32 written_bytes)
1816 snd_pcm_uframes_t write_offs_frames =
1817 (This->lcl_offs_frames + This->held_frames) % This->bufsize_frames;
1818 UINT32 write_offs_bytes = write_offs_frames * This->fmt->nBlockAlign;
1819 snd_pcm_uframes_t chunk_frames = This->bufsize_frames - write_offs_frames;
1820 UINT32 chunk_bytes = chunk_frames * This->fmt->nBlockAlign;
1822 if(written_bytes < chunk_bytes){
1823 memcpy(This->local_buffer + write_offs_bytes, buffer, written_bytes);
1824 }else{
1825 memcpy(This->local_buffer + write_offs_bytes, buffer, chunk_bytes);
1826 memcpy(This->local_buffer, buffer + chunk_bytes,
1827 written_bytes - chunk_bytes);
1831 static HRESULT WINAPI AudioRenderClient_ReleaseBuffer(
1832 IAudioRenderClient *iface, UINT32 written_frames, DWORD flags)
1834 ACImpl *This = impl_from_IAudioRenderClient(iface);
1835 UINT32 written_bytes = written_frames * This->fmt->nBlockAlign;
1836 BYTE *buffer;
1837 HRESULT hr;
1839 TRACE("(%p)->(%u, %x)\n", This, written_frames, flags);
1841 EnterCriticalSection(&This->lock);
1843 if(This->buf_state == NOT_LOCKED || !written_frames){
1844 This->buf_state = NOT_LOCKED;
1845 LeaveCriticalSection(&This->lock);
1846 return written_frames ? AUDCLNT_E_OUT_OF_ORDER : S_OK;
1849 if(This->buf_state == LOCKED_NORMAL)
1850 buffer = This->local_buffer +
1851 This->lcl_offs_frames * This->fmt->nBlockAlign;
1852 else
1853 buffer = This->tmp_buffer;
1855 if(flags & AUDCLNT_BUFFERFLAGS_SILENT){
1856 WAVEFORMATEXTENSIBLE *fmtex = (WAVEFORMATEXTENSIBLE*)This->fmt;
1857 if((This->fmt->wFormatTag == WAVE_FORMAT_PCM ||
1858 (This->fmt->wFormatTag == WAVE_FORMAT_EXTENSIBLE &&
1859 IsEqualGUID(&fmtex->SubFormat, &KSDATAFORMAT_SUBTYPE_PCM))) &&
1860 This->fmt->wBitsPerSample == 8)
1861 memset(buffer, 128, written_frames * This->fmt->nBlockAlign);
1862 else
1863 memset(buffer, 0, written_frames * This->fmt->nBlockAlign);
1866 if(This->held_frames){
1867 if(This->buf_state == LOCKED_WRAPPED)
1868 alsa_wrap_buffer(This, buffer, written_bytes);
1870 This->held_frames += written_frames;
1871 }else{
1872 snd_pcm_sframes_t written;
1874 written = alsa_write_best_effort(This->pcm_handle, buffer,
1875 written_frames);
1876 if(written < 0){
1877 LeaveCriticalSection(&This->lock);
1878 WARN("write failed: %ld (%s)\n", written, snd_strerror(written));
1879 return E_FAIL;
1882 if(written < written_frames){
1883 if(This->buf_state == LOCKED_WRAPPED)
1884 alsa_wrap_buffer(This,
1885 This->tmp_buffer + written * This->fmt->nBlockAlign,
1886 written_frames - written);
1888 This->held_frames = written_frames - written;
1892 if(This->started &&
1893 snd_pcm_state(This->pcm_handle) == SND_PCM_STATE_PREPARED){
1894 hr = alsa_consider_start(This);
1895 if(FAILED(hr)){
1896 LeaveCriticalSection(&This->lock);
1897 return hr;
1901 This->written_frames += written_frames;
1902 This->buf_state = NOT_LOCKED;
1904 LeaveCriticalSection(&This->lock);
1906 return S_OK;
1909 static const IAudioRenderClientVtbl AudioRenderClient_Vtbl = {
1910 AudioRenderClient_QueryInterface,
1911 AudioRenderClient_AddRef,
1912 AudioRenderClient_Release,
1913 AudioRenderClient_GetBuffer,
1914 AudioRenderClient_ReleaseBuffer
1917 static HRESULT WINAPI AudioCaptureClient_QueryInterface(
1918 IAudioCaptureClient *iface, REFIID riid, void **ppv)
1920 TRACE("(%p)->(%s, %p)\n", iface, debugstr_guid(riid), ppv);
1922 if(!ppv)
1923 return E_POINTER;
1924 *ppv = NULL;
1926 if(IsEqualIID(riid, &IID_IUnknown) ||
1927 IsEqualIID(riid, &IID_IAudioCaptureClient))
1928 *ppv = iface;
1929 if(*ppv){
1930 IUnknown_AddRef((IUnknown*)*ppv);
1931 return S_OK;
1934 WARN("Unknown interface %s\n", debugstr_guid(riid));
1935 return E_NOINTERFACE;
1938 static ULONG WINAPI AudioCaptureClient_AddRef(IAudioCaptureClient *iface)
1940 ACImpl *This = impl_from_IAudioCaptureClient(iface);
1941 return IAudioClient_AddRef(&This->IAudioClient_iface);
1944 static ULONG WINAPI AudioCaptureClient_Release(IAudioCaptureClient *iface)
1946 ACImpl *This = impl_from_IAudioCaptureClient(iface);
1947 return IAudioClient_Release(&This->IAudioClient_iface);
1950 static HRESULT WINAPI AudioCaptureClient_GetBuffer(IAudioCaptureClient *iface,
1951 BYTE **data, UINT32 *frames, DWORD *flags, UINT64 *devpos,
1952 UINT64 *qpcpos)
1954 ACImpl *This = impl_from_IAudioCaptureClient(iface);
1955 HRESULT hr;
1957 TRACE("(%p)->(%p, %p, %p, %p, %p)\n", This, data, frames, flags,
1958 devpos, qpcpos);
1960 if(!data || !frames || !flags)
1961 return E_POINTER;
1963 EnterCriticalSection(&This->lock);
1965 if(This->buf_state != NOT_LOCKED){
1966 LeaveCriticalSection(&This->lock);
1967 return AUDCLNT_E_OUT_OF_ORDER;
1970 hr = IAudioCaptureClient_GetNextPacketSize(iface, frames);
1971 if(FAILED(hr)){
1972 LeaveCriticalSection(&This->lock);
1973 return hr;
1976 *flags = 0;
1978 if(This->lcl_offs_frames + *frames > This->bufsize_frames){
1979 UINT32 chunk_bytes, offs_bytes, frames_bytes;
1980 if(This->tmp_buffer_frames < *frames){
1981 if(This->tmp_buffer)
1982 This->tmp_buffer = HeapReAlloc(GetProcessHeap(), 0,
1983 This->tmp_buffer, *frames * This->fmt->nBlockAlign);
1984 else
1985 This->tmp_buffer = HeapAlloc(GetProcessHeap(), 0,
1986 *frames * This->fmt->nBlockAlign);
1987 if(!This->tmp_buffer){
1988 LeaveCriticalSection(&This->lock);
1989 return E_OUTOFMEMORY;
1991 This->tmp_buffer_frames = *frames;
1994 *data = This->tmp_buffer;
1995 chunk_bytes = (This->bufsize_frames - This->lcl_offs_frames) *
1996 This->fmt->nBlockAlign;
1997 offs_bytes = This->lcl_offs_frames * This->fmt->nBlockAlign;
1998 frames_bytes = *frames * This->fmt->nBlockAlign;
1999 memcpy(This->tmp_buffer, This->local_buffer + offs_bytes, chunk_bytes);
2000 memcpy(This->tmp_buffer + chunk_bytes, This->local_buffer,
2001 frames_bytes - chunk_bytes);
2002 }else
2003 *data = This->local_buffer +
2004 This->lcl_offs_frames * This->fmt->nBlockAlign;
2006 This->buf_state = LOCKED_NORMAL;
2008 if(devpos || qpcpos)
2009 IAudioClock_GetPosition(&This->IAudioClock_iface, devpos, qpcpos);
2011 LeaveCriticalSection(&This->lock);
2013 return *frames ? S_OK : AUDCLNT_S_BUFFER_EMPTY;
2016 static HRESULT WINAPI AudioCaptureClient_ReleaseBuffer(
2017 IAudioCaptureClient *iface, UINT32 done)
2019 ACImpl *This = impl_from_IAudioCaptureClient(iface);
2021 TRACE("(%p)->(%u)\n", This, done);
2023 EnterCriticalSection(&This->lock);
2025 if(This->buf_state == NOT_LOCKED){
2026 LeaveCriticalSection(&This->lock);
2027 return AUDCLNT_E_OUT_OF_ORDER;
2030 This->held_frames -= done;
2031 This->lcl_offs_frames += done;
2032 This->lcl_offs_frames %= This->bufsize_frames;
2034 This->buf_state = NOT_LOCKED;
2036 LeaveCriticalSection(&This->lock);
2038 return S_OK;
2041 static HRESULT WINAPI AudioCaptureClient_GetNextPacketSize(
2042 IAudioCaptureClient *iface, UINT32 *frames)
2044 ACImpl *This = impl_from_IAudioCaptureClient(iface);
2046 TRACE("(%p)->(%p)\n", This, frames);
2048 return AudioClient_GetCurrentPadding(&This->IAudioClient_iface, frames);
2051 static const IAudioCaptureClientVtbl AudioCaptureClient_Vtbl =
2053 AudioCaptureClient_QueryInterface,
2054 AudioCaptureClient_AddRef,
2055 AudioCaptureClient_Release,
2056 AudioCaptureClient_GetBuffer,
2057 AudioCaptureClient_ReleaseBuffer,
2058 AudioCaptureClient_GetNextPacketSize
2061 static HRESULT WINAPI AudioClock_QueryInterface(IAudioClock *iface,
2062 REFIID riid, void **ppv)
2064 ACImpl *This = impl_from_IAudioClock(iface);
2066 TRACE("(%p)->(%s, %p)\n", iface, debugstr_guid(riid), ppv);
2068 if(!ppv)
2069 return E_POINTER;
2070 *ppv = NULL;
2072 if(IsEqualIID(riid, &IID_IUnknown) || IsEqualIID(riid, &IID_IAudioClock))
2073 *ppv = iface;
2074 else if(IsEqualIID(riid, &IID_IAudioClock2))
2075 *ppv = &This->IAudioClock2_iface;
2076 if(*ppv){
2077 IUnknown_AddRef((IUnknown*)*ppv);
2078 return S_OK;
2081 WARN("Unknown interface %s\n", debugstr_guid(riid));
2082 return E_NOINTERFACE;
2085 static ULONG WINAPI AudioClock_AddRef(IAudioClock *iface)
2087 ACImpl *This = impl_from_IAudioClock(iface);
2088 return IAudioClient_AddRef(&This->IAudioClient_iface);
2091 static ULONG WINAPI AudioClock_Release(IAudioClock *iface)
2093 ACImpl *This = impl_from_IAudioClock(iface);
2094 return IAudioClient_Release(&This->IAudioClient_iface);
2097 static HRESULT WINAPI AudioClock_GetFrequency(IAudioClock *iface, UINT64 *freq)
2099 ACImpl *This = impl_from_IAudioClock(iface);
2101 TRACE("(%p)->(%p)\n", This, freq);
2103 *freq = This->fmt->nSamplesPerSec;
2105 return S_OK;
2108 static HRESULT WINAPI AudioClock_GetPosition(IAudioClock *iface, UINT64 *pos,
2109 UINT64 *qpctime)
2111 ACImpl *This = impl_from_IAudioClock(iface);
2112 UINT32 pad;
2113 HRESULT hr;
2115 TRACE("(%p)->(%p, %p)\n", This, pos, qpctime);
2117 if(!pos)
2118 return E_POINTER;
2120 EnterCriticalSection(&This->lock);
2122 hr = IAudioClient_GetCurrentPadding(&This->IAudioClient_iface, &pad);
2123 if(FAILED(hr)){
2124 LeaveCriticalSection(&This->lock);
2125 return hr;
2128 if(This->dataflow == eRender)
2129 *pos = This->written_frames - pad;
2130 else if(This->dataflow == eCapture)
2131 *pos = This->written_frames + pad;
2133 LeaveCriticalSection(&This->lock);
2135 if(qpctime){
2136 LARGE_INTEGER stamp, freq;
2137 QueryPerformanceCounter(&stamp);
2138 QueryPerformanceFrequency(&freq);
2139 *qpctime = (stamp.QuadPart * (INT64)10000000) / freq.QuadPart;
2142 return S_OK;
2145 static HRESULT WINAPI AudioClock_GetCharacteristics(IAudioClock *iface,
2146 DWORD *chars)
2148 ACImpl *This = impl_from_IAudioClock(iface);
2150 TRACE("(%p)->(%p)\n", This, chars);
2152 if(!chars)
2153 return E_POINTER;
2155 *chars = AUDIOCLOCK_CHARACTERISTIC_FIXED_FREQ;
2157 return S_OK;
2160 static const IAudioClockVtbl AudioClock_Vtbl =
2162 AudioClock_QueryInterface,
2163 AudioClock_AddRef,
2164 AudioClock_Release,
2165 AudioClock_GetFrequency,
2166 AudioClock_GetPosition,
2167 AudioClock_GetCharacteristics
2170 static HRESULT WINAPI AudioClock2_QueryInterface(IAudioClock2 *iface,
2171 REFIID riid, void **ppv)
2173 ACImpl *This = impl_from_IAudioClock2(iface);
2174 return IAudioClock_QueryInterface(&This->IAudioClock_iface, riid, ppv);
2177 static ULONG WINAPI AudioClock2_AddRef(IAudioClock2 *iface)
2179 ACImpl *This = impl_from_IAudioClock2(iface);
2180 return IAudioClient_AddRef(&This->IAudioClient_iface);
2183 static ULONG WINAPI AudioClock2_Release(IAudioClock2 *iface)
2185 ACImpl *This = impl_from_IAudioClock2(iface);
2186 return IAudioClient_Release(&This->IAudioClient_iface);
2189 static HRESULT WINAPI AudioClock2_GetDevicePosition(IAudioClock2 *iface,
2190 UINT64 *pos, UINT64 *qpctime)
2192 ACImpl *This = impl_from_IAudioClock2(iface);
2194 FIXME("(%p)->(%p, %p)\n", This, pos, qpctime);
2196 return E_NOTIMPL;
2199 static const IAudioClock2Vtbl AudioClock2_Vtbl =
2201 AudioClock2_QueryInterface,
2202 AudioClock2_AddRef,
2203 AudioClock2_Release,
2204 AudioClock2_GetDevicePosition
2207 static AudioSessionWrapper *AudioSessionWrapper_Create(ACImpl *client)
2209 AudioSessionWrapper *ret;
2211 ret = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY,
2212 sizeof(AudioSessionWrapper));
2213 if(!ret)
2214 return NULL;
2216 ret->IAudioSessionControl2_iface.lpVtbl = &AudioSessionControl2_Vtbl;
2217 ret->ISimpleAudioVolume_iface.lpVtbl = &SimpleAudioVolume_Vtbl;
2218 ret->IChannelAudioVolume_iface.lpVtbl = &ChannelAudioVolume_Vtbl;
2220 ret->client = client;
2221 ret->session = client->session;
2222 AudioClient_AddRef(&client->IAudioClient_iface);
2224 return ret;
2227 static HRESULT WINAPI AudioSessionControl_QueryInterface(
2228 IAudioSessionControl2 *iface, REFIID riid, void **ppv)
2230 TRACE("(%p)->(%s, %p)\n", iface, debugstr_guid(riid), ppv);
2232 if(!ppv)
2233 return E_POINTER;
2234 *ppv = NULL;
2236 if(IsEqualIID(riid, &IID_IUnknown) ||
2237 IsEqualIID(riid, &IID_IAudioSessionControl) ||
2238 IsEqualIID(riid, &IID_IAudioSessionControl2))
2239 *ppv = iface;
2240 if(*ppv){
2241 IUnknown_AddRef((IUnknown*)*ppv);
2242 return S_OK;
2245 WARN("Unknown interface %s\n", debugstr_guid(riid));
2246 return E_NOINTERFACE;
2249 static ULONG WINAPI AudioSessionControl_AddRef(IAudioSessionControl2 *iface)
2251 AudioSessionWrapper *This = impl_from_IAudioSessionControl2(iface);
2252 ULONG ref;
2253 ref = InterlockedIncrement(&This->ref);
2254 TRACE("(%p) Refcount now %u\n", This, ref);
2255 return ref;
2258 static ULONG WINAPI AudioSessionControl_Release(IAudioSessionControl2 *iface)
2260 AudioSessionWrapper *This = impl_from_IAudioSessionControl2(iface);
2261 ULONG ref;
2262 ref = InterlockedDecrement(&This->ref);
2263 TRACE("(%p) Refcount now %u\n", This, ref);
2264 if(!ref){
2265 EnterCriticalSection(&This->client->lock);
2266 This->client->session_wrapper = NULL;
2267 LeaveCriticalSection(&This->client->lock);
2268 AudioClient_Release(&This->client->IAudioClient_iface);
2269 HeapFree(GetProcessHeap(), 0, This);
2271 return ref;
2274 static HRESULT WINAPI AudioSessionControl_GetState(IAudioSessionControl2 *iface,
2275 AudioSessionState *state)
2277 AudioSessionWrapper *This = impl_from_IAudioSessionControl2(iface);
2278 ACImpl *client;
2280 TRACE("(%p)->(%p)\n", This, state);
2282 if(!state)
2283 return NULL_PTR_ERR;
2285 EnterCriticalSection(&g_sessions_lock);
2287 if(list_empty(&This->session->clients)){
2288 *state = AudioSessionStateExpired;
2289 LeaveCriticalSection(&g_sessions_lock);
2290 return S_OK;
2293 LIST_FOR_EACH_ENTRY(client, &This->session->clients, ACImpl, entry){
2294 EnterCriticalSection(&client->lock);
2295 if(client->started){
2296 *state = AudioSessionStateActive;
2297 LeaveCriticalSection(&client->lock);
2298 LeaveCriticalSection(&g_sessions_lock);
2299 return S_OK;
2301 LeaveCriticalSection(&client->lock);
2304 LeaveCriticalSection(&g_sessions_lock);
2306 *state = AudioSessionStateInactive;
2308 return S_OK;
2311 static HRESULT WINAPI AudioSessionControl_GetDisplayName(
2312 IAudioSessionControl2 *iface, WCHAR **name)
2314 AudioSessionWrapper *This = impl_from_IAudioSessionControl2(iface);
2316 FIXME("(%p)->(%p) - stub\n", This, name);
2318 return E_NOTIMPL;
2321 static HRESULT WINAPI AudioSessionControl_SetDisplayName(
2322 IAudioSessionControl2 *iface, const WCHAR *name, const GUID *session)
2324 AudioSessionWrapper *This = impl_from_IAudioSessionControl2(iface);
2326 FIXME("(%p)->(%p, %s) - stub\n", This, name, debugstr_guid(session));
2328 return E_NOTIMPL;
2331 static HRESULT WINAPI AudioSessionControl_GetIconPath(
2332 IAudioSessionControl2 *iface, WCHAR **path)
2334 AudioSessionWrapper *This = impl_from_IAudioSessionControl2(iface);
2336 FIXME("(%p)->(%p) - stub\n", This, path);
2338 return E_NOTIMPL;
2341 static HRESULT WINAPI AudioSessionControl_SetIconPath(
2342 IAudioSessionControl2 *iface, const WCHAR *path, const GUID *session)
2344 AudioSessionWrapper *This = impl_from_IAudioSessionControl2(iface);
2346 FIXME("(%p)->(%p, %s) - stub\n", This, path, debugstr_guid(session));
2348 return E_NOTIMPL;
2351 static HRESULT WINAPI AudioSessionControl_GetGroupingParam(
2352 IAudioSessionControl2 *iface, GUID *group)
2354 AudioSessionWrapper *This = impl_from_IAudioSessionControl2(iface);
2356 FIXME("(%p)->(%p) - stub\n", This, group);
2358 return E_NOTIMPL;
2361 static HRESULT WINAPI AudioSessionControl_SetGroupingParam(
2362 IAudioSessionControl2 *iface, GUID *group, const GUID *session)
2364 AudioSessionWrapper *This = impl_from_IAudioSessionControl2(iface);
2366 FIXME("(%p)->(%s, %s) - stub\n", This, debugstr_guid(group),
2367 debugstr_guid(session));
2369 return E_NOTIMPL;
2372 static HRESULT WINAPI AudioSessionControl_RegisterAudioSessionNotification(
2373 IAudioSessionControl2 *iface, IAudioSessionEvents *events)
2375 AudioSessionWrapper *This = impl_from_IAudioSessionControl2(iface);
2377 FIXME("(%p)->(%p) - stub\n", This, events);
2379 return S_OK;
2382 static HRESULT WINAPI AudioSessionControl_UnregisterAudioSessionNotification(
2383 IAudioSessionControl2 *iface, IAudioSessionEvents *events)
2385 AudioSessionWrapper *This = impl_from_IAudioSessionControl2(iface);
2387 FIXME("(%p)->(%p) - stub\n", This, events);
2389 return S_OK;
2392 static HRESULT WINAPI AudioSessionControl_GetSessionIdentifier(
2393 IAudioSessionControl2 *iface, WCHAR **id)
2395 AudioSessionWrapper *This = impl_from_IAudioSessionControl2(iface);
2397 FIXME("(%p)->(%p) - stub\n", This, id);
2399 return E_NOTIMPL;
2402 static HRESULT WINAPI AudioSessionControl_GetSessionInstanceIdentifier(
2403 IAudioSessionControl2 *iface, WCHAR **id)
2405 AudioSessionWrapper *This = impl_from_IAudioSessionControl2(iface);
2407 FIXME("(%p)->(%p) - stub\n", This, id);
2409 return E_NOTIMPL;
2412 static HRESULT WINAPI AudioSessionControl_GetProcessId(
2413 IAudioSessionControl2 *iface, DWORD *pid)
2415 AudioSessionWrapper *This = impl_from_IAudioSessionControl2(iface);
2417 TRACE("(%p)->(%p)\n", This, pid);
2419 if(!pid)
2420 return E_POINTER;
2422 *pid = GetCurrentProcessId();
2424 return S_OK;
2427 static HRESULT WINAPI AudioSessionControl_IsSystemSoundsSession(
2428 IAudioSessionControl2 *iface)
2430 AudioSessionWrapper *This = impl_from_IAudioSessionControl2(iface);
2432 TRACE("(%p)\n", This);
2434 return S_FALSE;
2437 static HRESULT WINAPI AudioSessionControl_SetDuckingPreference(
2438 IAudioSessionControl2 *iface, BOOL optout)
2440 AudioSessionWrapper *This = impl_from_IAudioSessionControl2(iface);
2442 TRACE("(%p)->(%d)\n", This, optout);
2444 return S_OK;
2447 static const IAudioSessionControl2Vtbl AudioSessionControl2_Vtbl =
2449 AudioSessionControl_QueryInterface,
2450 AudioSessionControl_AddRef,
2451 AudioSessionControl_Release,
2452 AudioSessionControl_GetState,
2453 AudioSessionControl_GetDisplayName,
2454 AudioSessionControl_SetDisplayName,
2455 AudioSessionControl_GetIconPath,
2456 AudioSessionControl_SetIconPath,
2457 AudioSessionControl_GetGroupingParam,
2458 AudioSessionControl_SetGroupingParam,
2459 AudioSessionControl_RegisterAudioSessionNotification,
2460 AudioSessionControl_UnregisterAudioSessionNotification,
2461 AudioSessionControl_GetSessionIdentifier,
2462 AudioSessionControl_GetSessionInstanceIdentifier,
2463 AudioSessionControl_GetProcessId,
2464 AudioSessionControl_IsSystemSoundsSession,
2465 AudioSessionControl_SetDuckingPreference
2468 static HRESULT WINAPI SimpleAudioVolume_QueryInterface(
2469 ISimpleAudioVolume *iface, REFIID riid, void **ppv)
2471 TRACE("(%p)->(%s, %p)\n", iface, debugstr_guid(riid), ppv);
2473 if(!ppv)
2474 return E_POINTER;
2475 *ppv = NULL;
2477 if(IsEqualIID(riid, &IID_IUnknown) ||
2478 IsEqualIID(riid, &IID_ISimpleAudioVolume))
2479 *ppv = iface;
2480 if(*ppv){
2481 IUnknown_AddRef((IUnknown*)*ppv);
2482 return S_OK;
2485 WARN("Unknown interface %s\n", debugstr_guid(riid));
2486 return E_NOINTERFACE;
2489 static ULONG WINAPI SimpleAudioVolume_AddRef(ISimpleAudioVolume *iface)
2491 AudioSessionWrapper *This = impl_from_ISimpleAudioVolume(iface);
2492 return AudioSessionControl_AddRef(&This->IAudioSessionControl2_iface);
2495 static ULONG WINAPI SimpleAudioVolume_Release(ISimpleAudioVolume *iface)
2497 AudioSessionWrapper *This = impl_from_ISimpleAudioVolume(iface);
2498 return AudioSessionControl_Release(&This->IAudioSessionControl2_iface);
2501 static HRESULT WINAPI SimpleAudioVolume_SetMasterVolume(
2502 ISimpleAudioVolume *iface, float level, const GUID *context)
2504 AudioSessionWrapper *This = impl_from_ISimpleAudioVolume(iface);
2505 AudioSession *session = This->session;
2507 TRACE("(%p)->(%f, %s)\n", session, level, wine_dbgstr_guid(context));
2509 if(level < 0.f || level > 1.f)
2510 return E_INVALIDARG;
2512 if(context)
2513 FIXME("Notifications not supported yet\n");
2515 TRACE("ALSA does not support volume control\n");
2517 EnterCriticalSection(&session->lock);
2519 session->master_vol = level;
2521 LeaveCriticalSection(&session->lock);
2523 return S_OK;
2526 static HRESULT WINAPI SimpleAudioVolume_GetMasterVolume(
2527 ISimpleAudioVolume *iface, float *level)
2529 AudioSessionWrapper *This = impl_from_ISimpleAudioVolume(iface);
2530 AudioSession *session = This->session;
2532 TRACE("(%p)->(%p)\n", session, level);
2534 if(!level)
2535 return NULL_PTR_ERR;
2537 *level = session->master_vol;
2539 return S_OK;
2542 static HRESULT WINAPI SimpleAudioVolume_SetMute(ISimpleAudioVolume *iface,
2543 BOOL mute, const GUID *context)
2545 AudioSessionWrapper *This = impl_from_ISimpleAudioVolume(iface);
2546 AudioSession *session = This->session;
2548 FIXME("(%p)->(%u, %p) - stub\n", session, mute, context);
2550 return E_NOTIMPL;
2553 static HRESULT WINAPI SimpleAudioVolume_GetMute(ISimpleAudioVolume *iface,
2554 BOOL *mute)
2556 AudioSessionWrapper *This = impl_from_ISimpleAudioVolume(iface);
2557 AudioSession *session = This->session;
2559 FIXME("(%p)->(%p) - stub\n", session, mute);
2561 if(!mute)
2562 return NULL_PTR_ERR;
2564 return E_NOTIMPL;
2567 static const ISimpleAudioVolumeVtbl SimpleAudioVolume_Vtbl =
2569 SimpleAudioVolume_QueryInterface,
2570 SimpleAudioVolume_AddRef,
2571 SimpleAudioVolume_Release,
2572 SimpleAudioVolume_SetMasterVolume,
2573 SimpleAudioVolume_GetMasterVolume,
2574 SimpleAudioVolume_SetMute,
2575 SimpleAudioVolume_GetMute
2578 static HRESULT WINAPI AudioStreamVolume_QueryInterface(
2579 IAudioStreamVolume *iface, REFIID riid, void **ppv)
2581 TRACE("(%p)->(%s, %p)\n", iface, debugstr_guid(riid), ppv);
2583 if(!ppv)
2584 return E_POINTER;
2585 *ppv = NULL;
2587 if(IsEqualIID(riid, &IID_IUnknown) ||
2588 IsEqualIID(riid, &IID_IAudioStreamVolume))
2589 *ppv = iface;
2590 if(*ppv){
2591 IUnknown_AddRef((IUnknown*)*ppv);
2592 return S_OK;
2595 WARN("Unknown interface %s\n", debugstr_guid(riid));
2596 return E_NOINTERFACE;
2599 static ULONG WINAPI AudioStreamVolume_AddRef(IAudioStreamVolume *iface)
2601 ACImpl *This = impl_from_IAudioStreamVolume(iface);
2602 return IAudioClient_AddRef(&This->IAudioClient_iface);
2605 static ULONG WINAPI AudioStreamVolume_Release(IAudioStreamVolume *iface)
2607 ACImpl *This = impl_from_IAudioStreamVolume(iface);
2608 return IAudioClient_Release(&This->IAudioClient_iface);
2611 static HRESULT WINAPI AudioStreamVolume_GetChannelCount(
2612 IAudioStreamVolume *iface, UINT32 *out)
2614 ACImpl *This = impl_from_IAudioStreamVolume(iface);
2616 TRACE("(%p)->(%p)\n", This, out);
2618 if(!out)
2619 return E_POINTER;
2621 *out = This->fmt->nChannels;
2623 return S_OK;
2626 static HRESULT WINAPI AudioStreamVolume_SetChannelVolume(
2627 IAudioStreamVolume *iface, UINT32 index, float level)
2629 ACImpl *This = impl_from_IAudioStreamVolume(iface);
2631 TRACE("(%p)->(%d, %f)\n", This, index, level);
2633 if(level < 0.f || level > 1.f)
2634 return E_INVALIDARG;
2636 if(index >= This->fmt->nChannels)
2637 return E_INVALIDARG;
2639 TRACE("ALSA does not support volume control\n");
2641 EnterCriticalSection(&This->lock);
2643 This->vols[index] = level;
2645 LeaveCriticalSection(&This->lock);
2647 return S_OK;
2650 static HRESULT WINAPI AudioStreamVolume_GetChannelVolume(
2651 IAudioStreamVolume *iface, UINT32 index, float *level)
2653 ACImpl *This = impl_from_IAudioStreamVolume(iface);
2655 TRACE("(%p)->(%d, %p)\n", This, index, level);
2657 if(!level)
2658 return E_POINTER;
2660 if(index >= This->fmt->nChannels)
2661 return E_INVALIDARG;
2663 *level = This->vols[index];
2665 return S_OK;
2668 static HRESULT WINAPI AudioStreamVolume_SetAllVolumes(
2669 IAudioStreamVolume *iface, UINT32 count, const float *levels)
2671 ACImpl *This = impl_from_IAudioStreamVolume(iface);
2672 int i;
2674 TRACE("(%p)->(%d, %p)\n", This, count, levels);
2676 if(!levels)
2677 return E_POINTER;
2679 if(count != This->fmt->nChannels)
2680 return E_INVALIDARG;
2682 TRACE("ALSA does not support volume control\n");
2684 EnterCriticalSection(&This->lock);
2686 for(i = 0; i < count; ++i)
2687 This->vols[i] = levels[i];
2689 LeaveCriticalSection(&This->lock);
2691 return S_OK;
2694 static HRESULT WINAPI AudioStreamVolume_GetAllVolumes(
2695 IAudioStreamVolume *iface, UINT32 count, float *levels)
2697 ACImpl *This = impl_from_IAudioStreamVolume(iface);
2698 int i;
2700 TRACE("(%p)->(%d, %p)\n", This, count, levels);
2702 if(!levels)
2703 return E_POINTER;
2705 if(count != This->fmt->nChannels)
2706 return E_INVALIDARG;
2708 EnterCriticalSection(&This->lock);
2710 for(i = 0; i < count; ++i)
2711 levels[i] = This->vols[i];
2713 LeaveCriticalSection(&This->lock);
2715 return S_OK;
2718 static const IAudioStreamVolumeVtbl AudioStreamVolume_Vtbl =
2720 AudioStreamVolume_QueryInterface,
2721 AudioStreamVolume_AddRef,
2722 AudioStreamVolume_Release,
2723 AudioStreamVolume_GetChannelCount,
2724 AudioStreamVolume_SetChannelVolume,
2725 AudioStreamVolume_GetChannelVolume,
2726 AudioStreamVolume_SetAllVolumes,
2727 AudioStreamVolume_GetAllVolumes
2730 static HRESULT WINAPI ChannelAudioVolume_QueryInterface(
2731 IChannelAudioVolume *iface, REFIID riid, void **ppv)
2733 TRACE("(%p)->(%s, %p)\n", iface, debugstr_guid(riid), ppv);
2735 if(!ppv)
2736 return E_POINTER;
2737 *ppv = NULL;
2739 if(IsEqualIID(riid, &IID_IUnknown) ||
2740 IsEqualIID(riid, &IID_IChannelAudioVolume))
2741 *ppv = iface;
2742 if(*ppv){
2743 IUnknown_AddRef((IUnknown*)*ppv);
2744 return S_OK;
2747 WARN("Unknown interface %s\n", debugstr_guid(riid));
2748 return E_NOINTERFACE;
2751 static ULONG WINAPI ChannelAudioVolume_AddRef(IChannelAudioVolume *iface)
2753 AudioSessionWrapper *This = impl_from_IChannelAudioVolume(iface);
2754 return AudioSessionControl_AddRef(&This->IAudioSessionControl2_iface);
2757 static ULONG WINAPI ChannelAudioVolume_Release(IChannelAudioVolume *iface)
2759 AudioSessionWrapper *This = impl_from_IChannelAudioVolume(iface);
2760 return AudioSessionControl_Release(&This->IAudioSessionControl2_iface);
2763 static HRESULT WINAPI ChannelAudioVolume_GetChannelCount(
2764 IChannelAudioVolume *iface, UINT32 *out)
2766 AudioSessionWrapper *This = impl_from_IChannelAudioVolume(iface);
2767 AudioSession *session = This->session;
2769 TRACE("(%p)->(%p)\n", session, out);
2771 if(!out)
2772 return NULL_PTR_ERR;
2774 *out = session->channel_count;
2776 return S_OK;
2779 static HRESULT WINAPI ChannelAudioVolume_SetChannelVolume(
2780 IChannelAudioVolume *iface, UINT32 index, float level,
2781 const GUID *context)
2783 AudioSessionWrapper *This = impl_from_IChannelAudioVolume(iface);
2784 AudioSession *session = This->session;
2786 TRACE("(%p)->(%d, %f, %s)\n", session, index, level,
2787 wine_dbgstr_guid(context));
2789 if(level < 0.f || level > 1.f)
2790 return E_INVALIDARG;
2792 if(index >= session->channel_count)
2793 return E_INVALIDARG;
2795 if(context)
2796 FIXME("Notifications not supported yet\n");
2798 TRACE("ALSA does not support volume control\n");
2800 EnterCriticalSection(&session->lock);
2802 session->channel_vols[index] = level;
2804 LeaveCriticalSection(&session->lock);
2806 return S_OK;
2809 static HRESULT WINAPI ChannelAudioVolume_GetChannelVolume(
2810 IChannelAudioVolume *iface, UINT32 index, float *level)
2812 AudioSessionWrapper *This = impl_from_IChannelAudioVolume(iface);
2813 AudioSession *session = This->session;
2815 TRACE("(%p)->(%d, %p)\n", session, index, level);
2817 if(!level)
2818 return NULL_PTR_ERR;
2820 if(index >= session->channel_count)
2821 return E_INVALIDARG;
2823 *level = session->channel_vols[index];
2825 return S_OK;
2828 static HRESULT WINAPI ChannelAudioVolume_SetAllVolumes(
2829 IChannelAudioVolume *iface, UINT32 count, const float *levels,
2830 const GUID *context)
2832 AudioSessionWrapper *This = impl_from_IChannelAudioVolume(iface);
2833 AudioSession *session = This->session;
2834 int i;
2836 TRACE("(%p)->(%d, %p, %s)\n", session, count, levels,
2837 wine_dbgstr_guid(context));
2839 if(!levels)
2840 return NULL_PTR_ERR;
2842 if(count != session->channel_count)
2843 return E_INVALIDARG;
2845 if(context)
2846 FIXME("Notifications not supported yet\n");
2848 TRACE("ALSA does not support volume control\n");
2850 EnterCriticalSection(&session->lock);
2852 for(i = 0; i < count; ++i)
2853 session->channel_vols[i] = levels[i];
2855 LeaveCriticalSection(&session->lock);
2857 return S_OK;
2860 static HRESULT WINAPI ChannelAudioVolume_GetAllVolumes(
2861 IChannelAudioVolume *iface, UINT32 count, float *levels)
2863 AudioSessionWrapper *This = impl_from_IChannelAudioVolume(iface);
2864 AudioSession *session = This->session;
2865 int i;
2867 TRACE("(%p)->(%d, %p)\n", session, count, levels);
2869 if(!levels)
2870 return NULL_PTR_ERR;
2872 if(count != session->channel_count)
2873 return E_INVALIDARG;
2875 for(i = 0; i < count; ++i)
2876 levels[i] = session->channel_vols[i];
2878 return S_OK;
2881 static const IChannelAudioVolumeVtbl ChannelAudioVolume_Vtbl =
2883 ChannelAudioVolume_QueryInterface,
2884 ChannelAudioVolume_AddRef,
2885 ChannelAudioVolume_Release,
2886 ChannelAudioVolume_GetChannelCount,
2887 ChannelAudioVolume_SetChannelVolume,
2888 ChannelAudioVolume_GetChannelVolume,
2889 ChannelAudioVolume_SetAllVolumes,
2890 ChannelAudioVolume_GetAllVolumes