gphoto2.ds: Set supported groups.
[wine.git] / dlls / winepulse.drv / mmdevdrv.c
blob50b4188e5e5f77856358d7c2dea95c400c8fa997
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"
26 #include <poll.h>
27 #include <pthread.h>
29 #include <stdarg.h>
30 #include <unistd.h>
31 #include <math.h>
32 #include <stdio.h>
33 #include <errno.h>
35 #include <pulse/pulseaudio.h>
37 #include "windef.h"
38 #include "winbase.h"
39 #include "winnls.h"
40 #include "winreg.h"
41 #include "wine/debug.h"
42 #include "wine/unicode.h"
43 #include "wine/list.h"
45 #include "ole2.h"
46 #include "dshow.h"
47 #include "dsound.h"
48 #include "propsys.h"
50 #include "initguid.h"
51 #include "ks.h"
52 #include "ksmedia.h"
53 #include "propkey.h"
54 #include "mmdeviceapi.h"
55 #include "audioclient.h"
56 #include "endpointvolume.h"
57 #include "audiopolicy.h"
59 WINE_DEFAULT_DEBUG_CHANNEL(pulse);
61 #define NULL_PTR_ERR MAKE_HRESULT(SEVERITY_ERROR, FACILITY_WIN32, RPC_X_NULL_REF_POINTER)
63 /* From <dlls/mmdevapi/mmdevapi.h> */
64 enum DriverPriority {
65 Priority_Unavailable = 0,
66 Priority_Low,
67 Priority_Neutral,
68 Priority_Preferred
71 static const REFERENCE_TIME MinimumPeriod = 30000;
72 static const REFERENCE_TIME DefaultPeriod = 100000;
74 static pa_context *pulse_ctx;
75 static pa_mainloop *pulse_ml;
77 static HANDLE pulse_thread;
78 static pthread_mutex_t pulse_lock;
79 static pthread_cond_t pulse_cond = PTHREAD_COND_INITIALIZER;
80 static struct list g_sessions = LIST_INIT(g_sessions);
82 static UINT g_phys_speakers_mask = 0;
84 /* Mixer format + period times */
85 static WAVEFORMATEXTENSIBLE pulse_fmt[2];
86 static REFERENCE_TIME pulse_min_period[2], pulse_def_period[2];
88 static GUID pulse_render_guid =
89 { 0xfd47d9cc, 0x4218, 0x4135, { 0x9c, 0xe2, 0x0c, 0x19, 0x5c, 0x87, 0x40, 0x5b } };
90 static GUID pulse_capture_guid =
91 { 0x25da76d0, 0x033c, 0x4235, { 0x90, 0x02, 0x19, 0xf4, 0x88, 0x94, 0xac, 0x6f } };
93 BOOL WINAPI DllMain(HINSTANCE dll, DWORD reason, void *reserved)
95 if (reason == DLL_PROCESS_ATTACH) {
96 pthread_mutexattr_t attr;
98 DisableThreadLibraryCalls(dll);
100 pthread_mutexattr_init(&attr);
101 pthread_mutexattr_setprotocol(&attr, PTHREAD_PRIO_INHERIT);
103 if (pthread_mutex_init(&pulse_lock, &attr) != 0)
104 pthread_mutex_init(&pulse_lock, NULL);
105 } else if (reason == DLL_PROCESS_DETACH) {
106 if (pulse_thread)
107 SetThreadPriority(pulse_thread, 0);
108 if (pulse_ctx) {
109 pa_context_disconnect(pulse_ctx);
110 pa_context_unref(pulse_ctx);
112 if (pulse_ml)
113 pa_mainloop_quit(pulse_ml, 0);
114 if (pulse_thread) {
115 WaitForSingleObject(pulse_thread, INFINITE);
116 CloseHandle(pulse_thread);
119 return TRUE;
122 typedef struct ACImpl ACImpl;
124 typedef struct _AudioSession {
125 GUID guid;
126 struct list clients;
128 IMMDevice *device;
130 float master_vol;
131 UINT32 channel_count;
132 float *channel_vols;
133 BOOL mute;
135 struct list entry;
136 } AudioSession;
138 typedef struct _AudioSessionWrapper {
139 IAudioSessionControl2 IAudioSessionControl2_iface;
140 IChannelAudioVolume IChannelAudioVolume_iface;
141 ISimpleAudioVolume ISimpleAudioVolume_iface;
143 LONG ref;
145 ACImpl *client;
146 AudioSession *session;
147 } AudioSessionWrapper;
149 typedef struct _ACPacket {
150 struct list entry;
151 UINT64 qpcpos;
152 BYTE *data;
153 UINT32 discont;
154 } ACPacket;
156 struct ACImpl {
157 IAudioClient IAudioClient_iface;
158 IAudioRenderClient IAudioRenderClient_iface;
159 IAudioCaptureClient IAudioCaptureClient_iface;
160 IAudioClock IAudioClock_iface;
161 IAudioClock2 IAudioClock2_iface;
162 IAudioStreamVolume IAudioStreamVolume_iface;
163 IUnknown *marshal;
164 IMMDevice *parent;
165 struct list entry;
166 float vol[PA_CHANNELS_MAX];
168 LONG ref;
169 EDataFlow dataflow;
170 DWORD flags;
171 AUDCLNT_SHAREMODE share;
172 HANDLE event;
174 INT32 locked;
175 UINT32 bufsize_frames, bufsize_bytes, capture_period, pad, started, peek_ofs, wri_offs_bytes, lcl_offs_bytes;
176 UINT32 tmp_buffer_bytes, held_bytes, peek_len, peek_buffer_len;
177 BYTE *local_buffer, *tmp_buffer, *peek_buffer;
178 void *locked_ptr;
180 pa_stream *stream;
181 pa_sample_spec ss;
182 pa_channel_map map;
183 pa_buffer_attr attr;
185 INT64 clock_lastpos, clock_written;
187 AudioSession *session;
188 AudioSessionWrapper *session_wrapper;
189 struct list packet_free_head;
190 struct list packet_filled_head;
193 static const WCHAR defaultW[] = {'P','u','l','s','e','a','u','d','i','o',0};
195 static const IAudioClientVtbl AudioClient_Vtbl;
196 static const IAudioRenderClientVtbl AudioRenderClient_Vtbl;
197 static const IAudioCaptureClientVtbl AudioCaptureClient_Vtbl;
198 static const IAudioSessionControl2Vtbl AudioSessionControl2_Vtbl;
199 static const ISimpleAudioVolumeVtbl SimpleAudioVolume_Vtbl;
200 static const IChannelAudioVolumeVtbl ChannelAudioVolume_Vtbl;
201 static const IAudioClockVtbl AudioClock_Vtbl;
202 static const IAudioClock2Vtbl AudioClock2_Vtbl;
203 static const IAudioStreamVolumeVtbl AudioStreamVolume_Vtbl;
205 static AudioSessionWrapper *AudioSessionWrapper_Create(ACImpl *client);
207 static inline ACImpl *impl_from_IAudioClient(IAudioClient *iface)
209 return CONTAINING_RECORD(iface, ACImpl, IAudioClient_iface);
212 static inline ACImpl *impl_from_IAudioRenderClient(IAudioRenderClient *iface)
214 return CONTAINING_RECORD(iface, ACImpl, IAudioRenderClient_iface);
217 static inline ACImpl *impl_from_IAudioCaptureClient(IAudioCaptureClient *iface)
219 return CONTAINING_RECORD(iface, ACImpl, IAudioCaptureClient_iface);
222 static inline AudioSessionWrapper *impl_from_IAudioSessionControl2(IAudioSessionControl2 *iface)
224 return CONTAINING_RECORD(iface, AudioSessionWrapper, IAudioSessionControl2_iface);
227 static inline AudioSessionWrapper *impl_from_ISimpleAudioVolume(ISimpleAudioVolume *iface)
229 return CONTAINING_RECORD(iface, AudioSessionWrapper, ISimpleAudioVolume_iface);
232 static inline AudioSessionWrapper *impl_from_IChannelAudioVolume(IChannelAudioVolume *iface)
234 return CONTAINING_RECORD(iface, AudioSessionWrapper, IChannelAudioVolume_iface);
237 static inline ACImpl *impl_from_IAudioClock(IAudioClock *iface)
239 return CONTAINING_RECORD(iface, ACImpl, IAudioClock_iface);
242 static inline ACImpl *impl_from_IAudioClock2(IAudioClock2 *iface)
244 return CONTAINING_RECORD(iface, ACImpl, IAudioClock2_iface);
247 static inline ACImpl *impl_from_IAudioStreamVolume(IAudioStreamVolume *iface)
249 return CONTAINING_RECORD(iface, ACImpl, IAudioStreamVolume_iface);
252 /* Following pulseaudio design here, mainloop has the lock taken whenever
253 * it is handling something for pulse, and the lock is required whenever
254 * doing any pa_* call that can affect the state in any way
256 * pa_cond_wait is used when waiting on results, because the mainloop needs
257 * the same lock taken to affect the state
259 * This is basically the same as the pa_threaded_mainloop implementation,
260 * but that cannot be used because it uses pthread_create directly
262 * pa_threaded_mainloop_(un)lock -> pthread_mutex_(un)lock
263 * pa_threaded_mainloop_signal -> pthread_cond_signal
264 * pa_threaded_mainloop_wait -> pthread_cond_wait
267 static int pulse_poll_func(struct pollfd *ufds, unsigned long nfds, int timeout, void *userdata) {
268 int r;
269 pthread_mutex_unlock(&pulse_lock);
270 r = poll(ufds, nfds, timeout);
271 pthread_mutex_lock(&pulse_lock);
272 return r;
275 static DWORD CALLBACK pulse_mainloop_thread(void *tmp) {
276 int ret;
277 pulse_ml = pa_mainloop_new();
278 pa_mainloop_set_poll_func(pulse_ml, pulse_poll_func, NULL);
279 pthread_mutex_lock(&pulse_lock);
280 pthread_cond_signal(&pulse_cond);
281 pa_mainloop_run(pulse_ml, &ret);
282 pthread_mutex_unlock(&pulse_lock);
283 pa_mainloop_free(pulse_ml);
284 return ret;
287 static void pulse_contextcallback(pa_context *c, void *userdata)
289 switch (pa_context_get_state(c)) {
290 default:
291 FIXME("Unhandled state: %i\n", pa_context_get_state(c));
292 return;
294 case PA_CONTEXT_CONNECTING:
295 case PA_CONTEXT_UNCONNECTED:
296 case PA_CONTEXT_AUTHORIZING:
297 case PA_CONTEXT_SETTING_NAME:
298 case PA_CONTEXT_TERMINATED:
299 TRACE("State change to %i\n", pa_context_get_state(c));
300 return;
302 case PA_CONTEXT_READY:
303 TRACE("Ready\n");
304 break;
306 case PA_CONTEXT_FAILED:
307 WARN("Context failed: %s\n", pa_strerror(pa_context_errno(c)));
308 break;
310 pthread_cond_signal(&pulse_cond);
313 static void pulse_stream_state(pa_stream *s, void *user)
315 pa_stream_state_t state = pa_stream_get_state(s);
316 TRACE("Stream state changed to %i\n", state);
317 pthread_cond_signal(&pulse_cond);
320 static const enum pa_channel_position pulse_pos_from_wfx[] = {
321 PA_CHANNEL_POSITION_FRONT_LEFT,
322 PA_CHANNEL_POSITION_FRONT_RIGHT,
323 PA_CHANNEL_POSITION_FRONT_CENTER,
324 PA_CHANNEL_POSITION_LFE,
325 PA_CHANNEL_POSITION_REAR_LEFT,
326 PA_CHANNEL_POSITION_REAR_RIGHT,
327 PA_CHANNEL_POSITION_FRONT_LEFT_OF_CENTER,
328 PA_CHANNEL_POSITION_FRONT_RIGHT_OF_CENTER,
329 PA_CHANNEL_POSITION_REAR_CENTER,
330 PA_CHANNEL_POSITION_SIDE_LEFT,
331 PA_CHANNEL_POSITION_SIDE_RIGHT,
332 PA_CHANNEL_POSITION_TOP_CENTER,
333 PA_CHANNEL_POSITION_TOP_FRONT_LEFT,
334 PA_CHANNEL_POSITION_TOP_FRONT_CENTER,
335 PA_CHANNEL_POSITION_TOP_FRONT_RIGHT,
336 PA_CHANNEL_POSITION_TOP_REAR_LEFT,
337 PA_CHANNEL_POSITION_TOP_REAR_CENTER,
338 PA_CHANNEL_POSITION_TOP_REAR_RIGHT
341 static DWORD pulse_channel_map_to_channel_mask(const pa_channel_map *map) {
342 int i;
343 DWORD mask = 0;
345 for (i = 0; i < map->channels; ++i)
346 switch (map->map[i]) {
347 default: FIXME("Unhandled channel %s\n", pa_channel_position_to_string(map->map[i])); break;
348 case PA_CHANNEL_POSITION_FRONT_LEFT: mask |= SPEAKER_FRONT_LEFT; break;
349 case PA_CHANNEL_POSITION_MONO:
350 case PA_CHANNEL_POSITION_FRONT_CENTER: mask |= SPEAKER_FRONT_CENTER; break;
351 case PA_CHANNEL_POSITION_FRONT_RIGHT: mask |= SPEAKER_FRONT_RIGHT; break;
352 case PA_CHANNEL_POSITION_REAR_LEFT: mask |= SPEAKER_BACK_LEFT; break;
353 case PA_CHANNEL_POSITION_REAR_CENTER: mask |= SPEAKER_BACK_CENTER; break;
354 case PA_CHANNEL_POSITION_REAR_RIGHT: mask |= SPEAKER_BACK_RIGHT; break;
355 case PA_CHANNEL_POSITION_LFE: mask |= SPEAKER_LOW_FREQUENCY; break;
356 case PA_CHANNEL_POSITION_SIDE_LEFT: mask |= SPEAKER_SIDE_LEFT; break;
357 case PA_CHANNEL_POSITION_SIDE_RIGHT: mask |= SPEAKER_SIDE_RIGHT; break;
358 case PA_CHANNEL_POSITION_TOP_CENTER: mask |= SPEAKER_TOP_CENTER; break;
359 case PA_CHANNEL_POSITION_TOP_FRONT_LEFT: mask |= SPEAKER_TOP_FRONT_LEFT; break;
360 case PA_CHANNEL_POSITION_TOP_FRONT_CENTER: mask |= SPEAKER_TOP_FRONT_CENTER; break;
361 case PA_CHANNEL_POSITION_TOP_FRONT_RIGHT: mask |= SPEAKER_TOP_FRONT_RIGHT; break;
362 case PA_CHANNEL_POSITION_TOP_REAR_LEFT: mask |= SPEAKER_TOP_BACK_LEFT; break;
363 case PA_CHANNEL_POSITION_TOP_REAR_CENTER: mask |= SPEAKER_TOP_BACK_CENTER; break;
364 case PA_CHANNEL_POSITION_TOP_REAR_RIGHT: mask |= SPEAKER_TOP_BACK_RIGHT; break;
365 case PA_CHANNEL_POSITION_FRONT_LEFT_OF_CENTER: mask |= SPEAKER_FRONT_LEFT_OF_CENTER; break;
366 case PA_CHANNEL_POSITION_FRONT_RIGHT_OF_CENTER: mask |= SPEAKER_FRONT_RIGHT_OF_CENTER; break;
369 return mask;
372 static void pulse_probe_settings(int render, WAVEFORMATEXTENSIBLE *fmt) {
373 WAVEFORMATEX *wfx = &fmt->Format;
374 pa_stream *stream;
375 pa_channel_map map;
376 pa_sample_spec ss;
377 pa_buffer_attr attr;
378 int ret;
379 unsigned int length = 0;
381 pa_channel_map_init_auto(&map, 2, PA_CHANNEL_MAP_ALSA);
382 ss.rate = 48000;
383 ss.format = PA_SAMPLE_FLOAT32LE;
384 ss.channels = map.channels;
386 attr.maxlength = -1;
387 attr.tlength = -1;
388 attr.minreq = attr.fragsize = pa_frame_size(&ss);
389 attr.prebuf = 0;
391 stream = pa_stream_new(pulse_ctx, "format test stream", &ss, &map);
392 if (stream)
393 pa_stream_set_state_callback(stream, pulse_stream_state, NULL);
394 if (!stream)
395 ret = -1;
396 else if (render)
397 ret = pa_stream_connect_playback(stream, NULL, &attr,
398 PA_STREAM_START_CORKED|PA_STREAM_FIX_RATE|PA_STREAM_FIX_CHANNELS|PA_STREAM_EARLY_REQUESTS, NULL, NULL);
399 else
400 ret = pa_stream_connect_record(stream, NULL, &attr, PA_STREAM_START_CORKED|PA_STREAM_FIX_RATE|PA_STREAM_FIX_CHANNELS|PA_STREAM_EARLY_REQUESTS);
401 if (ret >= 0) {
402 while (pa_mainloop_iterate(pulse_ml, 1, &ret) >= 0 &&
403 pa_stream_get_state(stream) == PA_STREAM_CREATING)
405 if (pa_stream_get_state(stream) == PA_STREAM_READY) {
406 ss = *pa_stream_get_sample_spec(stream);
407 map = *pa_stream_get_channel_map(stream);
408 if (render)
409 length = pa_stream_get_buffer_attr(stream)->minreq;
410 else
411 length = pa_stream_get_buffer_attr(stream)->fragsize;
412 pa_stream_disconnect(stream);
413 while (pa_mainloop_iterate(pulse_ml, 1, &ret) >= 0 &&
414 pa_stream_get_state(stream) == PA_STREAM_READY)
419 if (stream)
420 pa_stream_unref(stream);
422 if (length)
423 pulse_def_period[!render] = pulse_min_period[!render] = pa_bytes_to_usec(10 * length, &ss);
425 if (pulse_min_period[!render] < MinimumPeriod)
426 pulse_min_period[!render] = MinimumPeriod;
428 if (pulse_def_period[!render] < DefaultPeriod)
429 pulse_def_period[!render] = DefaultPeriod;
431 wfx->wFormatTag = WAVE_FORMAT_EXTENSIBLE;
432 wfx->cbSize = sizeof(WAVEFORMATEXTENSIBLE) - sizeof(WAVEFORMATEX);
433 wfx->nChannels = ss.channels;
434 wfx->wBitsPerSample = 8 * pa_sample_size_of_format(ss.format);
435 wfx->nSamplesPerSec = ss.rate;
436 wfx->nBlockAlign = pa_frame_size(&ss);
437 wfx->nAvgBytesPerSec = wfx->nSamplesPerSec * wfx->nBlockAlign;
438 if (ss.format != PA_SAMPLE_S24_32LE)
439 fmt->Samples.wValidBitsPerSample = wfx->wBitsPerSample;
440 else
441 fmt->Samples.wValidBitsPerSample = 24;
442 if (ss.format == PA_SAMPLE_FLOAT32LE)
443 fmt->SubFormat = KSDATAFORMAT_SUBTYPE_IEEE_FLOAT;
444 else
445 fmt->SubFormat = KSDATAFORMAT_SUBTYPE_PCM;
447 fmt->dwChannelMask = pulse_channel_map_to_channel_mask(&map);
450 static HRESULT pulse_connect(void)
452 int len;
453 WCHAR path[MAX_PATH], *name;
454 char *str;
456 if (!pulse_thread)
458 if (!(pulse_thread = CreateThread(NULL, 0, pulse_mainloop_thread, NULL, 0, NULL)))
460 ERR("Failed to create mainloop thread.\n");
461 return E_FAIL;
463 SetThreadPriority(pulse_thread, THREAD_PRIORITY_TIME_CRITICAL);
464 pthread_cond_wait(&pulse_cond, &pulse_lock);
467 if (pulse_ctx && PA_CONTEXT_IS_GOOD(pa_context_get_state(pulse_ctx)))
468 return S_OK;
469 if (pulse_ctx)
470 pa_context_unref(pulse_ctx);
472 GetModuleFileNameW(NULL, path, sizeof(path)/sizeof(*path));
473 name = strrchrW(path, '\\');
474 if (!name)
475 name = path;
476 else
477 name++;
478 len = WideCharToMultiByte(CP_UNIXCP, 0, name, -1, NULL, 0, NULL, NULL);
479 str = pa_xmalloc(len);
480 WideCharToMultiByte(CP_UNIXCP, 0, name, -1, str, len, NULL, NULL);
481 TRACE("Name: %s\n", str);
482 pulse_ctx = pa_context_new(pa_mainloop_get_api(pulse_ml), str);
483 pa_xfree(str);
484 if (!pulse_ctx) {
485 ERR("Failed to create context\n");
486 return E_FAIL;
489 pa_context_set_state_callback(pulse_ctx, pulse_contextcallback, NULL);
491 TRACE("libpulse protocol version: %u. API Version %u\n", pa_context_get_protocol_version(pulse_ctx), PA_API_VERSION);
492 if (pa_context_connect(pulse_ctx, NULL, 0, NULL) < 0)
493 goto fail;
495 /* Wait for connection */
496 while (pthread_cond_wait(&pulse_cond, &pulse_lock)) {
497 pa_context_state_t state = pa_context_get_state(pulse_ctx);
499 if (state == PA_CONTEXT_FAILED || state == PA_CONTEXT_TERMINATED)
500 goto fail;
502 if (state == PA_CONTEXT_READY)
503 break;
506 TRACE("Connected to server %s with protocol version: %i.\n",
507 pa_context_get_server(pulse_ctx),
508 pa_context_get_server_protocol_version(pulse_ctx));
509 return S_OK;
511 fail:
512 pa_context_unref(pulse_ctx);
513 pulse_ctx = NULL;
514 return E_FAIL;
517 /* For default PulseAudio render device, OR together all of the
518 * PKEY_AudioEndpoint_PhysicalSpeakers values of the sinks. */
519 static void pulse_phys_speakers_cb(pa_context *c, const pa_sink_info *i, int eol, void *userdata)
521 if (i)
522 g_phys_speakers_mask |= pulse_channel_map_to_channel_mask(&i->channel_map);
525 /* some poorly-behaved applications call audio functions during DllMain, so we
526 * have to do as much as possible without creating a new thread. this function
527 * sets up a synchronous connection to verify the server is running and query
528 * static data. */
529 static HRESULT pulse_test_connect(void)
531 int len, ret;
532 WCHAR path[MAX_PATH], *name;
533 char *str;
534 pa_operation *o;
536 pulse_ml = pa_mainloop_new();
538 pa_mainloop_set_poll_func(pulse_ml, pulse_poll_func, NULL);
540 GetModuleFileNameW(NULL, path, sizeof(path)/sizeof(*path));
541 name = strrchrW(path, '\\');
542 if (!name)
543 name = path;
544 else
545 name++;
546 len = WideCharToMultiByte(CP_UNIXCP, 0, name, -1, NULL, 0, NULL, NULL);
547 str = pa_xmalloc(len);
548 WideCharToMultiByte(CP_UNIXCP, 0, name, -1, str, len, NULL, NULL);
549 TRACE("Name: %s\n", str);
550 pulse_ctx = pa_context_new(pa_mainloop_get_api(pulse_ml), str);
551 pa_xfree(str);
552 if (!pulse_ctx) {
553 ERR("Failed to create context\n");
554 pa_mainloop_free(pulse_ml);
555 pulse_ml = NULL;
556 return E_FAIL;
559 pa_context_set_state_callback(pulse_ctx, pulse_contextcallback, NULL);
561 TRACE("libpulse protocol version: %u. API Version %u\n", pa_context_get_protocol_version(pulse_ctx), PA_API_VERSION);
562 if (pa_context_connect(pulse_ctx, NULL, 0, NULL) < 0)
563 goto fail;
565 /* Wait for connection */
566 while (pa_mainloop_iterate(pulse_ml, 1, &ret) >= 0) {
567 pa_context_state_t state = pa_context_get_state(pulse_ctx);
569 if (state == PA_CONTEXT_FAILED || state == PA_CONTEXT_TERMINATED)
570 goto fail;
572 if (state == PA_CONTEXT_READY)
573 break;
576 if (pa_context_get_state(pulse_ctx) != PA_CONTEXT_READY)
577 goto fail;
579 TRACE("Test-connected to server %s with protocol version: %i.\n",
580 pa_context_get_server(pulse_ctx),
581 pa_context_get_server_protocol_version(pulse_ctx));
583 pulse_probe_settings(1, &pulse_fmt[0]);
584 pulse_probe_settings(0, &pulse_fmt[1]);
586 g_phys_speakers_mask = 0;
587 o = pa_context_get_sink_info_list(pulse_ctx, &pulse_phys_speakers_cb, NULL);
588 if (o) {
589 while (pa_mainloop_iterate(pulse_ml, 1, &ret) >= 0 &&
590 pa_operation_get_state(o) == PA_OPERATION_RUNNING)
592 pa_operation_unref(o);
595 pa_context_unref(pulse_ctx);
596 pulse_ctx = NULL;
597 pa_mainloop_free(pulse_ml);
598 pulse_ml = NULL;
600 return S_OK;
602 fail:
603 pa_context_unref(pulse_ctx);
604 pulse_ctx = NULL;
605 pa_mainloop_free(pulse_ml);
606 pulse_ml = NULL;
608 return E_FAIL;
611 static HRESULT pulse_stream_valid(ACImpl *This) {
612 if (!This->stream)
613 return AUDCLNT_E_NOT_INITIALIZED;
614 if (pa_stream_get_state(This->stream) != PA_STREAM_READY)
615 return AUDCLNT_E_DEVICE_INVALIDATED;
616 return S_OK;
619 static void silence_buffer(pa_sample_format_t format, BYTE *buffer, UINT32 bytes)
621 memset(buffer, format == PA_SAMPLE_U8 ? 0x80 : 0, bytes);
624 static void dump_attr(const pa_buffer_attr *attr) {
625 TRACE("maxlength: %u\n", attr->maxlength);
626 TRACE("minreq: %u\n", attr->minreq);
627 TRACE("fragsize: %u\n", attr->fragsize);
628 TRACE("tlength: %u\n", attr->tlength);
629 TRACE("prebuf: %u\n", attr->prebuf);
632 static void pulse_op_cb(pa_stream *s, int success, void *user) {
633 TRACE("Success: %i\n", success);
634 *(int*)user = success;
635 pthread_cond_signal(&pulse_cond);
638 static void pulse_attr_update(pa_stream *s, void *user) {
639 const pa_buffer_attr *attr = pa_stream_get_buffer_attr(s);
640 TRACE("New attributes or device moved:\n");
641 dump_attr(attr);
644 /* Here's the buffer setup:
646 * vvvvvvvv sent to HW already
647 * vvvvvvvv in Pulse buffer but rewindable
648 * [dddddddddddddddd] Pulse buffer
649 * [dddddddddddddddd--------] mmdevapi buffer
650 * ^^^^^^^^^^^^^^^^ pad
651 * ^ lcl_offs_bytes
652 * ^^^^^^^^^ held_bytes
653 * ^ wri_offs_bytes
655 * GetCurrentPadding is pad
657 * During pulse_wr_callback, we decrement pad, fill Pulse buffer, and move
658 * lcl_offs forward
660 * During Stop, we flush the Pulse buffer
662 static void pulse_wr_callback(pa_stream *s, size_t bytes, void *userdata)
664 ACImpl *This = userdata;
665 UINT32 oldpad = This->pad;
667 if(This->local_buffer){
668 UINT32 to_write;
669 BYTE *buf = This->local_buffer + This->lcl_offs_bytes;
671 if(This->pad > bytes){
672 This->clock_written += bytes;
673 This->pad -= bytes;
674 }else{
675 This->clock_written += This->pad;
676 This->pad = 0;
679 bytes = min(bytes, This->held_bytes);
681 if(This->lcl_offs_bytes + bytes > This->bufsize_bytes){
682 to_write = This->bufsize_bytes - This->lcl_offs_bytes;
683 TRACE("writing small chunk of %u bytes\n", to_write);
684 pa_stream_write(This->stream, buf, to_write, NULL, 0, PA_SEEK_RELATIVE);
685 This->held_bytes -= to_write;
686 to_write = bytes - to_write;
687 This->lcl_offs_bytes = 0;
688 buf = This->local_buffer;
689 }else
690 to_write = bytes;
692 TRACE("writing main chunk of %u bytes\n", to_write);
693 pa_stream_write(This->stream, buf, to_write, NULL, 0, PA_SEEK_RELATIVE);
694 This->lcl_offs_bytes += to_write;
695 This->lcl_offs_bytes %= This->bufsize_bytes;
696 This->held_bytes -= to_write;
697 }else{
698 if (bytes < This->bufsize_bytes)
699 This->pad = This->bufsize_bytes - bytes;
700 else
701 This->pad = 0;
703 if (oldpad == This->pad)
704 return;
706 assert(oldpad > This->pad);
708 This->clock_written += oldpad - This->pad;
709 TRACE("New pad: %zu (-%zu)\n", This->pad / pa_frame_size(&This->ss), (oldpad - This->pad) / pa_frame_size(&This->ss));
712 if (This->event)
713 SetEvent(This->event);
716 static void pulse_underflow_callback(pa_stream *s, void *userdata)
718 WARN("Underflow\n");
721 /* Latency is periodically updated even when nothing is played,
722 * because of PA_STREAM_AUTO_TIMING_UPDATE so use it as timer
724 * Perfect for passing all tests :)
726 static void pulse_latency_callback(pa_stream *s, void *userdata)
728 ACImpl *This = userdata;
729 if (!This->pad && This->event)
730 SetEvent(This->event);
733 static void pulse_started_callback(pa_stream *s, void *userdata)
735 TRACE("(Re)started playing\n");
738 static void pulse_rd_loop(ACImpl *This, size_t bytes)
740 while (bytes >= This->capture_period) {
741 ACPacket *p, *next;
742 LARGE_INTEGER stamp, freq;
743 BYTE *dst, *src;
744 size_t src_len, copy, rem = This->capture_period;
745 if (!(p = (ACPacket*)list_head(&This->packet_free_head))) {
746 p = (ACPacket*)list_head(&This->packet_filled_head);
747 if (!p->discont) {
748 next = (ACPacket*)p->entry.next;
749 next->discont = 1;
750 } else
751 p = (ACPacket*)list_tail(&This->packet_filled_head);
752 assert(This->pad == This->bufsize_bytes);
753 } else {
754 assert(This->pad < This->bufsize_bytes);
755 This->pad += This->capture_period;
756 assert(This->pad <= This->bufsize_bytes);
758 QueryPerformanceCounter(&stamp);
759 QueryPerformanceFrequency(&freq);
760 p->qpcpos = (stamp.QuadPart * (INT64)10000000) / freq.QuadPart;
761 p->discont = 0;
762 list_remove(&p->entry);
763 list_add_tail(&This->packet_filled_head, &p->entry);
765 dst = p->data;
766 while (rem) {
767 if (This->peek_len) {
768 copy = min(rem, This->peek_len - This->peek_ofs);
770 memcpy(dst, This->peek_buffer + This->peek_ofs, copy);
772 rem -= copy;
773 dst += copy;
774 This->peek_ofs += copy;
775 if(This->peek_len == This->peek_ofs)
776 This->peek_len = 0;
777 } else {
778 pa_stream_peek(This->stream, (const void**)&src, &src_len);
780 copy = min(rem, src_len);
782 memcpy(dst, src, rem);
784 dst += copy;
785 rem -= copy;
787 if (copy < src_len) {
788 if (src_len > This->peek_buffer_len) {
789 HeapFree(GetProcessHeap(), 0, This->peek_buffer);
790 This->peek_buffer = HeapAlloc(GetProcessHeap(), 0, src_len);
791 This->peek_buffer_len = src_len;
794 memcpy(This->peek_buffer, src + copy, src_len - copy);
795 This->peek_len = src_len - copy;
796 This->peek_ofs = 0;
799 pa_stream_drop(This->stream);
803 bytes -= This->capture_period;
807 static void pulse_rd_drop(ACImpl *This, size_t bytes)
809 while (bytes >= This->capture_period) {
810 size_t src_len, copy, rem = This->capture_period;
811 while (rem) {
812 const void *src;
813 pa_stream_peek(This->stream, &src, &src_len);
814 assert(src_len);
815 assert(This->peek_ofs < src_len);
816 src_len -= This->peek_ofs;
817 assert(src_len <= bytes);
819 copy = rem;
820 if (copy > src_len)
821 copy = src_len;
823 src_len -= copy;
824 rem -= copy;
826 if (!src_len) {
827 This->peek_ofs = 0;
828 pa_stream_drop(This->stream);
829 } else
830 This->peek_ofs += copy;
831 bytes -= copy;
836 static void pulse_rd_callback(pa_stream *s, size_t bytes, void *userdata)
838 ACImpl *This = userdata;
840 TRACE("Readable total: %zu, fragsize: %u\n", bytes, pa_stream_get_buffer_attr(s)->fragsize);
841 assert(bytes >= This->peek_ofs);
842 bytes -= This->peek_ofs;
843 if (bytes < This->capture_period)
844 return;
846 if (This->started)
847 pulse_rd_loop(This, bytes);
848 else
849 pulse_rd_drop(This, bytes);
851 if (This->event)
852 SetEvent(This->event);
855 static HRESULT pulse_stream_connect(ACImpl *This, UINT32 period_bytes) {
856 int ret;
857 char buffer[64];
858 static LONG number;
859 pa_buffer_attr attr;
860 if (This->stream) {
861 pa_stream_disconnect(This->stream);
862 while (pa_stream_get_state(This->stream) == PA_STREAM_READY)
863 pthread_cond_wait(&pulse_cond, &pulse_lock);
864 pa_stream_unref(This->stream);
866 ret = InterlockedIncrement(&number);
867 sprintf(buffer, "audio stream #%i", ret);
868 This->stream = pa_stream_new(pulse_ctx, buffer, &This->ss, &This->map);
870 if (!This->stream) {
871 WARN("pa_stream_new returned error %i\n", pa_context_errno(pulse_ctx));
872 return AUDCLNT_E_ENDPOINT_CREATE_FAILED;
875 pa_stream_set_state_callback(This->stream, pulse_stream_state, This);
876 pa_stream_set_buffer_attr_callback(This->stream, pulse_attr_update, This);
877 pa_stream_set_moved_callback(This->stream, pulse_attr_update, This);
879 /* PulseAudio will fill in correct values */
880 attr.minreq = attr.fragsize = period_bytes;
881 attr.maxlength = attr.tlength = This->bufsize_bytes;
882 attr.prebuf = pa_frame_size(&This->ss);
883 dump_attr(&attr);
884 if (This->dataflow == eRender)
885 ret = pa_stream_connect_playback(This->stream, NULL, &attr,
886 PA_STREAM_START_CORKED|PA_STREAM_START_UNMUTED|PA_STREAM_AUTO_TIMING_UPDATE|PA_STREAM_INTERPOLATE_TIMING|PA_STREAM_EARLY_REQUESTS, NULL, NULL);
887 else
888 ret = pa_stream_connect_record(This->stream, NULL, &attr,
889 PA_STREAM_START_CORKED|PA_STREAM_START_UNMUTED|PA_STREAM_AUTO_TIMING_UPDATE|PA_STREAM_INTERPOLATE_TIMING|PA_STREAM_EARLY_REQUESTS);
890 if (ret < 0) {
891 WARN("Returns %i\n", ret);
892 return AUDCLNT_E_ENDPOINT_CREATE_FAILED;
894 while (pa_stream_get_state(This->stream) == PA_STREAM_CREATING)
895 pthread_cond_wait(&pulse_cond, &pulse_lock);
896 if (pa_stream_get_state(This->stream) != PA_STREAM_READY)
897 return AUDCLNT_E_ENDPOINT_CREATE_FAILED;
899 if (This->dataflow == eRender) {
900 pa_stream_set_write_callback(This->stream, pulse_wr_callback, This);
901 pa_stream_set_underflow_callback(This->stream, pulse_underflow_callback, This);
902 pa_stream_set_started_callback(This->stream, pulse_started_callback, This);
903 } else
904 pa_stream_set_read_callback(This->stream, pulse_rd_callback, This);
905 return S_OK;
908 HRESULT WINAPI AUDDRV_GetEndpointIDs(EDataFlow flow, const WCHAR ***ids, GUID **keys,
909 UINT *num, UINT *def_index)
911 WCHAR *id;
913 TRACE("%d %p %p %p\n", flow, ids, num, def_index);
915 *num = 1;
916 *def_index = 0;
918 *ids = HeapAlloc(GetProcessHeap(), 0, sizeof(**ids));
919 *keys = NULL;
920 if (!*ids)
921 return E_OUTOFMEMORY;
923 (*ids)[0] = id = HeapAlloc(GetProcessHeap(), 0, sizeof(defaultW));
924 *keys = HeapAlloc(GetProcessHeap(), 0, sizeof(**keys));
925 if (!*keys || !id) {
926 HeapFree(GetProcessHeap(), 0, id);
927 HeapFree(GetProcessHeap(), 0, *keys);
928 HeapFree(GetProcessHeap(), 0, *ids);
929 *ids = NULL;
930 *keys = NULL;
931 return E_OUTOFMEMORY;
933 memcpy(id, defaultW, sizeof(defaultW));
935 if (flow == eRender)
936 (*keys)[0] = pulse_render_guid;
937 else
938 (*keys)[0] = pulse_capture_guid;
940 return S_OK;
943 int WINAPI AUDDRV_GetPriority(void)
945 HRESULT hr;
946 pthread_mutex_lock(&pulse_lock);
947 hr = pulse_test_connect();
948 pthread_mutex_unlock(&pulse_lock);
949 return SUCCEEDED(hr) ? Priority_Preferred : Priority_Unavailable;
952 HRESULT WINAPI AUDDRV_GetAudioEndpoint(GUID *guid, IMMDevice *dev, IAudioClient **out)
954 ACImpl *This;
955 int i;
956 EDataFlow dataflow;
957 HRESULT hr;
959 TRACE("%s %p %p\n", debugstr_guid(guid), dev, out);
960 if (IsEqualGUID(guid, &pulse_render_guid))
961 dataflow = eRender;
962 else if (IsEqualGUID(guid, &pulse_capture_guid))
963 dataflow = eCapture;
964 else
965 return E_UNEXPECTED;
967 *out = NULL;
969 This = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, sizeof(*This));
970 if (!This)
971 return E_OUTOFMEMORY;
973 This->IAudioClient_iface.lpVtbl = &AudioClient_Vtbl;
974 This->IAudioRenderClient_iface.lpVtbl = &AudioRenderClient_Vtbl;
975 This->IAudioCaptureClient_iface.lpVtbl = &AudioCaptureClient_Vtbl;
976 This->IAudioClock_iface.lpVtbl = &AudioClock_Vtbl;
977 This->IAudioClock2_iface.lpVtbl = &AudioClock2_Vtbl;
978 This->IAudioStreamVolume_iface.lpVtbl = &AudioStreamVolume_Vtbl;
979 This->dataflow = dataflow;
980 This->parent = dev;
981 for (i = 0; i < PA_CHANNELS_MAX; ++i)
982 This->vol[i] = 1.f;
984 hr = CoCreateFreeThreadedMarshaler((IUnknown*)&This->IAudioClient_iface, &This->marshal);
985 if (hr) {
986 HeapFree(GetProcessHeap(), 0, This);
987 return hr;
989 IMMDevice_AddRef(This->parent);
991 *out = &This->IAudioClient_iface;
992 IAudioClient_AddRef(&This->IAudioClient_iface);
994 return S_OK;
997 static HRESULT WINAPI AudioClient_QueryInterface(IAudioClient *iface,
998 REFIID riid, void **ppv)
1000 ACImpl *This = impl_from_IAudioClient(iface);
1002 TRACE("(%p)->(%s, %p)\n", iface, debugstr_guid(riid), ppv);
1004 if (!ppv)
1005 return E_POINTER;
1007 *ppv = NULL;
1008 if (IsEqualIID(riid, &IID_IUnknown) || IsEqualIID(riid, &IID_IAudioClient))
1009 *ppv = iface;
1010 if (*ppv) {
1011 IUnknown_AddRef((IUnknown*)*ppv);
1012 return S_OK;
1015 if (IsEqualIID(riid, &IID_IMarshal))
1016 return IUnknown_QueryInterface(This->marshal, riid, ppv);
1018 WARN("Unknown interface %s\n", debugstr_guid(riid));
1019 return E_NOINTERFACE;
1022 static ULONG WINAPI AudioClient_AddRef(IAudioClient *iface)
1024 ACImpl *This = impl_from_IAudioClient(iface);
1025 ULONG ref;
1026 ref = InterlockedIncrement(&This->ref);
1027 TRACE("(%p) Refcount now %u\n", This, ref);
1028 return ref;
1031 static ULONG WINAPI AudioClient_Release(IAudioClient *iface)
1033 ACImpl *This = impl_from_IAudioClient(iface);
1034 ULONG ref;
1035 ref = InterlockedDecrement(&This->ref);
1036 TRACE("(%p) Refcount now %u\n", This, ref);
1037 if (!ref) {
1038 if (This->stream) {
1039 pthread_mutex_lock(&pulse_lock);
1040 if (PA_STREAM_IS_GOOD(pa_stream_get_state(This->stream))) {
1041 pa_stream_disconnect(This->stream);
1042 while (PA_STREAM_IS_GOOD(pa_stream_get_state(This->stream)))
1043 pthread_cond_wait(&pulse_cond, &pulse_lock);
1045 pa_stream_unref(This->stream);
1046 This->stream = NULL;
1047 list_remove(&This->entry);
1048 pthread_mutex_unlock(&pulse_lock);
1050 IUnknown_Release(This->marshal);
1051 IMMDevice_Release(This->parent);
1052 HeapFree(GetProcessHeap(), 0, This->tmp_buffer);
1053 HeapFree(GetProcessHeap(), 0, This->peek_buffer);
1054 HeapFree(GetProcessHeap(), 0, This->local_buffer);
1055 HeapFree(GetProcessHeap(), 0, This);
1057 return ref;
1060 static void dump_fmt(const WAVEFORMATEX *fmt)
1062 TRACE("wFormatTag: 0x%x (", fmt->wFormatTag);
1063 switch(fmt->wFormatTag) {
1064 case WAVE_FORMAT_PCM:
1065 TRACE("WAVE_FORMAT_PCM");
1066 break;
1067 case WAVE_FORMAT_IEEE_FLOAT:
1068 TRACE("WAVE_FORMAT_IEEE_FLOAT");
1069 break;
1070 case WAVE_FORMAT_EXTENSIBLE:
1071 TRACE("WAVE_FORMAT_EXTENSIBLE");
1072 break;
1073 default:
1074 TRACE("Unknown");
1075 break;
1077 TRACE(")\n");
1079 TRACE("nChannels: %u\n", fmt->nChannels);
1080 TRACE("nSamplesPerSec: %u\n", fmt->nSamplesPerSec);
1081 TRACE("nAvgBytesPerSec: %u\n", fmt->nAvgBytesPerSec);
1082 TRACE("nBlockAlign: %u\n", fmt->nBlockAlign);
1083 TRACE("wBitsPerSample: %u\n", fmt->wBitsPerSample);
1084 TRACE("cbSize: %u\n", fmt->cbSize);
1086 if (fmt->wFormatTag == WAVE_FORMAT_EXTENSIBLE) {
1087 WAVEFORMATEXTENSIBLE *fmtex = (void*)fmt;
1088 TRACE("dwChannelMask: %08x\n", fmtex->dwChannelMask);
1089 TRACE("Samples: %04x\n", fmtex->Samples.wReserved);
1090 TRACE("SubFormat: %s\n", wine_dbgstr_guid(&fmtex->SubFormat));
1094 static WAVEFORMATEX *clone_format(const WAVEFORMATEX *fmt)
1096 WAVEFORMATEX *ret;
1097 size_t size;
1099 if (fmt->wFormatTag == WAVE_FORMAT_EXTENSIBLE)
1100 size = sizeof(WAVEFORMATEXTENSIBLE);
1101 else
1102 size = sizeof(WAVEFORMATEX);
1104 ret = CoTaskMemAlloc(size);
1105 if (!ret)
1106 return NULL;
1108 memcpy(ret, fmt, size);
1110 ret->cbSize = size - sizeof(WAVEFORMATEX);
1112 return ret;
1115 static DWORD get_channel_mask(unsigned int channels)
1117 switch(channels) {
1118 case 0:
1119 return 0;
1120 case 1:
1121 return KSAUDIO_SPEAKER_MONO;
1122 case 2:
1123 return KSAUDIO_SPEAKER_STEREO;
1124 case 3:
1125 return KSAUDIO_SPEAKER_STEREO | SPEAKER_LOW_FREQUENCY;
1126 case 4:
1127 return KSAUDIO_SPEAKER_QUAD; /* not _SURROUND */
1128 case 5:
1129 return KSAUDIO_SPEAKER_QUAD | SPEAKER_LOW_FREQUENCY;
1130 case 6:
1131 return KSAUDIO_SPEAKER_5POINT1; /* not 5POINT1_SURROUND */
1132 case 7:
1133 return KSAUDIO_SPEAKER_5POINT1 | SPEAKER_BACK_CENTER;
1134 case 8:
1135 return KSAUDIO_SPEAKER_7POINT1_SURROUND; /* Vista deprecates 7POINT1 */
1137 FIXME("Unknown speaker configuration: %u\n", channels);
1138 return 0;
1141 static void session_init_vols(AudioSession *session, UINT channels)
1143 if (session->channel_count < channels) {
1144 UINT i;
1146 if (session->channel_vols)
1147 session->channel_vols = HeapReAlloc(GetProcessHeap(), 0,
1148 session->channel_vols, sizeof(float) * channels);
1149 else
1150 session->channel_vols = HeapAlloc(GetProcessHeap(), 0,
1151 sizeof(float) * channels);
1152 if (!session->channel_vols)
1153 return;
1155 for(i = session->channel_count; i < channels; ++i)
1156 session->channel_vols[i] = 1.f;
1158 session->channel_count = channels;
1162 static AudioSession *create_session(const GUID *guid, IMMDevice *device,
1163 UINT num_channels)
1165 AudioSession *ret;
1167 ret = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, sizeof(AudioSession));
1168 if (!ret)
1169 return NULL;
1171 memcpy(&ret->guid, guid, sizeof(GUID));
1173 ret->device = device;
1175 list_init(&ret->clients);
1177 list_add_head(&g_sessions, &ret->entry);
1179 session_init_vols(ret, num_channels);
1181 ret->master_vol = 1.f;
1183 return ret;
1186 /* if channels == 0, then this will return or create a session with
1187 * matching dataflow and GUID. otherwise, channels must also match */
1188 static HRESULT get_audio_session(const GUID *sessionguid,
1189 IMMDevice *device, UINT channels, AudioSession **out)
1191 AudioSession *session;
1193 if (!sessionguid || IsEqualGUID(sessionguid, &GUID_NULL)) {
1194 *out = create_session(&GUID_NULL, device, channels);
1195 if (!*out)
1196 return E_OUTOFMEMORY;
1198 return S_OK;
1201 *out = NULL;
1202 LIST_FOR_EACH_ENTRY(session, &g_sessions, AudioSession, entry) {
1203 if (session->device == device &&
1204 IsEqualGUID(sessionguid, &session->guid)) {
1205 session_init_vols(session, channels);
1206 *out = session;
1207 break;
1211 if (!*out) {
1212 *out = create_session(sessionguid, device, channels);
1213 if (!*out)
1214 return E_OUTOFMEMORY;
1217 return S_OK;
1220 static HRESULT pulse_spec_from_waveformat(ACImpl *This, const WAVEFORMATEX *fmt)
1222 pa_channel_map_init(&This->map);
1223 This->ss.rate = fmt->nSamplesPerSec;
1224 This->ss.format = PA_SAMPLE_INVALID;
1226 switch(fmt->wFormatTag) {
1227 case WAVE_FORMAT_IEEE_FLOAT:
1228 if (!fmt->nChannels || fmt->nChannels > 2 || fmt->wBitsPerSample != 32)
1229 break;
1230 This->ss.format = PA_SAMPLE_FLOAT32LE;
1231 pa_channel_map_init_auto(&This->map, fmt->nChannels, PA_CHANNEL_MAP_ALSA);
1232 break;
1233 case WAVE_FORMAT_PCM:
1234 if (!fmt->nChannels || fmt->nChannels > 2)
1235 break;
1236 if (fmt->wBitsPerSample == 8)
1237 This->ss.format = PA_SAMPLE_U8;
1238 else if (fmt->wBitsPerSample == 16)
1239 This->ss.format = PA_SAMPLE_S16LE;
1240 else
1241 return AUDCLNT_E_UNSUPPORTED_FORMAT;
1242 pa_channel_map_init_auto(&This->map, fmt->nChannels, PA_CHANNEL_MAP_ALSA);
1243 break;
1244 case WAVE_FORMAT_EXTENSIBLE: {
1245 WAVEFORMATEXTENSIBLE *wfe = (WAVEFORMATEXTENSIBLE*)fmt;
1246 DWORD mask = wfe->dwChannelMask;
1247 DWORD i = 0, j;
1248 if (fmt->cbSize != (sizeof(*wfe) - sizeof(*fmt)) && fmt->cbSize != sizeof(*wfe))
1249 break;
1250 if (IsEqualGUID(&wfe->SubFormat, &KSDATAFORMAT_SUBTYPE_IEEE_FLOAT) &&
1251 (!wfe->Samples.wValidBitsPerSample || wfe->Samples.wValidBitsPerSample == 32) &&
1252 fmt->wBitsPerSample == 32)
1253 This->ss.format = PA_SAMPLE_FLOAT32LE;
1254 else if (IsEqualGUID(&wfe->SubFormat, &KSDATAFORMAT_SUBTYPE_PCM)) {
1255 DWORD valid = wfe->Samples.wValidBitsPerSample;
1256 if (!valid)
1257 valid = fmt->wBitsPerSample;
1258 if (!valid || valid > fmt->wBitsPerSample)
1259 break;
1260 switch (fmt->wBitsPerSample) {
1261 case 8:
1262 if (valid == 8)
1263 This->ss.format = PA_SAMPLE_U8;
1264 break;
1265 case 16:
1266 if (valid == 16)
1267 This->ss.format = PA_SAMPLE_S16LE;
1268 break;
1269 case 24:
1270 if (valid == 24)
1271 This->ss.format = PA_SAMPLE_S24LE;
1272 break;
1273 case 32:
1274 if (valid == 24)
1275 This->ss.format = PA_SAMPLE_S24_32LE;
1276 else if (valid == 32)
1277 This->ss.format = PA_SAMPLE_S32LE;
1278 break;
1279 default:
1280 return AUDCLNT_E_UNSUPPORTED_FORMAT;
1283 This->map.channels = fmt->nChannels;
1284 if (!mask || (mask & (SPEAKER_ALL|SPEAKER_RESERVED)))
1285 mask = get_channel_mask(fmt->nChannels);
1286 for (j = 0; j < sizeof(pulse_pos_from_wfx)/sizeof(*pulse_pos_from_wfx) && i < fmt->nChannels; ++j) {
1287 if (mask & (1 << j))
1288 This->map.map[i++] = pulse_pos_from_wfx[j];
1291 /* Special case for mono since pulse appears to map it differently */
1292 if (mask == SPEAKER_FRONT_CENTER)
1293 This->map.map[0] = PA_CHANNEL_POSITION_MONO;
1295 if (i < fmt->nChannels || (mask & SPEAKER_RESERVED)) {
1296 This->map.channels = 0;
1297 ERR("Invalid channel mask: %i/%i and %x(%x)\n", i, fmt->nChannels, mask, wfe->dwChannelMask);
1298 break;
1300 break;
1302 case WAVE_FORMAT_ALAW:
1303 case WAVE_FORMAT_MULAW:
1304 if (fmt->wBitsPerSample != 8) {
1305 FIXME("Unsupported bpp %u for LAW\n", fmt->wBitsPerSample);
1306 return AUDCLNT_E_UNSUPPORTED_FORMAT;
1308 if (fmt->nChannels != 1 && fmt->nChannels != 2) {
1309 FIXME("Unsupported channels %u for LAW\n", fmt->nChannels);
1310 return AUDCLNT_E_UNSUPPORTED_FORMAT;
1312 This->ss.format = fmt->wFormatTag == WAVE_FORMAT_MULAW ? PA_SAMPLE_ULAW : PA_SAMPLE_ALAW;
1313 pa_channel_map_init_auto(&This->map, fmt->nChannels, PA_CHANNEL_MAP_ALSA);
1314 break;
1315 default:
1316 WARN("Unhandled tag %x\n", fmt->wFormatTag);
1317 return AUDCLNT_E_UNSUPPORTED_FORMAT;
1319 This->ss.channels = This->map.channels;
1320 if (!pa_channel_map_valid(&This->map) || This->ss.format == PA_SAMPLE_INVALID) {
1321 ERR("Invalid format! Channel spec valid: %i, format: %i\n", pa_channel_map_valid(&This->map), This->ss.format);
1322 return AUDCLNT_E_UNSUPPORTED_FORMAT;
1324 return S_OK;
1327 static HRESULT WINAPI AudioClient_Initialize(IAudioClient *iface,
1328 AUDCLNT_SHAREMODE mode, DWORD flags, REFERENCE_TIME duration,
1329 REFERENCE_TIME period, const WAVEFORMATEX *fmt,
1330 const GUID *sessionguid)
1332 ACImpl *This = impl_from_IAudioClient(iface);
1333 HRESULT hr = S_OK;
1334 UINT period_bytes;
1336 TRACE("(%p)->(%x, %x, %s, %s, %p, %s)\n", This, mode, flags,
1337 wine_dbgstr_longlong(duration), wine_dbgstr_longlong(period), fmt, debugstr_guid(sessionguid));
1339 if (!fmt)
1340 return E_POINTER;
1342 if (mode != AUDCLNT_SHAREMODE_SHARED && mode != AUDCLNT_SHAREMODE_EXCLUSIVE)
1343 return AUDCLNT_E_NOT_INITIALIZED;
1344 if (mode == AUDCLNT_SHAREMODE_EXCLUSIVE)
1345 return AUDCLNT_E_EXCLUSIVE_MODE_NOT_ALLOWED;
1347 if (flags & ~(AUDCLNT_STREAMFLAGS_CROSSPROCESS |
1348 AUDCLNT_STREAMFLAGS_LOOPBACK |
1349 AUDCLNT_STREAMFLAGS_EVENTCALLBACK |
1350 AUDCLNT_STREAMFLAGS_NOPERSIST |
1351 AUDCLNT_STREAMFLAGS_RATEADJUST |
1352 AUDCLNT_SESSIONFLAGS_EXPIREWHENUNOWNED |
1353 AUDCLNT_SESSIONFLAGS_DISPLAY_HIDE |
1354 AUDCLNT_SESSIONFLAGS_DISPLAY_HIDEWHENEXPIRED)) {
1355 TRACE("Unknown flags: %08x\n", flags);
1356 return E_INVALIDARG;
1359 pthread_mutex_lock(&pulse_lock);
1361 hr = pulse_connect();
1362 if (FAILED(hr)) {
1363 pthread_mutex_unlock(&pulse_lock);
1364 return hr;
1367 if (This->stream) {
1368 pthread_mutex_unlock(&pulse_lock);
1369 return AUDCLNT_E_ALREADY_INITIALIZED;
1372 hr = pulse_spec_from_waveformat(This, fmt);
1373 TRACE("Obtaining format returns %08x\n", hr);
1374 dump_fmt(fmt);
1376 if (FAILED(hr))
1377 goto exit;
1379 if (mode == AUDCLNT_SHAREMODE_SHARED) {
1380 REFERENCE_TIME def = pulse_def_period[This->dataflow == eCapture];
1381 REFERENCE_TIME min = pulse_min_period[This->dataflow == eCapture];
1383 /* Switch to low latency mode if below 2 default periods,
1384 * which is 20 ms by default, this will increase the amount
1385 * of interrupts but allows very low latency. In dsound I
1386 * managed to get a total latency of ~8ms, which is well below
1387 * default
1389 if (duration < 2 * def)
1390 period = min;
1391 else
1392 period = def;
1393 if (duration < 2 * period)
1394 duration = 2 * period;
1396 /* Uh oh, really low latency requested.. */
1397 if (duration <= 2 * period)
1398 period /= 2;
1400 period_bytes = pa_frame_size(&This->ss) * MulDiv(period, This->ss.rate, 10000000);
1402 if (duration < 20000000)
1403 This->bufsize_frames = ceil((duration / 10000000.) * fmt->nSamplesPerSec);
1404 else
1405 This->bufsize_frames = 2 * fmt->nSamplesPerSec;
1406 This->bufsize_bytes = This->bufsize_frames * pa_frame_size(&This->ss);
1408 This->share = mode;
1409 This->flags = flags;
1410 hr = pulse_stream_connect(This, period_bytes);
1411 if (SUCCEEDED(hr)) {
1412 UINT32 unalign;
1413 const pa_buffer_attr *attr = pa_stream_get_buffer_attr(This->stream);
1414 This->attr = *attr;
1415 /* Update frames according to new size */
1416 dump_attr(attr);
1417 if (This->dataflow == eRender) {
1418 if (attr->tlength < This->bufsize_bytes) {
1419 TRACE("PulseAudio buffer too small (%u < %u), using tmp buffer\n", attr->tlength, This->bufsize_bytes);
1421 This->local_buffer = HeapAlloc(GetProcessHeap(), 0, This->bufsize_bytes);
1422 if(!This->local_buffer)
1423 hr = E_OUTOFMEMORY;
1425 } else {
1426 UINT32 i, capture_packets;
1428 This->capture_period = period_bytes = attr->fragsize;
1429 if ((unalign = This->bufsize_bytes % period_bytes))
1430 This->bufsize_bytes += period_bytes - unalign;
1431 This->bufsize_frames = This->bufsize_bytes / pa_frame_size(&This->ss);
1433 capture_packets = This->bufsize_bytes / This->capture_period;
1435 This->local_buffer = HeapAlloc(GetProcessHeap(), 0, This->bufsize_bytes + capture_packets * sizeof(ACPacket));
1436 if (!This->local_buffer)
1437 hr = E_OUTOFMEMORY;
1438 else {
1439 ACPacket *cur_packet = (ACPacket*)((char*)This->local_buffer + This->bufsize_bytes);
1440 BYTE *data = This->local_buffer;
1441 silence_buffer(This->ss.format, This->local_buffer, This->bufsize_bytes);
1442 list_init(&This->packet_free_head);
1443 list_init(&This->packet_filled_head);
1444 for (i = 0; i < capture_packets; ++i, ++cur_packet) {
1445 list_add_tail(&This->packet_free_head, &cur_packet->entry);
1446 cur_packet->data = data;
1447 data += This->capture_period;
1449 assert(!This->capture_period || This->bufsize_bytes == This->capture_period * capture_packets);
1450 assert(!capture_packets || data - This->bufsize_bytes == This->local_buffer);
1454 if (SUCCEEDED(hr))
1455 hr = get_audio_session(sessionguid, This->parent, fmt->nChannels, &This->session);
1456 if (SUCCEEDED(hr))
1457 list_add_tail(&This->session->clients, &This->entry);
1459 exit:
1460 if (FAILED(hr)) {
1461 HeapFree(GetProcessHeap(), 0, This->local_buffer);
1462 This->local_buffer = NULL;
1463 if (This->stream) {
1464 pa_stream_disconnect(This->stream);
1465 pa_stream_unref(This->stream);
1466 This->stream = NULL;
1469 pthread_mutex_unlock(&pulse_lock);
1470 return hr;
1473 static HRESULT WINAPI AudioClient_GetBufferSize(IAudioClient *iface,
1474 UINT32 *out)
1476 ACImpl *This = impl_from_IAudioClient(iface);
1477 HRESULT hr;
1479 TRACE("(%p)->(%p)\n", This, out);
1481 if (!out)
1482 return E_POINTER;
1484 pthread_mutex_lock(&pulse_lock);
1485 hr = pulse_stream_valid(This);
1486 if (SUCCEEDED(hr))
1487 *out = This->bufsize_frames;
1488 pthread_mutex_unlock(&pulse_lock);
1490 return hr;
1493 static HRESULT WINAPI AudioClient_GetStreamLatency(IAudioClient *iface,
1494 REFERENCE_TIME *latency)
1496 ACImpl *This = impl_from_IAudioClient(iface);
1497 const pa_buffer_attr *attr;
1498 REFERENCE_TIME lat;
1499 HRESULT hr;
1501 TRACE("(%p)->(%p)\n", This, latency);
1503 if (!latency)
1504 return E_POINTER;
1506 pthread_mutex_lock(&pulse_lock);
1507 hr = pulse_stream_valid(This);
1508 if (FAILED(hr)) {
1509 pthread_mutex_unlock(&pulse_lock);
1510 return hr;
1512 attr = pa_stream_get_buffer_attr(This->stream);
1513 if (This->dataflow == eRender){
1514 lat = attr->minreq / pa_frame_size(&This->ss);
1515 lat += pulse_def_period[0];
1516 }else
1517 lat = attr->fragsize / pa_frame_size(&This->ss);
1518 *latency = 10000000;
1519 *latency *= lat;
1520 *latency /= This->ss.rate;
1521 pthread_mutex_unlock(&pulse_lock);
1522 TRACE("Latency: %u ms\n", (DWORD)(*latency / 10000));
1523 return S_OK;
1526 static void ACImpl_GetRenderPad(ACImpl *This, UINT32 *out)
1528 *out = This->pad / pa_frame_size(&This->ss);
1531 static void ACImpl_GetCapturePad(ACImpl *This, UINT32 *out)
1533 ACPacket *packet = This->locked_ptr;
1534 if (!packet && !list_empty(&This->packet_filled_head)) {
1535 packet = (ACPacket*)list_head(&This->packet_filled_head);
1536 This->locked_ptr = packet;
1537 list_remove(&packet->entry);
1539 if (out)
1540 *out = This->pad / pa_frame_size(&This->ss);
1543 static HRESULT WINAPI AudioClient_GetCurrentPadding(IAudioClient *iface,
1544 UINT32 *out)
1546 ACImpl *This = impl_from_IAudioClient(iface);
1547 HRESULT hr;
1549 TRACE("(%p)->(%p)\n", This, out);
1551 if (!out)
1552 return E_POINTER;
1554 pthread_mutex_lock(&pulse_lock);
1555 hr = pulse_stream_valid(This);
1556 if (FAILED(hr)) {
1557 pthread_mutex_unlock(&pulse_lock);
1558 return hr;
1561 if (This->dataflow == eRender)
1562 ACImpl_GetRenderPad(This, out);
1563 else
1564 ACImpl_GetCapturePad(This, out);
1565 pthread_mutex_unlock(&pulse_lock);
1567 TRACE("%p Pad: %u ms (%u)\n", This, MulDiv(*out, 1000, This->ss.rate), *out);
1568 return S_OK;
1571 static HRESULT WINAPI AudioClient_IsFormatSupported(IAudioClient *iface,
1572 AUDCLNT_SHAREMODE mode, const WAVEFORMATEX *fmt,
1573 WAVEFORMATEX **out)
1575 ACImpl *This = impl_from_IAudioClient(iface);
1576 HRESULT hr = S_OK;
1577 WAVEFORMATEX *closest = NULL;
1578 BOOL exclusive;
1580 TRACE("(%p)->(%x, %p, %p)\n", This, mode, fmt, out);
1582 if (!fmt)
1583 return E_POINTER;
1585 if (out)
1586 *out = NULL;
1588 if (mode == AUDCLNT_SHAREMODE_EXCLUSIVE) {
1589 exclusive = 1;
1590 out = NULL;
1591 } else if (mode == AUDCLNT_SHAREMODE_SHARED) {
1592 exclusive = 0;
1593 if (!out)
1594 return E_POINTER;
1595 } else
1596 return E_INVALIDARG;
1598 if (fmt->nChannels == 0)
1599 return AUDCLNT_E_UNSUPPORTED_FORMAT;
1601 closest = clone_format(fmt);
1602 if (!closest)
1603 return E_OUTOFMEMORY;
1605 dump_fmt(fmt);
1607 switch (fmt->wFormatTag) {
1608 case WAVE_FORMAT_EXTENSIBLE: {
1609 WAVEFORMATEXTENSIBLE *ext = (WAVEFORMATEXTENSIBLE*)closest;
1611 if ((fmt->cbSize != sizeof(WAVEFORMATEXTENSIBLE) - sizeof(WAVEFORMATEX) &&
1612 fmt->cbSize != sizeof(WAVEFORMATEXTENSIBLE)) ||
1613 fmt->nBlockAlign != fmt->wBitsPerSample / 8 * fmt->nChannels ||
1614 ext->Samples.wValidBitsPerSample > fmt->wBitsPerSample ||
1615 fmt->nAvgBytesPerSec != fmt->nBlockAlign * fmt->nSamplesPerSec) {
1616 hr = E_INVALIDARG;
1617 break;
1620 if (exclusive) {
1621 UINT32 mask = 0, i, channels = 0;
1623 if (!(ext->dwChannelMask & (SPEAKER_ALL | SPEAKER_RESERVED))) {
1624 for (i = 1; !(i & SPEAKER_RESERVED); i <<= 1) {
1625 if (i & ext->dwChannelMask) {
1626 mask |= i;
1627 channels++;
1631 if (channels != fmt->nChannels || (ext->dwChannelMask & ~mask)) {
1632 hr = AUDCLNT_E_UNSUPPORTED_FORMAT;
1633 break;
1635 } else {
1636 hr = AUDCLNT_E_UNSUPPORTED_FORMAT;
1637 break;
1641 if (IsEqualGUID(&ext->SubFormat, &KSDATAFORMAT_SUBTYPE_IEEE_FLOAT)) {
1642 if (fmt->wBitsPerSample != 32) {
1643 hr = E_INVALIDARG;
1644 break;
1647 if (ext->Samples.wValidBitsPerSample != fmt->wBitsPerSample) {
1648 hr = S_FALSE;
1649 ext->Samples.wValidBitsPerSample = fmt->wBitsPerSample;
1651 } else if (IsEqualGUID(&ext->SubFormat, &KSDATAFORMAT_SUBTYPE_PCM)) {
1652 if (!fmt->wBitsPerSample || fmt->wBitsPerSample > 32 || fmt->wBitsPerSample % 8) {
1653 hr = E_INVALIDARG;
1654 break;
1657 if (ext->Samples.wValidBitsPerSample != fmt->wBitsPerSample &&
1658 !(fmt->wBitsPerSample == 32 &&
1659 ext->Samples.wValidBitsPerSample == 24)) {
1660 hr = S_FALSE;
1661 ext->Samples.wValidBitsPerSample = fmt->wBitsPerSample;
1662 break;
1664 } else {
1665 hr = AUDCLNT_E_UNSUPPORTED_FORMAT;
1666 break;
1669 break;
1672 case WAVE_FORMAT_ALAW:
1673 case WAVE_FORMAT_MULAW:
1674 if (fmt->wBitsPerSample != 8) {
1675 hr = E_INVALIDARG;
1676 break;
1678 /* Fall-through */
1679 case WAVE_FORMAT_IEEE_FLOAT:
1680 if (fmt->wFormatTag == WAVE_FORMAT_IEEE_FLOAT && fmt->wBitsPerSample != 32) {
1681 hr = E_INVALIDARG;
1682 break;
1684 /* Fall-through */
1685 case WAVE_FORMAT_PCM:
1686 if (fmt->wFormatTag == WAVE_FORMAT_PCM &&
1687 (!fmt->wBitsPerSample || fmt->wBitsPerSample > 32 || fmt->wBitsPerSample % 8)) {
1688 hr = E_INVALIDARG;
1689 break;
1692 if (fmt->nChannels > 2) {
1693 hr = AUDCLNT_E_UNSUPPORTED_FORMAT;
1694 break;
1697 * fmt->cbSize, fmt->nBlockAlign and fmt->nAvgBytesPerSec seem to be
1698 * ignored, invalid values are happily accepted.
1700 break;
1701 default:
1702 hr = AUDCLNT_E_UNSUPPORTED_FORMAT;
1703 break;
1706 if (exclusive && hr != S_OK) {
1707 hr = AUDCLNT_E_UNSUPPORTED_FORMAT;
1708 CoTaskMemFree(closest);
1709 } else if (hr != S_FALSE)
1710 CoTaskMemFree(closest);
1711 else
1712 *out = closest;
1714 /* Winepulse does not currently support exclusive mode, if you know of an
1715 * application that uses it, I will correct this..
1717 if (hr == S_OK && exclusive)
1718 return This->dataflow == eCapture ? AUDCLNT_E_UNSUPPORTED_FORMAT : AUDCLNT_E_EXCLUSIVE_MODE_NOT_ALLOWED;
1720 TRACE("returning: %08x %p\n", hr, out ? *out : NULL);
1721 return hr;
1724 static HRESULT WINAPI AudioClient_GetMixFormat(IAudioClient *iface,
1725 WAVEFORMATEX **pwfx)
1727 ACImpl *This = impl_from_IAudioClient(iface);
1728 WAVEFORMATEXTENSIBLE *fmt = &pulse_fmt[This->dataflow == eCapture];
1730 TRACE("(%p)->(%p)\n", This, pwfx);
1732 if (!pwfx)
1733 return E_POINTER;
1735 *pwfx = clone_format(&fmt->Format);
1736 if (!*pwfx)
1737 return E_OUTOFMEMORY;
1738 dump_fmt(*pwfx);
1739 return S_OK;
1742 static HRESULT WINAPI AudioClient_GetDevicePeriod(IAudioClient *iface,
1743 REFERENCE_TIME *defperiod, REFERENCE_TIME *minperiod)
1745 ACImpl *This = impl_from_IAudioClient(iface);
1747 TRACE("(%p)->(%p, %p)\n", This, defperiod, minperiod);
1749 if (!defperiod && !minperiod)
1750 return E_POINTER;
1752 if (defperiod)
1753 *defperiod = pulse_def_period[This->dataflow == eCapture];
1754 if (minperiod)
1755 *minperiod = pulse_min_period[This->dataflow == eCapture];
1757 return S_OK;
1760 static HRESULT WINAPI AudioClient_Start(IAudioClient *iface)
1762 ACImpl *This = impl_from_IAudioClient(iface);
1763 HRESULT hr = S_OK;
1764 int success;
1765 pa_operation *o;
1767 TRACE("(%p)\n", This);
1769 pthread_mutex_lock(&pulse_lock);
1770 hr = pulse_stream_valid(This);
1771 if (FAILED(hr)) {
1772 pthread_mutex_unlock(&pulse_lock);
1773 return hr;
1776 if ((This->flags & AUDCLNT_STREAMFLAGS_EVENTCALLBACK) && !This->event) {
1777 pthread_mutex_unlock(&pulse_lock);
1778 return AUDCLNT_E_EVENTHANDLE_NOT_SET;
1781 if (This->started) {
1782 pthread_mutex_unlock(&pulse_lock);
1783 return AUDCLNT_E_NOT_STOPPED;
1786 if (pa_stream_is_corked(This->stream)) {
1787 o = pa_stream_cork(This->stream, 0, pulse_op_cb, &success);
1788 if (o) {
1789 while(pa_operation_get_state(o) == PA_OPERATION_RUNNING)
1790 pthread_cond_wait(&pulse_cond, &pulse_lock);
1791 pa_operation_unref(o);
1792 } else
1793 success = 0;
1794 if (!success)
1795 hr = E_FAIL;
1798 if (SUCCEEDED(hr)) {
1799 This->started = TRUE;
1800 if (This->dataflow == eRender && This->event)
1801 pa_stream_set_latency_update_callback(This->stream, pulse_latency_callback, This);
1803 pthread_mutex_unlock(&pulse_lock);
1804 return hr;
1807 static HRESULT WINAPI AudioClient_Stop(IAudioClient *iface)
1809 ACImpl *This = impl_from_IAudioClient(iface);
1810 HRESULT hr = S_OK;
1811 pa_operation *o;
1812 int success;
1814 TRACE("(%p)\n", This);
1816 pthread_mutex_lock(&pulse_lock);
1817 hr = pulse_stream_valid(This);
1818 if (FAILED(hr)) {
1819 pthread_mutex_unlock(&pulse_lock);
1820 return hr;
1823 if (!This->started) {
1824 pthread_mutex_unlock(&pulse_lock);
1825 return S_FALSE;
1828 if (This->dataflow == eRender) {
1829 o = pa_stream_cork(This->stream, 1, pulse_op_cb, &success);
1830 if (o) {
1831 while(pa_operation_get_state(o) == PA_OPERATION_RUNNING)
1832 pthread_cond_wait(&pulse_cond, &pulse_lock);
1833 pa_operation_unref(o);
1834 } else
1835 success = 0;
1836 if (!success)
1837 hr = E_FAIL;
1839 if (SUCCEEDED(hr)) {
1840 This->started = FALSE;
1842 pthread_mutex_unlock(&pulse_lock);
1843 return hr;
1846 static HRESULT WINAPI AudioClient_Reset(IAudioClient *iface)
1848 ACImpl *This = impl_from_IAudioClient(iface);
1849 HRESULT hr = S_OK;
1851 TRACE("(%p)\n", This);
1853 pthread_mutex_lock(&pulse_lock);
1854 hr = pulse_stream_valid(This);
1855 if (FAILED(hr)) {
1856 pthread_mutex_unlock(&pulse_lock);
1857 return hr;
1860 if (This->started) {
1861 pthread_mutex_unlock(&pulse_lock);
1862 return AUDCLNT_E_NOT_STOPPED;
1865 if (This->locked) {
1866 pthread_mutex_unlock(&pulse_lock);
1867 return AUDCLNT_E_BUFFER_OPERATION_PENDING;
1870 if (This->dataflow == eRender) {
1871 /* If there is still data in the render buffer it needs to be removed from the server */
1872 int success = 0;
1873 if (This->pad) {
1874 pa_operation *o = pa_stream_flush(This->stream, pulse_op_cb, &success);
1875 if (o) {
1876 while(pa_operation_get_state(o) == PA_OPERATION_RUNNING)
1877 pthread_cond_wait(&pulse_cond, &pulse_lock);
1878 pa_operation_unref(o);
1881 if (success || !This->pad){
1882 This->clock_lastpos = This->clock_written = This->pad = 0;
1883 This->wri_offs_bytes = This->lcl_offs_bytes = This->held_bytes = 0;
1885 } else {
1886 ACPacket *p;
1887 This->clock_written += This->pad;
1888 This->pad = 0;
1890 if ((p = This->locked_ptr)) {
1891 This->locked_ptr = NULL;
1892 list_add_tail(&This->packet_free_head, &p->entry);
1894 list_move_tail(&This->packet_free_head, &This->packet_filled_head);
1896 pthread_mutex_unlock(&pulse_lock);
1898 return hr;
1901 static HRESULT WINAPI AudioClient_SetEventHandle(IAudioClient *iface,
1902 HANDLE event)
1904 ACImpl *This = impl_from_IAudioClient(iface);
1905 HRESULT hr;
1907 TRACE("(%p)->(%p)\n", This, event);
1909 if (!event)
1910 return E_INVALIDARG;
1912 pthread_mutex_lock(&pulse_lock);
1913 hr = pulse_stream_valid(This);
1914 if (FAILED(hr)) {
1915 pthread_mutex_unlock(&pulse_lock);
1916 return hr;
1919 if (!(This->flags & AUDCLNT_STREAMFLAGS_EVENTCALLBACK))
1920 hr = AUDCLNT_E_EVENTHANDLE_NOT_EXPECTED;
1921 else if (This->event)
1922 hr = HRESULT_FROM_WIN32(ERROR_INVALID_NAME);
1923 else
1924 This->event = event;
1925 pthread_mutex_unlock(&pulse_lock);
1926 return hr;
1929 static HRESULT WINAPI AudioClient_GetService(IAudioClient *iface, REFIID riid,
1930 void **ppv)
1932 ACImpl *This = impl_from_IAudioClient(iface);
1933 HRESULT hr;
1935 TRACE("(%p)->(%s, %p)\n", This, debugstr_guid(riid), ppv);
1937 if (!ppv)
1938 return E_POINTER;
1939 *ppv = NULL;
1941 pthread_mutex_lock(&pulse_lock);
1942 hr = pulse_stream_valid(This);
1943 pthread_mutex_unlock(&pulse_lock);
1944 if (FAILED(hr))
1945 return hr;
1947 if (IsEqualIID(riid, &IID_IAudioRenderClient)) {
1948 if (This->dataflow != eRender)
1949 return AUDCLNT_E_WRONG_ENDPOINT_TYPE;
1950 *ppv = &This->IAudioRenderClient_iface;
1951 } else if (IsEqualIID(riid, &IID_IAudioCaptureClient)) {
1952 if (This->dataflow != eCapture)
1953 return AUDCLNT_E_WRONG_ENDPOINT_TYPE;
1954 *ppv = &This->IAudioCaptureClient_iface;
1955 } else if (IsEqualIID(riid, &IID_IAudioClock)) {
1956 *ppv = &This->IAudioClock_iface;
1957 } else if (IsEqualIID(riid, &IID_IAudioStreamVolume)) {
1958 *ppv = &This->IAudioStreamVolume_iface;
1959 } else if (IsEqualIID(riid, &IID_IAudioSessionControl) ||
1960 IsEqualIID(riid, &IID_IChannelAudioVolume) ||
1961 IsEqualIID(riid, &IID_ISimpleAudioVolume)) {
1962 if (!This->session_wrapper) {
1963 This->session_wrapper = AudioSessionWrapper_Create(This);
1964 if (!This->session_wrapper)
1965 return E_OUTOFMEMORY;
1967 if (IsEqualIID(riid, &IID_IAudioSessionControl))
1968 *ppv = &This->session_wrapper->IAudioSessionControl2_iface;
1969 else if (IsEqualIID(riid, &IID_IChannelAudioVolume))
1970 *ppv = &This->session_wrapper->IChannelAudioVolume_iface;
1971 else if (IsEqualIID(riid, &IID_ISimpleAudioVolume))
1972 *ppv = &This->session_wrapper->ISimpleAudioVolume_iface;
1975 if (*ppv) {
1976 IUnknown_AddRef((IUnknown*)*ppv);
1977 return S_OK;
1980 FIXME("stub %s\n", debugstr_guid(riid));
1981 return E_NOINTERFACE;
1984 static const IAudioClientVtbl AudioClient_Vtbl =
1986 AudioClient_QueryInterface,
1987 AudioClient_AddRef,
1988 AudioClient_Release,
1989 AudioClient_Initialize,
1990 AudioClient_GetBufferSize,
1991 AudioClient_GetStreamLatency,
1992 AudioClient_GetCurrentPadding,
1993 AudioClient_IsFormatSupported,
1994 AudioClient_GetMixFormat,
1995 AudioClient_GetDevicePeriod,
1996 AudioClient_Start,
1997 AudioClient_Stop,
1998 AudioClient_Reset,
1999 AudioClient_SetEventHandle,
2000 AudioClient_GetService
2003 static HRESULT WINAPI AudioRenderClient_QueryInterface(
2004 IAudioRenderClient *iface, REFIID riid, void **ppv)
2006 ACImpl *This = impl_from_IAudioRenderClient(iface);
2007 TRACE("(%p)->(%s, %p)\n", iface, debugstr_guid(riid), ppv);
2009 if (!ppv)
2010 return E_POINTER;
2011 *ppv = NULL;
2013 if (IsEqualIID(riid, &IID_IUnknown) ||
2014 IsEqualIID(riid, &IID_IAudioRenderClient))
2015 *ppv = iface;
2016 if (*ppv) {
2017 IUnknown_AddRef((IUnknown*)*ppv);
2018 return S_OK;
2021 if (IsEqualIID(riid, &IID_IMarshal))
2022 return IUnknown_QueryInterface(This->marshal, riid, ppv);
2024 WARN("Unknown interface %s\n", debugstr_guid(riid));
2025 return E_NOINTERFACE;
2028 static ULONG WINAPI AudioRenderClient_AddRef(IAudioRenderClient *iface)
2030 ACImpl *This = impl_from_IAudioRenderClient(iface);
2031 return AudioClient_AddRef(&This->IAudioClient_iface);
2034 static ULONG WINAPI AudioRenderClient_Release(IAudioRenderClient *iface)
2036 ACImpl *This = impl_from_IAudioRenderClient(iface);
2037 return AudioClient_Release(&This->IAudioClient_iface);
2040 static void alloc_tmp_buffer(ACImpl *This, UINT32 bytes)
2042 if(This->tmp_buffer_bytes >= bytes)
2043 return;
2045 HeapFree(GetProcessHeap(), 0, This->tmp_buffer);
2046 This->tmp_buffer = HeapAlloc(GetProcessHeap(), 0, bytes);
2047 This->tmp_buffer_bytes = bytes;
2050 static HRESULT WINAPI AudioRenderClient_GetBuffer(IAudioRenderClient *iface,
2051 UINT32 frames, BYTE **data)
2053 ACImpl *This = impl_from_IAudioRenderClient(iface);
2054 size_t avail, req, bytes = frames * pa_frame_size(&This->ss);
2055 UINT32 pad;
2056 HRESULT hr = S_OK;
2057 int ret = -1;
2059 TRACE("(%p)->(%u, %p)\n", This, frames, data);
2061 if (!data)
2062 return E_POINTER;
2063 *data = NULL;
2065 pthread_mutex_lock(&pulse_lock);
2066 hr = pulse_stream_valid(This);
2067 if (FAILED(hr) || This->locked) {
2068 pthread_mutex_unlock(&pulse_lock);
2069 return FAILED(hr) ? hr : AUDCLNT_E_OUT_OF_ORDER;
2071 if (!frames) {
2072 pthread_mutex_unlock(&pulse_lock);
2073 return S_OK;
2076 ACImpl_GetRenderPad(This, &pad);
2077 avail = This->bufsize_frames - pad;
2078 if (avail < frames || bytes > This->bufsize_bytes) {
2079 pthread_mutex_unlock(&pulse_lock);
2080 WARN("Wanted to write %u, but only %zu available\n", frames, avail);
2081 return AUDCLNT_E_BUFFER_TOO_LARGE;
2084 if(This->local_buffer){
2085 if(This->wri_offs_bytes + bytes > This->bufsize_bytes){
2086 alloc_tmp_buffer(This, bytes);
2087 *data = This->tmp_buffer;
2088 This->locked = -frames;
2089 }else{
2090 *data = This->local_buffer + This->wri_offs_bytes;
2091 This->locked = frames;
2093 }else{
2094 req = bytes;
2095 ret = pa_stream_begin_write(This->stream, &This->locked_ptr, &req);
2096 if (ret < 0 || req < bytes) {
2097 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);
2098 if (ret >= 0)
2099 pa_stream_cancel_write(This->stream);
2100 alloc_tmp_buffer(This, bytes);
2101 *data = This->tmp_buffer;
2102 This->locked_ptr = NULL;
2103 } else
2104 *data = This->locked_ptr;
2106 This->locked = frames;
2109 silence_buffer(This->ss.format, *data, bytes);
2111 pthread_mutex_unlock(&pulse_lock);
2113 return hr;
2116 static void pulse_wrap_buffer(ACImpl *This, BYTE *buffer, UINT32 written_bytes)
2118 UINT32 chunk_bytes = This->bufsize_bytes - This->wri_offs_bytes;
2120 if(written_bytes <= chunk_bytes){
2121 memcpy(This->local_buffer + This->wri_offs_bytes, buffer, written_bytes);
2122 }else{
2123 memcpy(This->local_buffer + This->wri_offs_bytes, buffer, chunk_bytes);
2124 memcpy(This->local_buffer, buffer + chunk_bytes,
2125 written_bytes - chunk_bytes);
2129 static void pulse_free_noop(void *buf)
2133 static HRESULT WINAPI AudioRenderClient_ReleaseBuffer(
2134 IAudioRenderClient *iface, UINT32 written_frames, DWORD flags)
2136 ACImpl *This = impl_from_IAudioRenderClient(iface);
2137 UINT32 written_bytes = written_frames * pa_frame_size(&This->ss);
2139 TRACE("(%p)->(%u, %x)\n", This, written_frames, flags);
2141 pthread_mutex_lock(&pulse_lock);
2142 if (!This->locked || !written_frames) {
2143 if (This->locked_ptr)
2144 pa_stream_cancel_write(This->stream);
2145 This->locked = 0;
2146 This->locked_ptr = NULL;
2147 pthread_mutex_unlock(&pulse_lock);
2148 return written_frames ? AUDCLNT_E_OUT_OF_ORDER : S_OK;
2151 if (This->locked < written_frames) {
2152 pthread_mutex_unlock(&pulse_lock);
2153 return AUDCLNT_E_INVALID_SIZE;
2156 if(This->local_buffer){
2157 BYTE *buffer;
2159 if(This->locked >= 0)
2160 buffer = This->local_buffer + This->wri_offs_bytes;
2161 else
2162 buffer = This->tmp_buffer;
2164 if(flags & AUDCLNT_BUFFERFLAGS_SILENT)
2165 silence_buffer(This->ss.format, buffer, written_bytes);
2167 if(This->locked < 0)
2168 pulse_wrap_buffer(This, buffer, written_bytes);
2170 This->wri_offs_bytes += written_bytes;
2171 This->wri_offs_bytes %= This->bufsize_bytes;
2173 This->pad += written_bytes;
2174 This->held_bytes += written_bytes;
2176 if(This->held_bytes == This->pad){
2177 int e;
2178 UINT32 to_write = min(This->attr.tlength, written_bytes);
2180 /* nothing in PA, so send data immediately */
2182 TRACE("pre-writing %u bytes\n", to_write);
2184 e = pa_stream_write(This->stream, buffer, to_write, NULL, 0, PA_SEEK_RELATIVE);
2185 if(e)
2186 ERR("pa_stream_write failed: 0x%x\n", e);
2188 This->lcl_offs_bytes += to_write;
2189 This->lcl_offs_bytes %= This->bufsize_bytes;
2190 This->held_bytes -= to_write;
2193 }else{
2194 if (This->locked_ptr) {
2195 if (flags & AUDCLNT_BUFFERFLAGS_SILENT)
2196 silence_buffer(This->ss.format, This->locked_ptr, written_bytes);
2197 pa_stream_write(This->stream, This->locked_ptr, written_bytes, NULL, 0, PA_SEEK_RELATIVE);
2198 } else {
2199 if (flags & AUDCLNT_BUFFERFLAGS_SILENT)
2200 silence_buffer(This->ss.format, This->tmp_buffer, written_bytes);
2201 pa_stream_write(This->stream, This->tmp_buffer, written_bytes, pulse_free_noop, 0, PA_SEEK_RELATIVE);
2203 This->pad += written_bytes;
2206 if (!pa_stream_is_corked(This->stream)) {
2207 int success;
2208 pa_operation *o;
2209 o = pa_stream_trigger(This->stream, pulse_op_cb, &success);
2210 if (o) {
2211 while(pa_operation_get_state(o) == PA_OPERATION_RUNNING)
2212 pthread_cond_wait(&pulse_cond, &pulse_lock);
2213 pa_operation_unref(o);
2217 This->locked = 0;
2218 This->locked_ptr = NULL;
2219 TRACE("Released %u, pad %zu\n", written_frames, This->pad / pa_frame_size(&This->ss));
2220 assert(This->pad <= This->bufsize_bytes);
2222 pthread_mutex_unlock(&pulse_lock);
2223 return S_OK;
2226 static const IAudioRenderClientVtbl AudioRenderClient_Vtbl = {
2227 AudioRenderClient_QueryInterface,
2228 AudioRenderClient_AddRef,
2229 AudioRenderClient_Release,
2230 AudioRenderClient_GetBuffer,
2231 AudioRenderClient_ReleaseBuffer
2234 static HRESULT WINAPI AudioCaptureClient_QueryInterface(
2235 IAudioCaptureClient *iface, REFIID riid, void **ppv)
2237 ACImpl *This = impl_from_IAudioCaptureClient(iface);
2238 TRACE("(%p)->(%s, %p)\n", iface, debugstr_guid(riid), ppv);
2240 if (!ppv)
2241 return E_POINTER;
2242 *ppv = NULL;
2244 if (IsEqualIID(riid, &IID_IUnknown) ||
2245 IsEqualIID(riid, &IID_IAudioCaptureClient))
2246 *ppv = iface;
2247 if (*ppv) {
2248 IUnknown_AddRef((IUnknown*)*ppv);
2249 return S_OK;
2252 if (IsEqualIID(riid, &IID_IMarshal))
2253 return IUnknown_QueryInterface(This->marshal, riid, ppv);
2255 WARN("Unknown interface %s\n", debugstr_guid(riid));
2256 return E_NOINTERFACE;
2259 static ULONG WINAPI AudioCaptureClient_AddRef(IAudioCaptureClient *iface)
2261 ACImpl *This = impl_from_IAudioCaptureClient(iface);
2262 return IAudioClient_AddRef(&This->IAudioClient_iface);
2265 static ULONG WINAPI AudioCaptureClient_Release(IAudioCaptureClient *iface)
2267 ACImpl *This = impl_from_IAudioCaptureClient(iface);
2268 return IAudioClient_Release(&This->IAudioClient_iface);
2271 static HRESULT WINAPI AudioCaptureClient_GetBuffer(IAudioCaptureClient *iface,
2272 BYTE **data, UINT32 *frames, DWORD *flags, UINT64 *devpos,
2273 UINT64 *qpcpos)
2275 ACImpl *This = impl_from_IAudioCaptureClient(iface);
2276 HRESULT hr;
2277 ACPacket *packet;
2279 TRACE("(%p)->(%p, %p, %p, %p, %p)\n", This, data, frames, flags,
2280 devpos, qpcpos);
2282 if (!data || !frames || !flags)
2283 return E_POINTER;
2285 pthread_mutex_lock(&pulse_lock);
2286 hr = pulse_stream_valid(This);
2287 if (FAILED(hr) || This->locked) {
2288 pthread_mutex_unlock(&pulse_lock);
2289 return FAILED(hr) ? hr : AUDCLNT_E_OUT_OF_ORDER;
2292 ACImpl_GetCapturePad(This, NULL);
2293 if ((packet = This->locked_ptr)) {
2294 *frames = This->capture_period / pa_frame_size(&This->ss);
2295 *flags = 0;
2296 if (packet->discont)
2297 *flags |= AUDCLNT_BUFFERFLAGS_DATA_DISCONTINUITY;
2298 if (devpos) {
2299 if (packet->discont)
2300 *devpos = (This->clock_written + This->capture_period) / pa_frame_size(&This->ss);
2301 else
2302 *devpos = This->clock_written / pa_frame_size(&This->ss);
2304 if (qpcpos)
2305 *qpcpos = packet->qpcpos;
2306 *data = packet->data;
2308 else
2309 *frames = 0;
2310 This->locked = *frames;
2311 pthread_mutex_unlock(&pulse_lock);
2312 return *frames ? S_OK : AUDCLNT_S_BUFFER_EMPTY;
2315 static HRESULT WINAPI AudioCaptureClient_ReleaseBuffer(
2316 IAudioCaptureClient *iface, UINT32 done)
2318 ACImpl *This = impl_from_IAudioCaptureClient(iface);
2320 TRACE("(%p)->(%u)\n", This, done);
2322 pthread_mutex_lock(&pulse_lock);
2323 if (!This->locked && done) {
2324 pthread_mutex_unlock(&pulse_lock);
2325 return AUDCLNT_E_OUT_OF_ORDER;
2327 if (done && This->locked != done) {
2328 pthread_mutex_unlock(&pulse_lock);
2329 return AUDCLNT_E_INVALID_SIZE;
2331 if (done) {
2332 ACPacket *packet = This->locked_ptr;
2333 This->locked_ptr = NULL;
2334 This->pad -= This->capture_period;
2335 if (packet->discont)
2336 This->clock_written += 2 * This->capture_period;
2337 else
2338 This->clock_written += This->capture_period;
2339 list_add_tail(&This->packet_free_head, &packet->entry);
2341 This->locked = 0;
2342 pthread_mutex_unlock(&pulse_lock);
2343 return S_OK;
2346 static HRESULT WINAPI AudioCaptureClient_GetNextPacketSize(
2347 IAudioCaptureClient *iface, UINT32 *frames)
2349 ACImpl *This = impl_from_IAudioCaptureClient(iface);
2351 TRACE("(%p)->(%p)\n", This, frames);
2352 if (!frames)
2353 return E_POINTER;
2355 pthread_mutex_lock(&pulse_lock);
2356 ACImpl_GetCapturePad(This, NULL);
2357 if (This->locked_ptr)
2358 *frames = This->capture_period / pa_frame_size(&This->ss);
2359 else
2360 *frames = 0;
2361 pthread_mutex_unlock(&pulse_lock);
2362 return S_OK;
2365 static const IAudioCaptureClientVtbl AudioCaptureClient_Vtbl =
2367 AudioCaptureClient_QueryInterface,
2368 AudioCaptureClient_AddRef,
2369 AudioCaptureClient_Release,
2370 AudioCaptureClient_GetBuffer,
2371 AudioCaptureClient_ReleaseBuffer,
2372 AudioCaptureClient_GetNextPacketSize
2375 static HRESULT WINAPI AudioClock_QueryInterface(IAudioClock *iface,
2376 REFIID riid, void **ppv)
2378 ACImpl *This = impl_from_IAudioClock(iface);
2380 TRACE("(%p)->(%s, %p)\n", iface, debugstr_guid(riid), ppv);
2382 if (!ppv)
2383 return E_POINTER;
2384 *ppv = NULL;
2386 if (IsEqualIID(riid, &IID_IUnknown) || IsEqualIID(riid, &IID_IAudioClock))
2387 *ppv = iface;
2388 else if (IsEqualIID(riid, &IID_IAudioClock2))
2389 *ppv = &This->IAudioClock2_iface;
2390 if (*ppv) {
2391 IUnknown_AddRef((IUnknown*)*ppv);
2392 return S_OK;
2395 if (IsEqualIID(riid, &IID_IMarshal))
2396 return IUnknown_QueryInterface(This->marshal, riid, ppv);
2398 WARN("Unknown interface %s\n", debugstr_guid(riid));
2399 return E_NOINTERFACE;
2402 static ULONG WINAPI AudioClock_AddRef(IAudioClock *iface)
2404 ACImpl *This = impl_from_IAudioClock(iface);
2405 return IAudioClient_AddRef(&This->IAudioClient_iface);
2408 static ULONG WINAPI AudioClock_Release(IAudioClock *iface)
2410 ACImpl *This = impl_from_IAudioClock(iface);
2411 return IAudioClient_Release(&This->IAudioClient_iface);
2414 static HRESULT WINAPI AudioClock_GetFrequency(IAudioClock *iface, UINT64 *freq)
2416 ACImpl *This = impl_from_IAudioClock(iface);
2417 HRESULT hr;
2419 TRACE("(%p)->(%p)\n", This, freq);
2421 pthread_mutex_lock(&pulse_lock);
2422 hr = pulse_stream_valid(This);
2423 if (SUCCEEDED(hr)) {
2424 *freq = This->ss.rate;
2425 if (This->share == AUDCLNT_SHAREMODE_SHARED)
2426 *freq *= pa_frame_size(&This->ss);
2428 pthread_mutex_unlock(&pulse_lock);
2429 return hr;
2432 static HRESULT WINAPI AudioClock_GetPosition(IAudioClock *iface, UINT64 *pos,
2433 UINT64 *qpctime)
2435 ACImpl *This = impl_from_IAudioClock(iface);
2436 HRESULT hr;
2438 TRACE("(%p)->(%p, %p)\n", This, pos, qpctime);
2440 if (!pos)
2441 return E_POINTER;
2443 pthread_mutex_lock(&pulse_lock);
2444 hr = pulse_stream_valid(This);
2445 if (FAILED(hr)) {
2446 pthread_mutex_unlock(&pulse_lock);
2447 return hr;
2450 *pos = This->clock_written;
2452 if (This->share == AUDCLNT_SHAREMODE_EXCLUSIVE)
2453 *pos /= pa_frame_size(&This->ss);
2455 /* Make time never go backwards */
2456 if (*pos < This->clock_lastpos)
2457 *pos = This->clock_lastpos;
2458 else
2459 This->clock_lastpos = *pos;
2460 pthread_mutex_unlock(&pulse_lock);
2462 TRACE("%p Position: %u\n", This, (unsigned)*pos);
2464 if (qpctime) {
2465 LARGE_INTEGER stamp, freq;
2466 QueryPerformanceCounter(&stamp);
2467 QueryPerformanceFrequency(&freq);
2468 *qpctime = (stamp.QuadPart * (INT64)10000000) / freq.QuadPart;
2471 return S_OK;
2474 static HRESULT WINAPI AudioClock_GetCharacteristics(IAudioClock *iface,
2475 DWORD *chars)
2477 ACImpl *This = impl_from_IAudioClock(iface);
2479 TRACE("(%p)->(%p)\n", This, chars);
2481 if (!chars)
2482 return E_POINTER;
2484 *chars = AUDIOCLOCK_CHARACTERISTIC_FIXED_FREQ;
2486 return S_OK;
2489 static const IAudioClockVtbl AudioClock_Vtbl =
2491 AudioClock_QueryInterface,
2492 AudioClock_AddRef,
2493 AudioClock_Release,
2494 AudioClock_GetFrequency,
2495 AudioClock_GetPosition,
2496 AudioClock_GetCharacteristics
2499 static HRESULT WINAPI AudioClock2_QueryInterface(IAudioClock2 *iface,
2500 REFIID riid, void **ppv)
2502 ACImpl *This = impl_from_IAudioClock2(iface);
2503 return IAudioClock_QueryInterface(&This->IAudioClock_iface, riid, ppv);
2506 static ULONG WINAPI AudioClock2_AddRef(IAudioClock2 *iface)
2508 ACImpl *This = impl_from_IAudioClock2(iface);
2509 return IAudioClient_AddRef(&This->IAudioClient_iface);
2512 static ULONG WINAPI AudioClock2_Release(IAudioClock2 *iface)
2514 ACImpl *This = impl_from_IAudioClock2(iface);
2515 return IAudioClient_Release(&This->IAudioClient_iface);
2518 static HRESULT WINAPI AudioClock2_GetDevicePosition(IAudioClock2 *iface,
2519 UINT64 *pos, UINT64 *qpctime)
2521 ACImpl *This = impl_from_IAudioClock2(iface);
2522 HRESULT hr = AudioClock_GetPosition(&This->IAudioClock_iface, pos, qpctime);
2523 if (SUCCEEDED(hr) && This->share == AUDCLNT_SHAREMODE_SHARED)
2524 *pos /= pa_frame_size(&This->ss);
2525 return hr;
2528 static const IAudioClock2Vtbl AudioClock2_Vtbl =
2530 AudioClock2_QueryInterface,
2531 AudioClock2_AddRef,
2532 AudioClock2_Release,
2533 AudioClock2_GetDevicePosition
2536 static HRESULT WINAPI AudioStreamVolume_QueryInterface(
2537 IAudioStreamVolume *iface, REFIID riid, void **ppv)
2539 ACImpl *This = impl_from_IAudioStreamVolume(iface);
2541 TRACE("(%p)->(%s, %p)\n", iface, debugstr_guid(riid), ppv);
2543 if (!ppv)
2544 return E_POINTER;
2545 *ppv = NULL;
2547 if (IsEqualIID(riid, &IID_IUnknown) ||
2548 IsEqualIID(riid, &IID_IAudioStreamVolume))
2549 *ppv = iface;
2550 if (*ppv) {
2551 IUnknown_AddRef((IUnknown*)*ppv);
2552 return S_OK;
2555 if (IsEqualIID(riid, &IID_IMarshal))
2556 return IUnknown_QueryInterface(This->marshal, riid, ppv);
2558 WARN("Unknown interface %s\n", debugstr_guid(riid));
2559 return E_NOINTERFACE;
2562 static ULONG WINAPI AudioStreamVolume_AddRef(IAudioStreamVolume *iface)
2564 ACImpl *This = impl_from_IAudioStreamVolume(iface);
2565 return IAudioClient_AddRef(&This->IAudioClient_iface);
2568 static ULONG WINAPI AudioStreamVolume_Release(IAudioStreamVolume *iface)
2570 ACImpl *This = impl_from_IAudioStreamVolume(iface);
2571 return IAudioClient_Release(&This->IAudioClient_iface);
2574 static HRESULT WINAPI AudioStreamVolume_GetChannelCount(
2575 IAudioStreamVolume *iface, UINT32 *out)
2577 ACImpl *This = impl_from_IAudioStreamVolume(iface);
2579 TRACE("(%p)->(%p)\n", This, out);
2581 if (!out)
2582 return E_POINTER;
2584 *out = This->ss.channels;
2586 return S_OK;
2589 struct pulse_info_cb_data {
2590 UINT32 n;
2591 float *levels;
2594 static HRESULT WINAPI AudioStreamVolume_SetAllVolumes(
2595 IAudioStreamVolume *iface, UINT32 count, const float *levels)
2597 ACImpl *This = impl_from_IAudioStreamVolume(iface);
2598 HRESULT hr;
2599 int i;
2601 TRACE("(%p)->(%d, %p)\n", This, count, levels);
2603 if (!levels)
2604 return E_POINTER;
2606 if (count != This->ss.channels)
2607 return E_INVALIDARG;
2609 pthread_mutex_lock(&pulse_lock);
2610 hr = pulse_stream_valid(This);
2611 if (FAILED(hr))
2612 goto out;
2614 for (i = 0; i < count; ++i)
2615 This->vol[i] = levels[i];
2617 out:
2618 pthread_mutex_unlock(&pulse_lock);
2619 return hr;
2622 static HRESULT WINAPI AudioStreamVolume_GetAllVolumes(
2623 IAudioStreamVolume *iface, UINT32 count, float *levels)
2625 ACImpl *This = impl_from_IAudioStreamVolume(iface);
2626 HRESULT hr;
2627 int i;
2629 TRACE("(%p)->(%d, %p)\n", This, count, levels);
2631 if (!levels)
2632 return E_POINTER;
2634 if (count != This->ss.channels)
2635 return E_INVALIDARG;
2637 pthread_mutex_lock(&pulse_lock);
2638 hr = pulse_stream_valid(This);
2639 if (FAILED(hr))
2640 goto out;
2642 for (i = 0; i < count; ++i)
2643 levels[i] = This->vol[i];
2645 out:
2646 pthread_mutex_unlock(&pulse_lock);
2647 return hr;
2650 static HRESULT WINAPI AudioStreamVolume_SetChannelVolume(
2651 IAudioStreamVolume *iface, UINT32 index, float level)
2653 ACImpl *This = impl_from_IAudioStreamVolume(iface);
2654 HRESULT hr;
2655 float volumes[PA_CHANNELS_MAX];
2657 TRACE("(%p)->(%d, %f)\n", This, index, level);
2659 if (level < 0.f || level > 1.f)
2660 return E_INVALIDARG;
2662 if (index >= This->ss.channels)
2663 return E_INVALIDARG;
2665 hr = AudioStreamVolume_GetAllVolumes(iface, This->ss.channels, volumes);
2666 volumes[index] = level;
2667 if (SUCCEEDED(hr))
2668 hr = AudioStreamVolume_SetAllVolumes(iface, This->ss.channels, volumes);
2669 return hr;
2672 static HRESULT WINAPI AudioStreamVolume_GetChannelVolume(
2673 IAudioStreamVolume *iface, UINT32 index, float *level)
2675 ACImpl *This = impl_from_IAudioStreamVolume(iface);
2676 float volumes[PA_CHANNELS_MAX];
2677 HRESULT hr;
2679 TRACE("(%p)->(%d, %p)\n", This, index, level);
2681 if (!level)
2682 return E_POINTER;
2684 if (index >= This->ss.channels)
2685 return E_INVALIDARG;
2687 hr = AudioStreamVolume_GetAllVolumes(iface, This->ss.channels, volumes);
2688 if (SUCCEEDED(hr))
2689 *level = volumes[index];
2690 return hr;
2693 static const IAudioStreamVolumeVtbl AudioStreamVolume_Vtbl =
2695 AudioStreamVolume_QueryInterface,
2696 AudioStreamVolume_AddRef,
2697 AudioStreamVolume_Release,
2698 AudioStreamVolume_GetChannelCount,
2699 AudioStreamVolume_SetChannelVolume,
2700 AudioStreamVolume_GetChannelVolume,
2701 AudioStreamVolume_SetAllVolumes,
2702 AudioStreamVolume_GetAllVolumes
2705 static AudioSessionWrapper *AudioSessionWrapper_Create(ACImpl *client)
2707 AudioSessionWrapper *ret;
2709 ret = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY,
2710 sizeof(AudioSessionWrapper));
2711 if (!ret)
2712 return NULL;
2714 ret->IAudioSessionControl2_iface.lpVtbl = &AudioSessionControl2_Vtbl;
2715 ret->ISimpleAudioVolume_iface.lpVtbl = &SimpleAudioVolume_Vtbl;
2716 ret->IChannelAudioVolume_iface.lpVtbl = &ChannelAudioVolume_Vtbl;
2718 ret->ref = !client;
2720 ret->client = client;
2721 if (client) {
2722 ret->session = client->session;
2723 AudioClient_AddRef(&client->IAudioClient_iface);
2726 return ret;
2729 static HRESULT WINAPI AudioSessionControl_QueryInterface(
2730 IAudioSessionControl2 *iface, REFIID riid, void **ppv)
2732 TRACE("(%p)->(%s, %p)\n", iface, debugstr_guid(riid), ppv);
2734 if (!ppv)
2735 return E_POINTER;
2736 *ppv = NULL;
2738 if (IsEqualIID(riid, &IID_IUnknown) ||
2739 IsEqualIID(riid, &IID_IAudioSessionControl) ||
2740 IsEqualIID(riid, &IID_IAudioSessionControl2))
2741 *ppv = iface;
2742 if (*ppv) {
2743 IUnknown_AddRef((IUnknown*)*ppv);
2744 return S_OK;
2747 WARN("Unknown interface %s\n", debugstr_guid(riid));
2748 return E_NOINTERFACE;
2751 static ULONG WINAPI AudioSessionControl_AddRef(IAudioSessionControl2 *iface)
2753 AudioSessionWrapper *This = impl_from_IAudioSessionControl2(iface);
2754 ULONG ref;
2755 ref = InterlockedIncrement(&This->ref);
2756 TRACE("(%p) Refcount now %u\n", This, ref);
2757 return ref;
2760 static ULONG WINAPI AudioSessionControl_Release(IAudioSessionControl2 *iface)
2762 AudioSessionWrapper *This = impl_from_IAudioSessionControl2(iface);
2763 ULONG ref;
2764 ref = InterlockedDecrement(&This->ref);
2765 TRACE("(%p) Refcount now %u\n", This, ref);
2766 if (!ref) {
2767 if (This->client) {
2768 This->client->session_wrapper = NULL;
2769 AudioClient_Release(&This->client->IAudioClient_iface);
2771 HeapFree(GetProcessHeap(), 0, This);
2773 return ref;
2776 static HRESULT WINAPI AudioSessionControl_GetState(IAudioSessionControl2 *iface,
2777 AudioSessionState *state)
2779 AudioSessionWrapper *This = impl_from_IAudioSessionControl2(iface);
2780 ACImpl *client;
2782 TRACE("(%p)->(%p)\n", This, state);
2784 if (!state)
2785 return NULL_PTR_ERR;
2787 pthread_mutex_lock(&pulse_lock);
2788 if (list_empty(&This->session->clients)) {
2789 *state = AudioSessionStateExpired;
2790 goto out;
2792 LIST_FOR_EACH_ENTRY(client, &This->session->clients, ACImpl, entry) {
2793 if (client->started) {
2794 *state = AudioSessionStateActive;
2795 goto out;
2798 *state = AudioSessionStateInactive;
2800 out:
2801 pthread_mutex_unlock(&pulse_lock);
2802 return S_OK;
2805 static HRESULT WINAPI AudioSessionControl_GetDisplayName(
2806 IAudioSessionControl2 *iface, WCHAR **name)
2808 AudioSessionWrapper *This = impl_from_IAudioSessionControl2(iface);
2810 FIXME("(%p)->(%p) - stub\n", This, name);
2812 return E_NOTIMPL;
2815 static HRESULT WINAPI AudioSessionControl_SetDisplayName(
2816 IAudioSessionControl2 *iface, const WCHAR *name, const GUID *session)
2818 AudioSessionWrapper *This = impl_from_IAudioSessionControl2(iface);
2820 FIXME("(%p)->(%p, %s) - stub\n", This, name, debugstr_guid(session));
2822 return E_NOTIMPL;
2825 static HRESULT WINAPI AudioSessionControl_GetIconPath(
2826 IAudioSessionControl2 *iface, WCHAR **path)
2828 AudioSessionWrapper *This = impl_from_IAudioSessionControl2(iface);
2830 FIXME("(%p)->(%p) - stub\n", This, path);
2832 return E_NOTIMPL;
2835 static HRESULT WINAPI AudioSessionControl_SetIconPath(
2836 IAudioSessionControl2 *iface, const WCHAR *path, const GUID *session)
2838 AudioSessionWrapper *This = impl_from_IAudioSessionControl2(iface);
2840 FIXME("(%p)->(%p, %s) - stub\n", This, path, debugstr_guid(session));
2842 return E_NOTIMPL;
2845 static HRESULT WINAPI AudioSessionControl_GetGroupingParam(
2846 IAudioSessionControl2 *iface, GUID *group)
2848 AudioSessionWrapper *This = impl_from_IAudioSessionControl2(iface);
2850 FIXME("(%p)->(%p) - stub\n", This, group);
2852 return E_NOTIMPL;
2855 static HRESULT WINAPI AudioSessionControl_SetGroupingParam(
2856 IAudioSessionControl2 *iface, const GUID *group, const GUID *session)
2858 AudioSessionWrapper *This = impl_from_IAudioSessionControl2(iface);
2860 FIXME("(%p)->(%s, %s) - stub\n", This, debugstr_guid(group),
2861 debugstr_guid(session));
2863 return E_NOTIMPL;
2866 static HRESULT WINAPI AudioSessionControl_RegisterAudioSessionNotification(
2867 IAudioSessionControl2 *iface, IAudioSessionEvents *events)
2869 AudioSessionWrapper *This = impl_from_IAudioSessionControl2(iface);
2871 FIXME("(%p)->(%p) - stub\n", This, events);
2873 return S_OK;
2876 static HRESULT WINAPI AudioSessionControl_UnregisterAudioSessionNotification(
2877 IAudioSessionControl2 *iface, IAudioSessionEvents *events)
2879 AudioSessionWrapper *This = impl_from_IAudioSessionControl2(iface);
2881 FIXME("(%p)->(%p) - stub\n", This, events);
2883 return S_OK;
2886 static HRESULT WINAPI AudioSessionControl_GetSessionIdentifier(
2887 IAudioSessionControl2 *iface, WCHAR **id)
2889 AudioSessionWrapper *This = impl_from_IAudioSessionControl2(iface);
2891 FIXME("(%p)->(%p) - stub\n", This, id);
2893 return E_NOTIMPL;
2896 static HRESULT WINAPI AudioSessionControl_GetSessionInstanceIdentifier(
2897 IAudioSessionControl2 *iface, WCHAR **id)
2899 AudioSessionWrapper *This = impl_from_IAudioSessionControl2(iface);
2901 FIXME("(%p)->(%p) - stub\n", This, id);
2903 return E_NOTIMPL;
2906 static HRESULT WINAPI AudioSessionControl_GetProcessId(
2907 IAudioSessionControl2 *iface, DWORD *pid)
2909 AudioSessionWrapper *This = impl_from_IAudioSessionControl2(iface);
2911 TRACE("(%p)->(%p)\n", This, pid);
2913 if (!pid)
2914 return E_POINTER;
2916 *pid = GetCurrentProcessId();
2918 return S_OK;
2921 static HRESULT WINAPI AudioSessionControl_IsSystemSoundsSession(
2922 IAudioSessionControl2 *iface)
2924 AudioSessionWrapper *This = impl_from_IAudioSessionControl2(iface);
2926 TRACE("(%p)\n", This);
2928 return S_FALSE;
2931 static HRESULT WINAPI AudioSessionControl_SetDuckingPreference(
2932 IAudioSessionControl2 *iface, BOOL optout)
2934 AudioSessionWrapper *This = impl_from_IAudioSessionControl2(iface);
2936 TRACE("(%p)->(%d)\n", This, optout);
2938 return S_OK;
2941 static const IAudioSessionControl2Vtbl AudioSessionControl2_Vtbl =
2943 AudioSessionControl_QueryInterface,
2944 AudioSessionControl_AddRef,
2945 AudioSessionControl_Release,
2946 AudioSessionControl_GetState,
2947 AudioSessionControl_GetDisplayName,
2948 AudioSessionControl_SetDisplayName,
2949 AudioSessionControl_GetIconPath,
2950 AudioSessionControl_SetIconPath,
2951 AudioSessionControl_GetGroupingParam,
2952 AudioSessionControl_SetGroupingParam,
2953 AudioSessionControl_RegisterAudioSessionNotification,
2954 AudioSessionControl_UnregisterAudioSessionNotification,
2955 AudioSessionControl_GetSessionIdentifier,
2956 AudioSessionControl_GetSessionInstanceIdentifier,
2957 AudioSessionControl_GetProcessId,
2958 AudioSessionControl_IsSystemSoundsSession,
2959 AudioSessionControl_SetDuckingPreference
2962 typedef struct _SessionMgr {
2963 IAudioSessionManager2 IAudioSessionManager2_iface;
2965 LONG ref;
2967 IMMDevice *device;
2968 } SessionMgr;
2970 static HRESULT WINAPI AudioSessionManager_QueryInterface(IAudioSessionManager2 *iface,
2971 REFIID riid, void **ppv)
2973 TRACE("(%p)->(%s, %p)\n", iface, debugstr_guid(riid), ppv);
2975 if (!ppv)
2976 return E_POINTER;
2977 *ppv = NULL;
2979 if (IsEqualIID(riid, &IID_IUnknown) ||
2980 IsEqualIID(riid, &IID_IAudioSessionManager) ||
2981 IsEqualIID(riid, &IID_IAudioSessionManager2))
2982 *ppv = iface;
2983 if (*ppv) {
2984 IUnknown_AddRef((IUnknown*)*ppv);
2985 return S_OK;
2988 WARN("Unknown interface %s\n", debugstr_guid(riid));
2989 return E_NOINTERFACE;
2992 static inline SessionMgr *impl_from_IAudioSessionManager2(IAudioSessionManager2 *iface)
2994 return CONTAINING_RECORD(iface, SessionMgr, IAudioSessionManager2_iface);
2997 static ULONG WINAPI AudioSessionManager_AddRef(IAudioSessionManager2 *iface)
2999 SessionMgr *This = impl_from_IAudioSessionManager2(iface);
3000 ULONG ref;
3001 ref = InterlockedIncrement(&This->ref);
3002 TRACE("(%p) Refcount now %u\n", This, ref);
3003 return ref;
3006 static ULONG WINAPI AudioSessionManager_Release(IAudioSessionManager2 *iface)
3008 SessionMgr *This = impl_from_IAudioSessionManager2(iface);
3009 ULONG ref;
3010 ref = InterlockedDecrement(&This->ref);
3011 TRACE("(%p) Refcount now %u\n", This, ref);
3012 if (!ref)
3013 HeapFree(GetProcessHeap(), 0, This);
3014 return ref;
3017 static HRESULT WINAPI AudioSessionManager_GetAudioSessionControl(
3018 IAudioSessionManager2 *iface, const GUID *session_guid, DWORD flags,
3019 IAudioSessionControl **out)
3021 SessionMgr *This = impl_from_IAudioSessionManager2(iface);
3022 AudioSession *session;
3023 AudioSessionWrapper *wrapper;
3024 HRESULT hr;
3026 TRACE("(%p)->(%s, %x, %p)\n", This, debugstr_guid(session_guid),
3027 flags, out);
3029 hr = get_audio_session(session_guid, This->device, 0, &session);
3030 if (FAILED(hr))
3031 return hr;
3033 wrapper = AudioSessionWrapper_Create(NULL);
3034 if (!wrapper)
3035 return E_OUTOFMEMORY;
3037 wrapper->session = session;
3039 *out = (IAudioSessionControl*)&wrapper->IAudioSessionControl2_iface;
3041 return S_OK;
3044 static HRESULT WINAPI AudioSessionManager_GetSimpleAudioVolume(
3045 IAudioSessionManager2 *iface, const GUID *session_guid, DWORD flags,
3046 ISimpleAudioVolume **out)
3048 SessionMgr *This = impl_from_IAudioSessionManager2(iface);
3049 AudioSession *session;
3050 AudioSessionWrapper *wrapper;
3051 HRESULT hr;
3053 TRACE("(%p)->(%s, %x, %p)\n", This, debugstr_guid(session_guid),
3054 flags, out);
3056 hr = get_audio_session(session_guid, This->device, 0, &session);
3057 if (FAILED(hr))
3058 return hr;
3060 wrapper = AudioSessionWrapper_Create(NULL);
3061 if (!wrapper)
3062 return E_OUTOFMEMORY;
3064 wrapper->session = session;
3066 *out = &wrapper->ISimpleAudioVolume_iface;
3068 return S_OK;
3071 static HRESULT WINAPI AudioSessionManager_GetSessionEnumerator(
3072 IAudioSessionManager2 *iface, IAudioSessionEnumerator **out)
3074 SessionMgr *This = impl_from_IAudioSessionManager2(iface);
3075 FIXME("(%p)->(%p) - stub\n", This, out);
3076 return E_NOTIMPL;
3079 static HRESULT WINAPI AudioSessionManager_RegisterSessionNotification(
3080 IAudioSessionManager2 *iface, IAudioSessionNotification *notification)
3082 SessionMgr *This = impl_from_IAudioSessionManager2(iface);
3083 FIXME("(%p)->(%p) - stub\n", This, notification);
3084 return E_NOTIMPL;
3087 static HRESULT WINAPI AudioSessionManager_UnregisterSessionNotification(
3088 IAudioSessionManager2 *iface, IAudioSessionNotification *notification)
3090 SessionMgr *This = impl_from_IAudioSessionManager2(iface);
3091 FIXME("(%p)->(%p) - stub\n", This, notification);
3092 return E_NOTIMPL;
3095 static HRESULT WINAPI AudioSessionManager_RegisterDuckNotification(
3096 IAudioSessionManager2 *iface, const WCHAR *session_id,
3097 IAudioVolumeDuckNotification *notification)
3099 SessionMgr *This = impl_from_IAudioSessionManager2(iface);
3100 FIXME("(%p)->(%p) - stub\n", This, notification);
3101 return E_NOTIMPL;
3104 static HRESULT WINAPI AudioSessionManager_UnregisterDuckNotification(
3105 IAudioSessionManager2 *iface,
3106 IAudioVolumeDuckNotification *notification)
3108 SessionMgr *This = impl_from_IAudioSessionManager2(iface);
3109 FIXME("(%p)->(%p) - stub\n", This, notification);
3110 return E_NOTIMPL;
3113 static const IAudioSessionManager2Vtbl AudioSessionManager2_Vtbl =
3115 AudioSessionManager_QueryInterface,
3116 AudioSessionManager_AddRef,
3117 AudioSessionManager_Release,
3118 AudioSessionManager_GetAudioSessionControl,
3119 AudioSessionManager_GetSimpleAudioVolume,
3120 AudioSessionManager_GetSessionEnumerator,
3121 AudioSessionManager_RegisterSessionNotification,
3122 AudioSessionManager_UnregisterSessionNotification,
3123 AudioSessionManager_RegisterDuckNotification,
3124 AudioSessionManager_UnregisterDuckNotification
3127 static HRESULT WINAPI SimpleAudioVolume_QueryInterface(
3128 ISimpleAudioVolume *iface, REFIID riid, void **ppv)
3130 TRACE("(%p)->(%s, %p)\n", iface, debugstr_guid(riid), ppv);
3132 if (!ppv)
3133 return E_POINTER;
3134 *ppv = NULL;
3136 if (IsEqualIID(riid, &IID_IUnknown) ||
3137 IsEqualIID(riid, &IID_ISimpleAudioVolume))
3138 *ppv = iface;
3139 if (*ppv) {
3140 IUnknown_AddRef((IUnknown*)*ppv);
3141 return S_OK;
3144 WARN("Unknown interface %s\n", debugstr_guid(riid));
3145 return E_NOINTERFACE;
3148 static ULONG WINAPI SimpleAudioVolume_AddRef(ISimpleAudioVolume *iface)
3150 AudioSessionWrapper *This = impl_from_ISimpleAudioVolume(iface);
3151 return AudioSessionControl_AddRef(&This->IAudioSessionControl2_iface);
3154 static ULONG WINAPI SimpleAudioVolume_Release(ISimpleAudioVolume *iface)
3156 AudioSessionWrapper *This = impl_from_ISimpleAudioVolume(iface);
3157 return AudioSessionControl_Release(&This->IAudioSessionControl2_iface);
3160 static HRESULT WINAPI SimpleAudioVolume_SetMasterVolume(
3161 ISimpleAudioVolume *iface, float level, const GUID *context)
3163 AudioSessionWrapper *This = impl_from_ISimpleAudioVolume(iface);
3164 AudioSession *session = This->session;
3166 TRACE("(%p)->(%f, %s)\n", session, level, wine_dbgstr_guid(context));
3168 if (level < 0.f || level > 1.f)
3169 return E_INVALIDARG;
3171 if (context)
3172 FIXME("Notifications not supported yet\n");
3174 TRACE("PulseAudio does not support session volume control\n");
3176 pthread_mutex_lock(&pulse_lock);
3177 session->master_vol = level;
3178 pthread_mutex_unlock(&pulse_lock);
3180 return S_OK;
3183 static HRESULT WINAPI SimpleAudioVolume_GetMasterVolume(
3184 ISimpleAudioVolume *iface, float *level)
3186 AudioSessionWrapper *This = impl_from_ISimpleAudioVolume(iface);
3187 AudioSession *session = This->session;
3189 TRACE("(%p)->(%p)\n", session, level);
3191 if (!level)
3192 return NULL_PTR_ERR;
3194 *level = session->master_vol;
3196 return S_OK;
3199 static HRESULT WINAPI SimpleAudioVolume_SetMute(ISimpleAudioVolume *iface,
3200 BOOL mute, const GUID *context)
3202 AudioSessionWrapper *This = impl_from_ISimpleAudioVolume(iface);
3203 AudioSession *session = This->session;
3205 TRACE("(%p)->(%u, %s)\n", session, mute, debugstr_guid(context));
3207 if (context)
3208 FIXME("Notifications not supported yet\n");
3210 session->mute = mute;
3212 return S_OK;
3215 static HRESULT WINAPI SimpleAudioVolume_GetMute(ISimpleAudioVolume *iface,
3216 BOOL *mute)
3218 AudioSessionWrapper *This = impl_from_ISimpleAudioVolume(iface);
3219 AudioSession *session = This->session;
3221 TRACE("(%p)->(%p)\n", session, mute);
3223 if (!mute)
3224 return NULL_PTR_ERR;
3226 *mute = session->mute;
3228 return S_OK;
3231 static const ISimpleAudioVolumeVtbl SimpleAudioVolume_Vtbl =
3233 SimpleAudioVolume_QueryInterface,
3234 SimpleAudioVolume_AddRef,
3235 SimpleAudioVolume_Release,
3236 SimpleAudioVolume_SetMasterVolume,
3237 SimpleAudioVolume_GetMasterVolume,
3238 SimpleAudioVolume_SetMute,
3239 SimpleAudioVolume_GetMute
3242 static HRESULT WINAPI ChannelAudioVolume_QueryInterface(
3243 IChannelAudioVolume *iface, REFIID riid, void **ppv)
3245 TRACE("(%p)->(%s, %p)\n", iface, debugstr_guid(riid), ppv);
3247 if (!ppv)
3248 return E_POINTER;
3249 *ppv = NULL;
3251 if (IsEqualIID(riid, &IID_IUnknown) ||
3252 IsEqualIID(riid, &IID_IChannelAudioVolume))
3253 *ppv = iface;
3254 if (*ppv) {
3255 IUnknown_AddRef((IUnknown*)*ppv);
3256 return S_OK;
3259 WARN("Unknown interface %s\n", debugstr_guid(riid));
3260 return E_NOINTERFACE;
3263 static ULONG WINAPI ChannelAudioVolume_AddRef(IChannelAudioVolume *iface)
3265 AudioSessionWrapper *This = impl_from_IChannelAudioVolume(iface);
3266 return AudioSessionControl_AddRef(&This->IAudioSessionControl2_iface);
3269 static ULONG WINAPI ChannelAudioVolume_Release(IChannelAudioVolume *iface)
3271 AudioSessionWrapper *This = impl_from_IChannelAudioVolume(iface);
3272 return AudioSessionControl_Release(&This->IAudioSessionControl2_iface);
3275 static HRESULT WINAPI ChannelAudioVolume_GetChannelCount(
3276 IChannelAudioVolume *iface, UINT32 *out)
3278 AudioSessionWrapper *This = impl_from_IChannelAudioVolume(iface);
3279 AudioSession *session = This->session;
3281 TRACE("(%p)->(%p)\n", session, out);
3283 if (!out)
3284 return NULL_PTR_ERR;
3286 *out = session->channel_count;
3288 return S_OK;
3291 static HRESULT WINAPI ChannelAudioVolume_SetChannelVolume(
3292 IChannelAudioVolume *iface, UINT32 index, float level,
3293 const GUID *context)
3295 AudioSessionWrapper *This = impl_from_IChannelAudioVolume(iface);
3296 AudioSession *session = This->session;
3298 TRACE("(%p)->(%d, %f, %s)\n", session, index, level,
3299 wine_dbgstr_guid(context));
3301 if (level < 0.f || level > 1.f)
3302 return E_INVALIDARG;
3304 if (index >= session->channel_count)
3305 return E_INVALIDARG;
3307 if (context)
3308 FIXME("Notifications not supported yet\n");
3310 TRACE("PulseAudio does not support session volume control\n");
3312 pthread_mutex_lock(&pulse_lock);
3313 session->channel_vols[index] = level;
3314 pthread_mutex_unlock(&pulse_lock);
3316 return S_OK;
3319 static HRESULT WINAPI ChannelAudioVolume_GetChannelVolume(
3320 IChannelAudioVolume *iface, UINT32 index, float *level)
3322 AudioSessionWrapper *This = impl_from_IChannelAudioVolume(iface);
3323 AudioSession *session = This->session;
3325 TRACE("(%p)->(%d, %p)\n", session, index, level);
3327 if (!level)
3328 return NULL_PTR_ERR;
3330 if (index >= session->channel_count)
3331 return E_INVALIDARG;
3333 *level = session->channel_vols[index];
3335 return S_OK;
3338 static HRESULT WINAPI ChannelAudioVolume_SetAllVolumes(
3339 IChannelAudioVolume *iface, UINT32 count, const float *levels,
3340 const GUID *context)
3342 AudioSessionWrapper *This = impl_from_IChannelAudioVolume(iface);
3343 AudioSession *session = This->session;
3344 int i;
3346 TRACE("(%p)->(%d, %p, %s)\n", session, count, levels,
3347 wine_dbgstr_guid(context));
3349 if (!levels)
3350 return NULL_PTR_ERR;
3352 if (count != session->channel_count)
3353 return E_INVALIDARG;
3355 if (context)
3356 FIXME("Notifications not supported yet\n");
3358 TRACE("PulseAudio does not support session volume control\n");
3360 pthread_mutex_lock(&pulse_lock);
3361 for(i = 0; i < count; ++i)
3362 session->channel_vols[i] = levels[i];
3363 pthread_mutex_unlock(&pulse_lock);
3364 return S_OK;
3367 static HRESULT WINAPI ChannelAudioVolume_GetAllVolumes(
3368 IChannelAudioVolume *iface, UINT32 count, float *levels)
3370 AudioSessionWrapper *This = impl_from_IChannelAudioVolume(iface);
3371 AudioSession *session = This->session;
3372 int i;
3374 TRACE("(%p)->(%d, %p)\n", session, count, levels);
3376 if (!levels)
3377 return NULL_PTR_ERR;
3379 if (count != session->channel_count)
3380 return E_INVALIDARG;
3382 for(i = 0; i < count; ++i)
3383 levels[i] = session->channel_vols[i];
3385 return S_OK;
3388 static const IChannelAudioVolumeVtbl ChannelAudioVolume_Vtbl =
3390 ChannelAudioVolume_QueryInterface,
3391 ChannelAudioVolume_AddRef,
3392 ChannelAudioVolume_Release,
3393 ChannelAudioVolume_GetChannelCount,
3394 ChannelAudioVolume_SetChannelVolume,
3395 ChannelAudioVolume_GetChannelVolume,
3396 ChannelAudioVolume_SetAllVolumes,
3397 ChannelAudioVolume_GetAllVolumes
3400 HRESULT WINAPI AUDDRV_GetAudioSessionManager(IMMDevice *device,
3401 IAudioSessionManager2 **out)
3403 SessionMgr *This = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, sizeof(SessionMgr));
3404 *out = NULL;
3405 if (!This)
3406 return E_OUTOFMEMORY;
3407 This->IAudioSessionManager2_iface.lpVtbl = &AudioSessionManager2_Vtbl;
3408 This->device = device;
3409 This->ref = 1;
3410 *out = &This->IAudioSessionManager2_iface;
3411 return S_OK;
3414 HRESULT WINAPI AUDDRV_GetPropValue(GUID *guid, const PROPERTYKEY *prop, PROPVARIANT *out)
3416 TRACE("%s, (%s,%u), %p\n", wine_dbgstr_guid(guid), wine_dbgstr_guid(&prop->fmtid), prop->pid, out);
3418 if (IsEqualGUID(guid, &pulse_render_guid) && IsEqualPropertyKey(*prop, PKEY_AudioEndpoint_PhysicalSpeakers)) {
3419 out->vt = VT_UI4;
3420 out->u.ulVal = g_phys_speakers_mask;
3422 return out->u.ulVal ? S_OK : E_FAIL;
3425 return E_NOTIMPL;