winepulse: Move pulse_start to unix lib.
[wine.git] / dlls / winepulse.drv / mmdevdrv.c
blob46d3b308f3805bfca197180fb8423be70d4b498b
1 /*
2 * Copyright 2011-2012 Maarten Lankhorst
3 * Copyright 2010-2011 Maarten Lankhorst for CodeWeavers
4 * Copyright 2011 Andrew Eikum for CodeWeavers
6 * This library is free software; you can redistribute it and/or
7 * modify it under the terms of the GNU Lesser General Public
8 * License as published by the Free Software Foundation; either
9 * version 2.1 of the License, or (at your option) any later version.
11 * This library is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
14 * Lesser General Public License for more details.
16 * You should have received a copy of the GNU Lesser General Public
17 * License along with this library; if not, write to the Free Software
18 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
21 #define NONAMELESSUNION
22 #define COBJMACROS
23 #define _GNU_SOURCE
25 #include "config.h"
27 #include <stdarg.h>
28 #include <unistd.h>
29 #include <math.h>
30 #include <stdio.h>
31 #include <errno.h>
33 #include <pulse/pulseaudio.h>
35 #include "windef.h"
36 #include "winbase.h"
37 #include "winnls.h"
38 #include "winreg.h"
39 #include "winternl.h"
40 #include "wine/debug.h"
41 #include "wine/unicode.h"
42 #include "wine/list.h"
44 #include "ole2.h"
45 #include "dshow.h"
46 #include "dsound.h"
47 #include "propsys.h"
49 #include "initguid.h"
50 #include "ks.h"
51 #include "ksmedia.h"
52 #include "propkey.h"
53 #include "mmdeviceapi.h"
54 #include "audioclient.h"
55 #include "endpointvolume.h"
56 #include "audiopolicy.h"
58 #include "unixlib.h"
60 WINE_DEFAULT_DEBUG_CHANNEL(pulse);
62 static const struct unix_funcs *pulse;
64 #define NULL_PTR_ERR MAKE_HRESULT(SEVERITY_ERROR, FACILITY_WIN32, RPC_X_NULL_REF_POINTER)
66 /* From <dlls/mmdevapi/mmdevapi.h> */
67 enum DriverPriority {
68 Priority_Unavailable = 0,
69 Priority_Low,
70 Priority_Neutral,
71 Priority_Preferred
74 static struct pulse_config pulse_config;
76 static HANDLE pulse_thread;
77 static struct list g_sessions = LIST_INIT(g_sessions);
79 static GUID pulse_render_guid =
80 { 0xfd47d9cc, 0x4218, 0x4135, { 0x9c, 0xe2, 0x0c, 0x19, 0x5c, 0x87, 0x40, 0x5b } };
81 static GUID pulse_capture_guid =
82 { 0x25da76d0, 0x033c, 0x4235, { 0x90, 0x02, 0x19, 0xf4, 0x88, 0x94, 0xac, 0x6f } };
84 BOOL WINAPI DllMain(HINSTANCE dll, DWORD reason, void *reserved)
86 if (reason == DLL_PROCESS_ATTACH) {
87 DisableThreadLibraryCalls(dll);
88 if (__wine_init_unix_lib(dll, reason, NULL, &pulse))
89 return FALSE;
90 } else if (reason == DLL_PROCESS_DETACH) {
91 __wine_init_unix_lib(dll, reason, NULL, NULL);
92 if (pulse_thread) {
93 WaitForSingleObject(pulse_thread, INFINITE);
94 CloseHandle(pulse_thread);
97 return TRUE;
100 typedef struct ACImpl ACImpl;
102 typedef struct _AudioSession {
103 GUID guid;
104 struct list clients;
106 IMMDevice *device;
108 float master_vol;
109 UINT32 channel_count;
110 float *channel_vols;
111 BOOL mute;
113 struct list entry;
114 } AudioSession;
116 typedef struct _AudioSessionWrapper {
117 IAudioSessionControl2 IAudioSessionControl2_iface;
118 IChannelAudioVolume IChannelAudioVolume_iface;
119 ISimpleAudioVolume ISimpleAudioVolume_iface;
121 LONG ref;
123 ACImpl *client;
124 AudioSession *session;
125 } AudioSessionWrapper;
127 struct ACImpl {
128 IAudioClient3 IAudioClient3_iface;
129 IAudioRenderClient IAudioRenderClient_iface;
130 IAudioCaptureClient IAudioCaptureClient_iface;
131 IAudioClock IAudioClock_iface;
132 IAudioClock2 IAudioClock2_iface;
133 IAudioStreamVolume IAudioStreamVolume_iface;
134 IUnknown *marshal;
135 IMMDevice *parent;
136 struct list entry;
137 float vol[PA_CHANNELS_MAX];
139 LONG ref;
140 EDataFlow dataflow;
141 UINT32 channel_count;
142 HANDLE timer;
144 struct pulse_stream *pulse_stream;
146 AudioSession *session;
147 AudioSessionWrapper *session_wrapper;
150 static const WCHAR defaultW[] = {'P','u','l','s','e','a','u','d','i','o',0};
152 static const IAudioClient3Vtbl AudioClient3_Vtbl;
153 static const IAudioRenderClientVtbl AudioRenderClient_Vtbl;
154 static const IAudioCaptureClientVtbl AudioCaptureClient_Vtbl;
155 static const IAudioSessionControl2Vtbl AudioSessionControl2_Vtbl;
156 static const ISimpleAudioVolumeVtbl SimpleAudioVolume_Vtbl;
157 static const IChannelAudioVolumeVtbl ChannelAudioVolume_Vtbl;
158 static const IAudioClockVtbl AudioClock_Vtbl;
159 static const IAudioClock2Vtbl AudioClock2_Vtbl;
160 static const IAudioStreamVolumeVtbl AudioStreamVolume_Vtbl;
162 static AudioSessionWrapper *AudioSessionWrapper_Create(ACImpl *client);
164 static inline ACImpl *impl_from_IAudioClient3(IAudioClient3 *iface)
166 return CONTAINING_RECORD(iface, ACImpl, IAudioClient3_iface);
169 static inline ACImpl *impl_from_IAudioRenderClient(IAudioRenderClient *iface)
171 return CONTAINING_RECORD(iface, ACImpl, IAudioRenderClient_iface);
174 static inline ACImpl *impl_from_IAudioCaptureClient(IAudioCaptureClient *iface)
176 return CONTAINING_RECORD(iface, ACImpl, IAudioCaptureClient_iface);
179 static inline AudioSessionWrapper *impl_from_IAudioSessionControl2(IAudioSessionControl2 *iface)
181 return CONTAINING_RECORD(iface, AudioSessionWrapper, IAudioSessionControl2_iface);
184 static inline AudioSessionWrapper *impl_from_ISimpleAudioVolume(ISimpleAudioVolume *iface)
186 return CONTAINING_RECORD(iface, AudioSessionWrapper, ISimpleAudioVolume_iface);
189 static inline AudioSessionWrapper *impl_from_IChannelAudioVolume(IChannelAudioVolume *iface)
191 return CONTAINING_RECORD(iface, AudioSessionWrapper, IChannelAudioVolume_iface);
194 static inline ACImpl *impl_from_IAudioClock(IAudioClock *iface)
196 return CONTAINING_RECORD(iface, ACImpl, IAudioClock_iface);
199 static inline ACImpl *impl_from_IAudioClock2(IAudioClock2 *iface)
201 return CONTAINING_RECORD(iface, ACImpl, IAudioClock2_iface);
204 static inline ACImpl *impl_from_IAudioStreamVolume(IAudioStreamVolume *iface)
206 return CONTAINING_RECORD(iface, ACImpl, IAudioStreamVolume_iface);
209 static DWORD CALLBACK pulse_mainloop_thread(void *tmp) {
210 pulse->main_loop();
211 return 0;
214 static char *get_application_name(void)
216 WCHAR path[MAX_PATH], *name;
217 size_t len;
218 char *str;
220 GetModuleFileNameW(NULL, path, ARRAY_SIZE(path));
221 name = strrchrW(path, '\\');
222 if (!name)
223 name = path;
224 else
225 name++;
226 len = WideCharToMultiByte(CP_UTF8, 0, name, -1, NULL, 0, NULL, NULL);
227 if (!(str = malloc(len)))
228 return NULL;
229 WideCharToMultiByte(CP_UNIXCP, 0, name, -1, str, len, NULL, NULL);
230 return str;
233 static HRESULT pulse_stream_valid(ACImpl *This) {
234 if (!This->pulse_stream)
235 return AUDCLNT_E_NOT_INITIALIZED;
236 if (pa_stream_get_state(This->pulse_stream->stream) != PA_STREAM_READY)
237 return AUDCLNT_E_DEVICE_INVALIDATED;
238 return S_OK;
241 static void silence_buffer(pa_sample_format_t format, BYTE *buffer, UINT32 bytes)
243 memset(buffer, format == PA_SAMPLE_U8 ? 0x80 : 0, bytes);
246 static void pulse_op_cb(pa_stream *s, int success, void *user) {
247 TRACE("Success: %i\n", success);
248 *(int*)user = success;
249 pulse->broadcast();
252 static DWORD WINAPI pulse_timer_cb(void *user)
254 ACImpl *This = user;
255 pulse->timer_loop(This->pulse_stream);
256 return 0;
259 static void set_stream_volumes(ACImpl *This)
261 float master_vol = This->session->mute ? 0.0f : This->session->master_vol;
262 pulse->set_volumes(This->pulse_stream, master_vol, This->vol,
263 This->session->channel_vols);
266 HRESULT WINAPI AUDDRV_GetEndpointIDs(EDataFlow flow, const WCHAR ***ids, GUID **keys,
267 UINT *num, UINT *def_index)
269 WCHAR *id;
271 TRACE("%d %p %p %p\n", flow, ids, num, def_index);
273 *num = 1;
274 *def_index = 0;
276 *ids = HeapAlloc(GetProcessHeap(), 0, sizeof(**ids));
277 *keys = NULL;
278 if (!*ids)
279 return E_OUTOFMEMORY;
281 (*ids)[0] = id = HeapAlloc(GetProcessHeap(), 0, sizeof(defaultW));
282 *keys = HeapAlloc(GetProcessHeap(), 0, sizeof(**keys));
283 if (!*keys || !id) {
284 HeapFree(GetProcessHeap(), 0, id);
285 HeapFree(GetProcessHeap(), 0, *keys);
286 HeapFree(GetProcessHeap(), 0, *ids);
287 *ids = NULL;
288 *keys = NULL;
289 return E_OUTOFMEMORY;
291 memcpy(id, defaultW, sizeof(defaultW));
293 if (flow == eRender)
294 (*keys)[0] = pulse_render_guid;
295 else
296 (*keys)[0] = pulse_capture_guid;
298 return S_OK;
301 int WINAPI AUDDRV_GetPriority(void)
303 char *name;
304 HRESULT hr;
306 name = get_application_name();
307 hr = pulse->test_connect(name, &pulse_config);
308 free(name);
309 return SUCCEEDED(hr) ? Priority_Preferred : Priority_Unavailable;
312 HRESULT WINAPI AUDDRV_GetAudioEndpoint(GUID *guid, IMMDevice *dev, IAudioClient **out)
314 ACImpl *This;
315 int i;
316 EDataFlow dataflow;
317 HRESULT hr;
319 TRACE("%s %p %p\n", debugstr_guid(guid), dev, out);
320 if (IsEqualGUID(guid, &pulse_render_guid))
321 dataflow = eRender;
322 else if (IsEqualGUID(guid, &pulse_capture_guid))
323 dataflow = eCapture;
324 else
325 return E_UNEXPECTED;
327 *out = NULL;
329 This = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, sizeof(*This));
330 if (!This)
331 return E_OUTOFMEMORY;
333 This->IAudioClient3_iface.lpVtbl = &AudioClient3_Vtbl;
334 This->IAudioRenderClient_iface.lpVtbl = &AudioRenderClient_Vtbl;
335 This->IAudioCaptureClient_iface.lpVtbl = &AudioCaptureClient_Vtbl;
336 This->IAudioClock_iface.lpVtbl = &AudioClock_Vtbl;
337 This->IAudioClock2_iface.lpVtbl = &AudioClock2_Vtbl;
338 This->IAudioStreamVolume_iface.lpVtbl = &AudioStreamVolume_Vtbl;
339 This->dataflow = dataflow;
340 This->parent = dev;
341 for (i = 0; i < PA_CHANNELS_MAX; ++i)
342 This->vol[i] = 1.f;
344 hr = CoCreateFreeThreadedMarshaler((IUnknown*)&This->IAudioClient3_iface, &This->marshal);
345 if (hr) {
346 HeapFree(GetProcessHeap(), 0, This);
347 return hr;
349 IMMDevice_AddRef(This->parent);
351 *out = (IAudioClient *)&This->IAudioClient3_iface;
352 IAudioClient3_AddRef(&This->IAudioClient3_iface);
354 return S_OK;
357 static HRESULT WINAPI AudioClient_QueryInterface(IAudioClient3 *iface,
358 REFIID riid, void **ppv)
360 ACImpl *This = impl_from_IAudioClient3(iface);
362 TRACE("(%p)->(%s, %p)\n", iface, debugstr_guid(riid), ppv);
364 if (!ppv)
365 return E_POINTER;
367 *ppv = NULL;
368 if (IsEqualIID(riid, &IID_IUnknown) ||
369 IsEqualIID(riid, &IID_IAudioClient) ||
370 IsEqualIID(riid, &IID_IAudioClient2) ||
371 IsEqualIID(riid, &IID_IAudioClient3))
372 *ppv = iface;
373 if (*ppv) {
374 IUnknown_AddRef((IUnknown*)*ppv);
375 return S_OK;
378 if (IsEqualIID(riid, &IID_IMarshal))
379 return IUnknown_QueryInterface(This->marshal, riid, ppv);
381 WARN("Unknown interface %s\n", debugstr_guid(riid));
382 return E_NOINTERFACE;
385 static ULONG WINAPI AudioClient_AddRef(IAudioClient3 *iface)
387 ACImpl *This = impl_from_IAudioClient3(iface);
388 ULONG ref;
389 ref = InterlockedIncrement(&This->ref);
390 TRACE("(%p) Refcount now %u\n", This, ref);
391 return ref;
394 static ULONG WINAPI AudioClient_Release(IAudioClient3 *iface)
396 ACImpl *This = impl_from_IAudioClient3(iface);
397 ULONG ref;
398 ref = InterlockedDecrement(&This->ref);
399 TRACE("(%p) Refcount now %u\n", This, ref);
400 if (!ref) {
401 if (This->pulse_stream) {
402 pulse->release_stream(This->pulse_stream, This->timer);
403 This->pulse_stream = NULL;
404 pulse->lock();
405 list_remove(&This->entry);
406 pulse->unlock();
408 IUnknown_Release(This->marshal);
409 IMMDevice_Release(This->parent);
410 HeapFree(GetProcessHeap(), 0, This);
412 return ref;
415 static void dump_fmt(const WAVEFORMATEX *fmt)
417 TRACE("wFormatTag: 0x%x (", fmt->wFormatTag);
418 switch(fmt->wFormatTag) {
419 case WAVE_FORMAT_PCM:
420 TRACE("WAVE_FORMAT_PCM");
421 break;
422 case WAVE_FORMAT_IEEE_FLOAT:
423 TRACE("WAVE_FORMAT_IEEE_FLOAT");
424 break;
425 case WAVE_FORMAT_EXTENSIBLE:
426 TRACE("WAVE_FORMAT_EXTENSIBLE");
427 break;
428 default:
429 TRACE("Unknown");
430 break;
432 TRACE(")\n");
434 TRACE("nChannels: %u\n", fmt->nChannels);
435 TRACE("nSamplesPerSec: %u\n", fmt->nSamplesPerSec);
436 TRACE("nAvgBytesPerSec: %u\n", fmt->nAvgBytesPerSec);
437 TRACE("nBlockAlign: %u\n", fmt->nBlockAlign);
438 TRACE("wBitsPerSample: %u\n", fmt->wBitsPerSample);
439 TRACE("cbSize: %u\n", fmt->cbSize);
441 if (fmt->wFormatTag == WAVE_FORMAT_EXTENSIBLE) {
442 WAVEFORMATEXTENSIBLE *fmtex = (void*)fmt;
443 TRACE("dwChannelMask: %08x\n", fmtex->dwChannelMask);
444 TRACE("Samples: %04x\n", fmtex->Samples.wReserved);
445 TRACE("SubFormat: %s\n", wine_dbgstr_guid(&fmtex->SubFormat));
449 static WAVEFORMATEX *clone_format(const WAVEFORMATEX *fmt)
451 WAVEFORMATEX *ret;
452 size_t size;
454 if (fmt->wFormatTag == WAVE_FORMAT_EXTENSIBLE)
455 size = sizeof(WAVEFORMATEXTENSIBLE);
456 else
457 size = sizeof(WAVEFORMATEX);
459 ret = CoTaskMemAlloc(size);
460 if (!ret)
461 return NULL;
463 memcpy(ret, fmt, size);
465 ret->cbSize = size - sizeof(WAVEFORMATEX);
467 return ret;
470 static void session_init_vols(AudioSession *session, UINT channels)
472 if (session->channel_count < channels) {
473 UINT i;
475 if (session->channel_vols)
476 session->channel_vols = HeapReAlloc(GetProcessHeap(), 0,
477 session->channel_vols, sizeof(float) * channels);
478 else
479 session->channel_vols = HeapAlloc(GetProcessHeap(), 0,
480 sizeof(float) * channels);
481 if (!session->channel_vols)
482 return;
484 for(i = session->channel_count; i < channels; ++i)
485 session->channel_vols[i] = 1.f;
487 session->channel_count = channels;
491 static AudioSession *create_session(const GUID *guid, IMMDevice *device,
492 UINT num_channels)
494 AudioSession *ret;
496 ret = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, sizeof(AudioSession));
497 if (!ret)
498 return NULL;
500 memcpy(&ret->guid, guid, sizeof(GUID));
502 ret->device = device;
504 list_init(&ret->clients);
506 list_add_head(&g_sessions, &ret->entry);
508 session_init_vols(ret, num_channels);
510 ret->master_vol = 1.f;
512 return ret;
515 /* if channels == 0, then this will return or create a session with
516 * matching dataflow and GUID. otherwise, channels must also match */
517 static HRESULT get_audio_session(const GUID *sessionguid,
518 IMMDevice *device, UINT channels, AudioSession **out)
520 AudioSession *session;
522 if (!sessionguid || IsEqualGUID(sessionguid, &GUID_NULL)) {
523 *out = create_session(&GUID_NULL, device, channels);
524 if (!*out)
525 return E_OUTOFMEMORY;
527 return S_OK;
530 *out = NULL;
531 LIST_FOR_EACH_ENTRY(session, &g_sessions, AudioSession, entry) {
532 if (session->device == device &&
533 IsEqualGUID(sessionguid, &session->guid)) {
534 session_init_vols(session, channels);
535 *out = session;
536 break;
540 if (!*out) {
541 *out = create_session(sessionguid, device, channels);
542 if (!*out)
543 return E_OUTOFMEMORY;
546 return S_OK;
549 static HRESULT WINAPI AudioClient_Initialize(IAudioClient3 *iface,
550 AUDCLNT_SHAREMODE mode, DWORD flags, REFERENCE_TIME duration,
551 REFERENCE_TIME period, const WAVEFORMATEX *fmt,
552 const GUID *sessionguid)
554 ACImpl *This = impl_from_IAudioClient3(iface);
555 char *name;
556 HRESULT hr = S_OK;
558 TRACE("(%p)->(%x, %x, %s, %s, %p, %s)\n", This, mode, flags,
559 wine_dbgstr_longlong(duration), wine_dbgstr_longlong(period), fmt, debugstr_guid(sessionguid));
561 if (!fmt)
562 return E_POINTER;
563 dump_fmt(fmt);
565 if (mode != AUDCLNT_SHAREMODE_SHARED && mode != AUDCLNT_SHAREMODE_EXCLUSIVE)
566 return E_INVALIDARG;
567 if (mode == AUDCLNT_SHAREMODE_EXCLUSIVE)
568 return AUDCLNT_E_EXCLUSIVE_MODE_NOT_ALLOWED;
570 if (flags & ~(AUDCLNT_STREAMFLAGS_CROSSPROCESS |
571 AUDCLNT_STREAMFLAGS_LOOPBACK |
572 AUDCLNT_STREAMFLAGS_EVENTCALLBACK |
573 AUDCLNT_STREAMFLAGS_NOPERSIST |
574 AUDCLNT_STREAMFLAGS_RATEADJUST |
575 AUDCLNT_SESSIONFLAGS_EXPIREWHENUNOWNED |
576 AUDCLNT_SESSIONFLAGS_DISPLAY_HIDE |
577 AUDCLNT_SESSIONFLAGS_DISPLAY_HIDEWHENEXPIRED |
578 AUDCLNT_STREAMFLAGS_SRC_DEFAULT_QUALITY |
579 AUDCLNT_STREAMFLAGS_AUTOCONVERTPCM)) {
580 FIXME("Unknown flags: %08x\n", flags);
581 return E_INVALIDARG;
584 pulse->lock();
586 if (This->pulse_stream) {
587 pulse->unlock();
588 return AUDCLNT_E_ALREADY_INITIALIZED;
591 if (!pulse_thread)
593 if (!(pulse_thread = CreateThread(NULL, 0, pulse_mainloop_thread, NULL, 0, NULL)))
595 ERR("Failed to create mainloop thread.\n");
596 pulse->unlock();
597 return E_FAIL;
599 SetThreadPriority(pulse_thread, THREAD_PRIORITY_TIME_CRITICAL);
600 pulse->cond_wait();
603 name = get_application_name();
604 hr = pulse->create_stream(name, This->dataflow, mode, flags, duration, period, fmt,
605 &This->channel_count, &This->pulse_stream);
606 free(name);
607 if (SUCCEEDED(hr)) {
608 hr = get_audio_session(sessionguid, This->parent, This->channel_count, &This->session);
609 if (SUCCEEDED(hr)) {
610 set_stream_volumes(This);
611 list_add_tail(&This->session->clients, &This->entry);
612 } else {
613 pulse->release_stream(This->pulse_stream, NULL);
614 This->pulse_stream = NULL;
618 pulse->unlock();
619 return hr;
622 static HRESULT WINAPI AudioClient_GetBufferSize(IAudioClient3 *iface,
623 UINT32 *out)
625 ACImpl *This = impl_from_IAudioClient3(iface);
626 HRESULT hr;
628 TRACE("(%p)->(%p)\n", This, out);
630 if (!out)
631 return E_POINTER;
633 pulse->lock();
634 hr = pulse_stream_valid(This);
635 if (SUCCEEDED(hr))
636 *out = This->pulse_stream->bufsize_frames;
637 pulse->unlock();
639 return hr;
642 static HRESULT WINAPI AudioClient_GetStreamLatency(IAudioClient3 *iface,
643 REFERENCE_TIME *latency)
645 ACImpl *This = impl_from_IAudioClient3(iface);
646 const pa_buffer_attr *attr;
647 REFERENCE_TIME lat;
648 HRESULT hr;
650 TRACE("(%p)->(%p)\n", This, latency);
652 if (!latency)
653 return E_POINTER;
655 pulse->lock();
656 hr = pulse_stream_valid(This);
657 if (FAILED(hr)) {
658 pulse->unlock();
659 return hr;
661 attr = pa_stream_get_buffer_attr(This->pulse_stream->stream);
662 if (This->dataflow == eRender){
663 lat = attr->minreq / pa_frame_size(&This->pulse_stream->ss);
664 }else
665 lat = attr->fragsize / pa_frame_size(&This->pulse_stream->ss);
666 *latency = 10000000;
667 *latency *= lat;
668 *latency /= This->pulse_stream->ss.rate;
669 *latency += pulse_config.modes[0].def_period;
670 pulse->unlock();
671 TRACE("Latency: %u ms\n", (DWORD)(*latency / 10000));
672 return S_OK;
675 static void ACImpl_GetRenderPad(ACImpl *This, UINT32 *out)
677 *out = This->pulse_stream->held_bytes / pa_frame_size(&This->pulse_stream->ss);
680 static void ACImpl_GetCapturePad(ACImpl *This, UINT32 *out)
682 ACPacket *packet = This->pulse_stream->locked_ptr;
683 if (!packet && !list_empty(&This->pulse_stream->packet_filled_head)) {
684 packet = (ACPacket*)list_head(&This->pulse_stream->packet_filled_head);
685 This->pulse_stream->locked_ptr = packet;
686 list_remove(&packet->entry);
688 if (out)
689 *out = This->pulse_stream->held_bytes / pa_frame_size(&This->pulse_stream->ss);
692 static HRESULT WINAPI AudioClient_GetCurrentPadding(IAudioClient3 *iface,
693 UINT32 *out)
695 ACImpl *This = impl_from_IAudioClient3(iface);
696 HRESULT hr;
698 TRACE("(%p)->(%p)\n", This, out);
700 if (!out)
701 return E_POINTER;
703 pulse->lock();
704 hr = pulse_stream_valid(This);
705 if (FAILED(hr)) {
706 pulse->unlock();
707 return hr;
710 if (This->dataflow == eRender)
711 ACImpl_GetRenderPad(This, out);
712 else
713 ACImpl_GetCapturePad(This, out);
714 pulse->unlock();
716 TRACE("%p Pad: %u ms (%u)\n", This, MulDiv(*out, 1000, This->pulse_stream->ss.rate), *out);
717 return S_OK;
720 static HRESULT WINAPI AudioClient_IsFormatSupported(IAudioClient3 *iface,
721 AUDCLNT_SHAREMODE mode, const WAVEFORMATEX *fmt,
722 WAVEFORMATEX **out)
724 ACImpl *This = impl_from_IAudioClient3(iface);
725 HRESULT hr = S_OK;
726 WAVEFORMATEX *closest = NULL;
727 BOOL exclusive;
729 TRACE("(%p)->(%x, %p, %p)\n", This, mode, fmt, out);
731 if (!fmt)
732 return E_POINTER;
734 if (out)
735 *out = NULL;
737 if (mode == AUDCLNT_SHAREMODE_EXCLUSIVE) {
738 exclusive = 1;
739 out = NULL;
740 } else if (mode == AUDCLNT_SHAREMODE_SHARED) {
741 exclusive = 0;
742 if (!out)
743 return E_POINTER;
744 } else
745 return E_INVALIDARG;
747 if (fmt->nChannels == 0)
748 return AUDCLNT_E_UNSUPPORTED_FORMAT;
750 closest = clone_format(fmt);
751 if (!closest)
752 return E_OUTOFMEMORY;
754 dump_fmt(fmt);
756 switch (fmt->wFormatTag) {
757 case WAVE_FORMAT_EXTENSIBLE: {
758 WAVEFORMATEXTENSIBLE *ext = (WAVEFORMATEXTENSIBLE*)closest;
760 if ((fmt->cbSize != sizeof(WAVEFORMATEXTENSIBLE) - sizeof(WAVEFORMATEX) &&
761 fmt->cbSize != sizeof(WAVEFORMATEXTENSIBLE)) ||
762 fmt->nBlockAlign != fmt->wBitsPerSample / 8 * fmt->nChannels ||
763 ext->Samples.wValidBitsPerSample > fmt->wBitsPerSample ||
764 fmt->nAvgBytesPerSec != fmt->nBlockAlign * fmt->nSamplesPerSec) {
765 hr = E_INVALIDARG;
766 break;
769 if (exclusive) {
770 UINT32 mask = 0, i, channels = 0;
772 if (!(ext->dwChannelMask & (SPEAKER_ALL | SPEAKER_RESERVED))) {
773 for (i = 1; !(i & SPEAKER_RESERVED); i <<= 1) {
774 if (i & ext->dwChannelMask) {
775 mask |= i;
776 channels++;
780 if (channels != fmt->nChannels || (ext->dwChannelMask & ~mask)) {
781 hr = AUDCLNT_E_UNSUPPORTED_FORMAT;
782 break;
784 } else {
785 hr = AUDCLNT_E_UNSUPPORTED_FORMAT;
786 break;
790 if (IsEqualGUID(&ext->SubFormat, &KSDATAFORMAT_SUBTYPE_IEEE_FLOAT)) {
791 if (fmt->wBitsPerSample != 32) {
792 hr = E_INVALIDARG;
793 break;
796 if (ext->Samples.wValidBitsPerSample != fmt->wBitsPerSample) {
797 hr = S_FALSE;
798 ext->Samples.wValidBitsPerSample = fmt->wBitsPerSample;
800 } else if (IsEqualGUID(&ext->SubFormat, &KSDATAFORMAT_SUBTYPE_PCM)) {
801 if (!fmt->wBitsPerSample || fmt->wBitsPerSample > 32 || fmt->wBitsPerSample % 8) {
802 hr = E_INVALIDARG;
803 break;
806 if (ext->Samples.wValidBitsPerSample != fmt->wBitsPerSample &&
807 !(fmt->wBitsPerSample == 32 &&
808 ext->Samples.wValidBitsPerSample == 24)) {
809 hr = S_FALSE;
810 ext->Samples.wValidBitsPerSample = fmt->wBitsPerSample;
811 break;
813 } else {
814 hr = AUDCLNT_E_UNSUPPORTED_FORMAT;
815 break;
818 break;
821 case WAVE_FORMAT_ALAW:
822 case WAVE_FORMAT_MULAW:
823 if (fmt->wBitsPerSample != 8) {
824 hr = E_INVALIDARG;
825 break;
827 /* Fall-through */
828 case WAVE_FORMAT_IEEE_FLOAT:
829 if (fmt->wFormatTag == WAVE_FORMAT_IEEE_FLOAT && fmt->wBitsPerSample != 32) {
830 hr = E_INVALIDARG;
831 break;
833 /* Fall-through */
834 case WAVE_FORMAT_PCM:
835 if (fmt->wFormatTag == WAVE_FORMAT_PCM &&
836 (!fmt->wBitsPerSample || fmt->wBitsPerSample > 32 || fmt->wBitsPerSample % 8)) {
837 hr = E_INVALIDARG;
838 break;
841 if (fmt->nChannels > 2) {
842 hr = AUDCLNT_E_UNSUPPORTED_FORMAT;
843 break;
846 * fmt->cbSize, fmt->nBlockAlign and fmt->nAvgBytesPerSec seem to be
847 * ignored, invalid values are happily accepted.
849 break;
850 default:
851 hr = AUDCLNT_E_UNSUPPORTED_FORMAT;
852 break;
855 if (exclusive && hr != S_OK) {
856 hr = AUDCLNT_E_UNSUPPORTED_FORMAT;
857 CoTaskMemFree(closest);
858 } else if (hr != S_FALSE)
859 CoTaskMemFree(closest);
860 else
861 *out = closest;
863 /* Winepulse does not currently support exclusive mode, if you know of an
864 * application that uses it, I will correct this..
866 if (hr == S_OK && exclusive)
867 return This->dataflow == eCapture ? AUDCLNT_E_UNSUPPORTED_FORMAT : AUDCLNT_E_EXCLUSIVE_MODE_NOT_ALLOWED;
869 TRACE("returning: %08x %p\n", hr, out ? *out : NULL);
870 return hr;
873 static HRESULT WINAPI AudioClient_GetMixFormat(IAudioClient3 *iface,
874 WAVEFORMATEX **pwfx)
876 ACImpl *This = impl_from_IAudioClient3(iface);
878 TRACE("(%p)->(%p)\n", This, pwfx);
880 if (!pwfx)
881 return E_POINTER;
883 *pwfx = clone_format(&pulse_config.modes[This->dataflow == eCapture].format.Format);
884 if (!*pwfx)
885 return E_OUTOFMEMORY;
886 dump_fmt(*pwfx);
887 return S_OK;
890 static HRESULT WINAPI AudioClient_GetDevicePeriod(IAudioClient3 *iface,
891 REFERENCE_TIME *defperiod, REFERENCE_TIME *minperiod)
893 ACImpl *This = impl_from_IAudioClient3(iface);
895 TRACE("(%p)->(%p, %p)\n", This, defperiod, minperiod);
897 if (!defperiod && !minperiod)
898 return E_POINTER;
900 if (defperiod)
901 *defperiod = pulse_config.modes[This->dataflow == eCapture].def_period;
902 if (minperiod)
903 *minperiod = pulse_config.modes[This->dataflow == eCapture].min_period;
905 return S_OK;
908 static HRESULT WINAPI AudioClient_Start(IAudioClient3 *iface)
910 ACImpl *This = impl_from_IAudioClient3(iface);
911 HRESULT hr;
913 TRACE("(%p)\n", This);
915 if (!This->pulse_stream)
916 return AUDCLNT_E_NOT_INITIALIZED;
918 hr = pulse->start(This->pulse_stream);
919 if (FAILED(hr))
920 return hr;
922 if (!This->timer) {
923 This->timer = CreateThread(NULL, 0, pulse_timer_cb, This, 0, NULL);
924 SetThreadPriority(This->timer, THREAD_PRIORITY_TIME_CRITICAL);
927 return S_OK;
930 static HRESULT WINAPI AudioClient_Stop(IAudioClient3 *iface)
932 ACImpl *This = impl_from_IAudioClient3(iface);
933 TRACE("(%p)\n", This);
935 if (!This->pulse_stream)
936 return AUDCLNT_E_NOT_INITIALIZED;
938 return pulse->stop(This->pulse_stream);
941 static HRESULT WINAPI AudioClient_Reset(IAudioClient3 *iface)
943 ACImpl *This = impl_from_IAudioClient3(iface);
944 HRESULT hr = S_OK;
946 TRACE("(%p)\n", This);
948 pulse->lock();
949 hr = pulse_stream_valid(This);
950 if (FAILED(hr)) {
951 pulse->unlock();
952 return hr;
955 if (This->pulse_stream->started) {
956 pulse->unlock();
957 return AUDCLNT_E_NOT_STOPPED;
960 if (This->pulse_stream->locked) {
961 pulse->unlock();
962 return AUDCLNT_E_BUFFER_OPERATION_PENDING;
965 if (This->dataflow == eRender) {
966 /* If there is still data in the render buffer it needs to be removed from the server */
967 int success = 0;
968 if (This->pulse_stream->held_bytes) {
969 pa_operation *o = pa_stream_flush(This->pulse_stream->stream, pulse_op_cb, &success);
970 if (o) {
971 while(pa_operation_get_state(o) == PA_OPERATION_RUNNING)
972 pulse->cond_wait();
973 pa_operation_unref(o);
976 if (success || !This->pulse_stream->held_bytes){
977 This->pulse_stream->clock_lastpos = This->pulse_stream->clock_written = 0;
978 This->pulse_stream->pa_offs_bytes = This->pulse_stream->lcl_offs_bytes = This->pulse_stream->held_bytes = This->pulse_stream->pa_held_bytes = 0;
980 } else {
981 ACPacket *p;
982 This->pulse_stream->clock_written += This->pulse_stream->held_bytes;
983 This->pulse_stream->held_bytes = 0;
985 if ((p = This->pulse_stream->locked_ptr)) {
986 This->pulse_stream->locked_ptr = NULL;
987 list_add_tail(&This->pulse_stream->packet_free_head, &p->entry);
989 list_move_tail(&This->pulse_stream->packet_free_head, &This->pulse_stream->packet_filled_head);
991 pulse->unlock();
993 return hr;
996 static HRESULT WINAPI AudioClient_SetEventHandle(IAudioClient3 *iface,
997 HANDLE event)
999 ACImpl *This = impl_from_IAudioClient3(iface);
1000 HRESULT hr;
1002 TRACE("(%p)->(%p)\n", This, event);
1004 if (!event)
1005 return E_INVALIDARG;
1007 pulse->lock();
1008 hr = pulse_stream_valid(This);
1009 if (FAILED(hr)) {
1010 pulse->unlock();
1011 return hr;
1014 if (!(This->pulse_stream->flags & AUDCLNT_STREAMFLAGS_EVENTCALLBACK))
1015 hr = AUDCLNT_E_EVENTHANDLE_NOT_EXPECTED;
1016 else if (This->pulse_stream->event)
1017 hr = HRESULT_FROM_WIN32(ERROR_INVALID_NAME);
1018 else
1019 This->pulse_stream->event = event;
1020 pulse->unlock();
1021 return hr;
1024 static HRESULT WINAPI AudioClient_GetService(IAudioClient3 *iface, REFIID riid,
1025 void **ppv)
1027 ACImpl *This = impl_from_IAudioClient3(iface);
1028 HRESULT hr;
1030 TRACE("(%p)->(%s, %p)\n", This, debugstr_guid(riid), ppv);
1032 if (!ppv)
1033 return E_POINTER;
1034 *ppv = NULL;
1036 pulse->lock();
1037 hr = pulse_stream_valid(This);
1038 pulse->unlock();
1039 if (FAILED(hr))
1040 return hr;
1042 if (IsEqualIID(riid, &IID_IAudioRenderClient)) {
1043 if (This->dataflow != eRender)
1044 return AUDCLNT_E_WRONG_ENDPOINT_TYPE;
1045 *ppv = &This->IAudioRenderClient_iface;
1046 } else if (IsEqualIID(riid, &IID_IAudioCaptureClient)) {
1047 if (This->dataflow != eCapture)
1048 return AUDCLNT_E_WRONG_ENDPOINT_TYPE;
1049 *ppv = &This->IAudioCaptureClient_iface;
1050 } else if (IsEqualIID(riid, &IID_IAudioClock)) {
1051 *ppv = &This->IAudioClock_iface;
1052 } else if (IsEqualIID(riid, &IID_IAudioStreamVolume)) {
1053 *ppv = &This->IAudioStreamVolume_iface;
1054 } else if (IsEqualIID(riid, &IID_IAudioSessionControl) ||
1055 IsEqualIID(riid, &IID_IChannelAudioVolume) ||
1056 IsEqualIID(riid, &IID_ISimpleAudioVolume)) {
1057 if (!This->session_wrapper) {
1058 This->session_wrapper = AudioSessionWrapper_Create(This);
1059 if (!This->session_wrapper)
1060 return E_OUTOFMEMORY;
1062 if (IsEqualIID(riid, &IID_IAudioSessionControl))
1063 *ppv = &This->session_wrapper->IAudioSessionControl2_iface;
1064 else if (IsEqualIID(riid, &IID_IChannelAudioVolume))
1065 *ppv = &This->session_wrapper->IChannelAudioVolume_iface;
1066 else if (IsEqualIID(riid, &IID_ISimpleAudioVolume))
1067 *ppv = &This->session_wrapper->ISimpleAudioVolume_iface;
1070 if (*ppv) {
1071 IUnknown_AddRef((IUnknown*)*ppv);
1072 return S_OK;
1075 FIXME("stub %s\n", debugstr_guid(riid));
1076 return E_NOINTERFACE;
1079 static HRESULT WINAPI AudioClient_IsOffloadCapable(IAudioClient3 *iface,
1080 AUDIO_STREAM_CATEGORY category, BOOL *offload_capable)
1082 ACImpl *This = impl_from_IAudioClient3(iface);
1084 TRACE("(%p)->(0x%x, %p)\n", This, category, offload_capable);
1086 if(!offload_capable)
1087 return E_INVALIDARG;
1089 *offload_capable = FALSE;
1091 return S_OK;
1094 static HRESULT WINAPI AudioClient_SetClientProperties(IAudioClient3 *iface,
1095 const AudioClientProperties *prop)
1097 ACImpl *This = impl_from_IAudioClient3(iface);
1098 const Win8AudioClientProperties *legacy_prop = (const Win8AudioClientProperties *)prop;
1100 TRACE("(%p)->(%p)\n", This, prop);
1102 if(!legacy_prop)
1103 return E_POINTER;
1105 if(legacy_prop->cbSize == sizeof(AudioClientProperties)){
1106 TRACE("{ bIsOffload: %u, eCategory: 0x%x, Options: 0x%x }\n",
1107 legacy_prop->bIsOffload,
1108 legacy_prop->eCategory,
1109 prop->Options);
1110 }else if(legacy_prop->cbSize == sizeof(Win8AudioClientProperties)){
1111 TRACE("{ bIsOffload: %u, eCategory: 0x%x }\n",
1112 legacy_prop->bIsOffload,
1113 legacy_prop->eCategory);
1114 }else{
1115 WARN("Unsupported Size = %d\n", legacy_prop->cbSize);
1116 return E_INVALIDARG;
1120 if(legacy_prop->bIsOffload)
1121 return AUDCLNT_E_ENDPOINT_OFFLOAD_NOT_CAPABLE;
1123 return S_OK;
1126 static HRESULT WINAPI AudioClient_GetBufferSizeLimits(IAudioClient3 *iface,
1127 const WAVEFORMATEX *format, BOOL event_driven, REFERENCE_TIME *min_duration,
1128 REFERENCE_TIME *max_duration)
1130 ACImpl *This = impl_from_IAudioClient3(iface);
1132 FIXME("(%p)->(%p, %u, %p, %p)\n", This, format, event_driven, min_duration, max_duration);
1134 return E_NOTIMPL;
1137 static HRESULT WINAPI AudioClient_GetSharedModeEnginePeriod(IAudioClient3 *iface,
1138 const WAVEFORMATEX *format, UINT32 *default_period_frames, UINT32 *unit_period_frames,
1139 UINT32 *min_period_frames, UINT32 *max_period_frames)
1141 ACImpl *This = impl_from_IAudioClient3(iface);
1143 FIXME("(%p)->(%p, %p, %p, %p, %p)\n", This, format, default_period_frames, unit_period_frames,
1144 min_period_frames, max_period_frames);
1146 return E_NOTIMPL;
1149 static HRESULT WINAPI AudioClient_GetCurrentSharedModeEnginePeriod(IAudioClient3 *iface,
1150 WAVEFORMATEX **cur_format, UINT32 *cur_period_frames)
1152 ACImpl *This = impl_from_IAudioClient3(iface);
1154 FIXME("(%p)->(%p, %p)\n", This, cur_format, cur_period_frames);
1156 return E_NOTIMPL;
1159 static HRESULT WINAPI AudioClient_InitializeSharedAudioStream(IAudioClient3 *iface,
1160 DWORD flags, UINT32 period_frames, const WAVEFORMATEX *format,
1161 const GUID *session_guid)
1163 ACImpl *This = impl_from_IAudioClient3(iface);
1165 FIXME("(%p)->(0x%x, %u, %p, %s)\n", This, flags, period_frames, format, debugstr_guid(session_guid));
1167 return E_NOTIMPL;
1170 static const IAudioClient3Vtbl AudioClient3_Vtbl =
1172 AudioClient_QueryInterface,
1173 AudioClient_AddRef,
1174 AudioClient_Release,
1175 AudioClient_Initialize,
1176 AudioClient_GetBufferSize,
1177 AudioClient_GetStreamLatency,
1178 AudioClient_GetCurrentPadding,
1179 AudioClient_IsFormatSupported,
1180 AudioClient_GetMixFormat,
1181 AudioClient_GetDevicePeriod,
1182 AudioClient_Start,
1183 AudioClient_Stop,
1184 AudioClient_Reset,
1185 AudioClient_SetEventHandle,
1186 AudioClient_GetService,
1187 AudioClient_IsOffloadCapable,
1188 AudioClient_SetClientProperties,
1189 AudioClient_GetBufferSizeLimits,
1190 AudioClient_GetSharedModeEnginePeriod,
1191 AudioClient_GetCurrentSharedModeEnginePeriod,
1192 AudioClient_InitializeSharedAudioStream,
1195 static HRESULT WINAPI AudioRenderClient_QueryInterface(
1196 IAudioRenderClient *iface, REFIID riid, void **ppv)
1198 ACImpl *This = impl_from_IAudioRenderClient(iface);
1199 TRACE("(%p)->(%s, %p)\n", iface, debugstr_guid(riid), ppv);
1201 if (!ppv)
1202 return E_POINTER;
1203 *ppv = NULL;
1205 if (IsEqualIID(riid, &IID_IUnknown) ||
1206 IsEqualIID(riid, &IID_IAudioRenderClient))
1207 *ppv = iface;
1208 if (*ppv) {
1209 IUnknown_AddRef((IUnknown*)*ppv);
1210 return S_OK;
1213 if (IsEqualIID(riid, &IID_IMarshal))
1214 return IUnknown_QueryInterface(This->marshal, riid, ppv);
1216 WARN("Unknown interface %s\n", debugstr_guid(riid));
1217 return E_NOINTERFACE;
1220 static ULONG WINAPI AudioRenderClient_AddRef(IAudioRenderClient *iface)
1222 ACImpl *This = impl_from_IAudioRenderClient(iface);
1223 return AudioClient_AddRef(&This->IAudioClient3_iface);
1226 static ULONG WINAPI AudioRenderClient_Release(IAudioRenderClient *iface)
1228 ACImpl *This = impl_from_IAudioRenderClient(iface);
1229 return AudioClient_Release(&This->IAudioClient3_iface);
1232 static void alloc_tmp_buffer(ACImpl *This, UINT32 bytes)
1234 if(This->pulse_stream->tmp_buffer_bytes >= bytes)
1235 return;
1237 HeapFree(GetProcessHeap(), 0, This->pulse_stream->tmp_buffer);
1238 This->pulse_stream->tmp_buffer = HeapAlloc(GetProcessHeap(), 0, bytes);
1239 This->pulse_stream->tmp_buffer_bytes = bytes;
1242 static HRESULT WINAPI AudioRenderClient_GetBuffer(IAudioRenderClient *iface,
1243 UINT32 frames, BYTE **data)
1245 ACImpl *This = impl_from_IAudioRenderClient(iface);
1246 size_t bytes = frames * pa_frame_size(&This->pulse_stream->ss);
1247 HRESULT hr = S_OK;
1248 UINT32 wri_offs_bytes;
1250 TRACE("(%p)->(%u, %p)\n", This, frames, data);
1252 if (!data)
1253 return E_POINTER;
1254 *data = NULL;
1256 pulse->lock();
1257 hr = pulse_stream_valid(This);
1258 if (FAILED(hr) || This->pulse_stream->locked) {
1259 pulse->unlock();
1260 return FAILED(hr) ? hr : AUDCLNT_E_OUT_OF_ORDER;
1262 if (!frames) {
1263 pulse->unlock();
1264 return S_OK;
1267 if(This->pulse_stream->held_bytes / pa_frame_size(&This->pulse_stream->ss) + frames > This->pulse_stream->bufsize_frames){
1268 pulse->unlock();
1269 return AUDCLNT_E_BUFFER_TOO_LARGE;
1272 wri_offs_bytes = (This->pulse_stream->lcl_offs_bytes + This->pulse_stream->held_bytes) % This->pulse_stream->real_bufsize_bytes;
1273 if(wri_offs_bytes + bytes > This->pulse_stream->real_bufsize_bytes){
1274 alloc_tmp_buffer(This, bytes);
1275 *data = This->pulse_stream->tmp_buffer;
1276 This->pulse_stream->locked = -bytes;
1277 }else{
1278 *data = This->pulse_stream->local_buffer + wri_offs_bytes;
1279 This->pulse_stream->locked = bytes;
1282 silence_buffer(This->pulse_stream->ss.format, *data, bytes);
1284 pulse->unlock();
1286 return hr;
1289 static void pulse_wrap_buffer(ACImpl *This, BYTE *buffer, UINT32 written_bytes)
1291 UINT32 wri_offs_bytes = (This->pulse_stream->lcl_offs_bytes + This->pulse_stream->held_bytes) % This->pulse_stream->real_bufsize_bytes;
1292 UINT32 chunk_bytes = This->pulse_stream->real_bufsize_bytes - wri_offs_bytes;
1294 if(written_bytes <= chunk_bytes){
1295 memcpy(This->pulse_stream->local_buffer + wri_offs_bytes, buffer, written_bytes);
1296 }else{
1297 memcpy(This->pulse_stream->local_buffer + wri_offs_bytes, buffer, chunk_bytes);
1298 memcpy(This->pulse_stream->local_buffer, buffer + chunk_bytes,
1299 written_bytes - chunk_bytes);
1303 static HRESULT WINAPI AudioRenderClient_ReleaseBuffer(
1304 IAudioRenderClient *iface, UINT32 written_frames, DWORD flags)
1306 ACImpl *This = impl_from_IAudioRenderClient(iface);
1307 UINT32 written_bytes = written_frames * pa_frame_size(&This->pulse_stream->ss);
1308 BYTE *buffer;
1310 TRACE("(%p)->(%u, %x)\n", This, written_frames, flags);
1312 pulse->lock();
1313 if (!This->pulse_stream->locked || !written_frames) {
1314 This->pulse_stream->locked = 0;
1315 pulse->unlock();
1316 return written_frames ? AUDCLNT_E_OUT_OF_ORDER : S_OK;
1319 if(written_frames * pa_frame_size(&This->pulse_stream->ss) > (This->pulse_stream->locked >= 0 ? This->pulse_stream->locked : -This->pulse_stream->locked)){
1320 pulse->unlock();
1321 return AUDCLNT_E_INVALID_SIZE;
1324 if(This->pulse_stream->locked >= 0)
1325 buffer = This->pulse_stream->local_buffer + (This->pulse_stream->lcl_offs_bytes + This->pulse_stream->held_bytes) % This->pulse_stream->real_bufsize_bytes;
1326 else
1327 buffer = This->pulse_stream->tmp_buffer;
1329 if(flags & AUDCLNT_BUFFERFLAGS_SILENT)
1330 silence_buffer(This->pulse_stream->ss.format, buffer, written_bytes);
1332 if(This->pulse_stream->locked < 0)
1333 pulse_wrap_buffer(This, buffer, written_bytes);
1335 This->pulse_stream->held_bytes += written_bytes;
1336 This->pulse_stream->pa_held_bytes += written_bytes;
1337 if(This->pulse_stream->pa_held_bytes > This->pulse_stream->real_bufsize_bytes){
1338 This->pulse_stream->pa_offs_bytes += This->pulse_stream->pa_held_bytes - This->pulse_stream->real_bufsize_bytes;
1339 This->pulse_stream->pa_offs_bytes %= This->pulse_stream->real_bufsize_bytes;
1340 This->pulse_stream->pa_held_bytes = This->pulse_stream->real_bufsize_bytes;
1342 This->pulse_stream->clock_written += written_bytes;
1343 This->pulse_stream->locked = 0;
1345 TRACE("Released %u, held %zu\n", written_frames, This->pulse_stream->held_bytes / pa_frame_size(&This->pulse_stream->ss));
1347 pulse->unlock();
1349 return S_OK;
1352 static const IAudioRenderClientVtbl AudioRenderClient_Vtbl = {
1353 AudioRenderClient_QueryInterface,
1354 AudioRenderClient_AddRef,
1355 AudioRenderClient_Release,
1356 AudioRenderClient_GetBuffer,
1357 AudioRenderClient_ReleaseBuffer
1360 static HRESULT WINAPI AudioCaptureClient_QueryInterface(
1361 IAudioCaptureClient *iface, REFIID riid, void **ppv)
1363 ACImpl *This = impl_from_IAudioCaptureClient(iface);
1364 TRACE("(%p)->(%s, %p)\n", iface, debugstr_guid(riid), ppv);
1366 if (!ppv)
1367 return E_POINTER;
1368 *ppv = NULL;
1370 if (IsEqualIID(riid, &IID_IUnknown) ||
1371 IsEqualIID(riid, &IID_IAudioCaptureClient))
1372 *ppv = iface;
1373 if (*ppv) {
1374 IUnknown_AddRef((IUnknown*)*ppv);
1375 return S_OK;
1378 if (IsEqualIID(riid, &IID_IMarshal))
1379 return IUnknown_QueryInterface(This->marshal, riid, ppv);
1381 WARN("Unknown interface %s\n", debugstr_guid(riid));
1382 return E_NOINTERFACE;
1385 static ULONG WINAPI AudioCaptureClient_AddRef(IAudioCaptureClient *iface)
1387 ACImpl *This = impl_from_IAudioCaptureClient(iface);
1388 return IAudioClient3_AddRef(&This->IAudioClient3_iface);
1391 static ULONG WINAPI AudioCaptureClient_Release(IAudioCaptureClient *iface)
1393 ACImpl *This = impl_from_IAudioCaptureClient(iface);
1394 return IAudioClient3_Release(&This->IAudioClient3_iface);
1397 static HRESULT WINAPI AudioCaptureClient_GetBuffer(IAudioCaptureClient *iface,
1398 BYTE **data, UINT32 *frames, DWORD *flags, UINT64 *devpos,
1399 UINT64 *qpcpos)
1401 ACImpl *This = impl_from_IAudioCaptureClient(iface);
1402 HRESULT hr;
1403 ACPacket *packet;
1405 TRACE("(%p)->(%p, %p, %p, %p, %p)\n", This, data, frames, flags,
1406 devpos, qpcpos);
1408 if (!data)
1409 return E_POINTER;
1411 *data = NULL;
1413 if (!frames || !flags)
1414 return E_POINTER;
1416 pulse->lock();
1417 hr = pulse_stream_valid(This);
1418 if (FAILED(hr) || This->pulse_stream->locked) {
1419 pulse->unlock();
1420 return FAILED(hr) ? hr : AUDCLNT_E_OUT_OF_ORDER;
1423 ACImpl_GetCapturePad(This, NULL);
1424 if ((packet = This->pulse_stream->locked_ptr)) {
1425 *frames = This->pulse_stream->period_bytes / pa_frame_size(&This->pulse_stream->ss);
1426 *flags = 0;
1427 if (packet->discont)
1428 *flags |= AUDCLNT_BUFFERFLAGS_DATA_DISCONTINUITY;
1429 if (devpos) {
1430 if (packet->discont)
1431 *devpos = (This->pulse_stream->clock_written + This->pulse_stream->period_bytes) / pa_frame_size(&This->pulse_stream->ss);
1432 else
1433 *devpos = This->pulse_stream->clock_written / pa_frame_size(&This->pulse_stream->ss);
1435 if (qpcpos)
1436 *qpcpos = packet->qpcpos;
1437 *data = packet->data;
1439 else
1440 *frames = 0;
1441 This->pulse_stream->locked = *frames;
1442 pulse->unlock();
1443 return *frames ? S_OK : AUDCLNT_S_BUFFER_EMPTY;
1446 static HRESULT WINAPI AudioCaptureClient_ReleaseBuffer(
1447 IAudioCaptureClient *iface, UINT32 done)
1449 ACImpl *This = impl_from_IAudioCaptureClient(iface);
1451 TRACE("(%p)->(%u)\n", This, done);
1453 pulse->lock();
1454 if (!This->pulse_stream->locked && done) {
1455 pulse->unlock();
1456 return AUDCLNT_E_OUT_OF_ORDER;
1458 if (done && This->pulse_stream->locked != done) {
1459 pulse->unlock();
1460 return AUDCLNT_E_INVALID_SIZE;
1462 if (done) {
1463 ACPacket *packet = This->pulse_stream->locked_ptr;
1464 This->pulse_stream->locked_ptr = NULL;
1465 This->pulse_stream->held_bytes -= This->pulse_stream->period_bytes;
1466 if (packet->discont)
1467 This->pulse_stream->clock_written += 2 * This->pulse_stream->period_bytes;
1468 else
1469 This->pulse_stream->clock_written += This->pulse_stream->period_bytes;
1470 list_add_tail(&This->pulse_stream->packet_free_head, &packet->entry);
1472 This->pulse_stream->locked = 0;
1473 pulse->unlock();
1474 return S_OK;
1477 static HRESULT WINAPI AudioCaptureClient_GetNextPacketSize(
1478 IAudioCaptureClient *iface, UINT32 *frames)
1480 ACImpl *This = impl_from_IAudioCaptureClient(iface);
1482 TRACE("(%p)->(%p)\n", This, frames);
1483 if (!frames)
1484 return E_POINTER;
1486 pulse->lock();
1487 ACImpl_GetCapturePad(This, NULL);
1488 if (This->pulse_stream->locked_ptr)
1489 *frames = This->pulse_stream->period_bytes / pa_frame_size(&This->pulse_stream->ss);
1490 else
1491 *frames = 0;
1492 pulse->unlock();
1493 return S_OK;
1496 static const IAudioCaptureClientVtbl AudioCaptureClient_Vtbl =
1498 AudioCaptureClient_QueryInterface,
1499 AudioCaptureClient_AddRef,
1500 AudioCaptureClient_Release,
1501 AudioCaptureClient_GetBuffer,
1502 AudioCaptureClient_ReleaseBuffer,
1503 AudioCaptureClient_GetNextPacketSize
1506 static HRESULT WINAPI AudioClock_QueryInterface(IAudioClock *iface,
1507 REFIID riid, void **ppv)
1509 ACImpl *This = impl_from_IAudioClock(iface);
1511 TRACE("(%p)->(%s, %p)\n", iface, debugstr_guid(riid), ppv);
1513 if (!ppv)
1514 return E_POINTER;
1515 *ppv = NULL;
1517 if (IsEqualIID(riid, &IID_IUnknown) || IsEqualIID(riid, &IID_IAudioClock))
1518 *ppv = iface;
1519 else if (IsEqualIID(riid, &IID_IAudioClock2))
1520 *ppv = &This->IAudioClock2_iface;
1521 if (*ppv) {
1522 IUnknown_AddRef((IUnknown*)*ppv);
1523 return S_OK;
1526 if (IsEqualIID(riid, &IID_IMarshal))
1527 return IUnknown_QueryInterface(This->marshal, riid, ppv);
1529 WARN("Unknown interface %s\n", debugstr_guid(riid));
1530 return E_NOINTERFACE;
1533 static ULONG WINAPI AudioClock_AddRef(IAudioClock *iface)
1535 ACImpl *This = impl_from_IAudioClock(iface);
1536 return IAudioClient3_AddRef(&This->IAudioClient3_iface);
1539 static ULONG WINAPI AudioClock_Release(IAudioClock *iface)
1541 ACImpl *This = impl_from_IAudioClock(iface);
1542 return IAudioClient3_Release(&This->IAudioClient3_iface);
1545 static HRESULT WINAPI AudioClock_GetFrequency(IAudioClock *iface, UINT64 *freq)
1547 ACImpl *This = impl_from_IAudioClock(iface);
1548 HRESULT hr;
1550 TRACE("(%p)->(%p)\n", This, freq);
1552 pulse->lock();
1553 hr = pulse_stream_valid(This);
1554 if (SUCCEEDED(hr)) {
1555 *freq = This->pulse_stream->ss.rate;
1556 if (This->pulse_stream->share == AUDCLNT_SHAREMODE_SHARED)
1557 *freq *= pa_frame_size(&This->pulse_stream->ss);
1559 pulse->unlock();
1560 return hr;
1563 static HRESULT WINAPI AudioClock_GetPosition(IAudioClock *iface, UINT64 *pos,
1564 UINT64 *qpctime)
1566 ACImpl *This = impl_from_IAudioClock(iface);
1567 HRESULT hr;
1569 TRACE("(%p)->(%p, %p)\n", This, pos, qpctime);
1571 if (!pos)
1572 return E_POINTER;
1574 pulse->lock();
1575 hr = pulse_stream_valid(This);
1576 if (FAILED(hr)) {
1577 pulse->unlock();
1578 return hr;
1581 *pos = This->pulse_stream->clock_written - This->pulse_stream->held_bytes;
1583 if (This->pulse_stream->share == AUDCLNT_SHAREMODE_EXCLUSIVE)
1584 *pos /= pa_frame_size(&This->pulse_stream->ss);
1586 /* Make time never go backwards */
1587 if (*pos < This->pulse_stream->clock_lastpos)
1588 *pos = This->pulse_stream->clock_lastpos;
1589 else
1590 This->pulse_stream->clock_lastpos = *pos;
1591 pulse->unlock();
1593 TRACE("%p Position: %u\n", This, (unsigned)*pos);
1595 if (qpctime) {
1596 LARGE_INTEGER stamp, freq;
1597 QueryPerformanceCounter(&stamp);
1598 QueryPerformanceFrequency(&freq);
1599 *qpctime = (stamp.QuadPart * (INT64)10000000) / freq.QuadPart;
1602 return S_OK;
1605 static HRESULT WINAPI AudioClock_GetCharacteristics(IAudioClock *iface,
1606 DWORD *chars)
1608 ACImpl *This = impl_from_IAudioClock(iface);
1610 TRACE("(%p)->(%p)\n", This, chars);
1612 if (!chars)
1613 return E_POINTER;
1615 *chars = AUDIOCLOCK_CHARACTERISTIC_FIXED_FREQ;
1617 return S_OK;
1620 static const IAudioClockVtbl AudioClock_Vtbl =
1622 AudioClock_QueryInterface,
1623 AudioClock_AddRef,
1624 AudioClock_Release,
1625 AudioClock_GetFrequency,
1626 AudioClock_GetPosition,
1627 AudioClock_GetCharacteristics
1630 static HRESULT WINAPI AudioClock2_QueryInterface(IAudioClock2 *iface,
1631 REFIID riid, void **ppv)
1633 ACImpl *This = impl_from_IAudioClock2(iface);
1634 return IAudioClock_QueryInterface(&This->IAudioClock_iface, riid, ppv);
1637 static ULONG WINAPI AudioClock2_AddRef(IAudioClock2 *iface)
1639 ACImpl *This = impl_from_IAudioClock2(iface);
1640 return IAudioClient3_AddRef(&This->IAudioClient3_iface);
1643 static ULONG WINAPI AudioClock2_Release(IAudioClock2 *iface)
1645 ACImpl *This = impl_from_IAudioClock2(iface);
1646 return IAudioClient3_Release(&This->IAudioClient3_iface);
1649 static HRESULT WINAPI AudioClock2_GetDevicePosition(IAudioClock2 *iface,
1650 UINT64 *pos, UINT64 *qpctime)
1652 ACImpl *This = impl_from_IAudioClock2(iface);
1653 HRESULT hr = AudioClock_GetPosition(&This->IAudioClock_iface, pos, qpctime);
1654 if (SUCCEEDED(hr) && This->pulse_stream->share == AUDCLNT_SHAREMODE_SHARED)
1655 *pos /= pa_frame_size(&This->pulse_stream->ss);
1656 return hr;
1659 static const IAudioClock2Vtbl AudioClock2_Vtbl =
1661 AudioClock2_QueryInterface,
1662 AudioClock2_AddRef,
1663 AudioClock2_Release,
1664 AudioClock2_GetDevicePosition
1667 static HRESULT WINAPI AudioStreamVolume_QueryInterface(
1668 IAudioStreamVolume *iface, REFIID riid, void **ppv)
1670 ACImpl *This = impl_from_IAudioStreamVolume(iface);
1672 TRACE("(%p)->(%s, %p)\n", iface, debugstr_guid(riid), ppv);
1674 if (!ppv)
1675 return E_POINTER;
1676 *ppv = NULL;
1678 if (IsEqualIID(riid, &IID_IUnknown) ||
1679 IsEqualIID(riid, &IID_IAudioStreamVolume))
1680 *ppv = iface;
1681 if (*ppv) {
1682 IUnknown_AddRef((IUnknown*)*ppv);
1683 return S_OK;
1686 if (IsEqualIID(riid, &IID_IMarshal))
1687 return IUnknown_QueryInterface(This->marshal, riid, ppv);
1689 WARN("Unknown interface %s\n", debugstr_guid(riid));
1690 return E_NOINTERFACE;
1693 static ULONG WINAPI AudioStreamVolume_AddRef(IAudioStreamVolume *iface)
1695 ACImpl *This = impl_from_IAudioStreamVolume(iface);
1696 return IAudioClient3_AddRef(&This->IAudioClient3_iface);
1699 static ULONG WINAPI AudioStreamVolume_Release(IAudioStreamVolume *iface)
1701 ACImpl *This = impl_from_IAudioStreamVolume(iface);
1702 return IAudioClient3_Release(&This->IAudioClient3_iface);
1705 static HRESULT WINAPI AudioStreamVolume_GetChannelCount(
1706 IAudioStreamVolume *iface, UINT32 *out)
1708 ACImpl *This = impl_from_IAudioStreamVolume(iface);
1710 TRACE("(%p)->(%p)\n", This, out);
1712 if (!out)
1713 return E_POINTER;
1715 *out = This->channel_count;
1717 return S_OK;
1720 struct pulse_info_cb_data {
1721 UINT32 n;
1722 float *levels;
1725 static HRESULT WINAPI AudioStreamVolume_SetAllVolumes(
1726 IAudioStreamVolume *iface, UINT32 count, const float *levels)
1728 ACImpl *This = impl_from_IAudioStreamVolume(iface);
1729 HRESULT hr;
1730 int i;
1732 TRACE("(%p)->(%d, %p)\n", This, count, levels);
1734 if (!levels)
1735 return E_POINTER;
1737 if (count != This->channel_count)
1738 return E_INVALIDARG;
1740 pulse->lock();
1741 hr = pulse_stream_valid(This);
1742 if (FAILED(hr))
1743 goto out;
1745 for (i = 0; i < count; ++i)
1746 This->vol[i] = levels[i];
1748 set_stream_volumes(This);
1749 out:
1750 pulse->unlock();
1751 return hr;
1754 static HRESULT WINAPI AudioStreamVolume_GetAllVolumes(
1755 IAudioStreamVolume *iface, UINT32 count, float *levels)
1757 ACImpl *This = impl_from_IAudioStreamVolume(iface);
1758 HRESULT hr;
1759 int i;
1761 TRACE("(%p)->(%d, %p)\n", This, count, levels);
1763 if (!levels)
1764 return E_POINTER;
1766 if (count != This->channel_count)
1767 return E_INVALIDARG;
1769 pulse->lock();
1770 hr = pulse_stream_valid(This);
1771 if (FAILED(hr))
1772 goto out;
1774 for (i = 0; i < count; ++i)
1775 levels[i] = This->vol[i];
1777 out:
1778 pulse->unlock();
1779 return hr;
1782 static HRESULT WINAPI AudioStreamVolume_SetChannelVolume(
1783 IAudioStreamVolume *iface, UINT32 index, float level)
1785 ACImpl *This = impl_from_IAudioStreamVolume(iface);
1786 HRESULT hr;
1787 float volumes[PA_CHANNELS_MAX];
1789 TRACE("(%p)->(%d, %f)\n", This, index, level);
1791 if (level < 0.f || level > 1.f)
1792 return E_INVALIDARG;
1794 if (index >= This->channel_count)
1795 return E_INVALIDARG;
1797 hr = AudioStreamVolume_GetAllVolumes(iface, This->channel_count, volumes);
1798 volumes[index] = level;
1799 if (SUCCEEDED(hr))
1800 hr = AudioStreamVolume_SetAllVolumes(iface, This->channel_count, volumes);
1801 return hr;
1804 static HRESULT WINAPI AudioStreamVolume_GetChannelVolume(
1805 IAudioStreamVolume *iface, UINT32 index, float *level)
1807 ACImpl *This = impl_from_IAudioStreamVolume(iface);
1808 float volumes[PA_CHANNELS_MAX];
1809 HRESULT hr;
1811 TRACE("(%p)->(%d, %p)\n", This, index, level);
1813 if (!level)
1814 return E_POINTER;
1816 if (index >= This->channel_count)
1817 return E_INVALIDARG;
1819 hr = AudioStreamVolume_GetAllVolumes(iface, This->channel_count, volumes);
1820 if (SUCCEEDED(hr))
1821 *level = volumes[index];
1822 return hr;
1825 static const IAudioStreamVolumeVtbl AudioStreamVolume_Vtbl =
1827 AudioStreamVolume_QueryInterface,
1828 AudioStreamVolume_AddRef,
1829 AudioStreamVolume_Release,
1830 AudioStreamVolume_GetChannelCount,
1831 AudioStreamVolume_SetChannelVolume,
1832 AudioStreamVolume_GetChannelVolume,
1833 AudioStreamVolume_SetAllVolumes,
1834 AudioStreamVolume_GetAllVolumes
1837 static AudioSessionWrapper *AudioSessionWrapper_Create(ACImpl *client)
1839 AudioSessionWrapper *ret;
1841 ret = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY,
1842 sizeof(AudioSessionWrapper));
1843 if (!ret)
1844 return NULL;
1846 ret->IAudioSessionControl2_iface.lpVtbl = &AudioSessionControl2_Vtbl;
1847 ret->ISimpleAudioVolume_iface.lpVtbl = &SimpleAudioVolume_Vtbl;
1848 ret->IChannelAudioVolume_iface.lpVtbl = &ChannelAudioVolume_Vtbl;
1850 ret->ref = !client;
1852 ret->client = client;
1853 if (client) {
1854 ret->session = client->session;
1855 AudioClient_AddRef(&client->IAudioClient3_iface);
1858 return ret;
1861 static HRESULT WINAPI AudioSessionControl_QueryInterface(
1862 IAudioSessionControl2 *iface, REFIID riid, void **ppv)
1864 TRACE("(%p)->(%s, %p)\n", iface, debugstr_guid(riid), ppv);
1866 if (!ppv)
1867 return E_POINTER;
1868 *ppv = NULL;
1870 if (IsEqualIID(riid, &IID_IUnknown) ||
1871 IsEqualIID(riid, &IID_IAudioSessionControl) ||
1872 IsEqualIID(riid, &IID_IAudioSessionControl2))
1873 *ppv = iface;
1874 if (*ppv) {
1875 IUnknown_AddRef((IUnknown*)*ppv);
1876 return S_OK;
1879 WARN("Unknown interface %s\n", debugstr_guid(riid));
1880 return E_NOINTERFACE;
1883 static ULONG WINAPI AudioSessionControl_AddRef(IAudioSessionControl2 *iface)
1885 AudioSessionWrapper *This = impl_from_IAudioSessionControl2(iface);
1886 ULONG ref;
1887 ref = InterlockedIncrement(&This->ref);
1888 TRACE("(%p) Refcount now %u\n", This, ref);
1889 return ref;
1892 static ULONG WINAPI AudioSessionControl_Release(IAudioSessionControl2 *iface)
1894 AudioSessionWrapper *This = impl_from_IAudioSessionControl2(iface);
1895 ULONG ref;
1896 ref = InterlockedDecrement(&This->ref);
1897 TRACE("(%p) Refcount now %u\n", This, ref);
1898 if (!ref) {
1899 if (This->client) {
1900 This->client->session_wrapper = NULL;
1901 AudioClient_Release(&This->client->IAudioClient3_iface);
1903 HeapFree(GetProcessHeap(), 0, This);
1905 return ref;
1908 static HRESULT WINAPI AudioSessionControl_GetState(IAudioSessionControl2 *iface,
1909 AudioSessionState *state)
1911 AudioSessionWrapper *This = impl_from_IAudioSessionControl2(iface);
1912 ACImpl *client;
1914 TRACE("(%p)->(%p)\n", This, state);
1916 if (!state)
1917 return NULL_PTR_ERR;
1919 pulse->lock();
1920 if (list_empty(&This->session->clients)) {
1921 *state = AudioSessionStateExpired;
1922 goto out;
1924 LIST_FOR_EACH_ENTRY(client, &This->session->clients, ACImpl, entry) {
1925 if (client->pulse_stream->started) {
1926 *state = AudioSessionStateActive;
1927 goto out;
1930 *state = AudioSessionStateInactive;
1932 out:
1933 pulse->unlock();
1934 return S_OK;
1937 static HRESULT WINAPI AudioSessionControl_GetDisplayName(
1938 IAudioSessionControl2 *iface, WCHAR **name)
1940 AudioSessionWrapper *This = impl_from_IAudioSessionControl2(iface);
1942 FIXME("(%p)->(%p) - stub\n", This, name);
1944 return E_NOTIMPL;
1947 static HRESULT WINAPI AudioSessionControl_SetDisplayName(
1948 IAudioSessionControl2 *iface, const WCHAR *name, const GUID *session)
1950 AudioSessionWrapper *This = impl_from_IAudioSessionControl2(iface);
1952 FIXME("(%p)->(%p, %s) - stub\n", This, name, debugstr_guid(session));
1954 return E_NOTIMPL;
1957 static HRESULT WINAPI AudioSessionControl_GetIconPath(
1958 IAudioSessionControl2 *iface, WCHAR **path)
1960 AudioSessionWrapper *This = impl_from_IAudioSessionControl2(iface);
1962 FIXME("(%p)->(%p) - stub\n", This, path);
1964 return E_NOTIMPL;
1967 static HRESULT WINAPI AudioSessionControl_SetIconPath(
1968 IAudioSessionControl2 *iface, const WCHAR *path, const GUID *session)
1970 AudioSessionWrapper *This = impl_from_IAudioSessionControl2(iface);
1972 FIXME("(%p)->(%p, %s) - stub\n", This, path, debugstr_guid(session));
1974 return E_NOTIMPL;
1977 static HRESULT WINAPI AudioSessionControl_GetGroupingParam(
1978 IAudioSessionControl2 *iface, GUID *group)
1980 AudioSessionWrapper *This = impl_from_IAudioSessionControl2(iface);
1982 FIXME("(%p)->(%p) - stub\n", This, group);
1984 return E_NOTIMPL;
1987 static HRESULT WINAPI AudioSessionControl_SetGroupingParam(
1988 IAudioSessionControl2 *iface, const GUID *group, const GUID *session)
1990 AudioSessionWrapper *This = impl_from_IAudioSessionControl2(iface);
1992 FIXME("(%p)->(%s, %s) - stub\n", This, debugstr_guid(group),
1993 debugstr_guid(session));
1995 return E_NOTIMPL;
1998 static HRESULT WINAPI AudioSessionControl_RegisterAudioSessionNotification(
1999 IAudioSessionControl2 *iface, IAudioSessionEvents *events)
2001 AudioSessionWrapper *This = impl_from_IAudioSessionControl2(iface);
2003 FIXME("(%p)->(%p) - stub\n", This, events);
2005 return S_OK;
2008 static HRESULT WINAPI AudioSessionControl_UnregisterAudioSessionNotification(
2009 IAudioSessionControl2 *iface, IAudioSessionEvents *events)
2011 AudioSessionWrapper *This = impl_from_IAudioSessionControl2(iface);
2013 FIXME("(%p)->(%p) - stub\n", This, events);
2015 return S_OK;
2018 static HRESULT WINAPI AudioSessionControl_GetSessionIdentifier(
2019 IAudioSessionControl2 *iface, WCHAR **id)
2021 AudioSessionWrapper *This = impl_from_IAudioSessionControl2(iface);
2023 FIXME("(%p)->(%p) - stub\n", This, id);
2025 return E_NOTIMPL;
2028 static HRESULT WINAPI AudioSessionControl_GetSessionInstanceIdentifier(
2029 IAudioSessionControl2 *iface, WCHAR **id)
2031 AudioSessionWrapper *This = impl_from_IAudioSessionControl2(iface);
2033 FIXME("(%p)->(%p) - stub\n", This, id);
2035 return E_NOTIMPL;
2038 static HRESULT WINAPI AudioSessionControl_GetProcessId(
2039 IAudioSessionControl2 *iface, DWORD *pid)
2041 AudioSessionWrapper *This = impl_from_IAudioSessionControl2(iface);
2043 TRACE("(%p)->(%p)\n", This, pid);
2045 if (!pid)
2046 return E_POINTER;
2048 *pid = GetCurrentProcessId();
2050 return S_OK;
2053 static HRESULT WINAPI AudioSessionControl_IsSystemSoundsSession(
2054 IAudioSessionControl2 *iface)
2056 AudioSessionWrapper *This = impl_from_IAudioSessionControl2(iface);
2058 TRACE("(%p)\n", This);
2060 return S_FALSE;
2063 static HRESULT WINAPI AudioSessionControl_SetDuckingPreference(
2064 IAudioSessionControl2 *iface, BOOL optout)
2066 AudioSessionWrapper *This = impl_from_IAudioSessionControl2(iface);
2068 TRACE("(%p)->(%d)\n", This, optout);
2070 return S_OK;
2073 static const IAudioSessionControl2Vtbl AudioSessionControl2_Vtbl =
2075 AudioSessionControl_QueryInterface,
2076 AudioSessionControl_AddRef,
2077 AudioSessionControl_Release,
2078 AudioSessionControl_GetState,
2079 AudioSessionControl_GetDisplayName,
2080 AudioSessionControl_SetDisplayName,
2081 AudioSessionControl_GetIconPath,
2082 AudioSessionControl_SetIconPath,
2083 AudioSessionControl_GetGroupingParam,
2084 AudioSessionControl_SetGroupingParam,
2085 AudioSessionControl_RegisterAudioSessionNotification,
2086 AudioSessionControl_UnregisterAudioSessionNotification,
2087 AudioSessionControl_GetSessionIdentifier,
2088 AudioSessionControl_GetSessionInstanceIdentifier,
2089 AudioSessionControl_GetProcessId,
2090 AudioSessionControl_IsSystemSoundsSession,
2091 AudioSessionControl_SetDuckingPreference
2094 typedef struct _SessionMgr {
2095 IAudioSessionManager2 IAudioSessionManager2_iface;
2097 LONG ref;
2099 IMMDevice *device;
2100 } SessionMgr;
2102 static HRESULT WINAPI AudioSessionManager_QueryInterface(IAudioSessionManager2 *iface,
2103 REFIID riid, void **ppv)
2105 TRACE("(%p)->(%s, %p)\n", iface, debugstr_guid(riid), ppv);
2107 if (!ppv)
2108 return E_POINTER;
2109 *ppv = NULL;
2111 if (IsEqualIID(riid, &IID_IUnknown) ||
2112 IsEqualIID(riid, &IID_IAudioSessionManager) ||
2113 IsEqualIID(riid, &IID_IAudioSessionManager2))
2114 *ppv = iface;
2115 if (*ppv) {
2116 IUnknown_AddRef((IUnknown*)*ppv);
2117 return S_OK;
2120 WARN("Unknown interface %s\n", debugstr_guid(riid));
2121 return E_NOINTERFACE;
2124 static inline SessionMgr *impl_from_IAudioSessionManager2(IAudioSessionManager2 *iface)
2126 return CONTAINING_RECORD(iface, SessionMgr, IAudioSessionManager2_iface);
2129 static ULONG WINAPI AudioSessionManager_AddRef(IAudioSessionManager2 *iface)
2131 SessionMgr *This = impl_from_IAudioSessionManager2(iface);
2132 ULONG ref;
2133 ref = InterlockedIncrement(&This->ref);
2134 TRACE("(%p) Refcount now %u\n", This, ref);
2135 return ref;
2138 static ULONG WINAPI AudioSessionManager_Release(IAudioSessionManager2 *iface)
2140 SessionMgr *This = impl_from_IAudioSessionManager2(iface);
2141 ULONG ref;
2142 ref = InterlockedDecrement(&This->ref);
2143 TRACE("(%p) Refcount now %u\n", This, ref);
2144 if (!ref)
2145 HeapFree(GetProcessHeap(), 0, This);
2146 return ref;
2149 static HRESULT WINAPI AudioSessionManager_GetAudioSessionControl(
2150 IAudioSessionManager2 *iface, const GUID *session_guid, DWORD flags,
2151 IAudioSessionControl **out)
2153 SessionMgr *This = impl_from_IAudioSessionManager2(iface);
2154 AudioSession *session;
2155 AudioSessionWrapper *wrapper;
2156 HRESULT hr;
2158 TRACE("(%p)->(%s, %x, %p)\n", This, debugstr_guid(session_guid),
2159 flags, out);
2161 hr = get_audio_session(session_guid, This->device, 0, &session);
2162 if (FAILED(hr))
2163 return hr;
2165 wrapper = AudioSessionWrapper_Create(NULL);
2166 if (!wrapper)
2167 return E_OUTOFMEMORY;
2169 wrapper->session = session;
2171 *out = (IAudioSessionControl*)&wrapper->IAudioSessionControl2_iface;
2173 return S_OK;
2176 static HRESULT WINAPI AudioSessionManager_GetSimpleAudioVolume(
2177 IAudioSessionManager2 *iface, const GUID *session_guid, DWORD flags,
2178 ISimpleAudioVolume **out)
2180 SessionMgr *This = impl_from_IAudioSessionManager2(iface);
2181 AudioSession *session;
2182 AudioSessionWrapper *wrapper;
2183 HRESULT hr;
2185 TRACE("(%p)->(%s, %x, %p)\n", This, debugstr_guid(session_guid),
2186 flags, out);
2188 hr = get_audio_session(session_guid, This->device, 0, &session);
2189 if (FAILED(hr))
2190 return hr;
2192 wrapper = AudioSessionWrapper_Create(NULL);
2193 if (!wrapper)
2194 return E_OUTOFMEMORY;
2196 wrapper->session = session;
2198 *out = &wrapper->ISimpleAudioVolume_iface;
2200 return S_OK;
2203 static HRESULT WINAPI AudioSessionManager_GetSessionEnumerator(
2204 IAudioSessionManager2 *iface, IAudioSessionEnumerator **out)
2206 SessionMgr *This = impl_from_IAudioSessionManager2(iface);
2207 FIXME("(%p)->(%p) - stub\n", This, out);
2208 return E_NOTIMPL;
2211 static HRESULT WINAPI AudioSessionManager_RegisterSessionNotification(
2212 IAudioSessionManager2 *iface, IAudioSessionNotification *notification)
2214 SessionMgr *This = impl_from_IAudioSessionManager2(iface);
2215 FIXME("(%p)->(%p) - stub\n", This, notification);
2216 return E_NOTIMPL;
2219 static HRESULT WINAPI AudioSessionManager_UnregisterSessionNotification(
2220 IAudioSessionManager2 *iface, IAudioSessionNotification *notification)
2222 SessionMgr *This = impl_from_IAudioSessionManager2(iface);
2223 FIXME("(%p)->(%p) - stub\n", This, notification);
2224 return E_NOTIMPL;
2227 static HRESULT WINAPI AudioSessionManager_RegisterDuckNotification(
2228 IAudioSessionManager2 *iface, const WCHAR *session_id,
2229 IAudioVolumeDuckNotification *notification)
2231 SessionMgr *This = impl_from_IAudioSessionManager2(iface);
2232 FIXME("(%p)->(%p) - stub\n", This, notification);
2233 return E_NOTIMPL;
2236 static HRESULT WINAPI AudioSessionManager_UnregisterDuckNotification(
2237 IAudioSessionManager2 *iface,
2238 IAudioVolumeDuckNotification *notification)
2240 SessionMgr *This = impl_from_IAudioSessionManager2(iface);
2241 FIXME("(%p)->(%p) - stub\n", This, notification);
2242 return E_NOTIMPL;
2245 static const IAudioSessionManager2Vtbl AudioSessionManager2_Vtbl =
2247 AudioSessionManager_QueryInterface,
2248 AudioSessionManager_AddRef,
2249 AudioSessionManager_Release,
2250 AudioSessionManager_GetAudioSessionControl,
2251 AudioSessionManager_GetSimpleAudioVolume,
2252 AudioSessionManager_GetSessionEnumerator,
2253 AudioSessionManager_RegisterSessionNotification,
2254 AudioSessionManager_UnregisterSessionNotification,
2255 AudioSessionManager_RegisterDuckNotification,
2256 AudioSessionManager_UnregisterDuckNotification
2259 static HRESULT WINAPI SimpleAudioVolume_QueryInterface(
2260 ISimpleAudioVolume *iface, REFIID riid, void **ppv)
2262 TRACE("(%p)->(%s, %p)\n", iface, debugstr_guid(riid), ppv);
2264 if (!ppv)
2265 return E_POINTER;
2266 *ppv = NULL;
2268 if (IsEqualIID(riid, &IID_IUnknown) ||
2269 IsEqualIID(riid, &IID_ISimpleAudioVolume))
2270 *ppv = iface;
2271 if (*ppv) {
2272 IUnknown_AddRef((IUnknown*)*ppv);
2273 return S_OK;
2276 WARN("Unknown interface %s\n", debugstr_guid(riid));
2277 return E_NOINTERFACE;
2280 static ULONG WINAPI SimpleAudioVolume_AddRef(ISimpleAudioVolume *iface)
2282 AudioSessionWrapper *This = impl_from_ISimpleAudioVolume(iface);
2283 return AudioSessionControl_AddRef(&This->IAudioSessionControl2_iface);
2286 static ULONG WINAPI SimpleAudioVolume_Release(ISimpleAudioVolume *iface)
2288 AudioSessionWrapper *This = impl_from_ISimpleAudioVolume(iface);
2289 return AudioSessionControl_Release(&This->IAudioSessionControl2_iface);
2292 static HRESULT WINAPI SimpleAudioVolume_SetMasterVolume(
2293 ISimpleAudioVolume *iface, float level, const GUID *context)
2295 AudioSessionWrapper *This = impl_from_ISimpleAudioVolume(iface);
2296 AudioSession *session = This->session;
2297 ACImpl *client;
2299 TRACE("(%p)->(%f, %s)\n", session, level, wine_dbgstr_guid(context));
2301 if (level < 0.f || level > 1.f)
2302 return E_INVALIDARG;
2304 if (context)
2305 FIXME("Notifications not supported yet\n");
2307 TRACE("PulseAudio does not support session volume control\n");
2309 pulse->lock();
2310 session->master_vol = level;
2311 LIST_FOR_EACH_ENTRY(client, &This->session->clients, ACImpl, entry)
2312 set_stream_volumes(client);
2313 pulse->unlock();
2315 return S_OK;
2318 static HRESULT WINAPI SimpleAudioVolume_GetMasterVolume(
2319 ISimpleAudioVolume *iface, float *level)
2321 AudioSessionWrapper *This = impl_from_ISimpleAudioVolume(iface);
2322 AudioSession *session = This->session;
2324 TRACE("(%p)->(%p)\n", session, level);
2326 if (!level)
2327 return NULL_PTR_ERR;
2329 *level = session->master_vol;
2331 return S_OK;
2334 static HRESULT WINAPI SimpleAudioVolume_SetMute(ISimpleAudioVolume *iface,
2335 BOOL mute, const GUID *context)
2337 AudioSessionWrapper *This = impl_from_ISimpleAudioVolume(iface);
2338 AudioSession *session = This->session;
2339 ACImpl *client;
2341 TRACE("(%p)->(%u, %s)\n", session, mute, debugstr_guid(context));
2343 if (context)
2344 FIXME("Notifications not supported yet\n");
2346 pulse->lock();
2347 session->mute = mute;
2348 LIST_FOR_EACH_ENTRY(client, &This->session->clients, ACImpl, entry)
2349 set_stream_volumes(client);
2350 pulse->unlock();
2352 return S_OK;
2355 static HRESULT WINAPI SimpleAudioVolume_GetMute(ISimpleAudioVolume *iface,
2356 BOOL *mute)
2358 AudioSessionWrapper *This = impl_from_ISimpleAudioVolume(iface);
2359 AudioSession *session = This->session;
2361 TRACE("(%p)->(%p)\n", session, mute);
2363 if (!mute)
2364 return NULL_PTR_ERR;
2366 *mute = session->mute;
2368 return S_OK;
2371 static const ISimpleAudioVolumeVtbl SimpleAudioVolume_Vtbl =
2373 SimpleAudioVolume_QueryInterface,
2374 SimpleAudioVolume_AddRef,
2375 SimpleAudioVolume_Release,
2376 SimpleAudioVolume_SetMasterVolume,
2377 SimpleAudioVolume_GetMasterVolume,
2378 SimpleAudioVolume_SetMute,
2379 SimpleAudioVolume_GetMute
2382 static HRESULT WINAPI ChannelAudioVolume_QueryInterface(
2383 IChannelAudioVolume *iface, REFIID riid, void **ppv)
2385 TRACE("(%p)->(%s, %p)\n", iface, debugstr_guid(riid), ppv);
2387 if (!ppv)
2388 return E_POINTER;
2389 *ppv = NULL;
2391 if (IsEqualIID(riid, &IID_IUnknown) ||
2392 IsEqualIID(riid, &IID_IChannelAudioVolume))
2393 *ppv = iface;
2394 if (*ppv) {
2395 IUnknown_AddRef((IUnknown*)*ppv);
2396 return S_OK;
2399 WARN("Unknown interface %s\n", debugstr_guid(riid));
2400 return E_NOINTERFACE;
2403 static ULONG WINAPI ChannelAudioVolume_AddRef(IChannelAudioVolume *iface)
2405 AudioSessionWrapper *This = impl_from_IChannelAudioVolume(iface);
2406 return AudioSessionControl_AddRef(&This->IAudioSessionControl2_iface);
2409 static ULONG WINAPI ChannelAudioVolume_Release(IChannelAudioVolume *iface)
2411 AudioSessionWrapper *This = impl_from_IChannelAudioVolume(iface);
2412 return AudioSessionControl_Release(&This->IAudioSessionControl2_iface);
2415 static HRESULT WINAPI ChannelAudioVolume_GetChannelCount(
2416 IChannelAudioVolume *iface, UINT32 *out)
2418 AudioSessionWrapper *This = impl_from_IChannelAudioVolume(iface);
2419 AudioSession *session = This->session;
2421 TRACE("(%p)->(%p)\n", session, out);
2423 if (!out)
2424 return NULL_PTR_ERR;
2426 *out = session->channel_count;
2428 return S_OK;
2431 static HRESULT WINAPI ChannelAudioVolume_SetChannelVolume(
2432 IChannelAudioVolume *iface, UINT32 index, float level,
2433 const GUID *context)
2435 AudioSessionWrapper *This = impl_from_IChannelAudioVolume(iface);
2436 AudioSession *session = This->session;
2437 ACImpl *client;
2439 TRACE("(%p)->(%d, %f, %s)\n", session, index, level,
2440 wine_dbgstr_guid(context));
2442 if (level < 0.f || level > 1.f)
2443 return E_INVALIDARG;
2445 if (index >= session->channel_count)
2446 return E_INVALIDARG;
2448 if (context)
2449 FIXME("Notifications not supported yet\n");
2451 TRACE("PulseAudio does not support session volume control\n");
2453 pulse->lock();
2454 session->channel_vols[index] = level;
2455 LIST_FOR_EACH_ENTRY(client, &This->session->clients, ACImpl, entry)
2456 set_stream_volumes(client);
2457 pulse->unlock();
2459 return S_OK;
2462 static HRESULT WINAPI ChannelAudioVolume_GetChannelVolume(
2463 IChannelAudioVolume *iface, UINT32 index, float *level)
2465 AudioSessionWrapper *This = impl_from_IChannelAudioVolume(iface);
2466 AudioSession *session = This->session;
2468 TRACE("(%p)->(%d, %p)\n", session, index, level);
2470 if (!level)
2471 return NULL_PTR_ERR;
2473 if (index >= session->channel_count)
2474 return E_INVALIDARG;
2476 *level = session->channel_vols[index];
2478 return S_OK;
2481 static HRESULT WINAPI ChannelAudioVolume_SetAllVolumes(
2482 IChannelAudioVolume *iface, UINT32 count, const float *levels,
2483 const GUID *context)
2485 AudioSessionWrapper *This = impl_from_IChannelAudioVolume(iface);
2486 AudioSession *session = This->session;
2487 ACImpl *client;
2488 int i;
2490 TRACE("(%p)->(%d, %p, %s)\n", session, count, levels,
2491 wine_dbgstr_guid(context));
2493 if (!levels)
2494 return NULL_PTR_ERR;
2496 if (count != session->channel_count)
2497 return E_INVALIDARG;
2499 if (context)
2500 FIXME("Notifications not supported yet\n");
2502 TRACE("PulseAudio does not support session volume control\n");
2504 pulse->lock();
2505 for(i = 0; i < count; ++i)
2506 session->channel_vols[i] = levels[i];
2507 LIST_FOR_EACH_ENTRY(client, &This->session->clients, ACImpl, entry)
2508 set_stream_volumes(client);
2509 pulse->unlock();
2510 return S_OK;
2513 static HRESULT WINAPI ChannelAudioVolume_GetAllVolumes(
2514 IChannelAudioVolume *iface, UINT32 count, float *levels)
2516 AudioSessionWrapper *This = impl_from_IChannelAudioVolume(iface);
2517 AudioSession *session = This->session;
2518 int i;
2520 TRACE("(%p)->(%d, %p)\n", session, count, levels);
2522 if (!levels)
2523 return NULL_PTR_ERR;
2525 if (count != session->channel_count)
2526 return E_INVALIDARG;
2528 for(i = 0; i < count; ++i)
2529 levels[i] = session->channel_vols[i];
2531 return S_OK;
2534 static const IChannelAudioVolumeVtbl ChannelAudioVolume_Vtbl =
2536 ChannelAudioVolume_QueryInterface,
2537 ChannelAudioVolume_AddRef,
2538 ChannelAudioVolume_Release,
2539 ChannelAudioVolume_GetChannelCount,
2540 ChannelAudioVolume_SetChannelVolume,
2541 ChannelAudioVolume_GetChannelVolume,
2542 ChannelAudioVolume_SetAllVolumes,
2543 ChannelAudioVolume_GetAllVolumes
2546 HRESULT WINAPI AUDDRV_GetAudioSessionManager(IMMDevice *device,
2547 IAudioSessionManager2 **out)
2549 SessionMgr *This = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, sizeof(SessionMgr));
2550 *out = NULL;
2551 if (!This)
2552 return E_OUTOFMEMORY;
2553 This->IAudioSessionManager2_iface.lpVtbl = &AudioSessionManager2_Vtbl;
2554 This->device = device;
2555 This->ref = 1;
2556 *out = &This->IAudioSessionManager2_iface;
2557 return S_OK;
2560 HRESULT WINAPI AUDDRV_GetPropValue(GUID *guid, const PROPERTYKEY *prop, PROPVARIANT *out)
2562 TRACE("%s, (%s,%u), %p\n", wine_dbgstr_guid(guid), wine_dbgstr_guid(&prop->fmtid), prop->pid, out);
2564 if (IsEqualGUID(guid, &pulse_render_guid) && IsEqualPropertyKey(*prop, PKEY_AudioEndpoint_PhysicalSpeakers)) {
2565 out->vt = VT_UI4;
2566 out->ulVal = pulse_config.speakers_mask;
2568 return out->ulVal ? S_OK : E_FAIL;
2571 return E_NOTIMPL;