winepulse: Fix low latency support
[wine/multimedia.git] / dlls / winepulse.drv / mmdevdrv.c
blob7c07f542525d6618db96467eac487eaf96bf3eb2
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
20 * Pulseaudio driver support.. hell froze over
23 #define NONAMELESSUNION
24 #define COBJMACROS
25 #include "config.h"
26 #include <poll.h>
27 #include <pthread.h>
29 #include <stdarg.h>
30 #include <unistd.h>
31 #include <math.h>
32 #include <stdio.h>
34 #include <pulse/pulseaudio.h>
36 #include "windef.h"
37 #include "winbase.h"
38 #include "winnls.h"
39 #include "winreg.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 "mmdeviceapi.h"
53 #include "audioclient.h"
54 #include "endpointvolume.h"
55 #include "audiopolicy.h"
57 #include "wine/list.h"
59 #define NULL_PTR_ERR MAKE_HRESULT(SEVERITY_ERROR, FACILITY_WIN32, RPC_X_NULL_REF_POINTER)
61 WINE_DEFAULT_DEBUG_CHANNEL(pulse);
62 WINE_DECLARE_DEBUG_CHANNEL(winediag);
64 static const REFERENCE_TIME MinimumPeriod = 30000;
65 static const REFERENCE_TIME DefaultPeriod = 100000;
67 static pa_context *pulse_ctx;
68 static pa_mainloop *pulse_ml;
70 static HANDLE pulse_thread;
71 static pthread_mutex_t pulse_lock = PTHREAD_MUTEX_INITIALIZER;
72 static pthread_cond_t pulse_cond = PTHREAD_COND_INITIALIZER;
73 static struct list g_sessions = LIST_INIT(g_sessions);
75 /* Mixer format + period times */
76 static WAVEFORMATEXTENSIBLE pulse_fmt[2];
77 static REFERENCE_TIME pulse_min_period[2], pulse_def_period[2];
79 static DWORD pulse_stream_volume;
81 const WCHAR pulse_keyW[] = {'S','o','f','t','w','a','r','e','\\',
82 'W','i','n','e','\\','P','u','l','s','e',0};
83 const WCHAR pulse_streamW[] = { 'S','t','r','e','a','m','V','o','l',0 };
85 static GUID pulse_render_guid =
86 { 0xfd47d9cc, 0x4218, 0x4135, { 0x9c, 0xe2, 0x0c, 0x19, 0x5c, 0x87, 0x40, 0x5b } };
87 static GUID pulse_capture_guid =
88 { 0x25da76d0, 0x033c, 0x4235, { 0x90, 0x02, 0x19, 0xf4, 0x88, 0x94, 0xac, 0x6f } };
90 static HANDLE warn_once;
92 BOOL WINAPI DllMain(HINSTANCE dll, DWORD reason, void *reserved)
94 if (reason == DLL_PROCESS_ATTACH) {
95 HKEY key;
96 if (RegOpenKeyW(HKEY_CURRENT_USER, pulse_keyW, &key) == ERROR_SUCCESS) {
97 DWORD size = sizeof(pulse_stream_volume);
98 RegQueryValueExW(key, pulse_streamW, 0, NULL,
99 (BYTE*)&pulse_stream_volume, &size);
100 RegCloseKey(key);
102 DisableThreadLibraryCalls(dll);
103 } else if (reason == DLL_PROCESS_DETACH) {
104 if (pulse_ctx) {
105 pa_context_disconnect(pulse_ctx);
106 pa_context_unref(pulse_ctx);
108 if (pulse_ml)
109 pa_mainloop_quit(pulse_ml, 0);
110 if (pulse_thread)
111 CloseHandle(pulse_thread);
112 if (warn_once)
113 CloseHandle(warn_once);
115 return TRUE;
118 typedef struct ACImpl ACImpl;
120 typedef struct _AudioSession {
121 GUID guid;
122 struct list clients;
124 IMMDevice *device;
126 float master_vol;
127 UINT32 channel_count;
128 float *channel_vols;
129 BOOL mute;
131 struct list entry;
132 } AudioSession;
134 typedef struct _AudioSessionWrapper {
135 IAudioSessionControl2 IAudioSessionControl2_iface;
136 IChannelAudioVolume IChannelAudioVolume_iface;
137 ISimpleAudioVolume ISimpleAudioVolume_iface;
139 LONG ref;
141 ACImpl *client;
142 AudioSession *session;
143 } AudioSessionWrapper;
145 typedef struct _ACPacket {
146 struct list entry;
147 UINT64 qpcpos;
148 BYTE *data;
149 UINT32 discont;
150 } ACPacket;
152 struct ACImpl {
153 IAudioClient IAudioClient_iface;
154 IAudioRenderClient IAudioRenderClient_iface;
155 IAudioCaptureClient IAudioCaptureClient_iface;
156 IAudioClock IAudioClock_iface;
157 IAudioClock2 IAudioClock2_iface;
158 IAudioStreamVolume IAudioStreamVolume_iface;
159 IMMDevice *parent;
160 struct list entry;
161 float vol[PA_CHANNELS_MAX];
163 LONG ref;
164 EDataFlow dataflow;
165 DWORD flags;
166 AUDCLNT_SHAREMODE share;
167 HANDLE event;
169 UINT32 bufsize_frames, bufsize_bytes, locked, capture_period, pad, started, peek_ofs;
170 void *locked_ptr, *tmp_buffer;
172 pa_stream *stream;
173 pa_sample_spec ss;
174 pa_channel_map map;
176 INT64 clock_lastpos, clock_written;
178 AudioSession *session;
179 AudioSessionWrapper *session_wrapper;
180 struct list packet_free_head;
181 struct list packet_filled_head;
184 static const WCHAR defaultW[] = {'P','u','l','s','e','a','u','d','i','o',0};
186 static const IAudioClientVtbl AudioClient_Vtbl;
187 static const IAudioRenderClientVtbl AudioRenderClient_Vtbl;
188 static const IAudioCaptureClientVtbl AudioCaptureClient_Vtbl;
189 static const IAudioSessionControl2Vtbl AudioSessionControl2_Vtbl;
190 static const ISimpleAudioVolumeVtbl SimpleAudioVolume_Vtbl;
191 static const IChannelAudioVolumeVtbl ChannelAudioVolume_Vtbl;
192 static const IAudioClockVtbl AudioClock_Vtbl;
193 static const IAudioClock2Vtbl AudioClock2_Vtbl;
194 static const IAudioStreamVolumeVtbl AudioStreamVolume_Vtbl;
196 static AudioSessionWrapper *AudioSessionWrapper_Create(ACImpl *client);
198 static inline ACImpl *impl_from_IAudioClient(IAudioClient *iface)
200 return CONTAINING_RECORD(iface, ACImpl, IAudioClient_iface);
203 static inline ACImpl *impl_from_IAudioRenderClient(IAudioRenderClient *iface)
205 return CONTAINING_RECORD(iface, ACImpl, IAudioRenderClient_iface);
208 static inline ACImpl *impl_from_IAudioCaptureClient(IAudioCaptureClient *iface)
210 return CONTAINING_RECORD(iface, ACImpl, IAudioCaptureClient_iface);
213 static inline AudioSessionWrapper *impl_from_IAudioSessionControl2(IAudioSessionControl2 *iface)
215 return CONTAINING_RECORD(iface, AudioSessionWrapper, IAudioSessionControl2_iface);
218 static inline AudioSessionWrapper *impl_from_ISimpleAudioVolume(ISimpleAudioVolume *iface)
220 return CONTAINING_RECORD(iface, AudioSessionWrapper, ISimpleAudioVolume_iface);
223 static inline AudioSessionWrapper *impl_from_IChannelAudioVolume(IChannelAudioVolume *iface)
225 return CONTAINING_RECORD(iface, AudioSessionWrapper, IChannelAudioVolume_iface);
228 static inline ACImpl *impl_from_IAudioClock(IAudioClock *iface)
230 return CONTAINING_RECORD(iface, ACImpl, IAudioClock_iface);
233 static inline ACImpl *impl_from_IAudioClock2(IAudioClock2 *iface)
235 return CONTAINING_RECORD(iface, ACImpl, IAudioClock2_iface);
238 static inline ACImpl *impl_from_IAudioStreamVolume(IAudioStreamVolume *iface)
240 return CONTAINING_RECORD(iface, ACImpl, IAudioStreamVolume_iface);
243 /* Following pulseaudio design here, mainloop has the lock taken whenever
244 * it is handling something for pulse, and the lock is required whenever
245 * doing any pa_* call that can affect the state in any way
247 * pa_cond_wait is used when waiting on results, because the mainloop needs
248 * the same lock taken to affect the state
250 * This is basically the same as the pa_threaded_mainloop implementation,
251 * but that cannot be used because it uses pthread_create directly
253 * pa_threaded_mainloop_(un)lock -> pthread_mutex_(un)lock
254 * pa_threaded_mainloop_signal -> pthread_cond_signal
255 * pa_threaded_mainloop_wait -> pthread_cond_wait
258 static int pulse_poll_func(struct pollfd *ufds, unsigned long nfds, int timeout, void *userdata) {
259 int r;
260 pthread_mutex_unlock(&pulse_lock);
261 r = poll(ufds, nfds, timeout);
262 pthread_mutex_lock(&pulse_lock);
263 return r;
266 static DWORD CALLBACK pulse_mainloop_thread(void *tmp) {
267 int ret;
268 pulse_ml = pa_mainloop_new();
269 pa_mainloop_set_poll_func(pulse_ml, pulse_poll_func, NULL);
270 pthread_mutex_lock(&pulse_lock);
271 pthread_cond_signal(&pulse_cond);
272 pa_mainloop_run(pulse_ml, &ret);
273 pthread_mutex_unlock(&pulse_lock);
274 pa_mainloop_free(pulse_ml);
275 CloseHandle(pulse_thread);
276 return ret;
279 static void pulse_contextcallback(pa_context *c, void *userdata);
280 static void pulse_stream_state(pa_stream *s, void *user);
282 static const enum pa_channel_position pulse_pos_from_wfx[] = {
283 PA_CHANNEL_POSITION_FRONT_LEFT,
284 PA_CHANNEL_POSITION_FRONT_RIGHT,
285 PA_CHANNEL_POSITION_FRONT_CENTER,
286 PA_CHANNEL_POSITION_LFE,
287 PA_CHANNEL_POSITION_REAR_LEFT,
288 PA_CHANNEL_POSITION_REAR_RIGHT,
289 PA_CHANNEL_POSITION_FRONT_LEFT_OF_CENTER,
290 PA_CHANNEL_POSITION_FRONT_RIGHT_OF_CENTER,
291 PA_CHANNEL_POSITION_REAR_CENTER,
292 PA_CHANNEL_POSITION_SIDE_LEFT,
293 PA_CHANNEL_POSITION_SIDE_RIGHT,
294 PA_CHANNEL_POSITION_TOP_CENTER,
295 PA_CHANNEL_POSITION_TOP_FRONT_LEFT,
296 PA_CHANNEL_POSITION_TOP_FRONT_CENTER,
297 PA_CHANNEL_POSITION_TOP_FRONT_RIGHT,
298 PA_CHANNEL_POSITION_TOP_REAR_LEFT,
299 PA_CHANNEL_POSITION_TOP_REAR_CENTER,
300 PA_CHANNEL_POSITION_TOP_REAR_RIGHT
303 static void pulse_probe_settings(int render, WAVEFORMATEXTENSIBLE *fmt) {
304 WAVEFORMATEX *wfx = &fmt->Format;
305 pa_stream *stream;
306 pa_channel_map map;
307 pa_sample_spec ss;
308 pa_buffer_attr attr;
309 int ret, i;
310 unsigned int length = 0;
312 pa_channel_map_init_auto(&map, 2, PA_CHANNEL_MAP_ALSA);
313 ss.rate = 48000;
314 ss.format = PA_SAMPLE_FLOAT32LE;
315 ss.channels = map.channels;
317 attr.maxlength = -1;
318 attr.tlength = -1;
319 attr.minreq = attr.fragsize = pa_frame_size(&ss);
320 attr.prebuf = 0;
322 stream = pa_stream_new(pulse_ctx, "format test stream", &ss, &map);
323 if (stream)
324 pa_stream_set_state_callback(stream, pulse_stream_state, NULL);
325 if (!stream)
326 ret = -1;
327 else if (render)
328 ret = pa_stream_connect_playback(stream, NULL, &attr,
329 PA_STREAM_START_CORKED|PA_STREAM_FIX_RATE|PA_STREAM_FIX_CHANNELS|PA_STREAM_EARLY_REQUESTS, NULL, NULL);
330 else
331 ret = pa_stream_connect_record(stream, NULL, &attr, PA_STREAM_START_CORKED|PA_STREAM_FIX_RATE|PA_STREAM_FIX_CHANNELS|PA_STREAM_EARLY_REQUESTS);
332 if (ret >= 0) {
333 while (pa_stream_get_state(stream) == PA_STREAM_CREATING)
334 pthread_cond_wait(&pulse_cond, &pulse_lock);
335 if (pa_stream_get_state(stream) == PA_STREAM_READY) {
336 ss = *pa_stream_get_sample_spec(stream);
337 map = *pa_stream_get_channel_map(stream);
338 if (render)
339 length = pa_stream_get_buffer_attr(stream)->minreq;
340 else
341 length = pa_stream_get_buffer_attr(stream)->fragsize;
342 pa_stream_disconnect(stream);
343 while (pa_stream_get_state(stream) == PA_STREAM_READY)
344 pthread_cond_wait(&pulse_cond, &pulse_lock);
347 if (stream)
348 pa_stream_unref(stream);
349 if (length)
350 pulse_def_period[!render] = pulse_min_period[!render] = pa_bytes_to_usec(10 * length, &ss);
351 else
352 pulse_min_period[!render] = MinimumPeriod;
353 if (pulse_def_period[!render] <= DefaultPeriod)
354 pulse_def_period[!render] = DefaultPeriod;
356 wfx->wFormatTag = WAVE_FORMAT_EXTENSIBLE;
357 wfx->cbSize = sizeof(WAVEFORMATEXTENSIBLE) - sizeof(WAVEFORMATEX);
358 wfx->nChannels = ss.channels;
359 wfx->wBitsPerSample = 8 * pa_sample_size_of_format(ss.format);
360 wfx->nSamplesPerSec = ss.rate;
361 wfx->nBlockAlign = pa_frame_size(&ss);
362 wfx->nAvgBytesPerSec = wfx->nSamplesPerSec * wfx->nBlockAlign;
363 if (ss.format != PA_SAMPLE_S24_32LE)
364 fmt->Samples.wValidBitsPerSample = wfx->wBitsPerSample;
365 else
366 fmt->Samples.wValidBitsPerSample = 24;
367 if (ss.format == PA_SAMPLE_FLOAT32LE)
368 fmt->SubFormat = KSDATAFORMAT_SUBTYPE_IEEE_FLOAT;
369 else
370 fmt->SubFormat = KSDATAFORMAT_SUBTYPE_PCM;
372 fmt->dwChannelMask = 0;
373 for (i = 0; i < map.channels; ++i)
374 switch (map.map[i]) {
375 default: FIXME("Unhandled channel %s\n", pa_channel_position_to_string(map.map[i])); break;
376 case PA_CHANNEL_POSITION_FRONT_LEFT: fmt->dwChannelMask |= SPEAKER_FRONT_LEFT; break;
377 case PA_CHANNEL_POSITION_MONO:
378 case PA_CHANNEL_POSITION_FRONT_CENTER: fmt->dwChannelMask |= SPEAKER_FRONT_CENTER; break;
379 case PA_CHANNEL_POSITION_FRONT_RIGHT: fmt->dwChannelMask |= SPEAKER_FRONT_RIGHT; break;
380 case PA_CHANNEL_POSITION_REAR_LEFT: fmt->dwChannelMask |= SPEAKER_BACK_LEFT; break;
381 case PA_CHANNEL_POSITION_REAR_CENTER: fmt->dwChannelMask |= SPEAKER_BACK_CENTER; break;
382 case PA_CHANNEL_POSITION_REAR_RIGHT: fmt->dwChannelMask |= SPEAKER_BACK_RIGHT; break;
383 case PA_CHANNEL_POSITION_LFE: fmt->dwChannelMask |= SPEAKER_LOW_FREQUENCY; break;
384 case PA_CHANNEL_POSITION_SIDE_LEFT: fmt->dwChannelMask |= SPEAKER_SIDE_LEFT; break;
385 case PA_CHANNEL_POSITION_SIDE_RIGHT: fmt->dwChannelMask |= SPEAKER_SIDE_RIGHT; break;
386 case PA_CHANNEL_POSITION_TOP_CENTER: fmt->dwChannelMask |= SPEAKER_TOP_CENTER; break;
387 case PA_CHANNEL_POSITION_TOP_FRONT_LEFT: fmt->dwChannelMask |= SPEAKER_TOP_FRONT_LEFT; break;
388 case PA_CHANNEL_POSITION_TOP_FRONT_CENTER: fmt->dwChannelMask |= SPEAKER_TOP_FRONT_CENTER; break;
389 case PA_CHANNEL_POSITION_TOP_FRONT_RIGHT: fmt->dwChannelMask |= SPEAKER_TOP_FRONT_RIGHT; break;
390 case PA_CHANNEL_POSITION_TOP_REAR_LEFT: fmt->dwChannelMask |= SPEAKER_TOP_BACK_LEFT; break;
391 case PA_CHANNEL_POSITION_TOP_REAR_CENTER: fmt->dwChannelMask |= SPEAKER_TOP_BACK_CENTER; break;
392 case PA_CHANNEL_POSITION_TOP_REAR_RIGHT: fmt->dwChannelMask |= SPEAKER_TOP_BACK_RIGHT; break;
396 static HRESULT pulse_connect(void)
398 int len;
399 WCHAR path[PATH_MAX], *name;
400 char *str;
402 if (!pulse_thread)
404 if (!(pulse_thread = CreateThread(NULL, 0, pulse_mainloop_thread, NULL, 0, NULL)))
406 ERR("Failed to create mainloop thread.");
407 return E_FAIL;
409 SetThreadPriority(pulse_thread, THREAD_PRIORITY_TIME_CRITICAL);
410 pthread_cond_wait(&pulse_cond, &pulse_lock);
413 if (pulse_ctx && PA_CONTEXT_IS_GOOD(pa_context_get_state(pulse_ctx)))
414 return S_OK;
415 if (pulse_ctx)
416 pa_context_unref(pulse_ctx);
418 GetModuleFileNameW(NULL, path, sizeof(path)/sizeof(*path));
419 name = strrchrW(path, '\\');
420 if (!name)
421 name = path;
422 else
423 name++;
424 len = WideCharToMultiByte(CP_UNIXCP, 0, name, -1, NULL, 0, NULL, NULL);
425 str = pa_xmalloc(len);
426 WideCharToMultiByte(CP_UNIXCP, 0, name, -1, str, len, NULL, NULL);
427 TRACE("Name: %s\n", str);
428 pulse_ctx = pa_context_new(pa_mainloop_get_api(pulse_ml), str);
429 pa_xfree(str);
430 if (!pulse_ctx) {
431 ERR("Failed to create context\n");
432 return E_FAIL;
435 pa_context_set_state_callback(pulse_ctx, pulse_contextcallback, NULL);
437 TRACE("libpulse protocol version: %u. API Version %u\n", pa_context_get_protocol_version(pulse_ctx), PA_API_VERSION);
438 if (pa_context_connect(pulse_ctx, NULL, 0, NULL) < 0)
439 goto fail;
441 /* Wait for connection */
442 while (pthread_cond_wait(&pulse_cond, &pulse_lock)) {
443 pa_context_state_t state = pa_context_get_state(pulse_ctx);
445 if (state == PA_CONTEXT_FAILED || state == PA_CONTEXT_TERMINATED)
446 goto fail;
448 if (state == PA_CONTEXT_READY)
449 break;
452 TRACE("Connected to server %s with protocol version: %i.\n",
453 pa_context_get_server(pulse_ctx),
454 pa_context_get_server_protocol_version(pulse_ctx));
455 pulse_probe_settings(1, &pulse_fmt[0]);
456 pulse_probe_settings(0, &pulse_fmt[1]);
457 return S_OK;
459 fail:
460 pa_context_unref(pulse_ctx);
461 pulse_ctx = NULL;
462 return E_FAIL;
465 static void pulse_contextcallback(pa_context *c, void *userdata) {
466 switch (pa_context_get_state(c)) {
467 default:
468 FIXME("Unhandled state: %i\n", pa_context_get_state(c));
469 case PA_CONTEXT_CONNECTING:
470 case PA_CONTEXT_UNCONNECTED:
471 case PA_CONTEXT_AUTHORIZING:
472 case PA_CONTEXT_SETTING_NAME:
473 case PA_CONTEXT_TERMINATED:
474 TRACE("State change to %i\n", pa_context_get_state(c));
475 return;
477 case PA_CONTEXT_READY:
478 TRACE("Ready\n");
479 break;
481 case PA_CONTEXT_FAILED:
482 ERR("Context failed: %s\n", pa_strerror(pa_context_errno(c)));
483 break;
485 pthread_cond_signal(&pulse_cond);
488 static HRESULT pulse_stream_valid(ACImpl *This) {
489 if (!This->stream)
490 return AUDCLNT_E_NOT_INITIALIZED;
491 if (!This->stream || pa_stream_get_state(This->stream) != PA_STREAM_READY)
492 return AUDCLNT_E_DEVICE_INVALIDATED;
493 return S_OK;
496 static void dump_attr(const pa_buffer_attr *attr) {
497 TRACE("maxlength: %u\n", attr->maxlength);
498 TRACE("minreq: %u\n", attr->minreq);
499 TRACE("fragsize: %u\n", attr->fragsize);
500 TRACE("tlength: %u\n", attr->tlength);
501 TRACE("prebuf: %u\n", attr->prebuf);
504 static void pulse_op_cb(pa_stream *s, int success, void *user) {
505 TRACE("Success: %i\n", success);
506 *(int*)user = success;
507 pthread_cond_signal(&pulse_cond);
510 static void pulse_ctx_op_cb(pa_context *c, int success, void *user) {
511 TRACE("Success: %i\n", success);
512 *(int*)user = success;
513 pthread_cond_signal(&pulse_cond);
516 static void pulse_attr_update(pa_stream *s, void *user) {
517 const pa_buffer_attr *attr = pa_stream_get_buffer_attr(s);
518 TRACE("New attributes or device moved:\n");
519 dump_attr(attr);
522 static void pulse_wr_callback(pa_stream *s, size_t bytes, void *userdata)
524 ACImpl *This = userdata;
525 UINT32 oldpad = This->pad;
527 if (bytes < This->bufsize_bytes)
528 This->pad = This->bufsize_bytes - bytes;
529 else
530 This->pad = 0;
532 assert(oldpad >= This->pad);
534 This->clock_written += oldpad - This->pad;
535 TRACE("New pad: %zu (-%zu)\n", This->pad / pa_frame_size(&This->ss), (oldpad - This->pad) / pa_frame_size(&This->ss));
537 if (This->event)
538 SetEvent(This->event);
541 static void pulse_underflow_callback(pa_stream *s, void *userdata)
543 WARN("Underflow\n");
546 /* Latency is periodically updated even when nothing is played,
547 * because of PA_STREAM_AUTO_TIMING_UPDATE so use it as timer
549 * Perfect for passing all tests :)
551 static void pulse_latency_callback(pa_stream *s, void *userdata)
553 ACImpl *This = userdata;
554 if (!This->pad && This->event)
555 SetEvent(This->event);
558 static void pulse_started_callback(pa_stream *s, void *userdata)
560 ACImpl *This = userdata;
562 TRACE("(Re)started playing\n");
563 if (This->event)
564 SetEvent(This->event);
567 static void pulse_rd_loop(ACImpl *This, size_t bytes)
569 while (bytes >= This->capture_period) {
570 ACPacket *p, *next;
571 LARGE_INTEGER stamp, freq;
572 BYTE *dst, *src;
573 size_t src_len, copy, rem = This->capture_period;
574 if (!(p = (ACPacket*)list_head(&This->packet_free_head))) {
575 p = (ACPacket*)list_head(&This->packet_filled_head);
576 if (!p->discont) {
577 next = (ACPacket*)p->entry.next;
578 next->discont = 1;
579 } else
580 p = (ACPacket*)list_tail(&This->packet_filled_head);
581 assert(This->pad == This->bufsize_bytes);
582 } else {
583 assert(This->pad < This->bufsize_bytes);
584 This->pad += This->capture_period;
585 assert(This->pad <= This->bufsize_bytes);
587 QueryPerformanceCounter(&stamp);
588 QueryPerformanceFrequency(&freq);
589 p->qpcpos = (stamp.QuadPart * (INT64)10000000) / freq.QuadPart;
590 p->discont = 0;
591 list_remove(&p->entry);
592 list_add_tail(&This->packet_filled_head, &p->entry);
594 dst = p->data;
595 while (rem) {
596 pa_stream_peek(This->stream, (const void**)&src, &src_len);
597 assert(src_len);
598 assert(This->peek_ofs < src_len);
599 src += This->peek_ofs;
600 src_len -= This->peek_ofs;
601 assert(src_len <= bytes);
603 copy = rem;
604 if (copy > src_len)
605 copy = src_len;
606 memcpy(dst, src, rem);
607 src += copy;
608 src_len -= copy;
609 dst += copy;
610 rem -= copy;
612 if (!src_len) {
613 This->peek_ofs = 0;
614 pa_stream_drop(This->stream);
615 } else
616 This->peek_ofs += copy;
618 bytes -= This->capture_period;
622 static void pulse_rd_drop(ACImpl *This, size_t bytes)
624 while (bytes >= This->capture_period) {
625 size_t src_len, copy, rem = This->capture_period;
626 while (rem) {
627 const void *src;
628 pa_stream_peek(This->stream, &src, &src_len);
629 assert(src_len);
630 assert(This->peek_ofs < src_len);
631 src_len -= This->peek_ofs;
632 assert(src_len <= bytes);
634 copy = rem;
635 if (copy > src_len)
636 copy = src_len;
638 src_len -= copy;
639 rem -= copy;
641 if (!src_len) {
642 This->peek_ofs = 0;
643 pa_stream_drop(This->stream);
644 } else
645 This->peek_ofs += copy;
646 bytes -= copy;
651 static void pulse_rd_callback(pa_stream *s, size_t bytes, void *userdata)
653 ACImpl *This = userdata;
655 TRACE("Readable total: %zu, fragsize: %u\n", bytes, pa_stream_get_buffer_attr(s)->fragsize);
656 assert(bytes >= This->peek_ofs);
657 bytes -= This->peek_ofs;
658 if (bytes < This->capture_period)
659 return;
661 if (This->started)
662 pulse_rd_loop(This, bytes);
663 else
664 pulse_rd_drop(This, bytes);
666 if (This->event)
667 SetEvent(This->event);
670 static void pulse_stream_state(pa_stream *s, void *user)
672 pa_stream_state_t state = pa_stream_get_state(s);
673 TRACE("Stream state changed to %i\n", state);
674 pthread_cond_signal(&pulse_cond);
677 static HRESULT pulse_stream_connect(ACImpl *This, UINT32 period_bytes) {
678 int ret;
679 char buffer[64];
680 static LONG number;
681 pa_buffer_attr attr;
682 if (This->stream) {
683 pa_stream_disconnect(This->stream);
684 while (pa_stream_get_state(This->stream) == PA_STREAM_READY)
685 pthread_cond_wait(&pulse_cond, &pulse_lock);
686 pa_stream_unref(This->stream);
688 ret = InterlockedIncrement(&number);
689 sprintf(buffer, "audio stream #%i", ret);
690 This->stream = pa_stream_new(pulse_ctx, buffer, &This->ss, &This->map);
691 pa_stream_set_state_callback(This->stream, pulse_stream_state, This);
692 pa_stream_set_buffer_attr_callback(This->stream, pulse_attr_update, This);
693 pa_stream_set_moved_callback(This->stream, pulse_attr_update, This);
695 /* Pulseaudio will fill in correct values */
696 attr.minreq = attr.fragsize = period_bytes;
697 attr.maxlength = attr.tlength = This->bufsize_bytes;
698 attr.prebuf = pa_frame_size(&This->ss);
699 dump_attr(&attr);
700 if (This->dataflow == eRender)
701 ret = pa_stream_connect_playback(This->stream, NULL, &attr,
702 PA_STREAM_START_CORKED|PA_STREAM_START_UNMUTED|PA_STREAM_AUTO_TIMING_UPDATE|PA_STREAM_INTERPOLATE_TIMING|PA_STREAM_EARLY_REQUESTS, NULL, NULL);
703 else
704 ret = pa_stream_connect_record(This->stream, NULL, &attr,
705 PA_STREAM_START_CORKED|PA_STREAM_START_UNMUTED|PA_STREAM_AUTO_TIMING_UPDATE|PA_STREAM_INTERPOLATE_TIMING|PA_STREAM_EARLY_REQUESTS);
706 if (ret < 0) {
707 WARN("Returns %i\n", ret);
708 return AUDCLNT_E_ENDPOINT_CREATE_FAILED;
710 while (pa_stream_get_state(This->stream) == PA_STREAM_CREATING)
711 pthread_cond_wait(&pulse_cond, &pulse_lock);
712 if (pa_stream_get_state(This->stream) != PA_STREAM_READY)
713 return AUDCLNT_E_ENDPOINT_CREATE_FAILED;
715 if (This->dataflow == eRender) {
716 pa_stream_set_write_callback(This->stream, pulse_wr_callback, This);
717 pa_stream_set_underflow_callback(This->stream, pulse_underflow_callback, This);
718 pa_stream_set_started_callback(This->stream, pulse_started_callback, This);
719 } else
720 pa_stream_set_read_callback(This->stream, pulse_rd_callback, This);
721 return S_OK;
724 HRESULT WINAPI AUDDRV_GetEndpointIDs(EDataFlow flow, const WCHAR ***ids, GUID **keys,
725 UINT *num, UINT *def_index)
727 HRESULT hr = S_OK;
728 TRACE("%d %p %p %p\n", flow, ids, num, def_index);
730 pthread_mutex_lock(&pulse_lock);
731 hr = pulse_connect();
732 pthread_mutex_unlock(&pulse_lock);
733 if (FAILED(hr))
734 return hr;
735 *num = 1;
736 *def_index = 0;
738 *ids = HeapAlloc(GetProcessHeap(), 0, sizeof(**ids));
739 if (!*ids)
740 return E_OUTOFMEMORY;
741 (*ids)[0] = defaultW;
743 *keys = HeapAlloc(GetProcessHeap(), 0, sizeof(**keys));
744 if (!*keys) {
745 HeapFree(GetProcessHeap(), 0, *ids);
746 *ids = NULL;
747 return E_OUTOFMEMORY;
749 if (flow == eRender)
750 (*keys)[0] = pulse_render_guid;
751 else
752 (*keys)[0] = pulse_capture_guid;
754 return S_OK;
757 int WINAPI AUDDRV_GetPriority(void)
759 HRESULT hr;
760 if (getenv("WINENOPULSE")) {
761 FIXME_(winediag)("winepulse has been temporarily disabled through the environment\n");
762 return 0;
764 pthread_mutex_lock(&pulse_lock);
765 hr = pulse_connect();
766 pthread_mutex_unlock(&pulse_lock);
767 return SUCCEEDED(hr) ? 3 : 0;
770 HRESULT WINAPI AUDDRV_GetAudioEndpoint(GUID *guid, IMMDevice *dev, IAudioClient **out)
772 HRESULT hr;
773 ACImpl *This;
774 int i;
775 EDataFlow dataflow;
777 /* Give one visible warning per session
778 * Sadly wine has chosen not to accept the winepulse patch, so support ourselves
780 if (!warn_once && (warn_once = CreateEventA(0, 0, 0, "__winepulse_warn_event")) && GetLastError() != ERROR_ALREADY_EXISTS) {
781 FIXME_(winediag)("Winepulse is not officially supported by the wine project\n");
782 FIXME_(winediag)("For sound related feedback and support, please visit http://ubuntuforums.org/showthread.php?t=1960599\n");
783 } else {
784 WARN_(winediag)("Winepulse is not officially supported by the wine project\n");
785 WARN_(winediag)("For sound related feedback and support, please visit http://ubuntuforums.org/showthread.php?t=1960599\n");
788 TRACE("%s %p %p\n", debugstr_guid(guid), dev, out);
789 if (IsEqualGUID(guid, &pulse_render_guid))
790 dataflow = eRender;
791 else if (IsEqualGUID(guid, &pulse_capture_guid))
792 dataflow = eCapture;
793 else
794 return E_UNEXPECTED;
796 *out = NULL;
797 pthread_mutex_lock(&pulse_lock);
798 hr = pulse_connect();
799 pthread_mutex_unlock(&pulse_lock);
800 if (FAILED(hr))
801 return hr;
803 This = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, sizeof(*This));
804 if (!This)
805 return E_OUTOFMEMORY;
807 This->IAudioClient_iface.lpVtbl = &AudioClient_Vtbl;
808 This->IAudioRenderClient_iface.lpVtbl = &AudioRenderClient_Vtbl;
809 This->IAudioCaptureClient_iface.lpVtbl = &AudioCaptureClient_Vtbl;
810 This->IAudioClock_iface.lpVtbl = &AudioClock_Vtbl;
811 This->IAudioClock2_iface.lpVtbl = &AudioClock2_Vtbl;
812 This->IAudioStreamVolume_iface.lpVtbl = &AudioStreamVolume_Vtbl;
813 This->dataflow = dataflow;
814 This->parent = dev;
815 for (i = 0; i < PA_CHANNELS_MAX; ++i)
816 This->vol[i] = 1.f;
817 IMMDevice_AddRef(This->parent);
819 *out = &This->IAudioClient_iface;
820 IAudioClient_AddRef(&This->IAudioClient_iface);
822 return S_OK;
825 static HRESULT WINAPI AudioClient_QueryInterface(IAudioClient *iface,
826 REFIID riid, void **ppv)
828 TRACE("(%p)->(%s, %p)\n", iface, debugstr_guid(riid), ppv);
830 if (!ppv)
831 return E_POINTER;
832 *ppv = NULL;
833 if (IsEqualIID(riid, &IID_IUnknown) || IsEqualIID(riid, &IID_IAudioClient))
834 *ppv = iface;
835 if (*ppv) {
836 IUnknown_AddRef((IUnknown*)*ppv);
837 return S_OK;
839 WARN("Unknown interface %s\n", debugstr_guid(riid));
840 return E_NOINTERFACE;
843 static ULONG WINAPI AudioClient_AddRef(IAudioClient *iface)
845 ACImpl *This = impl_from_IAudioClient(iface);
846 ULONG ref;
847 ref = InterlockedIncrement(&This->ref);
848 TRACE("(%p) Refcount now %u\n", This, ref);
849 return ref;
852 static ULONG WINAPI AudioClient_Release(IAudioClient *iface)
854 ACImpl *This = impl_from_IAudioClient(iface);
855 ULONG ref;
856 ref = InterlockedDecrement(&This->ref);
857 TRACE("(%p) Refcount now %u\n", This, ref);
858 if (!ref) {
859 if (This->stream) {
860 pthread_mutex_lock(&pulse_lock);
861 if (PA_STREAM_IS_GOOD(pa_stream_get_state(This->stream))) {
862 pa_stream_disconnect(This->stream);
863 while (PA_STREAM_IS_GOOD(pa_stream_get_state(This->stream)))
864 pthread_cond_wait(&pulse_cond, &pulse_lock);
866 pa_stream_unref(This->stream);
867 This->stream = NULL;
868 list_remove(&This->entry);
869 pthread_mutex_unlock(&pulse_lock);
871 IMMDevice_Release(This->parent);
872 HeapFree(GetProcessHeap(), 0, This->tmp_buffer);
873 HeapFree(GetProcessHeap(), 0, This);
875 return ref;
878 static void dump_fmt(const WAVEFORMATEX *fmt)
880 TRACE("wFormatTag: 0x%x (", fmt->wFormatTag);
881 switch(fmt->wFormatTag) {
882 case WAVE_FORMAT_PCM:
883 TRACE("WAVE_FORMAT_PCM");
884 break;
885 case WAVE_FORMAT_IEEE_FLOAT:
886 TRACE("WAVE_FORMAT_IEEE_FLOAT");
887 break;
888 case WAVE_FORMAT_EXTENSIBLE:
889 TRACE("WAVE_FORMAT_EXTENSIBLE");
890 break;
891 default:
892 TRACE("Unknown");
893 break;
895 TRACE(")\n");
897 TRACE("nChannels: %u\n", fmt->nChannels);
898 TRACE("nSamplesPerSec: %u\n", fmt->nSamplesPerSec);
899 TRACE("nAvgBytesPerSec: %u\n", fmt->nAvgBytesPerSec);
900 TRACE("nBlockAlign: %u\n", fmt->nBlockAlign);
901 TRACE("wBitsPerSample: %u\n", fmt->wBitsPerSample);
902 TRACE("cbSize: %u\n", fmt->cbSize);
904 if (fmt->wFormatTag == WAVE_FORMAT_EXTENSIBLE) {
905 WAVEFORMATEXTENSIBLE *fmtex = (void*)fmt;
906 TRACE("dwChannelMask: %08x\n", fmtex->dwChannelMask);
907 TRACE("Samples: %04x\n", fmtex->Samples.wReserved);
908 TRACE("SubFormat: %s\n", wine_dbgstr_guid(&fmtex->SubFormat));
912 static WAVEFORMATEX *clone_format(const WAVEFORMATEX *fmt)
914 WAVEFORMATEX *ret;
915 size_t size;
917 if (fmt->wFormatTag == WAVE_FORMAT_EXTENSIBLE)
918 size = sizeof(WAVEFORMATEXTENSIBLE);
919 else
920 size = sizeof(WAVEFORMATEX);
922 ret = CoTaskMemAlloc(size);
923 if (!ret)
924 return NULL;
926 memcpy(ret, fmt, size);
928 ret->cbSize = size - sizeof(WAVEFORMATEX);
930 return ret;
933 static DWORD get_channel_mask(unsigned int channels)
935 switch(channels) {
936 case 0:
937 return 0;
938 case 1:
939 return SPEAKER_FRONT_CENTER;
940 case 2:
941 return SPEAKER_FRONT_LEFT | SPEAKER_FRONT_RIGHT;
942 case 3:
943 return SPEAKER_FRONT_LEFT | SPEAKER_FRONT_RIGHT |
944 SPEAKER_LOW_FREQUENCY;
945 case 4:
946 return SPEAKER_FRONT_LEFT | SPEAKER_FRONT_RIGHT | SPEAKER_BACK_LEFT |
947 SPEAKER_BACK_RIGHT;
948 case 5:
949 return SPEAKER_FRONT_LEFT | SPEAKER_FRONT_RIGHT | SPEAKER_BACK_LEFT |
950 SPEAKER_BACK_RIGHT | SPEAKER_LOW_FREQUENCY;
951 case 6:
952 return SPEAKER_FRONT_LEFT | SPEAKER_FRONT_RIGHT | SPEAKER_BACK_LEFT |
953 SPEAKER_BACK_RIGHT | SPEAKER_LOW_FREQUENCY | SPEAKER_FRONT_CENTER;
954 case 7:
955 return SPEAKER_FRONT_LEFT | SPEAKER_FRONT_RIGHT | SPEAKER_BACK_LEFT |
956 SPEAKER_BACK_RIGHT | SPEAKER_LOW_FREQUENCY | SPEAKER_FRONT_CENTER |
957 SPEAKER_BACK_CENTER;
958 case 8:
959 return SPEAKER_FRONT_LEFT | SPEAKER_FRONT_RIGHT | SPEAKER_BACK_LEFT |
960 SPEAKER_BACK_RIGHT | SPEAKER_LOW_FREQUENCY | SPEAKER_FRONT_CENTER |
961 SPEAKER_SIDE_LEFT | SPEAKER_SIDE_RIGHT;
963 FIXME("Unknown speaker configuration: %u\n", channels);
964 return 0;
967 static void session_init_vols(AudioSession *session, UINT channels)
969 if (session->channel_count < channels) {
970 UINT i;
972 if (session->channel_vols)
973 session->channel_vols = HeapReAlloc(GetProcessHeap(), 0,
974 session->channel_vols, sizeof(float) * channels);
975 else
976 session->channel_vols = HeapAlloc(GetProcessHeap(), 0,
977 sizeof(float) * channels);
978 if (!session->channel_vols)
979 return;
981 for(i = session->channel_count; i < channels; ++i)
982 session->channel_vols[i] = 1.f;
984 session->channel_count = channels;
988 static AudioSession *create_session(const GUID *guid, IMMDevice *device,
989 UINT num_channels)
991 AudioSession *ret;
993 ret = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, sizeof(AudioSession));
994 if (!ret)
995 return NULL;
997 memcpy(&ret->guid, guid, sizeof(GUID));
999 ret->device = device;
1001 list_init(&ret->clients);
1003 list_add_head(&g_sessions, &ret->entry);
1005 session_init_vols(ret, num_channels);
1007 ret->master_vol = 1.f;
1009 return ret;
1012 /* if channels == 0, then this will return or create a session with
1013 * matching dataflow and GUID. otherwise, channels must also match */
1014 static HRESULT get_audio_session(const GUID *sessionguid,
1015 IMMDevice *device, UINT channels, AudioSession **out)
1017 AudioSession *session;
1019 if (!sessionguid || IsEqualGUID(sessionguid, &GUID_NULL)) {
1020 *out = create_session(&GUID_NULL, device, channels);
1021 if (!*out)
1022 return E_OUTOFMEMORY;
1024 return S_OK;
1027 *out = NULL;
1028 LIST_FOR_EACH_ENTRY(session, &g_sessions, AudioSession, entry) {
1029 if (session->device == device &&
1030 IsEqualGUID(sessionguid, &session->guid)) {
1031 session_init_vols(session, channels);
1032 *out = session;
1033 break;
1037 if (!*out) {
1038 *out = create_session(sessionguid, device, channels);
1039 if (!*out)
1040 return E_OUTOFMEMORY;
1043 return S_OK;
1046 static HRESULT pulse_spec_from_waveformat(ACImpl *This, const WAVEFORMATEX *fmt)
1048 pa_channel_map_init(&This->map);
1049 This->ss.rate = fmt->nSamplesPerSec;
1050 This->ss.format = PA_SAMPLE_INVALID;
1051 switch(fmt->wFormatTag) {
1052 case WAVE_FORMAT_IEEE_FLOAT:
1053 if (!fmt->nChannels || fmt->nChannels > 2 || fmt->wBitsPerSample != 32)
1054 break;
1055 This->ss.format = PA_SAMPLE_FLOAT32LE;
1056 pa_channel_map_init_auto(&This->map, fmt->nChannels, PA_CHANNEL_MAP_ALSA);
1057 break;
1058 case WAVE_FORMAT_PCM:
1059 if (!fmt->nChannels || fmt->nChannels > 2)
1060 break;
1061 if (fmt->wBitsPerSample == 8)
1062 This->ss.format = PA_SAMPLE_U8;
1063 else if (fmt->wBitsPerSample == 16)
1064 This->ss.format = PA_SAMPLE_S16LE;
1065 else
1066 return AUDCLNT_E_UNSUPPORTED_FORMAT;
1067 pa_channel_map_init_auto(&This->map, fmt->nChannels, PA_CHANNEL_MAP_ALSA);
1068 break;
1069 case WAVE_FORMAT_EXTENSIBLE: {
1070 WAVEFORMATEXTENSIBLE *wfe = (WAVEFORMATEXTENSIBLE*)fmt;
1071 DWORD mask = wfe->dwChannelMask;
1072 DWORD i = 0, j;
1073 if (fmt->cbSize != (sizeof(*wfe) - sizeof(*fmt)) && fmt->cbSize != sizeof(*wfe))
1074 break;
1075 if (IsEqualGUID(&wfe->SubFormat, &KSDATAFORMAT_SUBTYPE_IEEE_FLOAT) &&
1076 (!wfe->Samples.wValidBitsPerSample || wfe->Samples.wValidBitsPerSample == 32) &&
1077 fmt->wBitsPerSample == 32)
1078 This->ss.format = PA_SAMPLE_FLOAT32LE;
1079 else if (IsEqualGUID(&wfe->SubFormat, &KSDATAFORMAT_SUBTYPE_PCM)) {
1080 DWORD valid = wfe->Samples.wValidBitsPerSample;
1081 if (!valid)
1082 valid = fmt->wBitsPerSample;
1083 if (!valid || valid > fmt->wBitsPerSample)
1084 break;
1085 switch (fmt->wBitsPerSample) {
1086 case 8:
1087 if (valid == 8)
1088 This->ss.format = PA_SAMPLE_U8;
1089 break;
1090 case 16:
1091 if (valid == 16)
1092 This->ss.format = PA_SAMPLE_S16LE;
1093 break;
1094 case 24:
1095 if (valid == 24)
1096 This->ss.format = PA_SAMPLE_S24LE;
1097 break;
1098 case 32:
1099 if (valid == 24)
1100 This->ss.format = PA_SAMPLE_S24_32LE;
1101 else if (valid == 32)
1102 This->ss.format = PA_SAMPLE_S32LE;
1103 break;
1104 default:
1105 return AUDCLNT_E_UNSUPPORTED_FORMAT;
1108 This->map.channels = fmt->nChannels;
1109 if (!mask || mask == SPEAKER_ALL)
1110 mask = get_channel_mask(fmt->nChannels);
1111 else if (mask == ~0U && fmt->nChannels == 1)
1112 mask = SPEAKER_FRONT_CENTER;
1113 for (j = 0; j < sizeof(pulse_pos_from_wfx)/sizeof(*pulse_pos_from_wfx) && i < fmt->nChannels; ++j) {
1114 if (mask & (1 << j))
1115 This->map.map[i++] = pulse_pos_from_wfx[j];
1118 /* Special case for mono since pulse appears to map it differently */
1119 if (mask == SPEAKER_FRONT_CENTER)
1120 This->map.map[0] = PA_CHANNEL_POSITION_MONO;
1122 if (i < fmt->nChannels || (mask & SPEAKER_RESERVED)) {
1123 This->map.channels = 0;
1124 ERR("Invalid channel mask: %i/%i and %x(%x)\n", i, fmt->nChannels, mask, wfe->dwChannelMask);
1125 break;
1127 break;
1129 case WAVE_FORMAT_ALAW:
1130 case WAVE_FORMAT_MULAW:
1131 if (fmt->wBitsPerSample != 8) {
1132 FIXME("Unsupported bpp %u for LAW\n", fmt->wBitsPerSample);
1133 return AUDCLNT_E_UNSUPPORTED_FORMAT;
1135 if (fmt->nChannels != 1 && fmt->nChannels != 2) {
1136 FIXME("Unsupported channels %u for LAW\n", fmt->nChannels);
1137 return AUDCLNT_E_UNSUPPORTED_FORMAT;
1139 This->ss.format = fmt->wFormatTag == WAVE_FORMAT_MULAW ? PA_SAMPLE_ULAW : PA_SAMPLE_ALAW;
1140 pa_channel_map_init_auto(&This->map, fmt->nChannels, PA_CHANNEL_MAP_ALSA);
1141 break;
1142 default:
1143 WARN("Unhandled tag %x\n", fmt->wFormatTag);
1144 return AUDCLNT_E_UNSUPPORTED_FORMAT;
1146 This->ss.channels = This->map.channels;
1147 if (!pa_channel_map_valid(&This->map) || This->ss.format == PA_SAMPLE_INVALID) {
1148 ERR("Invalid format! Channel spec valid: %i, format: %i\n", pa_channel_map_valid(&This->map), This->ss.format);
1149 dump_fmt(fmt);
1150 return AUDCLNT_E_UNSUPPORTED_FORMAT;
1152 return S_OK;
1155 static HRESULT WINAPI AudioClient_Initialize(IAudioClient *iface,
1156 AUDCLNT_SHAREMODE mode, DWORD flags, REFERENCE_TIME duration,
1157 REFERENCE_TIME period, const WAVEFORMATEX *fmt,
1158 const GUID *sessionguid)
1160 ACImpl *This = impl_from_IAudioClient(iface);
1161 HRESULT hr = S_OK;
1162 UINT period_bytes;
1164 TRACE("(%p)->(%x, %x, %s, %s, %p, %s)\n", This, mode, flags,
1165 wine_dbgstr_longlong(duration), wine_dbgstr_longlong(period), fmt, debugstr_guid(sessionguid));
1167 if (!fmt)
1168 return E_POINTER;
1170 if (mode != AUDCLNT_SHAREMODE_SHARED && mode != AUDCLNT_SHAREMODE_EXCLUSIVE)
1171 return AUDCLNT_E_NOT_INITIALIZED;
1172 if (mode == AUDCLNT_SHAREMODE_EXCLUSIVE)
1173 return AUDCLNT_E_EXCLUSIVE_MODE_NOT_ALLOWED;
1175 if (flags & ~(AUDCLNT_STREAMFLAGS_CROSSPROCESS |
1176 AUDCLNT_STREAMFLAGS_LOOPBACK |
1177 AUDCLNT_STREAMFLAGS_EVENTCALLBACK |
1178 AUDCLNT_STREAMFLAGS_NOPERSIST |
1179 AUDCLNT_STREAMFLAGS_RATEADJUST |
1180 AUDCLNT_SESSIONFLAGS_EXPIREWHENUNOWNED |
1181 AUDCLNT_SESSIONFLAGS_DISPLAY_HIDE |
1182 AUDCLNT_SESSIONFLAGS_DISPLAY_HIDEWHENEXPIRED)) {
1183 TRACE("Unknown flags: %08x\n", flags);
1184 return E_INVALIDARG;
1187 pthread_mutex_lock(&pulse_lock);
1188 if (This->stream) {
1189 pthread_mutex_unlock(&pulse_lock);
1190 return AUDCLNT_E_ALREADY_INITIALIZED;
1193 hr = pulse_spec_from_waveformat(This, fmt);
1194 if (FAILED(hr))
1195 goto exit;
1197 if (mode == AUDCLNT_SHAREMODE_SHARED) {
1198 REFERENCE_TIME def = pulse_def_period[This->dataflow == eCapture];
1199 REFERENCE_TIME min = pulse_min_period[This->dataflow == eCapture];
1201 /* Switch to low latency mode if below 2 default periods,
1202 * which is 20 ms by default, this will increase the amount
1203 * of interrupts but allows very low latency. In dsound I
1204 * managed to get a total latency of ~8ms, which is well below
1205 * default
1207 if (duration < 2 * def)
1208 period = min;
1209 else
1210 period = def;
1211 if (duration < 2 * period)
1212 duration = 2 * period;
1214 /* Uh oh, really low latency requested.. */
1215 if (duration <= 2 * period)
1216 period /= 2;
1218 period_bytes = pa_frame_size(&This->ss) * MulDiv(period, This->ss.rate, 10000000);
1220 if (duration < 20000000)
1221 This->bufsize_frames = ceil((duration / 10000000.) * fmt->nSamplesPerSec);
1222 else
1223 This->bufsize_frames = 2 * fmt->nSamplesPerSec;
1224 This->bufsize_bytes = This->bufsize_frames * pa_frame_size(&This->ss);
1226 This->share = mode;
1227 This->flags = flags;
1228 hr = pulse_stream_connect(This, period_bytes);
1229 if (SUCCEEDED(hr)) {
1230 UINT32 unalign;
1231 const pa_buffer_attr *attr = pa_stream_get_buffer_attr(This->stream);
1232 /* Update frames according to new size */
1233 dump_attr(attr);
1234 if (This->dataflow == eRender)
1235 This->bufsize_bytes = attr->tlength;
1236 else {
1237 This->capture_period = period_bytes = attr->fragsize;
1238 if ((unalign = This->bufsize_bytes % period_bytes))
1239 This->bufsize_bytes += period_bytes - unalign;
1241 This->bufsize_frames = This->bufsize_bytes / pa_frame_size(&This->ss);
1243 if (SUCCEEDED(hr)) {
1244 UINT32 i, capture_packets = This->capture_period ? This->bufsize_bytes / This->capture_period : 0;
1245 This->tmp_buffer = HeapAlloc(GetProcessHeap(), 0, This->bufsize_bytes + capture_packets * sizeof(ACPacket));
1246 if (!This->tmp_buffer)
1247 hr = E_OUTOFMEMORY;
1248 else {
1249 ACPacket *cur_packet = (ACPacket*)((char*)This->tmp_buffer + This->bufsize_bytes);
1250 BYTE *data = This->tmp_buffer;
1251 memset(This->tmp_buffer, This->ss.format == PA_SAMPLE_U8 ? 0x80 : 0, This->bufsize_bytes);
1252 list_init(&This->packet_free_head);
1253 list_init(&This->packet_filled_head);
1254 for (i = 0; i < capture_packets; ++i, ++cur_packet) {
1255 list_add_tail(&This->packet_free_head, &cur_packet->entry);
1256 cur_packet->data = data;
1257 data += This->capture_period;
1259 assert(!This->capture_period || This->bufsize_bytes == This->capture_period * capture_packets);
1260 assert(!capture_packets || data - This->bufsize_bytes == This->tmp_buffer);
1263 if (SUCCEEDED(hr))
1264 hr = get_audio_session(sessionguid, This->parent, fmt->nChannels, &This->session);
1265 if (SUCCEEDED(hr))
1266 list_add_tail(&This->session->clients, &This->entry);
1268 exit:
1269 if (FAILED(hr)) {
1270 HeapFree(GetProcessHeap(), 0, This->tmp_buffer);
1271 This->tmp_buffer = NULL;
1272 if (This->stream) {
1273 pa_stream_disconnect(This->stream);
1274 pa_stream_unref(This->stream);
1275 This->stream = NULL;
1278 pthread_mutex_unlock(&pulse_lock);
1279 return hr;
1282 static HRESULT WINAPI AudioClient_GetBufferSize(IAudioClient *iface,
1283 UINT32 *out)
1285 ACImpl *This = impl_from_IAudioClient(iface);
1286 HRESULT hr;
1288 TRACE("(%p)->(%p)\n", This, out);
1290 if (!out)
1291 return E_POINTER;
1293 pthread_mutex_lock(&pulse_lock);
1294 hr = pulse_stream_valid(This);
1295 if (SUCCEEDED(hr))
1296 *out = This->bufsize_frames;
1297 pthread_mutex_unlock(&pulse_lock);
1299 return hr;
1302 static HRESULT WINAPI AudioClient_GetStreamLatency(IAudioClient *iface,
1303 REFERENCE_TIME *latency)
1305 ACImpl *This = impl_from_IAudioClient(iface);
1306 const pa_buffer_attr *attr;
1307 REFERENCE_TIME lat;
1308 HRESULT hr;
1310 TRACE("(%p)->(%p)\n", This, latency);
1312 if (!latency)
1313 return E_POINTER;
1315 pthread_mutex_lock(&pulse_lock);
1316 hr = pulse_stream_valid(This);
1317 if (FAILED(hr)) {
1318 pthread_mutex_unlock(&pulse_lock);
1319 return hr;
1321 attr = pa_stream_get_buffer_attr(This->stream);
1322 if (This->dataflow == eRender)
1323 lat = attr->minreq / pa_frame_size(&This->ss);
1324 else
1325 lat = attr->fragsize / pa_frame_size(&This->ss);
1326 *latency = 10000000;
1327 *latency *= lat;
1328 *latency /= This->ss.rate;
1329 pthread_mutex_unlock(&pulse_lock);
1330 TRACE("Latency: %u ms\n", (DWORD)(*latency / 10000));
1331 return S_OK;
1334 static void ACImpl_GetRenderPad(ACImpl *This, UINT32 *out)
1336 *out = This->pad / pa_frame_size(&This->ss);
1339 static void ACImpl_GetCapturePad(ACImpl *This, UINT32 *out)
1341 ACPacket *packet = This->locked_ptr;
1342 if (!packet && !list_empty(&This->packet_filled_head)) {
1343 packet = (ACPacket*)list_head(&This->packet_filled_head);
1344 This->locked_ptr = packet;
1345 list_remove(&packet->entry);
1347 if (out)
1348 *out = This->pad / pa_frame_size(&This->ss);
1351 static HRESULT WINAPI AudioClient_GetCurrentPadding(IAudioClient *iface,
1352 UINT32 *out)
1354 ACImpl *This = impl_from_IAudioClient(iface);
1355 HRESULT hr;
1357 TRACE("(%p)->(%p)\n", This, out);
1359 if (!out)
1360 return E_POINTER;
1362 pthread_mutex_lock(&pulse_lock);
1363 hr = pulse_stream_valid(This);
1364 if (FAILED(hr)) {
1365 pthread_mutex_unlock(&pulse_lock);
1366 return hr;
1369 if (This->dataflow == eRender)
1370 ACImpl_GetRenderPad(This, out);
1371 else
1372 ACImpl_GetCapturePad(This, out);
1373 pthread_mutex_unlock(&pulse_lock);
1375 TRACE("%p Pad: %u ms (%u)\n", This, MulDiv(*out, 1000, This->ss.rate), *out);
1376 return S_OK;
1379 static HRESULT WINAPI AudioClient_IsFormatSupported(IAudioClient *iface,
1380 AUDCLNT_SHAREMODE mode, const WAVEFORMATEX *fmt,
1381 WAVEFORMATEX **out)
1383 ACImpl *This = impl_from_IAudioClient(iface);
1384 HRESULT hr = S_OK;
1385 WAVEFORMATEX *closest = NULL;
1387 TRACE("(%p)->(%x, %p, %p)\n", This, mode, fmt, out);
1389 if (!fmt || (mode == AUDCLNT_SHAREMODE_SHARED && !out))
1390 return E_POINTER;
1392 if (out)
1393 *out = NULL;
1394 if (mode != AUDCLNT_SHAREMODE_SHARED && mode != AUDCLNT_SHAREMODE_EXCLUSIVE)
1395 return E_INVALIDARG;
1396 if (mode == AUDCLNT_SHAREMODE_EXCLUSIVE)
1397 return This->dataflow == eCapture ? AUDCLNT_E_UNSUPPORTED_FORMAT : AUDCLNT_E_EXCLUSIVE_MODE_NOT_ALLOWED;
1398 switch (fmt->wFormatTag) {
1399 case WAVE_FORMAT_EXTENSIBLE:
1400 if (fmt->cbSize < sizeof(WAVEFORMATEXTENSIBLE) - sizeof(WAVEFORMATEX))
1401 return E_INVALIDARG;
1402 dump_fmt(fmt);
1403 break;
1404 case WAVE_FORMAT_ALAW:
1405 case WAVE_FORMAT_MULAW:
1406 case WAVE_FORMAT_IEEE_FLOAT:
1407 case WAVE_FORMAT_PCM:
1408 dump_fmt(fmt);
1409 break;
1410 default:
1411 dump_fmt(fmt);
1412 return AUDCLNT_E_UNSUPPORTED_FORMAT;
1414 if (fmt->nChannels == 0)
1415 return AUDCLNT_E_UNSUPPORTED_FORMAT;
1416 closest = clone_format(fmt);
1417 if (!closest) {
1418 if (out)
1419 *out = NULL;
1420 return E_OUTOFMEMORY;
1423 if (fmt->wFormatTag == WAVE_FORMAT_EXTENSIBLE) {
1424 UINT32 mask = 0, i, channels = 0;
1425 WAVEFORMATEXTENSIBLE *ext = (WAVEFORMATEXTENSIBLE*)closest;
1427 if ((fmt->nChannels > 1 && ext->dwChannelMask == SPEAKER_ALL) ||
1428 (fmt->nChannels == 1 && ext->dwChannelMask == ~0U)) {
1429 mask = ext->dwChannelMask;
1430 channels = fmt->nChannels;
1431 } else if (ext->dwChannelMask) {
1432 for (i = 1; !(i & SPEAKER_RESERVED); i <<= 1) {
1433 if (i & ext->dwChannelMask) {
1434 mask |= i;
1435 channels++;
1438 if (channels < fmt->nChannels)
1439 mask = get_channel_mask(fmt->nChannels);
1440 } else
1441 mask = ext->dwChannelMask;
1442 if (ext->dwChannelMask != mask) {
1443 ext->dwChannelMask = mask;
1444 hr = S_FALSE;
1448 if (hr == S_OK || !out) {
1449 CoTaskMemFree(closest);
1450 if (out)
1451 *out = NULL;
1452 } else if (closest) {
1453 closest->nBlockAlign =
1454 closest->nChannels * closest->wBitsPerSample / 8;
1455 closest->nAvgBytesPerSec =
1456 closest->nBlockAlign * closest->nSamplesPerSec;
1457 *out = closest;
1460 TRACE("returning: %08x %p\n", hr, out ? *out : NULL);
1461 return hr;
1464 static HRESULT WINAPI AudioClient_GetMixFormat(IAudioClient *iface,
1465 WAVEFORMATEX **pwfx)
1467 ACImpl *This = impl_from_IAudioClient(iface);
1468 WAVEFORMATEXTENSIBLE *fmt = &pulse_fmt[This->dataflow == eCapture];
1470 TRACE("(%p)->(%p)\n", This, pwfx);
1472 if (!pwfx)
1473 return E_POINTER;
1475 *pwfx = clone_format(&fmt->Format);
1476 if (!*pwfx)
1477 return E_OUTOFMEMORY;
1478 dump_fmt(*pwfx);
1479 return S_OK;
1482 static HRESULT WINAPI AudioClient_GetDevicePeriod(IAudioClient *iface,
1483 REFERENCE_TIME *defperiod, REFERENCE_TIME *minperiod)
1485 ACImpl *This = impl_from_IAudioClient(iface);
1487 TRACE("(%p)->(%p, %p)\n", This, defperiod, minperiod);
1489 if (!defperiod && !minperiod)
1490 return E_POINTER;
1492 if (defperiod)
1493 *defperiod = pulse_def_period[This->dataflow == eCapture];
1494 if (minperiod)
1495 *minperiod = pulse_min_period[This->dataflow == eCapture];
1497 return S_OK;
1500 static HRESULT WINAPI AudioClient_Start(IAudioClient *iface)
1502 ACImpl *This = impl_from_IAudioClient(iface);
1503 HRESULT hr = S_OK;
1504 int success;
1505 pa_operation *o;
1507 TRACE("(%p)\n", This);
1509 pthread_mutex_lock(&pulse_lock);
1510 hr = pulse_stream_valid(This);
1511 if (FAILED(hr)) {
1512 pthread_mutex_unlock(&pulse_lock);
1513 return hr;
1516 if ((This->flags & AUDCLNT_STREAMFLAGS_EVENTCALLBACK) && !This->event) {
1517 pthread_mutex_unlock(&pulse_lock);
1518 return AUDCLNT_E_EVENTHANDLE_NOT_SET;
1521 if (This->started) {
1522 pthread_mutex_unlock(&pulse_lock);
1523 return AUDCLNT_E_NOT_STOPPED;
1526 if (pa_stream_is_corked(This->stream)) {
1527 o = pa_stream_cork(This->stream, 0, pulse_op_cb, &success);
1528 if (o) {
1529 while(pa_operation_get_state(o) == PA_OPERATION_RUNNING)
1530 pthread_cond_wait(&pulse_cond, &pulse_lock);
1531 pa_operation_unref(o);
1532 } else
1533 success = 0;
1534 if (!success)
1535 hr = E_FAIL;
1537 if (SUCCEEDED(hr)) {
1538 This->started = TRUE;
1539 if (This->dataflow == eRender && This->event)
1540 pa_stream_set_latency_update_callback(This->stream, pulse_latency_callback, This);
1542 pthread_mutex_unlock(&pulse_lock);
1543 return hr;
1546 static HRESULT WINAPI AudioClient_Stop(IAudioClient *iface)
1548 ACImpl *This = impl_from_IAudioClient(iface);
1549 HRESULT hr = S_OK;
1550 pa_operation *o;
1551 int success;
1553 TRACE("(%p)\n", This);
1555 pthread_mutex_lock(&pulse_lock);
1556 hr = pulse_stream_valid(This);
1557 if (FAILED(hr)) {
1558 pthread_mutex_unlock(&pulse_lock);
1559 return hr;
1562 if (!This->started) {
1563 pthread_mutex_unlock(&pulse_lock);
1564 return S_FALSE;
1567 if (This->dataflow == eRender) {
1568 o = pa_stream_cork(This->stream, 1, pulse_op_cb, &success);
1569 if (o) {
1570 while(pa_operation_get_state(o) == PA_OPERATION_RUNNING)
1571 pthread_cond_wait(&pulse_cond, &pulse_lock);
1572 pa_operation_unref(o);
1573 } else
1574 success = 0;
1575 if (!success)
1576 hr = E_FAIL;
1578 if (SUCCEEDED(hr)) {
1579 This->started = FALSE;
1581 pthread_mutex_unlock(&pulse_lock);
1582 return hr;
1585 static HRESULT WINAPI AudioClient_Reset(IAudioClient *iface)
1587 ACImpl *This = impl_from_IAudioClient(iface);
1588 HRESULT hr = S_OK;
1590 TRACE("(%p)\n", This);
1592 pthread_mutex_lock(&pulse_lock);
1593 hr = pulse_stream_valid(This);
1594 if (FAILED(hr)) {
1595 pthread_mutex_unlock(&pulse_lock);
1596 return hr;
1599 if (This->started) {
1600 pthread_mutex_unlock(&pulse_lock);
1601 return AUDCLNT_E_NOT_STOPPED;
1604 if (This->locked) {
1605 pthread_mutex_unlock(&pulse_lock);
1606 return AUDCLNT_E_BUFFER_OPERATION_PENDING;
1609 if (This->dataflow == eRender) {
1610 /* If there is still data in the render buffer it needs to be removed from the server */
1611 int success = 0;
1612 if (This->pad) {
1613 pa_operation *o = pa_stream_flush(This->stream, pulse_op_cb, &success);
1614 if (o) {
1615 while(pa_operation_get_state(o) == PA_OPERATION_RUNNING)
1616 pthread_cond_wait(&pulse_cond, &pulse_lock);
1617 pa_operation_unref(o);
1620 if (success || !This->pad)
1621 This->clock_lastpos = This->clock_written = This->pad = 0;
1622 } else {
1623 ACPacket *p;
1624 This->clock_written += This->pad;
1625 This->pad = 0;
1627 if ((p = This->locked_ptr)) {
1628 This->locked_ptr = NULL;
1629 list_add_tail(&This->packet_free_head, &p->entry);
1631 list_move_tail(&This->packet_free_head, &This->packet_filled_head);
1633 pthread_mutex_unlock(&pulse_lock);
1635 return hr;
1638 static HRESULT WINAPI AudioClient_SetEventHandle(IAudioClient *iface,
1639 HANDLE event)
1641 ACImpl *This = impl_from_IAudioClient(iface);
1642 HRESULT hr;
1644 TRACE("(%p)->(%p)\n", This, event);
1646 if (!event)
1647 return E_INVALIDARG;
1649 pthread_mutex_lock(&pulse_lock);
1650 hr = pulse_stream_valid(This);
1651 if (FAILED(hr)) {
1652 pthread_mutex_unlock(&pulse_lock);
1653 return hr;
1656 if (!(This->flags & AUDCLNT_STREAMFLAGS_EVENTCALLBACK))
1657 hr = AUDCLNT_E_EVENTHANDLE_NOT_EXPECTED;
1658 else if (This->event)
1659 hr = HRESULT_FROM_WIN32(ERROR_INVALID_NAME);
1660 else
1661 This->event = event;
1662 pthread_mutex_unlock(&pulse_lock);
1663 return hr;
1666 static HRESULT WINAPI AudioClient_GetService(IAudioClient *iface, REFIID riid,
1667 void **ppv)
1669 ACImpl *This = impl_from_IAudioClient(iface);
1670 HRESULT hr;
1672 TRACE("(%p)->(%s, %p)\n", This, debugstr_guid(riid), ppv);
1674 if (!ppv)
1675 return E_POINTER;
1676 *ppv = NULL;
1678 pthread_mutex_lock(&pulse_lock);
1679 hr = pulse_stream_valid(This);
1680 pthread_mutex_unlock(&pulse_lock);
1681 if (FAILED(hr))
1682 return hr;
1684 if (IsEqualIID(riid, &IID_IAudioRenderClient)) {
1685 if (This->dataflow != eRender)
1686 return AUDCLNT_E_WRONG_ENDPOINT_TYPE;
1687 *ppv = &This->IAudioRenderClient_iface;
1688 } else if (IsEqualIID(riid, &IID_IAudioCaptureClient)) {
1689 if (This->dataflow != eCapture)
1690 return AUDCLNT_E_WRONG_ENDPOINT_TYPE;
1691 *ppv = &This->IAudioCaptureClient_iface;
1692 } else if (IsEqualIID(riid, &IID_IAudioClock)) {
1693 *ppv = &This->IAudioClock_iface;
1694 } else if (IsEqualIID(riid, &IID_IAudioStreamVolume)) {
1695 *ppv = &This->IAudioStreamVolume_iface;
1696 } else if (IsEqualIID(riid, &IID_IAudioSessionControl) ||
1697 IsEqualIID(riid, &IID_IChannelAudioVolume) ||
1698 IsEqualIID(riid, &IID_ISimpleAudioVolume)) {
1699 if (!This->session_wrapper) {
1700 This->session_wrapper = AudioSessionWrapper_Create(This);
1701 if (!This->session_wrapper)
1702 return E_OUTOFMEMORY;
1704 if (IsEqualIID(riid, &IID_IAudioSessionControl))
1705 *ppv = &This->session_wrapper->IAudioSessionControl2_iface;
1706 else if (IsEqualIID(riid, &IID_IChannelAudioVolume))
1707 *ppv = &This->session_wrapper->IChannelAudioVolume_iface;
1708 else if (IsEqualIID(riid, &IID_ISimpleAudioVolume))
1709 *ppv = &This->session_wrapper->ISimpleAudioVolume_iface;
1712 if (*ppv) {
1713 IUnknown_AddRef((IUnknown*)*ppv);
1714 return S_OK;
1717 FIXME("stub %s\n", debugstr_guid(riid));
1718 return E_NOINTERFACE;
1721 static const IAudioClientVtbl AudioClient_Vtbl =
1723 AudioClient_QueryInterface,
1724 AudioClient_AddRef,
1725 AudioClient_Release,
1726 AudioClient_Initialize,
1727 AudioClient_GetBufferSize,
1728 AudioClient_GetStreamLatency,
1729 AudioClient_GetCurrentPadding,
1730 AudioClient_IsFormatSupported,
1731 AudioClient_GetMixFormat,
1732 AudioClient_GetDevicePeriod,
1733 AudioClient_Start,
1734 AudioClient_Stop,
1735 AudioClient_Reset,
1736 AudioClient_SetEventHandle,
1737 AudioClient_GetService
1740 static HRESULT WINAPI AudioRenderClient_QueryInterface(
1741 IAudioRenderClient *iface, REFIID riid, void **ppv)
1743 TRACE("(%p)->(%s, %p)\n", iface, debugstr_guid(riid), ppv);
1745 if (!ppv)
1746 return E_POINTER;
1747 *ppv = NULL;
1749 if (IsEqualIID(riid, &IID_IUnknown) ||
1750 IsEqualIID(riid, &IID_IAudioRenderClient))
1751 *ppv = iface;
1752 if (*ppv) {
1753 IUnknown_AddRef((IUnknown*)*ppv);
1754 return S_OK;
1757 WARN("Unknown interface %s\n", debugstr_guid(riid));
1758 return E_NOINTERFACE;
1761 static ULONG WINAPI AudioRenderClient_AddRef(IAudioRenderClient *iface)
1763 ACImpl *This = impl_from_IAudioRenderClient(iface);
1764 return AudioClient_AddRef(&This->IAudioClient_iface);
1767 static ULONG WINAPI AudioRenderClient_Release(IAudioRenderClient *iface)
1769 ACImpl *This = impl_from_IAudioRenderClient(iface);
1770 return AudioClient_Release(&This->IAudioClient_iface);
1773 static HRESULT WINAPI AudioRenderClient_GetBuffer(IAudioRenderClient *iface,
1774 UINT32 frames, BYTE **data)
1776 ACImpl *This = impl_from_IAudioRenderClient(iface);
1777 size_t avail, req, bytes = frames * pa_frame_size(&This->ss);
1778 UINT32 pad;
1779 HRESULT hr = S_OK;
1780 int ret = -1;
1782 TRACE("(%p)->(%u, %p)\n", This, frames, data);
1784 if (!data)
1785 return E_POINTER;
1786 *data = NULL;
1788 pthread_mutex_lock(&pulse_lock);
1789 hr = pulse_stream_valid(This);
1790 if (FAILED(hr) || This->locked) {
1791 pthread_mutex_unlock(&pulse_lock);
1792 return FAILED(hr) ? hr : AUDCLNT_E_OUT_OF_ORDER;
1794 if (!frames) {
1795 pthread_mutex_unlock(&pulse_lock);
1796 return S_OK;
1799 ACImpl_GetRenderPad(This, &pad);
1800 avail = This->bufsize_frames - pad;
1801 if (avail < frames || bytes > This->bufsize_bytes) {
1802 pthread_mutex_unlock(&pulse_lock);
1803 WARN("Wanted to write %u, but only %zu available\n", frames, avail);
1804 return AUDCLNT_E_BUFFER_TOO_LARGE;
1807 This->locked = frames;
1808 req = bytes;
1809 ret = pa_stream_begin_write(This->stream, &This->locked_ptr, &req);
1810 if (ret < 0 || req < bytes) {
1811 FIXME("%p Not using pulse locked data: %i %zu/%u %u/%u\n", This, ret, req/pa_frame_size(&This->ss), frames, pad, This->bufsize_frames);
1812 if (ret >= 0)
1813 pa_stream_cancel_write(This->stream);
1814 *data = This->tmp_buffer;
1815 This->locked_ptr = NULL;
1816 } else
1817 *data = This->locked_ptr;
1818 pthread_mutex_unlock(&pulse_lock);
1819 return hr;
1822 static HRESULT WINAPI AudioRenderClient_ReleaseBuffer(
1823 IAudioRenderClient *iface, UINT32 written_frames, DWORD flags)
1825 ACImpl *This = impl_from_IAudioRenderClient(iface);
1826 UINT32 written_bytes = written_frames * pa_frame_size(&This->ss);
1827 UINT32 period;
1829 TRACE("(%p)->(%u, %x)\n", This, written_frames, flags);
1831 pthread_mutex_lock(&pulse_lock);
1832 if (!This->locked || !written_frames) {
1833 if (This->locked_ptr)
1834 pa_stream_cancel_write(This->stream);
1835 This->locked = 0;
1836 This->locked_ptr = NULL;
1837 pthread_mutex_unlock(&pulse_lock);
1838 return written_frames ? AUDCLNT_E_OUT_OF_ORDER : S_OK;
1841 if (This->locked < written_frames) {
1842 pthread_mutex_unlock(&pulse_lock);
1843 return AUDCLNT_E_INVALID_SIZE;
1846 if (flags & AUDCLNT_BUFFERFLAGS_SILENT) {
1847 if (This->ss.format == PA_SAMPLE_U8)
1848 memset(This->tmp_buffer, 128, written_bytes);
1849 else
1850 memset(This->tmp_buffer, 0, written_bytes);
1853 This->locked = 0;
1854 if (This->locked_ptr)
1855 pa_stream_write(This->stream, This->locked_ptr, written_bytes, NULL, 0, PA_SEEK_RELATIVE);
1856 else
1857 pa_stream_write(This->stream, This->tmp_buffer, written_bytes, NULL, 0, PA_SEEK_RELATIVE);
1858 This->pad += written_bytes;
1859 This->locked_ptr = NULL;
1860 TRACE("Released %u, pad %zu\n", written_frames, This->pad / pa_frame_size(&This->ss));
1861 assert(This->pad <= This->bufsize_bytes);
1863 period = pa_stream_get_buffer_attr(This->stream)->minreq;
1864 /* Require a minimum of 3 periods filled, if possible */
1865 if (This->event && This->pad + period <= This->bufsize_bytes && This->pad < period * 3)
1866 SetEvent(This->event);
1867 pthread_mutex_unlock(&pulse_lock);
1868 return S_OK;
1871 static const IAudioRenderClientVtbl AudioRenderClient_Vtbl = {
1872 AudioRenderClient_QueryInterface,
1873 AudioRenderClient_AddRef,
1874 AudioRenderClient_Release,
1875 AudioRenderClient_GetBuffer,
1876 AudioRenderClient_ReleaseBuffer
1879 static HRESULT WINAPI AudioCaptureClient_QueryInterface(
1880 IAudioCaptureClient *iface, REFIID riid, void **ppv)
1882 TRACE("(%p)->(%s, %p)\n", iface, debugstr_guid(riid), ppv);
1884 if (!ppv)
1885 return E_POINTER;
1886 *ppv = NULL;
1888 if (IsEqualIID(riid, &IID_IUnknown) ||
1889 IsEqualIID(riid, &IID_IAudioCaptureClient))
1890 *ppv = iface;
1891 if (*ppv) {
1892 IUnknown_AddRef((IUnknown*)*ppv);
1893 return S_OK;
1896 WARN("Unknown interface %s\n", debugstr_guid(riid));
1897 return E_NOINTERFACE;
1900 static ULONG WINAPI AudioCaptureClient_AddRef(IAudioCaptureClient *iface)
1902 ACImpl *This = impl_from_IAudioCaptureClient(iface);
1903 return IAudioClient_AddRef(&This->IAudioClient_iface);
1906 static ULONG WINAPI AudioCaptureClient_Release(IAudioCaptureClient *iface)
1908 ACImpl *This = impl_from_IAudioCaptureClient(iface);
1909 return IAudioClient_Release(&This->IAudioClient_iface);
1912 static HRESULT WINAPI AudioCaptureClient_GetBuffer(IAudioCaptureClient *iface,
1913 BYTE **data, UINT32 *frames, DWORD *flags, UINT64 *devpos,
1914 UINT64 *qpcpos)
1916 ACImpl *This = impl_from_IAudioCaptureClient(iface);
1917 HRESULT hr;
1918 ACPacket *packet;
1920 TRACE("(%p)->(%p, %p, %p, %p, %p)\n", This, data, frames, flags,
1921 devpos, qpcpos);
1923 if (!data || !frames || !flags)
1924 return E_POINTER;
1926 pthread_mutex_lock(&pulse_lock);
1927 hr = pulse_stream_valid(This);
1928 if (FAILED(hr) || This->locked) {
1929 pthread_mutex_unlock(&pulse_lock);
1930 return FAILED(hr) ? hr : AUDCLNT_E_OUT_OF_ORDER;
1933 ACImpl_GetCapturePad(This, NULL);
1934 if ((packet = This->locked_ptr)) {
1935 *frames = This->capture_period / pa_frame_size(&This->ss);
1936 *flags = 0;
1937 if (packet->discont)
1938 *flags |= AUDCLNT_BUFFERFLAGS_DATA_DISCONTINUITY;
1939 if (devpos) {
1940 if (packet->discont)
1941 *devpos = (This->clock_written + This->capture_period) / pa_frame_size(&This->ss);
1942 else
1943 *devpos = This->clock_written / pa_frame_size(&This->ss);
1945 if (qpcpos)
1946 *qpcpos = packet->qpcpos;
1947 *data = packet->data;
1949 else
1950 *frames = 0;
1951 This->locked = *frames;
1952 pthread_mutex_unlock(&pulse_lock);
1953 return *frames ? S_OK : AUDCLNT_S_BUFFER_EMPTY;
1956 static HRESULT WINAPI AudioCaptureClient_ReleaseBuffer(
1957 IAudioCaptureClient *iface, UINT32 done)
1959 ACImpl *This = impl_from_IAudioCaptureClient(iface);
1961 TRACE("(%p)->(%u)\n", This, done);
1963 pthread_mutex_lock(&pulse_lock);
1964 if (!This->locked && done) {
1965 pthread_mutex_unlock(&pulse_lock);
1966 return AUDCLNT_E_OUT_OF_ORDER;
1968 if (done && This->locked != done) {
1969 pthread_mutex_unlock(&pulse_lock);
1970 return AUDCLNT_E_INVALID_SIZE;
1972 if (done) {
1973 ACPacket *packet = This->locked_ptr;
1974 This->locked_ptr = NULL;
1975 This->pad -= This->capture_period;
1976 if (packet->discont)
1977 This->clock_written += 2 * This->capture_period;
1978 else
1979 This->clock_written += This->capture_period;
1980 list_add_tail(&This->packet_free_head, &packet->entry);
1982 This->locked = 0;
1983 pthread_mutex_unlock(&pulse_lock);
1984 return S_OK;
1987 static HRESULT WINAPI AudioCaptureClient_GetNextPacketSize(
1988 IAudioCaptureClient *iface, UINT32 *frames)
1990 ACImpl *This = impl_from_IAudioCaptureClient(iface);
1991 ACPacket *p;
1993 TRACE("(%p)->(%p)\n", This, frames);
1994 if (!frames)
1995 return E_POINTER;
1997 pthread_mutex_lock(&pulse_lock);
1998 ACImpl_GetCapturePad(This, NULL);
1999 p = This->locked_ptr;
2000 if (p)
2001 *frames = This->capture_period / pa_frame_size(&This->ss);
2002 else
2003 *frames = 0;
2004 pthread_mutex_unlock(&pulse_lock);
2005 return S_OK;
2008 static const IAudioCaptureClientVtbl AudioCaptureClient_Vtbl =
2010 AudioCaptureClient_QueryInterface,
2011 AudioCaptureClient_AddRef,
2012 AudioCaptureClient_Release,
2013 AudioCaptureClient_GetBuffer,
2014 AudioCaptureClient_ReleaseBuffer,
2015 AudioCaptureClient_GetNextPacketSize
2018 static HRESULT WINAPI AudioClock_QueryInterface(IAudioClock *iface,
2019 REFIID riid, void **ppv)
2021 ACImpl *This = impl_from_IAudioClock(iface);
2023 TRACE("(%p)->(%s, %p)\n", iface, debugstr_guid(riid), ppv);
2025 if (!ppv)
2026 return E_POINTER;
2027 *ppv = NULL;
2029 if (IsEqualIID(riid, &IID_IUnknown) || IsEqualIID(riid, &IID_IAudioClock))
2030 *ppv = iface;
2031 else if (IsEqualIID(riid, &IID_IAudioClock2))
2032 *ppv = &This->IAudioClock2_iface;
2033 if (*ppv) {
2034 IUnknown_AddRef((IUnknown*)*ppv);
2035 return S_OK;
2038 WARN("Unknown interface %s\n", debugstr_guid(riid));
2039 return E_NOINTERFACE;
2042 static ULONG WINAPI AudioClock_AddRef(IAudioClock *iface)
2044 ACImpl *This = impl_from_IAudioClock(iface);
2045 return IAudioClient_AddRef(&This->IAudioClient_iface);
2048 static ULONG WINAPI AudioClock_Release(IAudioClock *iface)
2050 ACImpl *This = impl_from_IAudioClock(iface);
2051 return IAudioClient_Release(&This->IAudioClient_iface);
2054 static HRESULT WINAPI AudioClock_GetFrequency(IAudioClock *iface, UINT64 *freq)
2056 ACImpl *This = impl_from_IAudioClock(iface);
2057 HRESULT hr;
2059 TRACE("(%p)->(%p)\n", This, freq);
2061 pthread_mutex_lock(&pulse_lock);
2062 hr = pulse_stream_valid(This);
2063 if (SUCCEEDED(hr))
2064 *freq = This->ss.rate * pa_frame_size(&This->ss);
2065 pthread_mutex_unlock(&pulse_lock);
2066 return hr;
2069 static HRESULT WINAPI AudioClock_GetPosition(IAudioClock *iface, UINT64 *pos,
2070 UINT64 *qpctime)
2072 ACImpl *This = impl_from_IAudioClock(iface);
2073 HRESULT hr;
2075 TRACE("(%p)->(%p, %p)\n", This, pos, qpctime);
2077 if (!pos)
2078 return E_POINTER;
2080 pthread_mutex_lock(&pulse_lock);
2081 hr = pulse_stream_valid(This);
2082 if (FAILED(hr)) {
2083 pthread_mutex_unlock(&pulse_lock);
2084 return hr;
2087 *pos = This->clock_written;
2089 /* Make time never go backwards */
2090 if (*pos < This->clock_lastpos)
2091 *pos = This->clock_lastpos;
2092 else
2093 This->clock_lastpos = *pos;
2094 pthread_mutex_unlock(&pulse_lock);
2096 TRACE("%p Position: %u\n", This, (unsigned)*pos);
2098 if (qpctime) {
2099 LARGE_INTEGER stamp, freq;
2100 QueryPerformanceCounter(&stamp);
2101 QueryPerformanceFrequency(&freq);
2102 *qpctime = (stamp.QuadPart * (INT64)10000000) / freq.QuadPart;
2105 return S_OK;
2108 static HRESULT WINAPI AudioClock_GetCharacteristics(IAudioClock *iface,
2109 DWORD *chars)
2111 ACImpl *This = impl_from_IAudioClock(iface);
2113 TRACE("(%p)->(%p)\n", This, chars);
2115 if (!chars)
2116 return E_POINTER;
2118 *chars = AUDIOCLOCK_CHARACTERISTIC_FIXED_FREQ;
2120 return S_OK;
2123 static const IAudioClockVtbl AudioClock_Vtbl =
2125 AudioClock_QueryInterface,
2126 AudioClock_AddRef,
2127 AudioClock_Release,
2128 AudioClock_GetFrequency,
2129 AudioClock_GetPosition,
2130 AudioClock_GetCharacteristics
2133 static HRESULT WINAPI AudioClock2_QueryInterface(IAudioClock2 *iface,
2134 REFIID riid, void **ppv)
2136 ACImpl *This = impl_from_IAudioClock2(iface);
2137 return IAudioClock_QueryInterface(&This->IAudioClock_iface, riid, ppv);
2140 static ULONG WINAPI AudioClock2_AddRef(IAudioClock2 *iface)
2142 ACImpl *This = impl_from_IAudioClock2(iface);
2143 return IAudioClient_AddRef(&This->IAudioClient_iface);
2146 static ULONG WINAPI AudioClock2_Release(IAudioClock2 *iface)
2148 ACImpl *This = impl_from_IAudioClock2(iface);
2149 return IAudioClient_Release(&This->IAudioClient_iface);
2152 static HRESULT WINAPI AudioClock2_GetDevicePosition(IAudioClock2 *iface,
2153 UINT64 *pos, UINT64 *qpctime)
2155 ACImpl *This = impl_from_IAudioClock2(iface);
2156 HRESULT hr = AudioClock_GetPosition(&This->IAudioClock_iface, pos, qpctime);
2157 if (SUCCEEDED(hr))
2158 *pos /= pa_frame_size(&This->ss);
2159 return hr;
2162 static const IAudioClock2Vtbl AudioClock2_Vtbl =
2164 AudioClock2_QueryInterface,
2165 AudioClock2_AddRef,
2166 AudioClock2_Release,
2167 AudioClock2_GetDevicePosition
2170 static HRESULT WINAPI AudioStreamVolume_QueryInterface(
2171 IAudioStreamVolume *iface, REFIID riid, void **ppv)
2173 TRACE("(%p)->(%s, %p)\n", iface, debugstr_guid(riid), ppv);
2175 if (!ppv)
2176 return E_POINTER;
2177 *ppv = NULL;
2179 if (IsEqualIID(riid, &IID_IUnknown) ||
2180 IsEqualIID(riid, &IID_IAudioStreamVolume))
2181 *ppv = iface;
2182 if (*ppv) {
2183 IUnknown_AddRef((IUnknown*)*ppv);
2184 return S_OK;
2187 WARN("Unknown interface %s\n", debugstr_guid(riid));
2188 return E_NOINTERFACE;
2191 static ULONG WINAPI AudioStreamVolume_AddRef(IAudioStreamVolume *iface)
2193 ACImpl *This = impl_from_IAudioStreamVolume(iface);
2194 return IAudioClient_AddRef(&This->IAudioClient_iface);
2197 static ULONG WINAPI AudioStreamVolume_Release(IAudioStreamVolume *iface)
2199 ACImpl *This = impl_from_IAudioStreamVolume(iface);
2200 return IAudioClient_Release(&This->IAudioClient_iface);
2203 static HRESULT WINAPI AudioStreamVolume_GetChannelCount(
2204 IAudioStreamVolume *iface, UINT32 *out)
2206 ACImpl *This = impl_from_IAudioStreamVolume(iface);
2208 TRACE("(%p)->(%p)\n", This, out);
2210 if (!out)
2211 return E_POINTER;
2213 *out = This->ss.channels;
2215 return S_OK;
2218 struct pulse_info_cb_data {
2219 UINT32 n;
2220 float *levels;
2223 static void pulse_sink_input_info_cb(pa_context *c, const pa_sink_input_info *info, int eol, void *data)
2225 struct pulse_info_cb_data *d = data;
2226 int i;
2227 if (eol)
2228 return;
2229 for (i = 0; i < d->n; ++i)
2230 d->levels[i] = (float)info->volume.values[i] / (float)PA_VOLUME_NORM;
2231 pthread_cond_signal(&pulse_cond);
2234 static void pulse_source_info_cb(pa_context *c, const pa_source_info *info, int eol, void *data)
2236 struct pulse_info_cb_data *d = data;
2237 int i;
2238 if (eol)
2239 return;
2240 for (i = 0; i < d->n; ++i)
2241 d->levels[i] = (float)info->volume.values[i] / (float)PA_VOLUME_NORM;
2242 pthread_cond_signal(&pulse_cond);
2245 static HRESULT WINAPI AudioStreamVolume_SetAllVolumes(
2246 IAudioStreamVolume *iface, UINT32 count, const float *levels)
2248 ACImpl *This = impl_from_IAudioStreamVolume(iface);
2249 pa_operation *o;
2250 HRESULT hr;
2251 int success = 0, i;
2252 pa_cvolume cv;
2254 TRACE("(%p)->(%d, %p)\n", This, count, levels);
2256 if (!levels)
2257 return E_POINTER;
2259 if (count != This->ss.channels)
2260 return E_INVALIDARG;
2262 pthread_mutex_lock(&pulse_lock);
2263 hr = pulse_stream_valid(This);
2264 if (FAILED(hr))
2265 goto out;
2267 if (pulse_stream_volume) {
2268 cv.channels = count;
2269 for (i = 0; i < cv.channels; ++i)
2270 cv.values[i] = levels[i] * (float)PA_VOLUME_NORM;
2271 if (This->dataflow == eRender)
2272 o = pa_context_set_sink_input_volume(pulse_ctx, pa_stream_get_index(This->stream), &cv, pulse_ctx_op_cb, &success);
2273 else
2274 o = pa_context_set_source_volume_by_index(pulse_ctx, pa_stream_get_device_index(This->stream), &cv, pulse_ctx_op_cb, &success);
2275 if (o) {
2276 while(pa_operation_get_state(o) == PA_OPERATION_RUNNING)
2277 pthread_cond_wait(&pulse_cond, &pulse_lock);
2278 pa_operation_unref(o);
2280 if (!success)
2281 hr = AUDCLNT_E_BUFFER_ERROR;
2282 } else {
2283 int i;
2284 for (i = 0; i < count; ++i)
2285 This->vol[i] = levels[i];
2288 out:
2289 pthread_mutex_unlock(&pulse_lock);
2290 return hr;
2293 static HRESULT WINAPI AudioStreamVolume_GetAllVolumes(
2294 IAudioStreamVolume *iface, UINT32 count, float *levels)
2296 ACImpl *This = impl_from_IAudioStreamVolume(iface);
2297 pa_operation *o;
2298 HRESULT hr;
2299 struct pulse_info_cb_data info;
2301 TRACE("(%p)->(%d, %p)\n", This, count, levels);
2303 if (!levels)
2304 return E_POINTER;
2306 if (count != This->ss.channels)
2307 return E_INVALIDARG;
2309 pthread_mutex_lock(&pulse_lock);
2310 hr = pulse_stream_valid(This);
2311 if (FAILED(hr))
2312 goto out;
2314 if (pulse_stream_volume) {
2315 info.n = count;
2316 info.levels = levels;
2317 if (This->dataflow == eRender)
2318 o = pa_context_get_sink_input_info(pulse_ctx, pa_stream_get_index(This->stream), pulse_sink_input_info_cb, &info);
2319 else
2320 o = pa_context_get_source_info_by_index(pulse_ctx, pa_stream_get_device_index(This->stream), pulse_source_info_cb, &info);
2321 if (o) {
2322 while(pa_operation_get_state(o) == PA_OPERATION_RUNNING)
2323 pthread_cond_wait(&pulse_cond, &pulse_lock);
2324 pa_operation_unref(o);
2325 } else
2326 hr = AUDCLNT_E_BUFFER_ERROR;
2327 } else {
2328 int i;
2329 for (i = 0; i < count; ++i)
2330 levels[i] = This->vol[i];
2333 out:
2334 pthread_mutex_unlock(&pulse_lock);
2335 return hr;
2338 static HRESULT WINAPI AudioStreamVolume_SetChannelVolume(
2339 IAudioStreamVolume *iface, UINT32 index, float level)
2341 ACImpl *This = impl_from_IAudioStreamVolume(iface);
2342 HRESULT hr;
2343 float volumes[PA_CHANNELS_MAX];
2345 TRACE("(%p)->(%d, %f)\n", This, index, level);
2347 if (level < 0.f || level > 1.f)
2348 return E_INVALIDARG;
2350 if (index >= This->ss.channels)
2351 return E_INVALIDARG;
2353 hr = AudioStreamVolume_GetAllVolumes(iface, This->ss.channels, volumes);
2354 volumes[index] = level;
2355 if (SUCCEEDED(hr))
2356 hr = AudioStreamVolume_SetAllVolumes(iface, This->ss.channels, volumes);
2357 return hr;
2360 static HRESULT WINAPI AudioStreamVolume_GetChannelVolume(
2361 IAudioStreamVolume *iface, UINT32 index, float *level)
2363 ACImpl *This = impl_from_IAudioStreamVolume(iface);
2364 float volumes[PA_CHANNELS_MAX];
2365 HRESULT hr;
2367 TRACE("(%p)->(%d, %p)\n", This, index, level);
2369 if (!level)
2370 return E_POINTER;
2372 if (index >= This->ss.channels)
2373 return E_INVALIDARG;
2375 hr = AudioStreamVolume_GetAllVolumes(iface, This->ss.channels, volumes);
2376 if (SUCCEEDED(hr))
2377 *level = volumes[index];
2378 return hr;
2381 static const IAudioStreamVolumeVtbl AudioStreamVolume_Vtbl =
2383 AudioStreamVolume_QueryInterface,
2384 AudioStreamVolume_AddRef,
2385 AudioStreamVolume_Release,
2386 AudioStreamVolume_GetChannelCount,
2387 AudioStreamVolume_SetChannelVolume,
2388 AudioStreamVolume_GetChannelVolume,
2389 AudioStreamVolume_SetAllVolumes,
2390 AudioStreamVolume_GetAllVolumes
2393 static AudioSessionWrapper *AudioSessionWrapper_Create(ACImpl *client)
2395 AudioSessionWrapper *ret;
2397 ret = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY,
2398 sizeof(AudioSessionWrapper));
2399 if (!ret)
2400 return NULL;
2402 ret->IAudioSessionControl2_iface.lpVtbl = &AudioSessionControl2_Vtbl;
2403 ret->ISimpleAudioVolume_iface.lpVtbl = &SimpleAudioVolume_Vtbl;
2404 ret->IChannelAudioVolume_iface.lpVtbl = &ChannelAudioVolume_Vtbl;
2406 ret->ref = !client;
2408 ret->client = client;
2409 if (client) {
2410 ret->session = client->session;
2411 AudioClient_AddRef(&client->IAudioClient_iface);
2414 return ret;
2417 static HRESULT WINAPI AudioSessionControl_QueryInterface(
2418 IAudioSessionControl2 *iface, REFIID riid, void **ppv)
2420 TRACE("(%p)->(%s, %p)\n", iface, debugstr_guid(riid), ppv);
2422 if (!ppv)
2423 return E_POINTER;
2424 *ppv = NULL;
2426 if (IsEqualIID(riid, &IID_IUnknown) ||
2427 IsEqualIID(riid, &IID_IAudioSessionControl) ||
2428 IsEqualIID(riid, &IID_IAudioSessionControl2))
2429 *ppv = iface;
2430 if (*ppv) {
2431 IUnknown_AddRef((IUnknown*)*ppv);
2432 return S_OK;
2435 WARN("Unknown interface %s\n", debugstr_guid(riid));
2436 return E_NOINTERFACE;
2439 static ULONG WINAPI AudioSessionControl_AddRef(IAudioSessionControl2 *iface)
2441 AudioSessionWrapper *This = impl_from_IAudioSessionControl2(iface);
2442 ULONG ref;
2443 ref = InterlockedIncrement(&This->ref);
2444 TRACE("(%p) Refcount now %u\n", This, ref);
2445 return ref;
2448 static ULONG WINAPI AudioSessionControl_Release(IAudioSessionControl2 *iface)
2450 AudioSessionWrapper *This = impl_from_IAudioSessionControl2(iface);
2451 ULONG ref;
2452 ref = InterlockedDecrement(&This->ref);
2453 TRACE("(%p) Refcount now %u\n", This, ref);
2454 if (!ref) {
2455 if (This->client) {
2456 This->client->session_wrapper = NULL;
2457 AudioClient_Release(&This->client->IAudioClient_iface);
2459 HeapFree(GetProcessHeap(), 0, This);
2461 return ref;
2464 static HRESULT WINAPI AudioSessionControl_GetState(IAudioSessionControl2 *iface,
2465 AudioSessionState *state)
2467 AudioSessionWrapper *This = impl_from_IAudioSessionControl2(iface);
2468 ACImpl *client;
2470 TRACE("(%p)->(%p)\n", This, state);
2472 if (!state)
2473 return NULL_PTR_ERR;
2475 pthread_mutex_lock(&pulse_lock);
2476 if (list_empty(&This->session->clients)) {
2477 *state = AudioSessionStateExpired;
2478 goto out;
2480 LIST_FOR_EACH_ENTRY(client, &This->session->clients, ACImpl, entry) {
2481 if (client->started) {
2482 *state = AudioSessionStateActive;
2483 goto out;
2486 *state = AudioSessionStateInactive;
2488 out:
2489 pthread_mutex_unlock(&pulse_lock);
2490 return S_OK;
2493 static HRESULT WINAPI AudioSessionControl_GetDisplayName(
2494 IAudioSessionControl2 *iface, WCHAR **name)
2496 AudioSessionWrapper *This = impl_from_IAudioSessionControl2(iface);
2498 FIXME("(%p)->(%p) - stub\n", This, name);
2500 return E_NOTIMPL;
2503 static HRESULT WINAPI AudioSessionControl_SetDisplayName(
2504 IAudioSessionControl2 *iface, const WCHAR *name, const GUID *session)
2506 AudioSessionWrapper *This = impl_from_IAudioSessionControl2(iface);
2508 FIXME("(%p)->(%p, %s) - stub\n", This, name, debugstr_guid(session));
2510 return E_NOTIMPL;
2513 static HRESULT WINAPI AudioSessionControl_GetIconPath(
2514 IAudioSessionControl2 *iface, WCHAR **path)
2516 AudioSessionWrapper *This = impl_from_IAudioSessionControl2(iface);
2518 FIXME("(%p)->(%p) - stub\n", This, path);
2520 return E_NOTIMPL;
2523 static HRESULT WINAPI AudioSessionControl_SetIconPath(
2524 IAudioSessionControl2 *iface, const WCHAR *path, const GUID *session)
2526 AudioSessionWrapper *This = impl_from_IAudioSessionControl2(iface);
2528 FIXME("(%p)->(%p, %s) - stub\n", This, path, debugstr_guid(session));
2530 return E_NOTIMPL;
2533 static HRESULT WINAPI AudioSessionControl_GetGroupingParam(
2534 IAudioSessionControl2 *iface, GUID *group)
2536 AudioSessionWrapper *This = impl_from_IAudioSessionControl2(iface);
2538 FIXME("(%p)->(%p) - stub\n", This, group);
2540 return E_NOTIMPL;
2543 static HRESULT WINAPI AudioSessionControl_SetGroupingParam(
2544 IAudioSessionControl2 *iface, const GUID *group, const GUID *session)
2546 AudioSessionWrapper *This = impl_from_IAudioSessionControl2(iface);
2548 FIXME("(%p)->(%s, %s) - stub\n", This, debugstr_guid(group),
2549 debugstr_guid(session));
2551 return E_NOTIMPL;
2554 static HRESULT WINAPI AudioSessionControl_RegisterAudioSessionNotification(
2555 IAudioSessionControl2 *iface, IAudioSessionEvents *events)
2557 AudioSessionWrapper *This = impl_from_IAudioSessionControl2(iface);
2559 FIXME("(%p)->(%p) - stub\n", This, events);
2561 return S_OK;
2564 static HRESULT WINAPI AudioSessionControl_UnregisterAudioSessionNotification(
2565 IAudioSessionControl2 *iface, IAudioSessionEvents *events)
2567 AudioSessionWrapper *This = impl_from_IAudioSessionControl2(iface);
2569 FIXME("(%p)->(%p) - stub\n", This, events);
2571 return S_OK;
2574 static HRESULT WINAPI AudioSessionControl_GetSessionIdentifier(
2575 IAudioSessionControl2 *iface, WCHAR **id)
2577 AudioSessionWrapper *This = impl_from_IAudioSessionControl2(iface);
2579 FIXME("(%p)->(%p) - stub\n", This, id);
2581 return E_NOTIMPL;
2584 static HRESULT WINAPI AudioSessionControl_GetSessionInstanceIdentifier(
2585 IAudioSessionControl2 *iface, WCHAR **id)
2587 AudioSessionWrapper *This = impl_from_IAudioSessionControl2(iface);
2589 FIXME("(%p)->(%p) - stub\n", This, id);
2591 return E_NOTIMPL;
2594 static HRESULT WINAPI AudioSessionControl_GetProcessId(
2595 IAudioSessionControl2 *iface, DWORD *pid)
2597 AudioSessionWrapper *This = impl_from_IAudioSessionControl2(iface);
2599 TRACE("(%p)->(%p)\n", This, pid);
2601 if (!pid)
2602 return E_POINTER;
2604 *pid = GetCurrentProcessId();
2606 return S_OK;
2609 static HRESULT WINAPI AudioSessionControl_IsSystemSoundsSession(
2610 IAudioSessionControl2 *iface)
2612 AudioSessionWrapper *This = impl_from_IAudioSessionControl2(iface);
2614 TRACE("(%p)\n", This);
2616 return S_FALSE;
2619 static HRESULT WINAPI AudioSessionControl_SetDuckingPreference(
2620 IAudioSessionControl2 *iface, BOOL optout)
2622 AudioSessionWrapper *This = impl_from_IAudioSessionControl2(iface);
2624 TRACE("(%p)->(%d)\n", This, optout);
2626 return S_OK;
2629 static const IAudioSessionControl2Vtbl AudioSessionControl2_Vtbl =
2631 AudioSessionControl_QueryInterface,
2632 AudioSessionControl_AddRef,
2633 AudioSessionControl_Release,
2634 AudioSessionControl_GetState,
2635 AudioSessionControl_GetDisplayName,
2636 AudioSessionControl_SetDisplayName,
2637 AudioSessionControl_GetIconPath,
2638 AudioSessionControl_SetIconPath,
2639 AudioSessionControl_GetGroupingParam,
2640 AudioSessionControl_SetGroupingParam,
2641 AudioSessionControl_RegisterAudioSessionNotification,
2642 AudioSessionControl_UnregisterAudioSessionNotification,
2643 AudioSessionControl_GetSessionIdentifier,
2644 AudioSessionControl_GetSessionInstanceIdentifier,
2645 AudioSessionControl_GetProcessId,
2646 AudioSessionControl_IsSystemSoundsSession,
2647 AudioSessionControl_SetDuckingPreference
2650 typedef struct _SessionMgr {
2651 IAudioSessionManager2 IAudioSessionManager2_iface;
2653 LONG ref;
2655 IMMDevice *device;
2656 } SessionMgr;
2658 static HRESULT WINAPI AudioSessionManager_QueryInterface(IAudioSessionManager2 *iface,
2659 REFIID riid, void **ppv)
2661 TRACE("(%p)->(%s, %p)\n", iface, debugstr_guid(riid), ppv);
2663 if (!ppv)
2664 return E_POINTER;
2665 *ppv = NULL;
2667 if (IsEqualIID(riid, &IID_IUnknown) ||
2668 IsEqualIID(riid, &IID_IAudioSessionManager) ||
2669 IsEqualIID(riid, &IID_IAudioSessionManager2))
2670 *ppv = iface;
2671 if (*ppv) {
2672 IUnknown_AddRef((IUnknown*)*ppv);
2673 return S_OK;
2676 WARN("Unknown interface %s\n", debugstr_guid(riid));
2677 return E_NOINTERFACE;
2680 static inline SessionMgr *impl_from_IAudioSessionManager2(IAudioSessionManager2 *iface)
2682 return CONTAINING_RECORD(iface, SessionMgr, IAudioSessionManager2_iface);
2685 static ULONG WINAPI AudioSessionManager_AddRef(IAudioSessionManager2 *iface)
2687 SessionMgr *This = impl_from_IAudioSessionManager2(iface);
2688 ULONG ref;
2689 ref = InterlockedIncrement(&This->ref);
2690 TRACE("(%p) Refcount now %u\n", This, ref);
2691 return ref;
2694 static ULONG WINAPI AudioSessionManager_Release(IAudioSessionManager2 *iface)
2696 SessionMgr *This = impl_from_IAudioSessionManager2(iface);
2697 ULONG ref;
2698 ref = InterlockedDecrement(&This->ref);
2699 TRACE("(%p) Refcount now %u\n", This, ref);
2700 if (!ref)
2701 HeapFree(GetProcessHeap(), 0, This);
2702 return ref;
2705 static HRESULT WINAPI AudioSessionManager_GetAudioSessionControl(
2706 IAudioSessionManager2 *iface, const GUID *session_guid, DWORD flags,
2707 IAudioSessionControl **out)
2709 SessionMgr *This = impl_from_IAudioSessionManager2(iface);
2710 AudioSession *session;
2711 AudioSessionWrapper *wrapper;
2712 HRESULT hr;
2714 TRACE("(%p)->(%s, %x, %p)\n", This, debugstr_guid(session_guid),
2715 flags, out);
2717 hr = get_audio_session(session_guid, This->device, 0, &session);
2718 if (FAILED(hr))
2719 return hr;
2721 wrapper = AudioSessionWrapper_Create(NULL);
2722 if (!wrapper)
2723 return E_OUTOFMEMORY;
2725 wrapper->session = session;
2727 *out = (IAudioSessionControl*)&wrapper->IAudioSessionControl2_iface;
2729 return S_OK;
2732 static HRESULT WINAPI AudioSessionManager_GetSimpleAudioVolume(
2733 IAudioSessionManager2 *iface, const GUID *session_guid, DWORD flags,
2734 ISimpleAudioVolume **out)
2736 SessionMgr *This = impl_from_IAudioSessionManager2(iface);
2737 AudioSession *session;
2738 AudioSessionWrapper *wrapper;
2739 HRESULT hr;
2741 TRACE("(%p)->(%s, %x, %p)\n", This, debugstr_guid(session_guid),
2742 flags, out);
2744 hr = get_audio_session(session_guid, This->device, 0, &session);
2745 if (FAILED(hr))
2746 return hr;
2748 wrapper = AudioSessionWrapper_Create(NULL);
2749 if (!wrapper)
2750 return E_OUTOFMEMORY;
2752 wrapper->session = session;
2754 *out = &wrapper->ISimpleAudioVolume_iface;
2756 return S_OK;
2759 static HRESULT WINAPI AudioSessionManager_GetSessionEnumerator(
2760 IAudioSessionManager2 *iface, IAudioSessionEnumerator **out)
2762 SessionMgr *This = impl_from_IAudioSessionManager2(iface);
2763 FIXME("(%p)->(%p) - stub\n", This, out);
2764 return E_NOTIMPL;
2767 static HRESULT WINAPI AudioSessionManager_RegisterSessionNotification(
2768 IAudioSessionManager2 *iface, IAudioSessionNotification *notification)
2770 SessionMgr *This = impl_from_IAudioSessionManager2(iface);
2771 FIXME("(%p)->(%p) - stub\n", This, notification);
2772 return E_NOTIMPL;
2775 static HRESULT WINAPI AudioSessionManager_UnregisterSessionNotification(
2776 IAudioSessionManager2 *iface, IAudioSessionNotification *notification)
2778 SessionMgr *This = impl_from_IAudioSessionManager2(iface);
2779 FIXME("(%p)->(%p) - stub\n", This, notification);
2780 return E_NOTIMPL;
2783 static HRESULT WINAPI AudioSessionManager_RegisterDuckNotification(
2784 IAudioSessionManager2 *iface, const WCHAR *session_id,
2785 IAudioVolumeDuckNotification *notification)
2787 SessionMgr *This = impl_from_IAudioSessionManager2(iface);
2788 FIXME("(%p)->(%p) - stub\n", This, notification);
2789 return E_NOTIMPL;
2792 static HRESULT WINAPI AudioSessionManager_UnregisterDuckNotification(
2793 IAudioSessionManager2 *iface,
2794 IAudioVolumeDuckNotification *notification)
2796 SessionMgr *This = impl_from_IAudioSessionManager2(iface);
2797 FIXME("(%p)->(%p) - stub\n", This, notification);
2798 return E_NOTIMPL;
2801 static const IAudioSessionManager2Vtbl AudioSessionManager2_Vtbl =
2803 AudioSessionManager_QueryInterface,
2804 AudioSessionManager_AddRef,
2805 AudioSessionManager_Release,
2806 AudioSessionManager_GetAudioSessionControl,
2807 AudioSessionManager_GetSimpleAudioVolume,
2808 AudioSessionManager_GetSessionEnumerator,
2809 AudioSessionManager_RegisterSessionNotification,
2810 AudioSessionManager_UnregisterSessionNotification,
2811 AudioSessionManager_RegisterDuckNotification,
2812 AudioSessionManager_UnregisterDuckNotification
2815 static HRESULT WINAPI SimpleAudioVolume_QueryInterface(
2816 ISimpleAudioVolume *iface, REFIID riid, void **ppv)
2818 TRACE("(%p)->(%s, %p)\n", iface, debugstr_guid(riid), ppv);
2820 if (!ppv)
2821 return E_POINTER;
2822 *ppv = NULL;
2824 if (IsEqualIID(riid, &IID_IUnknown) ||
2825 IsEqualIID(riid, &IID_ISimpleAudioVolume))
2826 *ppv = iface;
2827 if (*ppv) {
2828 IUnknown_AddRef((IUnknown*)*ppv);
2829 return S_OK;
2832 WARN("Unknown interface %s\n", debugstr_guid(riid));
2833 return E_NOINTERFACE;
2836 static ULONG WINAPI SimpleAudioVolume_AddRef(ISimpleAudioVolume *iface)
2838 AudioSessionWrapper *This = impl_from_ISimpleAudioVolume(iface);
2839 return AudioSessionControl_AddRef(&This->IAudioSessionControl2_iface);
2842 static ULONG WINAPI SimpleAudioVolume_Release(ISimpleAudioVolume *iface)
2844 AudioSessionWrapper *This = impl_from_ISimpleAudioVolume(iface);
2845 return AudioSessionControl_Release(&This->IAudioSessionControl2_iface);
2848 static HRESULT WINAPI SimpleAudioVolume_SetMasterVolume(
2849 ISimpleAudioVolume *iface, float level, const GUID *context)
2851 AudioSessionWrapper *This = impl_from_ISimpleAudioVolume(iface);
2852 AudioSession *session = This->session;
2854 TRACE("(%p)->(%f, %s)\n", session, level, wine_dbgstr_guid(context));
2856 if (level < 0.f || level > 1.f)
2857 return E_INVALIDARG;
2859 if (context)
2860 FIXME("Notifications not supported yet\n");
2862 TRACE("Pulseaudio does not support session volume control\n");
2864 pthread_mutex_lock(&pulse_lock);
2865 session->master_vol = level;
2866 pthread_mutex_unlock(&pulse_lock);
2868 return S_OK;
2871 static HRESULT WINAPI SimpleAudioVolume_GetMasterVolume(
2872 ISimpleAudioVolume *iface, float *level)
2874 AudioSessionWrapper *This = impl_from_ISimpleAudioVolume(iface);
2875 AudioSession *session = This->session;
2877 TRACE("(%p)->(%p)\n", session, level);
2879 if (!level)
2880 return NULL_PTR_ERR;
2882 *level = session->master_vol;
2884 return S_OK;
2887 static HRESULT WINAPI SimpleAudioVolume_SetMute(ISimpleAudioVolume *iface,
2888 BOOL mute, const GUID *context)
2890 AudioSessionWrapper *This = impl_from_ISimpleAudioVolume(iface);
2891 AudioSession *session = This->session;
2893 TRACE("(%p)->(%u, %p)\n", session, mute, context);
2895 if (context)
2896 FIXME("Notifications not supported yet\n");
2898 session->mute = mute;
2900 return S_OK;
2903 static HRESULT WINAPI SimpleAudioVolume_GetMute(ISimpleAudioVolume *iface,
2904 BOOL *mute)
2906 AudioSessionWrapper *This = impl_from_ISimpleAudioVolume(iface);
2907 AudioSession *session = This->session;
2909 TRACE("(%p)->(%p)\n", session, mute);
2911 if (!mute)
2912 return NULL_PTR_ERR;
2914 *mute = session->mute;
2916 return S_OK;
2919 static const ISimpleAudioVolumeVtbl SimpleAudioVolume_Vtbl =
2921 SimpleAudioVolume_QueryInterface,
2922 SimpleAudioVolume_AddRef,
2923 SimpleAudioVolume_Release,
2924 SimpleAudioVolume_SetMasterVolume,
2925 SimpleAudioVolume_GetMasterVolume,
2926 SimpleAudioVolume_SetMute,
2927 SimpleAudioVolume_GetMute
2930 static HRESULT WINAPI ChannelAudioVolume_QueryInterface(
2931 IChannelAudioVolume *iface, REFIID riid, void **ppv)
2933 TRACE("(%p)->(%s, %p)\n", iface, debugstr_guid(riid), ppv);
2935 if (!ppv)
2936 return E_POINTER;
2937 *ppv = NULL;
2939 if (IsEqualIID(riid, &IID_IUnknown) ||
2940 IsEqualIID(riid, &IID_IChannelAudioVolume))
2941 *ppv = iface;
2942 if (*ppv) {
2943 IUnknown_AddRef((IUnknown*)*ppv);
2944 return S_OK;
2947 WARN("Unknown interface %s\n", debugstr_guid(riid));
2948 return E_NOINTERFACE;
2951 static ULONG WINAPI ChannelAudioVolume_AddRef(IChannelAudioVolume *iface)
2953 AudioSessionWrapper *This = impl_from_IChannelAudioVolume(iface);
2954 return AudioSessionControl_AddRef(&This->IAudioSessionControl2_iface);
2957 static ULONG WINAPI ChannelAudioVolume_Release(IChannelAudioVolume *iface)
2959 AudioSessionWrapper *This = impl_from_IChannelAudioVolume(iface);
2960 return AudioSessionControl_Release(&This->IAudioSessionControl2_iface);
2963 static HRESULT WINAPI ChannelAudioVolume_GetChannelCount(
2964 IChannelAudioVolume *iface, UINT32 *out)
2966 AudioSessionWrapper *This = impl_from_IChannelAudioVolume(iface);
2967 AudioSession *session = This->session;
2969 TRACE("(%p)->(%p)\n", session, out);
2971 if (!out)
2972 return NULL_PTR_ERR;
2974 *out = session->channel_count;
2976 return S_OK;
2979 static HRESULT WINAPI ChannelAudioVolume_SetChannelVolume(
2980 IChannelAudioVolume *iface, UINT32 index, float level,
2981 const GUID *context)
2983 AudioSessionWrapper *This = impl_from_IChannelAudioVolume(iface);
2984 AudioSession *session = This->session;
2986 TRACE("(%p)->(%d, %f, %s)\n", session, index, level,
2987 wine_dbgstr_guid(context));
2989 if (level < 0.f || level > 1.f)
2990 return E_INVALIDARG;
2992 if (index >= session->channel_count)
2993 return E_INVALIDARG;
2995 if (context)
2996 FIXME("Notifications not supported yet\n");
2998 TRACE("Pulseaudio does not support session volume control\n");
3000 pthread_mutex_lock(&pulse_lock);
3001 session->channel_vols[index] = level;
3002 pthread_mutex_unlock(&pulse_lock);
3004 return S_OK;
3007 static HRESULT WINAPI ChannelAudioVolume_GetChannelVolume(
3008 IChannelAudioVolume *iface, UINT32 index, float *level)
3010 AudioSessionWrapper *This = impl_from_IChannelAudioVolume(iface);
3011 AudioSession *session = This->session;
3013 TRACE("(%p)->(%d, %p)\n", session, index, level);
3015 if (!level)
3016 return NULL_PTR_ERR;
3018 if (index >= session->channel_count)
3019 return E_INVALIDARG;
3021 *level = session->channel_vols[index];
3023 return S_OK;
3026 static HRESULT WINAPI ChannelAudioVolume_SetAllVolumes(
3027 IChannelAudioVolume *iface, UINT32 count, const float *levels,
3028 const GUID *context)
3030 AudioSessionWrapper *This = impl_from_IChannelAudioVolume(iface);
3031 AudioSession *session = This->session;
3032 int i;
3034 TRACE("(%p)->(%d, %p, %s)\n", session, count, levels,
3035 wine_dbgstr_guid(context));
3037 if (!levels)
3038 return NULL_PTR_ERR;
3040 if (count != session->channel_count)
3041 return E_INVALIDARG;
3043 if (context)
3044 FIXME("Notifications not supported yet\n");
3046 TRACE("Pulseaudio does not support session volume control\n");
3048 pthread_mutex_lock(&pulse_lock);
3049 for(i = 0; i < count; ++i)
3050 session->channel_vols[i] = levels[i];
3051 pthread_mutex_unlock(&pulse_lock);
3052 return S_OK;
3055 static HRESULT WINAPI ChannelAudioVolume_GetAllVolumes(
3056 IChannelAudioVolume *iface, UINT32 count, float *levels)
3058 AudioSessionWrapper *This = impl_from_IChannelAudioVolume(iface);
3059 AudioSession *session = This->session;
3060 int i;
3062 TRACE("(%p)->(%d, %p)\n", session, count, levels);
3064 if (!levels)
3065 return NULL_PTR_ERR;
3067 if (count != session->channel_count)
3068 return E_INVALIDARG;
3070 for(i = 0; i < count; ++i)
3071 levels[i] = session->channel_vols[i];
3073 return S_OK;
3076 static const IChannelAudioVolumeVtbl ChannelAudioVolume_Vtbl =
3078 ChannelAudioVolume_QueryInterface,
3079 ChannelAudioVolume_AddRef,
3080 ChannelAudioVolume_Release,
3081 ChannelAudioVolume_GetChannelCount,
3082 ChannelAudioVolume_SetChannelVolume,
3083 ChannelAudioVolume_GetChannelVolume,
3084 ChannelAudioVolume_SetAllVolumes,
3085 ChannelAudioVolume_GetAllVolumes
3088 HRESULT WINAPI AUDDRV_GetAudioSessionManager(IMMDevice *device,
3089 IAudioSessionManager2 **out)
3091 SessionMgr *This = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, sizeof(SessionMgr));
3092 *out = NULL;
3093 if (!This)
3094 return E_OUTOFMEMORY;
3095 This->IAudioSessionManager2_iface.lpVtbl = &AudioSessionManager2_Vtbl;
3096 This->device = device;
3097 This->ref = 1;
3098 *out = &This->IAudioSessionManager2_iface;
3099 return S_OK;