winepulse.drv: Buffer data locally when needed.
[wine.git] / dlls / winepulse.drv / mmdevdrv.c
blob04e9a0b8f6a9358eaa097d85f1bf2b9e74402086
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;
177 BYTE *local_buffer, *tmp_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 ERR("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 TRACE("Test-connected to server %s with protocol version: %i.\n",
577 pa_context_get_server(pulse_ctx),
578 pa_context_get_server_protocol_version(pulse_ctx));
580 pulse_probe_settings(1, &pulse_fmt[0]);
581 pulse_probe_settings(0, &pulse_fmt[1]);
583 g_phys_speakers_mask = 0;
584 o = pa_context_get_sink_info_list(pulse_ctx, &pulse_phys_speakers_cb, NULL);
585 if (o) {
586 while (pa_mainloop_iterate(pulse_ml, 1, &ret) >= 0 &&
587 pa_operation_get_state(o) == PA_OPERATION_RUNNING)
589 pa_operation_unref(o);
592 pa_context_unref(pulse_ctx);
593 pulse_ctx = NULL;
594 pa_mainloop_free(pulse_ml);
595 pulse_ml = NULL;
597 return S_OK;
599 fail:
600 pa_context_unref(pulse_ctx);
601 pulse_ctx = NULL;
602 pa_mainloop_free(pulse_ml);
603 pulse_ml = NULL;
605 return E_FAIL;
608 static HRESULT pulse_stream_valid(ACImpl *This) {
609 if (!This->stream)
610 return AUDCLNT_E_NOT_INITIALIZED;
611 if (!This->stream || pa_stream_get_state(This->stream) != PA_STREAM_READY)
612 return AUDCLNT_E_DEVICE_INVALIDATED;
613 return S_OK;
616 static void silence_buffer(pa_sample_format_t format, BYTE *buffer, UINT32 bytes)
618 memset(buffer, format == PA_SAMPLE_U8 ? 0x80 : 0, bytes);
621 static void dump_attr(const pa_buffer_attr *attr) {
622 TRACE("maxlength: %u\n", attr->maxlength);
623 TRACE("minreq: %u\n", attr->minreq);
624 TRACE("fragsize: %u\n", attr->fragsize);
625 TRACE("tlength: %u\n", attr->tlength);
626 TRACE("prebuf: %u\n", attr->prebuf);
629 static void pulse_op_cb(pa_stream *s, int success, void *user) {
630 TRACE("Success: %i\n", success);
631 *(int*)user = success;
632 pthread_cond_signal(&pulse_cond);
635 static void pulse_attr_update(pa_stream *s, void *user) {
636 const pa_buffer_attr *attr = pa_stream_get_buffer_attr(s);
637 TRACE("New attributes or device moved:\n");
638 dump_attr(attr);
641 /* Here's the buffer setup:
643 * vvvvvvvv sent to HW already
644 * vvvvvvvv in Pulse buffer but rewindable
645 * [dddddddddddddddd] Pulse buffer
646 * [dddddddddddddddd--------] mmdevapi buffer
647 * ^^^^^^^^^^^^^^^^ pad
648 * ^ lcl_offs_bytes
649 * ^^^^^^^^^ held_bytes
650 * ^ wri_offs_bytes
652 * GetCurrentPadding is pad
654 * During pulse_wr_callback, we decrement pad, fill Pulse buffer, and move
655 * lcl_offs forward
657 * During Stop, we flush the Pulse buffer
659 static void pulse_wr_callback(pa_stream *s, size_t bytes, void *userdata)
661 ACImpl *This = userdata;
662 UINT32 oldpad = This->pad;
664 if(This->local_buffer){
665 size_t to_write;
666 BYTE *buf = This->local_buffer + This->lcl_offs_bytes;
668 if(This->pad > bytes){
669 This->clock_written += bytes;
670 This->pad -= bytes;
671 }else{
672 This->clock_written += This->pad;
673 This->pad = 0;
676 bytes = min(bytes, This->held_bytes);
678 if(This->lcl_offs_bytes + bytes > This->bufsize_bytes){
679 to_write = This->bufsize_bytes - This->lcl_offs_bytes;
680 TRACE("writing small chunk of %u bytes\n", to_write);
681 pa_stream_write(This->stream, buf, to_write, NULL, 0, PA_SEEK_RELATIVE);
682 This->held_bytes -= to_write;
683 to_write = bytes - to_write;
684 This->lcl_offs_bytes = 0;
685 buf = This->local_buffer;
686 }else
687 to_write = bytes;
689 TRACE("writing main chunk of %u bytes\n", to_write);
690 pa_stream_write(This->stream, buf, to_write, NULL, 0, PA_SEEK_RELATIVE);
691 This->lcl_offs_bytes += to_write;
692 This->lcl_offs_bytes %= This->bufsize_bytes;
693 This->held_bytes -= to_write;
694 }else{
695 if (bytes < This->bufsize_bytes)
696 This->pad = This->bufsize_bytes - bytes;
697 else
698 This->pad = 0;
700 if (oldpad == This->pad)
701 return;
703 assert(oldpad > This->pad);
705 This->clock_written += oldpad - This->pad;
706 TRACE("New pad: %zu (-%zu)\n", This->pad / pa_frame_size(&This->ss), (oldpad - This->pad) / pa_frame_size(&This->ss));
709 if (This->event)
710 SetEvent(This->event);
713 static void pulse_underflow_callback(pa_stream *s, void *userdata)
715 WARN("Underflow\n");
718 /* Latency is periodically updated even when nothing is played,
719 * because of PA_STREAM_AUTO_TIMING_UPDATE so use it as timer
721 * Perfect for passing all tests :)
723 static void pulse_latency_callback(pa_stream *s, void *userdata)
725 ACImpl *This = userdata;
726 if (!This->pad && This->event)
727 SetEvent(This->event);
730 static void pulse_started_callback(pa_stream *s, void *userdata)
732 TRACE("(Re)started playing\n");
735 static void pulse_rd_loop(ACImpl *This, size_t bytes)
737 while (bytes >= This->capture_period) {
738 ACPacket *p, *next;
739 LARGE_INTEGER stamp, freq;
740 BYTE *dst, *src;
741 size_t src_len, copy, rem = This->capture_period;
742 if (!(p = (ACPacket*)list_head(&This->packet_free_head))) {
743 p = (ACPacket*)list_head(&This->packet_filled_head);
744 if (!p->discont) {
745 next = (ACPacket*)p->entry.next;
746 next->discont = 1;
747 } else
748 p = (ACPacket*)list_tail(&This->packet_filled_head);
749 assert(This->pad == This->bufsize_bytes);
750 } else {
751 assert(This->pad < This->bufsize_bytes);
752 This->pad += This->capture_period;
753 assert(This->pad <= This->bufsize_bytes);
755 QueryPerformanceCounter(&stamp);
756 QueryPerformanceFrequency(&freq);
757 p->qpcpos = (stamp.QuadPart * (INT64)10000000) / freq.QuadPart;
758 p->discont = 0;
759 list_remove(&p->entry);
760 list_add_tail(&This->packet_filled_head, &p->entry);
762 dst = p->data;
763 while (rem) {
764 pa_stream_peek(This->stream, (const void**)&src, &src_len);
765 assert(src_len);
766 assert(This->peek_ofs < src_len);
767 src += This->peek_ofs;
768 src_len -= This->peek_ofs;
769 assert(src_len <= bytes);
771 copy = rem;
772 if (copy > src_len)
773 copy = src_len;
774 memcpy(dst, src, rem);
775 src += copy;
776 src_len -= copy;
777 dst += copy;
778 rem -= copy;
780 if (!src_len) {
781 This->peek_ofs = 0;
782 pa_stream_drop(This->stream);
783 } else
784 This->peek_ofs += copy;
786 bytes -= This->capture_period;
790 static void pulse_rd_drop(ACImpl *This, size_t bytes)
792 while (bytes >= This->capture_period) {
793 size_t src_len, copy, rem = This->capture_period;
794 while (rem) {
795 const void *src;
796 pa_stream_peek(This->stream, &src, &src_len);
797 assert(src_len);
798 assert(This->peek_ofs < src_len);
799 src_len -= This->peek_ofs;
800 assert(src_len <= bytes);
802 copy = rem;
803 if (copy > src_len)
804 copy = src_len;
806 src_len -= copy;
807 rem -= copy;
809 if (!src_len) {
810 This->peek_ofs = 0;
811 pa_stream_drop(This->stream);
812 } else
813 This->peek_ofs += copy;
814 bytes -= copy;
819 static void pulse_rd_callback(pa_stream *s, size_t bytes, void *userdata)
821 ACImpl *This = userdata;
823 TRACE("Readable total: %zu, fragsize: %u\n", bytes, pa_stream_get_buffer_attr(s)->fragsize);
824 assert(bytes >= This->peek_ofs);
825 bytes -= This->peek_ofs;
826 if (bytes < This->capture_period)
827 return;
829 if (This->started)
830 pulse_rd_loop(This, bytes);
831 else
832 pulse_rd_drop(This, bytes);
834 if (This->event)
835 SetEvent(This->event);
838 static HRESULT pulse_stream_connect(ACImpl *This, UINT32 period_bytes) {
839 int ret;
840 char buffer[64];
841 static LONG number;
842 pa_buffer_attr attr;
843 if (This->stream) {
844 pa_stream_disconnect(This->stream);
845 while (pa_stream_get_state(This->stream) == PA_STREAM_READY)
846 pthread_cond_wait(&pulse_cond, &pulse_lock);
847 pa_stream_unref(This->stream);
849 ret = InterlockedIncrement(&number);
850 sprintf(buffer, "audio stream #%i", ret);
851 This->stream = pa_stream_new(pulse_ctx, buffer, &This->ss, &This->map);
853 if (!This->stream) {
854 WARN("pa_stream_new returned error %i\n", pa_context_errno(pulse_ctx));
855 return AUDCLNT_E_ENDPOINT_CREATE_FAILED;
858 pa_stream_set_state_callback(This->stream, pulse_stream_state, This);
859 pa_stream_set_buffer_attr_callback(This->stream, pulse_attr_update, This);
860 pa_stream_set_moved_callback(This->stream, pulse_attr_update, This);
862 /* PulseAudio will fill in correct values */
863 attr.minreq = attr.fragsize = period_bytes;
864 attr.maxlength = attr.tlength = This->bufsize_bytes;
865 attr.prebuf = pa_frame_size(&This->ss);
866 dump_attr(&attr);
867 if (This->dataflow == eRender)
868 ret = pa_stream_connect_playback(This->stream, NULL, &attr,
869 PA_STREAM_START_CORKED|PA_STREAM_START_UNMUTED|PA_STREAM_AUTO_TIMING_UPDATE|PA_STREAM_INTERPOLATE_TIMING|PA_STREAM_EARLY_REQUESTS, NULL, NULL);
870 else
871 ret = pa_stream_connect_record(This->stream, NULL, &attr,
872 PA_STREAM_START_CORKED|PA_STREAM_START_UNMUTED|PA_STREAM_AUTO_TIMING_UPDATE|PA_STREAM_INTERPOLATE_TIMING|PA_STREAM_EARLY_REQUESTS);
873 if (ret < 0) {
874 WARN("Returns %i\n", ret);
875 return AUDCLNT_E_ENDPOINT_CREATE_FAILED;
877 while (pa_stream_get_state(This->stream) == PA_STREAM_CREATING)
878 pthread_cond_wait(&pulse_cond, &pulse_lock);
879 if (pa_stream_get_state(This->stream) != PA_STREAM_READY)
880 return AUDCLNT_E_ENDPOINT_CREATE_FAILED;
882 if (This->dataflow == eRender) {
883 pa_stream_set_write_callback(This->stream, pulse_wr_callback, This);
884 pa_stream_set_underflow_callback(This->stream, pulse_underflow_callback, This);
885 pa_stream_set_started_callback(This->stream, pulse_started_callback, This);
886 } else
887 pa_stream_set_read_callback(This->stream, pulse_rd_callback, This);
888 return S_OK;
891 HRESULT WINAPI AUDDRV_GetEndpointIDs(EDataFlow flow, const WCHAR ***ids, GUID **keys,
892 UINT *num, UINT *def_index)
894 WCHAR *id;
896 TRACE("%d %p %p %p\n", flow, ids, num, def_index);
898 *num = 1;
899 *def_index = 0;
901 *ids = HeapAlloc(GetProcessHeap(), 0, sizeof(**ids));
902 *keys = NULL;
903 if (!*ids)
904 return E_OUTOFMEMORY;
906 (*ids)[0] = id = HeapAlloc(GetProcessHeap(), 0, sizeof(defaultW));
907 *keys = HeapAlloc(GetProcessHeap(), 0, sizeof(**keys));
908 if (!*keys || !id) {
909 HeapFree(GetProcessHeap(), 0, id);
910 HeapFree(GetProcessHeap(), 0, *keys);
911 HeapFree(GetProcessHeap(), 0, *ids);
912 *ids = NULL;
913 *keys = NULL;
914 return E_OUTOFMEMORY;
916 memcpy(id, defaultW, sizeof(defaultW));
918 if (flow == eRender)
919 (*keys)[0] = pulse_render_guid;
920 else
921 (*keys)[0] = pulse_capture_guid;
923 return S_OK;
926 int WINAPI AUDDRV_GetPriority(void)
928 HRESULT hr;
929 pthread_mutex_lock(&pulse_lock);
930 hr = pulse_test_connect();
931 pthread_mutex_unlock(&pulse_lock);
932 return SUCCEEDED(hr) ? Priority_Preferred : Priority_Unavailable;
935 HRESULT WINAPI AUDDRV_GetAudioEndpoint(GUID *guid, IMMDevice *dev, IAudioClient **out)
937 ACImpl *This;
938 int i;
939 EDataFlow dataflow;
940 HRESULT hr;
942 TRACE("%s %p %p\n", debugstr_guid(guid), dev, out);
943 if (IsEqualGUID(guid, &pulse_render_guid))
944 dataflow = eRender;
945 else if (IsEqualGUID(guid, &pulse_capture_guid))
946 dataflow = eCapture;
947 else
948 return E_UNEXPECTED;
950 *out = NULL;
952 This = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, sizeof(*This));
953 if (!This)
954 return E_OUTOFMEMORY;
956 This->IAudioClient_iface.lpVtbl = &AudioClient_Vtbl;
957 This->IAudioRenderClient_iface.lpVtbl = &AudioRenderClient_Vtbl;
958 This->IAudioCaptureClient_iface.lpVtbl = &AudioCaptureClient_Vtbl;
959 This->IAudioClock_iface.lpVtbl = &AudioClock_Vtbl;
960 This->IAudioClock2_iface.lpVtbl = &AudioClock2_Vtbl;
961 This->IAudioStreamVolume_iface.lpVtbl = &AudioStreamVolume_Vtbl;
962 This->dataflow = dataflow;
963 This->parent = dev;
964 for (i = 0; i < PA_CHANNELS_MAX; ++i)
965 This->vol[i] = 1.f;
967 hr = CoCreateFreeThreadedMarshaler((IUnknown*)This, &This->marshal);
968 if (hr) {
969 HeapFree(GetProcessHeap(), 0, This);
970 return hr;
972 IMMDevice_AddRef(This->parent);
974 *out = &This->IAudioClient_iface;
975 IAudioClient_AddRef(&This->IAudioClient_iface);
977 return S_OK;
980 static HRESULT WINAPI AudioClient_QueryInterface(IAudioClient *iface,
981 REFIID riid, void **ppv)
983 ACImpl *This = impl_from_IAudioClient(iface);
985 TRACE("(%p)->(%s, %p)\n", iface, debugstr_guid(riid), ppv);
987 if (!ppv)
988 return E_POINTER;
990 *ppv = NULL;
991 if (IsEqualIID(riid, &IID_IUnknown) || IsEqualIID(riid, &IID_IAudioClient))
992 *ppv = iface;
993 if (*ppv) {
994 IUnknown_AddRef((IUnknown*)*ppv);
995 return S_OK;
998 if (IsEqualIID(riid, &IID_IMarshal))
999 return IUnknown_QueryInterface(This->marshal, riid, ppv);
1001 WARN("Unknown interface %s\n", debugstr_guid(riid));
1002 return E_NOINTERFACE;
1005 static ULONG WINAPI AudioClient_AddRef(IAudioClient *iface)
1007 ACImpl *This = impl_from_IAudioClient(iface);
1008 ULONG ref;
1009 ref = InterlockedIncrement(&This->ref);
1010 TRACE("(%p) Refcount now %u\n", This, ref);
1011 return ref;
1014 static ULONG WINAPI AudioClient_Release(IAudioClient *iface)
1016 ACImpl *This = impl_from_IAudioClient(iface);
1017 ULONG ref;
1018 ref = InterlockedDecrement(&This->ref);
1019 TRACE("(%p) Refcount now %u\n", This, ref);
1020 if (!ref) {
1021 if (This->stream) {
1022 pthread_mutex_lock(&pulse_lock);
1023 if (PA_STREAM_IS_GOOD(pa_stream_get_state(This->stream))) {
1024 pa_stream_disconnect(This->stream);
1025 while (PA_STREAM_IS_GOOD(pa_stream_get_state(This->stream)))
1026 pthread_cond_wait(&pulse_cond, &pulse_lock);
1028 pa_stream_unref(This->stream);
1029 This->stream = NULL;
1030 list_remove(&This->entry);
1031 pthread_mutex_unlock(&pulse_lock);
1033 IUnknown_Release(This->marshal);
1034 IMMDevice_Release(This->parent);
1035 HeapFree(GetProcessHeap(), 0, This->tmp_buffer);
1036 HeapFree(GetProcessHeap(), 0, This->local_buffer);
1037 HeapFree(GetProcessHeap(), 0, This);
1039 return ref;
1042 static void dump_fmt(const WAVEFORMATEX *fmt)
1044 TRACE("wFormatTag: 0x%x (", fmt->wFormatTag);
1045 switch(fmt->wFormatTag) {
1046 case WAVE_FORMAT_PCM:
1047 TRACE("WAVE_FORMAT_PCM");
1048 break;
1049 case WAVE_FORMAT_IEEE_FLOAT:
1050 TRACE("WAVE_FORMAT_IEEE_FLOAT");
1051 break;
1052 case WAVE_FORMAT_EXTENSIBLE:
1053 TRACE("WAVE_FORMAT_EXTENSIBLE");
1054 break;
1055 default:
1056 TRACE("Unknown");
1057 break;
1059 TRACE(")\n");
1061 TRACE("nChannels: %u\n", fmt->nChannels);
1062 TRACE("nSamplesPerSec: %u\n", fmt->nSamplesPerSec);
1063 TRACE("nAvgBytesPerSec: %u\n", fmt->nAvgBytesPerSec);
1064 TRACE("nBlockAlign: %u\n", fmt->nBlockAlign);
1065 TRACE("wBitsPerSample: %u\n", fmt->wBitsPerSample);
1066 TRACE("cbSize: %u\n", fmt->cbSize);
1068 if (fmt->wFormatTag == WAVE_FORMAT_EXTENSIBLE) {
1069 WAVEFORMATEXTENSIBLE *fmtex = (void*)fmt;
1070 TRACE("dwChannelMask: %08x\n", fmtex->dwChannelMask);
1071 TRACE("Samples: %04x\n", fmtex->Samples.wReserved);
1072 TRACE("SubFormat: %s\n", wine_dbgstr_guid(&fmtex->SubFormat));
1076 static WAVEFORMATEX *clone_format(const WAVEFORMATEX *fmt)
1078 WAVEFORMATEX *ret;
1079 size_t size;
1081 if (fmt->wFormatTag == WAVE_FORMAT_EXTENSIBLE)
1082 size = sizeof(WAVEFORMATEXTENSIBLE);
1083 else
1084 size = sizeof(WAVEFORMATEX);
1086 ret = CoTaskMemAlloc(size);
1087 if (!ret)
1088 return NULL;
1090 memcpy(ret, fmt, size);
1092 ret->cbSize = size - sizeof(WAVEFORMATEX);
1094 return ret;
1097 static DWORD get_channel_mask(unsigned int channels)
1099 switch(channels) {
1100 case 0:
1101 return 0;
1102 case 1:
1103 return KSAUDIO_SPEAKER_MONO;
1104 case 2:
1105 return KSAUDIO_SPEAKER_STEREO;
1106 case 3:
1107 return KSAUDIO_SPEAKER_STEREO | SPEAKER_LOW_FREQUENCY;
1108 case 4:
1109 return KSAUDIO_SPEAKER_QUAD; /* not _SURROUND */
1110 case 5:
1111 return KSAUDIO_SPEAKER_QUAD | SPEAKER_LOW_FREQUENCY;
1112 case 6:
1113 return KSAUDIO_SPEAKER_5POINT1; /* not 5POINT1_SURROUND */
1114 case 7:
1115 return KSAUDIO_SPEAKER_5POINT1 | SPEAKER_BACK_CENTER;
1116 case 8:
1117 return KSAUDIO_SPEAKER_7POINT1_SURROUND; /* Vista deprecates 7POINT1 */
1119 FIXME("Unknown speaker configuration: %u\n", channels);
1120 return 0;
1123 static void session_init_vols(AudioSession *session, UINT channels)
1125 if (session->channel_count < channels) {
1126 UINT i;
1128 if (session->channel_vols)
1129 session->channel_vols = HeapReAlloc(GetProcessHeap(), 0,
1130 session->channel_vols, sizeof(float) * channels);
1131 else
1132 session->channel_vols = HeapAlloc(GetProcessHeap(), 0,
1133 sizeof(float) * channels);
1134 if (!session->channel_vols)
1135 return;
1137 for(i = session->channel_count; i < channels; ++i)
1138 session->channel_vols[i] = 1.f;
1140 session->channel_count = channels;
1144 static AudioSession *create_session(const GUID *guid, IMMDevice *device,
1145 UINT num_channels)
1147 AudioSession *ret;
1149 ret = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, sizeof(AudioSession));
1150 if (!ret)
1151 return NULL;
1153 memcpy(&ret->guid, guid, sizeof(GUID));
1155 ret->device = device;
1157 list_init(&ret->clients);
1159 list_add_head(&g_sessions, &ret->entry);
1161 session_init_vols(ret, num_channels);
1163 ret->master_vol = 1.f;
1165 return ret;
1168 /* if channels == 0, then this will return or create a session with
1169 * matching dataflow and GUID. otherwise, channels must also match */
1170 static HRESULT get_audio_session(const GUID *sessionguid,
1171 IMMDevice *device, UINT channels, AudioSession **out)
1173 AudioSession *session;
1175 if (!sessionguid || IsEqualGUID(sessionguid, &GUID_NULL)) {
1176 *out = create_session(&GUID_NULL, device, channels);
1177 if (!*out)
1178 return E_OUTOFMEMORY;
1180 return S_OK;
1183 *out = NULL;
1184 LIST_FOR_EACH_ENTRY(session, &g_sessions, AudioSession, entry) {
1185 if (session->device == device &&
1186 IsEqualGUID(sessionguid, &session->guid)) {
1187 session_init_vols(session, channels);
1188 *out = session;
1189 break;
1193 if (!*out) {
1194 *out = create_session(sessionguid, device, channels);
1195 if (!*out)
1196 return E_OUTOFMEMORY;
1199 return S_OK;
1202 static HRESULT pulse_spec_from_waveformat(ACImpl *This, const WAVEFORMATEX *fmt)
1204 pa_channel_map_init(&This->map);
1205 This->ss.rate = fmt->nSamplesPerSec;
1206 This->ss.format = PA_SAMPLE_INVALID;
1208 switch(fmt->wFormatTag) {
1209 case WAVE_FORMAT_IEEE_FLOAT:
1210 if (!fmt->nChannels || fmt->nChannels > 2 || fmt->wBitsPerSample != 32)
1211 break;
1212 This->ss.format = PA_SAMPLE_FLOAT32LE;
1213 pa_channel_map_init_auto(&This->map, fmt->nChannels, PA_CHANNEL_MAP_ALSA);
1214 break;
1215 case WAVE_FORMAT_PCM:
1216 if (!fmt->nChannels || fmt->nChannels > 2)
1217 break;
1218 if (fmt->wBitsPerSample == 8)
1219 This->ss.format = PA_SAMPLE_U8;
1220 else if (fmt->wBitsPerSample == 16)
1221 This->ss.format = PA_SAMPLE_S16LE;
1222 else
1223 return AUDCLNT_E_UNSUPPORTED_FORMAT;
1224 pa_channel_map_init_auto(&This->map, fmt->nChannels, PA_CHANNEL_MAP_ALSA);
1225 break;
1226 case WAVE_FORMAT_EXTENSIBLE: {
1227 WAVEFORMATEXTENSIBLE *wfe = (WAVEFORMATEXTENSIBLE*)fmt;
1228 DWORD mask = wfe->dwChannelMask;
1229 DWORD i = 0, j;
1230 if (fmt->cbSize != (sizeof(*wfe) - sizeof(*fmt)) && fmt->cbSize != sizeof(*wfe))
1231 break;
1232 if (IsEqualGUID(&wfe->SubFormat, &KSDATAFORMAT_SUBTYPE_IEEE_FLOAT) &&
1233 (!wfe->Samples.wValidBitsPerSample || wfe->Samples.wValidBitsPerSample == 32) &&
1234 fmt->wBitsPerSample == 32)
1235 This->ss.format = PA_SAMPLE_FLOAT32LE;
1236 else if (IsEqualGUID(&wfe->SubFormat, &KSDATAFORMAT_SUBTYPE_PCM)) {
1237 DWORD valid = wfe->Samples.wValidBitsPerSample;
1238 if (!valid)
1239 valid = fmt->wBitsPerSample;
1240 if (!valid || valid > fmt->wBitsPerSample)
1241 break;
1242 switch (fmt->wBitsPerSample) {
1243 case 8:
1244 if (valid == 8)
1245 This->ss.format = PA_SAMPLE_U8;
1246 break;
1247 case 16:
1248 if (valid == 16)
1249 This->ss.format = PA_SAMPLE_S16LE;
1250 break;
1251 case 24:
1252 if (valid == 24)
1253 This->ss.format = PA_SAMPLE_S24LE;
1254 break;
1255 case 32:
1256 if (valid == 24)
1257 This->ss.format = PA_SAMPLE_S24_32LE;
1258 else if (valid == 32)
1259 This->ss.format = PA_SAMPLE_S32LE;
1260 break;
1261 default:
1262 return AUDCLNT_E_UNSUPPORTED_FORMAT;
1265 This->map.channels = fmt->nChannels;
1266 if (!mask || (mask & (SPEAKER_ALL|SPEAKER_RESERVED)))
1267 mask = get_channel_mask(fmt->nChannels);
1268 for (j = 0; j < sizeof(pulse_pos_from_wfx)/sizeof(*pulse_pos_from_wfx) && i < fmt->nChannels; ++j) {
1269 if (mask & (1 << j))
1270 This->map.map[i++] = pulse_pos_from_wfx[j];
1273 /* Special case for mono since pulse appears to map it differently */
1274 if (mask == SPEAKER_FRONT_CENTER)
1275 This->map.map[0] = PA_CHANNEL_POSITION_MONO;
1277 if (i < fmt->nChannels || (mask & SPEAKER_RESERVED)) {
1278 This->map.channels = 0;
1279 ERR("Invalid channel mask: %i/%i and %x(%x)\n", i, fmt->nChannels, mask, wfe->dwChannelMask);
1280 break;
1282 break;
1284 case WAVE_FORMAT_ALAW:
1285 case WAVE_FORMAT_MULAW:
1286 if (fmt->wBitsPerSample != 8) {
1287 FIXME("Unsupported bpp %u for LAW\n", fmt->wBitsPerSample);
1288 return AUDCLNT_E_UNSUPPORTED_FORMAT;
1290 if (fmt->nChannels != 1 && fmt->nChannels != 2) {
1291 FIXME("Unsupported channels %u for LAW\n", fmt->nChannels);
1292 return AUDCLNT_E_UNSUPPORTED_FORMAT;
1294 This->ss.format = fmt->wFormatTag == WAVE_FORMAT_MULAW ? PA_SAMPLE_ULAW : PA_SAMPLE_ALAW;
1295 pa_channel_map_init_auto(&This->map, fmt->nChannels, PA_CHANNEL_MAP_ALSA);
1296 break;
1297 default:
1298 WARN("Unhandled tag %x\n", fmt->wFormatTag);
1299 return AUDCLNT_E_UNSUPPORTED_FORMAT;
1301 This->ss.channels = This->map.channels;
1302 if (!pa_channel_map_valid(&This->map) || This->ss.format == PA_SAMPLE_INVALID) {
1303 ERR("Invalid format! Channel spec valid: %i, format: %i\n", pa_channel_map_valid(&This->map), This->ss.format);
1304 return AUDCLNT_E_UNSUPPORTED_FORMAT;
1306 return S_OK;
1309 static HRESULT WINAPI AudioClient_Initialize(IAudioClient *iface,
1310 AUDCLNT_SHAREMODE mode, DWORD flags, REFERENCE_TIME duration,
1311 REFERENCE_TIME period, const WAVEFORMATEX *fmt,
1312 const GUID *sessionguid)
1314 ACImpl *This = impl_from_IAudioClient(iface);
1315 HRESULT hr = S_OK;
1316 UINT period_bytes;
1318 TRACE("(%p)->(%x, %x, %s, %s, %p, %s)\n", This, mode, flags,
1319 wine_dbgstr_longlong(duration), wine_dbgstr_longlong(period), fmt, debugstr_guid(sessionguid));
1321 if (!fmt)
1322 return E_POINTER;
1324 if (mode != AUDCLNT_SHAREMODE_SHARED && mode != AUDCLNT_SHAREMODE_EXCLUSIVE)
1325 return AUDCLNT_E_NOT_INITIALIZED;
1326 if (mode == AUDCLNT_SHAREMODE_EXCLUSIVE)
1327 return AUDCLNT_E_EXCLUSIVE_MODE_NOT_ALLOWED;
1329 if (flags & ~(AUDCLNT_STREAMFLAGS_CROSSPROCESS |
1330 AUDCLNT_STREAMFLAGS_LOOPBACK |
1331 AUDCLNT_STREAMFLAGS_EVENTCALLBACK |
1332 AUDCLNT_STREAMFLAGS_NOPERSIST |
1333 AUDCLNT_STREAMFLAGS_RATEADJUST |
1334 AUDCLNT_SESSIONFLAGS_EXPIREWHENUNOWNED |
1335 AUDCLNT_SESSIONFLAGS_DISPLAY_HIDE |
1336 AUDCLNT_SESSIONFLAGS_DISPLAY_HIDEWHENEXPIRED)) {
1337 TRACE("Unknown flags: %08x\n", flags);
1338 return E_INVALIDARG;
1341 pthread_mutex_lock(&pulse_lock);
1343 hr = pulse_connect();
1344 if (FAILED(hr)) {
1345 pthread_mutex_unlock(&pulse_lock);
1346 return hr;
1349 if (This->stream) {
1350 pthread_mutex_unlock(&pulse_lock);
1351 return AUDCLNT_E_ALREADY_INITIALIZED;
1354 hr = pulse_spec_from_waveformat(This, fmt);
1355 TRACE("Obtaining format returns %08x\n", hr);
1356 dump_fmt(fmt);
1358 if (FAILED(hr))
1359 goto exit;
1361 if (mode == AUDCLNT_SHAREMODE_SHARED) {
1362 REFERENCE_TIME def = pulse_def_period[This->dataflow == eCapture];
1363 REFERENCE_TIME min = pulse_min_period[This->dataflow == eCapture];
1365 /* Switch to low latency mode if below 2 default periods,
1366 * which is 20 ms by default, this will increase the amount
1367 * of interrupts but allows very low latency. In dsound I
1368 * managed to get a total latency of ~8ms, which is well below
1369 * default
1371 if (duration < 2 * def)
1372 period = min;
1373 else
1374 period = def;
1375 if (duration < 2 * period)
1376 duration = 2 * period;
1378 /* Uh oh, really low latency requested.. */
1379 if (duration <= 2 * period)
1380 period /= 2;
1382 period_bytes = pa_frame_size(&This->ss) * MulDiv(period, This->ss.rate, 10000000);
1384 if (duration < 20000000)
1385 This->bufsize_frames = ceil((duration / 10000000.) * fmt->nSamplesPerSec);
1386 else
1387 This->bufsize_frames = 2 * fmt->nSamplesPerSec;
1388 This->bufsize_bytes = This->bufsize_frames * pa_frame_size(&This->ss);
1390 This->share = mode;
1391 This->flags = flags;
1392 hr = pulse_stream_connect(This, period_bytes);
1393 if (SUCCEEDED(hr)) {
1394 UINT32 unalign;
1395 const pa_buffer_attr *attr = pa_stream_get_buffer_attr(This->stream);
1396 This->attr = *attr;
1397 /* Update frames according to new size */
1398 dump_attr(attr);
1399 if (This->dataflow == eRender) {
1400 if (attr->tlength < This->bufsize_bytes) {
1401 TRACE("PulseAudio buffer too small (%u < %u), using tmp buffer\n", attr->tlength, This->bufsize_bytes);
1403 This->local_buffer = HeapAlloc(GetProcessHeap(), 0, This->bufsize_bytes);
1404 if(!This->local_buffer)
1405 hr = E_OUTOFMEMORY;
1407 } else {
1408 UINT32 i, capture_packets;
1410 This->capture_period = period_bytes = attr->fragsize;
1411 if ((unalign = This->bufsize_bytes % period_bytes))
1412 This->bufsize_bytes += period_bytes - unalign;
1413 This->bufsize_frames = This->bufsize_bytes / pa_frame_size(&This->ss);
1415 capture_packets = This->bufsize_bytes / This->capture_period;
1417 This->local_buffer = HeapAlloc(GetProcessHeap(), 0, This->bufsize_bytes + capture_packets * sizeof(ACPacket));
1418 if (!This->local_buffer)
1419 hr = E_OUTOFMEMORY;
1420 else {
1421 ACPacket *cur_packet = (ACPacket*)((char*)This->local_buffer + This->bufsize_bytes);
1422 BYTE *data = This->local_buffer;
1423 silence_buffer(This->ss.format, This->local_buffer, This->bufsize_bytes);
1424 list_init(&This->packet_free_head);
1425 list_init(&This->packet_filled_head);
1426 for (i = 0; i < capture_packets; ++i, ++cur_packet) {
1427 list_add_tail(&This->packet_free_head, &cur_packet->entry);
1428 cur_packet->data = data;
1429 data += This->capture_period;
1431 assert(!This->capture_period || This->bufsize_bytes == This->capture_period * capture_packets);
1432 assert(!capture_packets || data - This->bufsize_bytes == This->local_buffer);
1436 if (SUCCEEDED(hr))
1437 hr = get_audio_session(sessionguid, This->parent, fmt->nChannels, &This->session);
1438 if (SUCCEEDED(hr))
1439 list_add_tail(&This->session->clients, &This->entry);
1441 exit:
1442 if (FAILED(hr)) {
1443 if(This->local_buffer)
1444 HeapFree(GetProcessHeap(), 0, This->local_buffer);
1445 This->local_buffer = NULL;
1446 if (This->stream) {
1447 pa_stream_disconnect(This->stream);
1448 pa_stream_unref(This->stream);
1449 This->stream = NULL;
1452 pthread_mutex_unlock(&pulse_lock);
1453 return hr;
1456 static HRESULT WINAPI AudioClient_GetBufferSize(IAudioClient *iface,
1457 UINT32 *out)
1459 ACImpl *This = impl_from_IAudioClient(iface);
1460 HRESULT hr;
1462 TRACE("(%p)->(%p)\n", This, out);
1464 if (!out)
1465 return E_POINTER;
1467 pthread_mutex_lock(&pulse_lock);
1468 hr = pulse_stream_valid(This);
1469 if (SUCCEEDED(hr))
1470 *out = This->bufsize_frames;
1471 pthread_mutex_unlock(&pulse_lock);
1473 return hr;
1476 static HRESULT WINAPI AudioClient_GetStreamLatency(IAudioClient *iface,
1477 REFERENCE_TIME *latency)
1479 ACImpl *This = impl_from_IAudioClient(iface);
1480 const pa_buffer_attr *attr;
1481 REFERENCE_TIME lat;
1482 HRESULT hr;
1484 TRACE("(%p)->(%p)\n", This, latency);
1486 if (!latency)
1487 return E_POINTER;
1489 pthread_mutex_lock(&pulse_lock);
1490 hr = pulse_stream_valid(This);
1491 if (FAILED(hr)) {
1492 pthread_mutex_unlock(&pulse_lock);
1493 return hr;
1495 attr = pa_stream_get_buffer_attr(This->stream);
1496 if (This->dataflow == eRender){
1497 lat = attr->minreq / pa_frame_size(&This->ss);
1498 lat += pulse_def_period[0];
1499 }else
1500 lat = attr->fragsize / pa_frame_size(&This->ss);
1501 *latency = 10000000;
1502 *latency *= lat;
1503 *latency /= This->ss.rate;
1504 pthread_mutex_unlock(&pulse_lock);
1505 TRACE("Latency: %u ms\n", (DWORD)(*latency / 10000));
1506 return S_OK;
1509 static void ACImpl_GetRenderPad(ACImpl *This, UINT32 *out)
1511 *out = This->pad / pa_frame_size(&This->ss);
1514 static void ACImpl_GetCapturePad(ACImpl *This, UINT32 *out)
1516 ACPacket *packet = This->locked_ptr;
1517 if (!packet && !list_empty(&This->packet_filled_head)) {
1518 packet = (ACPacket*)list_head(&This->packet_filled_head);
1519 This->locked_ptr = packet;
1520 list_remove(&packet->entry);
1522 if (out)
1523 *out = This->pad / pa_frame_size(&This->ss);
1526 static HRESULT WINAPI AudioClient_GetCurrentPadding(IAudioClient *iface,
1527 UINT32 *out)
1529 ACImpl *This = impl_from_IAudioClient(iface);
1530 HRESULT hr;
1532 TRACE("(%p)->(%p)\n", This, out);
1534 if (!out)
1535 return E_POINTER;
1537 pthread_mutex_lock(&pulse_lock);
1538 hr = pulse_stream_valid(This);
1539 if (FAILED(hr)) {
1540 pthread_mutex_unlock(&pulse_lock);
1541 return hr;
1544 if (This->dataflow == eRender)
1545 ACImpl_GetRenderPad(This, out);
1546 else
1547 ACImpl_GetCapturePad(This, out);
1548 pthread_mutex_unlock(&pulse_lock);
1550 TRACE("%p Pad: %u ms (%u)\n", This, MulDiv(*out, 1000, This->ss.rate), *out);
1551 return S_OK;
1554 static HRESULT WINAPI AudioClient_IsFormatSupported(IAudioClient *iface,
1555 AUDCLNT_SHAREMODE mode, const WAVEFORMATEX *fmt,
1556 WAVEFORMATEX **out)
1558 ACImpl *This = impl_from_IAudioClient(iface);
1559 HRESULT hr = S_OK;
1560 WAVEFORMATEX *closest = NULL;
1561 BOOL exclusive;
1563 TRACE("(%p)->(%x, %p, %p)\n", This, mode, fmt, out);
1565 if (!fmt)
1566 return E_POINTER;
1568 if (out)
1569 *out = NULL;
1571 if (mode == AUDCLNT_SHAREMODE_EXCLUSIVE) {
1572 exclusive = 1;
1573 out = NULL;
1574 } else if (mode == AUDCLNT_SHAREMODE_SHARED) {
1575 exclusive = 0;
1576 if (!out)
1577 return E_POINTER;
1578 } else
1579 return E_INVALIDARG;
1581 if (fmt->nChannels == 0)
1582 return AUDCLNT_E_UNSUPPORTED_FORMAT;
1584 closest = clone_format(fmt);
1585 if (!closest)
1586 return E_OUTOFMEMORY;
1588 dump_fmt(fmt);
1590 switch (fmt->wFormatTag) {
1591 case WAVE_FORMAT_EXTENSIBLE: {
1592 WAVEFORMATEXTENSIBLE *ext = (WAVEFORMATEXTENSIBLE*)closest;
1594 if ((fmt->cbSize != sizeof(WAVEFORMATEXTENSIBLE) - sizeof(WAVEFORMATEX) &&
1595 fmt->cbSize != sizeof(WAVEFORMATEXTENSIBLE)) ||
1596 fmt->nBlockAlign != fmt->wBitsPerSample / 8 * fmt->nChannels ||
1597 ext->Samples.wValidBitsPerSample > fmt->wBitsPerSample ||
1598 fmt->nAvgBytesPerSec != fmt->nBlockAlign * fmt->nSamplesPerSec) {
1599 hr = E_INVALIDARG;
1600 break;
1603 if (exclusive) {
1604 UINT32 mask = 0, i, channels = 0;
1606 if (!(ext->dwChannelMask & (SPEAKER_ALL | SPEAKER_RESERVED))) {
1607 for (i = 1; !(i & SPEAKER_RESERVED); i <<= 1) {
1608 if (i & ext->dwChannelMask) {
1609 mask |= i;
1610 channels++;
1614 if (channels != fmt->nChannels || (ext->dwChannelMask & ~mask)) {
1615 hr = AUDCLNT_E_UNSUPPORTED_FORMAT;
1616 break;
1618 } else {
1619 hr = AUDCLNT_E_UNSUPPORTED_FORMAT;
1620 break;
1624 if (IsEqualGUID(&ext->SubFormat, &KSDATAFORMAT_SUBTYPE_IEEE_FLOAT)) {
1625 if (fmt->wBitsPerSample != 32) {
1626 hr = E_INVALIDARG;
1627 break;
1630 if (ext->Samples.wValidBitsPerSample != fmt->wBitsPerSample) {
1631 hr = S_FALSE;
1632 ext->Samples.wValidBitsPerSample = fmt->wBitsPerSample;
1634 } else if (IsEqualGUID(&ext->SubFormat, &KSDATAFORMAT_SUBTYPE_PCM)) {
1635 if (!fmt->wBitsPerSample || fmt->wBitsPerSample > 32 || fmt->wBitsPerSample % 8) {
1636 hr = E_INVALIDARG;
1637 break;
1640 if (ext->Samples.wValidBitsPerSample != fmt->wBitsPerSample &&
1641 !(fmt->wBitsPerSample == 32 &&
1642 ext->Samples.wValidBitsPerSample == 24)) {
1643 hr = S_FALSE;
1644 ext->Samples.wValidBitsPerSample = fmt->wBitsPerSample;
1645 break;
1647 } else {
1648 hr = AUDCLNT_E_UNSUPPORTED_FORMAT;
1649 break;
1652 break;
1655 case WAVE_FORMAT_ALAW:
1656 case WAVE_FORMAT_MULAW:
1657 if (fmt->wBitsPerSample != 8) {
1658 hr = E_INVALIDARG;
1659 break;
1661 /* Fall-through */
1662 case WAVE_FORMAT_IEEE_FLOAT:
1663 if (fmt->wFormatTag == WAVE_FORMAT_IEEE_FLOAT && fmt->wBitsPerSample != 32) {
1664 hr = E_INVALIDARG;
1665 break;
1667 /* Fall-through */
1668 case WAVE_FORMAT_PCM:
1669 if (fmt->wFormatTag == WAVE_FORMAT_PCM &&
1670 (!fmt->wBitsPerSample || fmt->wBitsPerSample > 32 || fmt->wBitsPerSample % 8)) {
1671 hr = E_INVALIDARG;
1672 break;
1675 if (fmt->nChannels > 2) {
1676 hr = AUDCLNT_E_UNSUPPORTED_FORMAT;
1677 break;
1680 * fmt->cbSize, fmt->nBlockAlign and fmt->nAvgBytesPerSec seem to be
1681 * ignored, invalid values are happily accepted.
1683 break;
1684 default:
1685 hr = AUDCLNT_E_UNSUPPORTED_FORMAT;
1686 break;
1689 if (exclusive && hr != S_OK) {
1690 hr = AUDCLNT_E_UNSUPPORTED_FORMAT;
1691 CoTaskMemFree(closest);
1692 } else if (hr != S_FALSE)
1693 CoTaskMemFree(closest);
1694 else
1695 *out = closest;
1697 /* Winepulse does not currently support exclusive mode, if you know of an
1698 * application that uses it, I will correct this..
1700 if (hr == S_OK && exclusive)
1701 return This->dataflow == eCapture ? AUDCLNT_E_UNSUPPORTED_FORMAT : AUDCLNT_E_EXCLUSIVE_MODE_NOT_ALLOWED;
1703 TRACE("returning: %08x %p\n", hr, out ? *out : NULL);
1704 return hr;
1707 static HRESULT WINAPI AudioClient_GetMixFormat(IAudioClient *iface,
1708 WAVEFORMATEX **pwfx)
1710 ACImpl *This = impl_from_IAudioClient(iface);
1711 WAVEFORMATEXTENSIBLE *fmt = &pulse_fmt[This->dataflow == eCapture];
1713 TRACE("(%p)->(%p)\n", This, pwfx);
1715 if (!pwfx)
1716 return E_POINTER;
1718 *pwfx = clone_format(&fmt->Format);
1719 if (!*pwfx)
1720 return E_OUTOFMEMORY;
1721 dump_fmt(*pwfx);
1722 return S_OK;
1725 static HRESULT WINAPI AudioClient_GetDevicePeriod(IAudioClient *iface,
1726 REFERENCE_TIME *defperiod, REFERENCE_TIME *minperiod)
1728 ACImpl *This = impl_from_IAudioClient(iface);
1730 TRACE("(%p)->(%p, %p)\n", This, defperiod, minperiod);
1732 if (!defperiod && !minperiod)
1733 return E_POINTER;
1735 if (defperiod)
1736 *defperiod = pulse_def_period[This->dataflow == eCapture];
1737 if (minperiod)
1738 *minperiod = pulse_min_period[This->dataflow == eCapture];
1740 return S_OK;
1743 static HRESULT WINAPI AudioClient_Start(IAudioClient *iface)
1745 ACImpl *This = impl_from_IAudioClient(iface);
1746 HRESULT hr = S_OK;
1747 int success;
1748 pa_operation *o;
1750 TRACE("(%p)\n", This);
1752 pthread_mutex_lock(&pulse_lock);
1753 hr = pulse_stream_valid(This);
1754 if (FAILED(hr)) {
1755 pthread_mutex_unlock(&pulse_lock);
1756 return hr;
1759 if ((This->flags & AUDCLNT_STREAMFLAGS_EVENTCALLBACK) && !This->event) {
1760 pthread_mutex_unlock(&pulse_lock);
1761 return AUDCLNT_E_EVENTHANDLE_NOT_SET;
1764 if (This->started) {
1765 pthread_mutex_unlock(&pulse_lock);
1766 return AUDCLNT_E_NOT_STOPPED;
1769 if (pa_stream_is_corked(This->stream)) {
1770 o = pa_stream_cork(This->stream, 0, pulse_op_cb, &success);
1771 if (o) {
1772 while(pa_operation_get_state(o) == PA_OPERATION_RUNNING)
1773 pthread_cond_wait(&pulse_cond, &pulse_lock);
1774 pa_operation_unref(o);
1775 } else
1776 success = 0;
1777 if (!success)
1778 hr = E_FAIL;
1781 if (SUCCEEDED(hr)) {
1782 This->started = TRUE;
1783 if (This->dataflow == eRender && This->event)
1784 pa_stream_set_latency_update_callback(This->stream, pulse_latency_callback, This);
1786 pthread_mutex_unlock(&pulse_lock);
1787 return hr;
1790 static HRESULT WINAPI AudioClient_Stop(IAudioClient *iface)
1792 ACImpl *This = impl_from_IAudioClient(iface);
1793 HRESULT hr = S_OK;
1794 pa_operation *o;
1795 int success;
1797 TRACE("(%p)\n", This);
1799 pthread_mutex_lock(&pulse_lock);
1800 hr = pulse_stream_valid(This);
1801 if (FAILED(hr)) {
1802 pthread_mutex_unlock(&pulse_lock);
1803 return hr;
1806 if (!This->started) {
1807 pthread_mutex_unlock(&pulse_lock);
1808 return S_FALSE;
1811 if (This->dataflow == eRender) {
1812 o = pa_stream_cork(This->stream, 1, pulse_op_cb, &success);
1813 if (o) {
1814 while(pa_operation_get_state(o) == PA_OPERATION_RUNNING)
1815 pthread_cond_wait(&pulse_cond, &pulse_lock);
1816 pa_operation_unref(o);
1817 } else
1818 success = 0;
1819 if (!success)
1820 hr = E_FAIL;
1822 if (SUCCEEDED(hr)) {
1823 This->started = FALSE;
1825 pthread_mutex_unlock(&pulse_lock);
1826 return hr;
1829 static HRESULT WINAPI AudioClient_Reset(IAudioClient *iface)
1831 ACImpl *This = impl_from_IAudioClient(iface);
1832 HRESULT hr = S_OK;
1834 TRACE("(%p)\n", This);
1836 pthread_mutex_lock(&pulse_lock);
1837 hr = pulse_stream_valid(This);
1838 if (FAILED(hr)) {
1839 pthread_mutex_unlock(&pulse_lock);
1840 return hr;
1843 if (This->started) {
1844 pthread_mutex_unlock(&pulse_lock);
1845 return AUDCLNT_E_NOT_STOPPED;
1848 if (This->locked) {
1849 pthread_mutex_unlock(&pulse_lock);
1850 return AUDCLNT_E_BUFFER_OPERATION_PENDING;
1853 if (This->dataflow == eRender) {
1854 /* If there is still data in the render buffer it needs to be removed from the server */
1855 int success = 0;
1856 if (This->pad) {
1857 pa_operation *o = pa_stream_flush(This->stream, pulse_op_cb, &success);
1858 if (o) {
1859 while(pa_operation_get_state(o) == PA_OPERATION_RUNNING)
1860 pthread_cond_wait(&pulse_cond, &pulse_lock);
1861 pa_operation_unref(o);
1864 if (success || !This->pad){
1865 This->clock_lastpos = This->clock_written = This->pad = 0;
1866 This->wri_offs_bytes = This->lcl_offs_bytes = This->held_bytes = 0;
1868 } else {
1869 ACPacket *p;
1870 This->clock_written += This->pad;
1871 This->pad = 0;
1873 if ((p = This->locked_ptr)) {
1874 This->locked_ptr = NULL;
1875 list_add_tail(&This->packet_free_head, &p->entry);
1877 list_move_tail(&This->packet_free_head, &This->packet_filled_head);
1879 pthread_mutex_unlock(&pulse_lock);
1881 return hr;
1884 static HRESULT WINAPI AudioClient_SetEventHandle(IAudioClient *iface,
1885 HANDLE event)
1887 ACImpl *This = impl_from_IAudioClient(iface);
1888 HRESULT hr;
1890 TRACE("(%p)->(%p)\n", This, event);
1892 if (!event)
1893 return E_INVALIDARG;
1895 pthread_mutex_lock(&pulse_lock);
1896 hr = pulse_stream_valid(This);
1897 if (FAILED(hr)) {
1898 pthread_mutex_unlock(&pulse_lock);
1899 return hr;
1902 if (!(This->flags & AUDCLNT_STREAMFLAGS_EVENTCALLBACK))
1903 hr = AUDCLNT_E_EVENTHANDLE_NOT_EXPECTED;
1904 else if (This->event)
1905 hr = HRESULT_FROM_WIN32(ERROR_INVALID_NAME);
1906 else
1907 This->event = event;
1908 pthread_mutex_unlock(&pulse_lock);
1909 return hr;
1912 static HRESULT WINAPI AudioClient_GetService(IAudioClient *iface, REFIID riid,
1913 void **ppv)
1915 ACImpl *This = impl_from_IAudioClient(iface);
1916 HRESULT hr;
1918 TRACE("(%p)->(%s, %p)\n", This, debugstr_guid(riid), ppv);
1920 if (!ppv)
1921 return E_POINTER;
1922 *ppv = NULL;
1924 pthread_mutex_lock(&pulse_lock);
1925 hr = pulse_stream_valid(This);
1926 pthread_mutex_unlock(&pulse_lock);
1927 if (FAILED(hr))
1928 return hr;
1930 if (IsEqualIID(riid, &IID_IAudioRenderClient)) {
1931 if (This->dataflow != eRender)
1932 return AUDCLNT_E_WRONG_ENDPOINT_TYPE;
1933 *ppv = &This->IAudioRenderClient_iface;
1934 } else if (IsEqualIID(riid, &IID_IAudioCaptureClient)) {
1935 if (This->dataflow != eCapture)
1936 return AUDCLNT_E_WRONG_ENDPOINT_TYPE;
1937 *ppv = &This->IAudioCaptureClient_iface;
1938 } else if (IsEqualIID(riid, &IID_IAudioClock)) {
1939 *ppv = &This->IAudioClock_iface;
1940 } else if (IsEqualIID(riid, &IID_IAudioStreamVolume)) {
1941 *ppv = &This->IAudioStreamVolume_iface;
1942 } else if (IsEqualIID(riid, &IID_IAudioSessionControl) ||
1943 IsEqualIID(riid, &IID_IChannelAudioVolume) ||
1944 IsEqualIID(riid, &IID_ISimpleAudioVolume)) {
1945 if (!This->session_wrapper) {
1946 This->session_wrapper = AudioSessionWrapper_Create(This);
1947 if (!This->session_wrapper)
1948 return E_OUTOFMEMORY;
1950 if (IsEqualIID(riid, &IID_IAudioSessionControl))
1951 *ppv = &This->session_wrapper->IAudioSessionControl2_iface;
1952 else if (IsEqualIID(riid, &IID_IChannelAudioVolume))
1953 *ppv = &This->session_wrapper->IChannelAudioVolume_iface;
1954 else if (IsEqualIID(riid, &IID_ISimpleAudioVolume))
1955 *ppv = &This->session_wrapper->ISimpleAudioVolume_iface;
1958 if (*ppv) {
1959 IUnknown_AddRef((IUnknown*)*ppv);
1960 return S_OK;
1963 FIXME("stub %s\n", debugstr_guid(riid));
1964 return E_NOINTERFACE;
1967 static const IAudioClientVtbl AudioClient_Vtbl =
1969 AudioClient_QueryInterface,
1970 AudioClient_AddRef,
1971 AudioClient_Release,
1972 AudioClient_Initialize,
1973 AudioClient_GetBufferSize,
1974 AudioClient_GetStreamLatency,
1975 AudioClient_GetCurrentPadding,
1976 AudioClient_IsFormatSupported,
1977 AudioClient_GetMixFormat,
1978 AudioClient_GetDevicePeriod,
1979 AudioClient_Start,
1980 AudioClient_Stop,
1981 AudioClient_Reset,
1982 AudioClient_SetEventHandle,
1983 AudioClient_GetService
1986 static HRESULT WINAPI AudioRenderClient_QueryInterface(
1987 IAudioRenderClient *iface, REFIID riid, void **ppv)
1989 ACImpl *This = impl_from_IAudioRenderClient(iface);
1990 TRACE("(%p)->(%s, %p)\n", iface, debugstr_guid(riid), ppv);
1992 if (!ppv)
1993 return E_POINTER;
1994 *ppv = NULL;
1996 if (IsEqualIID(riid, &IID_IUnknown) ||
1997 IsEqualIID(riid, &IID_IAudioRenderClient))
1998 *ppv = iface;
1999 if (*ppv) {
2000 IUnknown_AddRef((IUnknown*)*ppv);
2001 return S_OK;
2004 if (IsEqualIID(riid, &IID_IMarshal))
2005 return IUnknown_QueryInterface(This->marshal, riid, ppv);
2007 WARN("Unknown interface %s\n", debugstr_guid(riid));
2008 return E_NOINTERFACE;
2011 static ULONG WINAPI AudioRenderClient_AddRef(IAudioRenderClient *iface)
2013 ACImpl *This = impl_from_IAudioRenderClient(iface);
2014 return AudioClient_AddRef(&This->IAudioClient_iface);
2017 static ULONG WINAPI AudioRenderClient_Release(IAudioRenderClient *iface)
2019 ACImpl *This = impl_from_IAudioRenderClient(iface);
2020 return AudioClient_Release(&This->IAudioClient_iface);
2023 static void alloc_tmp_buffer(ACImpl *This, UINT32 bytes)
2025 if(This->tmp_buffer_bytes >= bytes)
2026 return;
2028 HeapFree(GetProcessHeap(), 0, This->tmp_buffer);
2029 This->tmp_buffer = HeapAlloc(GetProcessHeap(), 0, bytes);
2030 This->tmp_buffer_bytes = bytes;
2033 static HRESULT WINAPI AudioRenderClient_GetBuffer(IAudioRenderClient *iface,
2034 UINT32 frames, BYTE **data)
2036 ACImpl *This = impl_from_IAudioRenderClient(iface);
2037 size_t avail, req, bytes = frames * pa_frame_size(&This->ss);
2038 UINT32 pad;
2039 HRESULT hr = S_OK;
2040 int ret = -1;
2042 TRACE("(%p)->(%u, %p)\n", This, frames, data);
2044 if (!data)
2045 return E_POINTER;
2046 *data = NULL;
2048 pthread_mutex_lock(&pulse_lock);
2049 hr = pulse_stream_valid(This);
2050 if (FAILED(hr) || This->locked) {
2051 pthread_mutex_unlock(&pulse_lock);
2052 return FAILED(hr) ? hr : AUDCLNT_E_OUT_OF_ORDER;
2054 if (!frames) {
2055 pthread_mutex_unlock(&pulse_lock);
2056 return S_OK;
2059 ACImpl_GetRenderPad(This, &pad);
2060 avail = This->bufsize_frames - pad;
2061 if (avail < frames || bytes > This->bufsize_bytes) {
2062 pthread_mutex_unlock(&pulse_lock);
2063 WARN("Wanted to write %u, but only %zu available\n", frames, avail);
2064 return AUDCLNT_E_BUFFER_TOO_LARGE;
2067 if(This->local_buffer){
2068 if(This->wri_offs_bytes + bytes > This->bufsize_bytes){
2069 alloc_tmp_buffer(This, bytes);
2070 *data = This->tmp_buffer;
2071 This->locked = -frames;
2072 }else{
2073 *data = This->local_buffer + This->wri_offs_bytes;
2074 This->locked = frames;
2076 }else{
2077 req = bytes;
2078 ret = pa_stream_begin_write(This->stream, &This->locked_ptr, &req);
2079 if (ret < 0 || req < bytes) {
2080 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);
2081 if (ret >= 0)
2082 pa_stream_cancel_write(This->stream);
2083 alloc_tmp_buffer(This, bytes);
2084 *data = This->tmp_buffer;
2085 This->locked_ptr = NULL;
2086 } else
2087 *data = This->locked_ptr;
2089 This->locked = frames;
2092 silence_buffer(This->ss.format, *data, bytes);
2094 pthread_mutex_unlock(&pulse_lock);
2096 return hr;
2099 static void pulse_wrap_buffer(ACImpl *This, BYTE *buffer, UINT32 written_bytes)
2101 UINT32 chunk_bytes = This->bufsize_bytes - This->wri_offs_bytes;
2103 if(written_bytes <= chunk_bytes){
2104 memcpy(This->local_buffer + This->wri_offs_bytes, buffer, written_bytes);
2105 }else{
2106 memcpy(This->local_buffer + This->wri_offs_bytes, buffer, chunk_bytes);
2107 memcpy(This->local_buffer, buffer + chunk_bytes,
2108 written_bytes - chunk_bytes);
2112 static void pulse_free_noop(void *buf)
2116 static HRESULT WINAPI AudioRenderClient_ReleaseBuffer(
2117 IAudioRenderClient *iface, UINT32 written_frames, DWORD flags)
2119 ACImpl *This = impl_from_IAudioRenderClient(iface);
2120 UINT32 written_bytes = written_frames * pa_frame_size(&This->ss);
2122 TRACE("(%p)->(%u, %x)\n", This, written_frames, flags);
2124 pthread_mutex_lock(&pulse_lock);
2125 if (!This->locked || !written_frames) {
2126 if (This->locked_ptr)
2127 pa_stream_cancel_write(This->stream);
2128 This->locked = 0;
2129 This->locked_ptr = NULL;
2130 pthread_mutex_unlock(&pulse_lock);
2131 return written_frames ? AUDCLNT_E_OUT_OF_ORDER : S_OK;
2134 if (This->locked < written_frames) {
2135 pthread_mutex_unlock(&pulse_lock);
2136 return AUDCLNT_E_INVALID_SIZE;
2139 if(This->local_buffer){
2140 BYTE *buffer;
2142 if(This->locked >= 0)
2143 buffer = This->local_buffer + This->wri_offs_bytes;
2144 else
2145 buffer = This->tmp_buffer;
2147 if(flags & AUDCLNT_BUFFERFLAGS_SILENT)
2148 silence_buffer(This->ss.format, buffer, written_bytes);
2150 if(This->locked < 0)
2151 pulse_wrap_buffer(This, buffer, written_bytes);
2153 This->wri_offs_bytes += written_bytes;
2154 This->wri_offs_bytes %= This->bufsize_bytes;
2156 This->pad += written_bytes;
2157 This->held_bytes += written_bytes;
2159 if(This->held_bytes == This->pad){
2160 int e;
2161 UINT32 to_write = min(This->attr.tlength, written_bytes);
2163 /* nothing in PA, so send data immediately */
2165 TRACE("pre-writing %u bytes\n", to_write);
2167 e = pa_stream_write(This->stream, buffer, to_write, NULL, 0, PA_SEEK_RELATIVE);
2168 if(e)
2169 ERR("pa_stream_write failed: 0x%x\n", e);
2171 This->lcl_offs_bytes += to_write;
2172 This->lcl_offs_bytes %= This->bufsize_bytes;
2173 This->held_bytes -= to_write;
2176 }else{
2177 if (This->locked_ptr) {
2178 if (flags & AUDCLNT_BUFFERFLAGS_SILENT)
2179 silence_buffer(This->ss.format, This->locked_ptr, written_bytes);
2180 pa_stream_write(This->stream, This->locked_ptr, written_bytes, NULL, 0, PA_SEEK_RELATIVE);
2181 } else {
2182 if (flags & AUDCLNT_BUFFERFLAGS_SILENT)
2183 silence_buffer(This->ss.format, This->tmp_buffer, written_bytes);
2184 pa_stream_write(This->stream, This->tmp_buffer, written_bytes, pulse_free_noop, 0, PA_SEEK_RELATIVE);
2186 This->pad += written_bytes;
2189 if (!pa_stream_is_corked(This->stream)) {
2190 int success;
2191 pa_operation *o;
2192 o = pa_stream_trigger(This->stream, pulse_op_cb, &success);
2193 if (o) {
2194 while(pa_operation_get_state(o) == PA_OPERATION_RUNNING)
2195 pthread_cond_wait(&pulse_cond, &pulse_lock);
2196 pa_operation_unref(o);
2200 This->locked = 0;
2201 This->locked_ptr = NULL;
2202 TRACE("Released %u, pad %zu\n", written_frames, This->pad / pa_frame_size(&This->ss));
2203 assert(This->pad <= This->bufsize_bytes);
2205 pthread_mutex_unlock(&pulse_lock);
2206 return S_OK;
2209 static const IAudioRenderClientVtbl AudioRenderClient_Vtbl = {
2210 AudioRenderClient_QueryInterface,
2211 AudioRenderClient_AddRef,
2212 AudioRenderClient_Release,
2213 AudioRenderClient_GetBuffer,
2214 AudioRenderClient_ReleaseBuffer
2217 static HRESULT WINAPI AudioCaptureClient_QueryInterface(
2218 IAudioCaptureClient *iface, REFIID riid, void **ppv)
2220 ACImpl *This = impl_from_IAudioCaptureClient(iface);
2221 TRACE("(%p)->(%s, %p)\n", iface, debugstr_guid(riid), ppv);
2223 if (!ppv)
2224 return E_POINTER;
2225 *ppv = NULL;
2227 if (IsEqualIID(riid, &IID_IUnknown) ||
2228 IsEqualIID(riid, &IID_IAudioCaptureClient))
2229 *ppv = iface;
2230 if (*ppv) {
2231 IUnknown_AddRef((IUnknown*)*ppv);
2232 return S_OK;
2235 if (IsEqualIID(riid, &IID_IMarshal))
2236 return IUnknown_QueryInterface(This->marshal, riid, ppv);
2238 WARN("Unknown interface %s\n", debugstr_guid(riid));
2239 return E_NOINTERFACE;
2242 static ULONG WINAPI AudioCaptureClient_AddRef(IAudioCaptureClient *iface)
2244 ACImpl *This = impl_from_IAudioCaptureClient(iface);
2245 return IAudioClient_AddRef(&This->IAudioClient_iface);
2248 static ULONG WINAPI AudioCaptureClient_Release(IAudioCaptureClient *iface)
2250 ACImpl *This = impl_from_IAudioCaptureClient(iface);
2251 return IAudioClient_Release(&This->IAudioClient_iface);
2254 static HRESULT WINAPI AudioCaptureClient_GetBuffer(IAudioCaptureClient *iface,
2255 BYTE **data, UINT32 *frames, DWORD *flags, UINT64 *devpos,
2256 UINT64 *qpcpos)
2258 ACImpl *This = impl_from_IAudioCaptureClient(iface);
2259 HRESULT hr;
2260 ACPacket *packet;
2262 TRACE("(%p)->(%p, %p, %p, %p, %p)\n", This, data, frames, flags,
2263 devpos, qpcpos);
2265 if (!data || !frames || !flags)
2266 return E_POINTER;
2268 pthread_mutex_lock(&pulse_lock);
2269 hr = pulse_stream_valid(This);
2270 if (FAILED(hr) || This->locked) {
2271 pthread_mutex_unlock(&pulse_lock);
2272 return FAILED(hr) ? hr : AUDCLNT_E_OUT_OF_ORDER;
2275 ACImpl_GetCapturePad(This, NULL);
2276 if ((packet = This->locked_ptr)) {
2277 *frames = This->capture_period / pa_frame_size(&This->ss);
2278 *flags = 0;
2279 if (packet->discont)
2280 *flags |= AUDCLNT_BUFFERFLAGS_DATA_DISCONTINUITY;
2281 if (devpos) {
2282 if (packet->discont)
2283 *devpos = (This->clock_written + This->capture_period) / pa_frame_size(&This->ss);
2284 else
2285 *devpos = This->clock_written / pa_frame_size(&This->ss);
2287 if (qpcpos)
2288 *qpcpos = packet->qpcpos;
2289 *data = packet->data;
2291 else
2292 *frames = 0;
2293 This->locked = *frames;
2294 pthread_mutex_unlock(&pulse_lock);
2295 return *frames ? S_OK : AUDCLNT_S_BUFFER_EMPTY;
2298 static HRESULT WINAPI AudioCaptureClient_ReleaseBuffer(
2299 IAudioCaptureClient *iface, UINT32 done)
2301 ACImpl *This = impl_from_IAudioCaptureClient(iface);
2303 TRACE("(%p)->(%u)\n", This, done);
2305 pthread_mutex_lock(&pulse_lock);
2306 if (!This->locked && done) {
2307 pthread_mutex_unlock(&pulse_lock);
2308 return AUDCLNT_E_OUT_OF_ORDER;
2310 if (done && This->locked != done) {
2311 pthread_mutex_unlock(&pulse_lock);
2312 return AUDCLNT_E_INVALID_SIZE;
2314 if (done) {
2315 ACPacket *packet = This->locked_ptr;
2316 This->locked_ptr = NULL;
2317 This->pad -= This->capture_period;
2318 if (packet->discont)
2319 This->clock_written += 2 * This->capture_period;
2320 else
2321 This->clock_written += This->capture_period;
2322 list_add_tail(&This->packet_free_head, &packet->entry);
2324 This->locked = 0;
2325 pthread_mutex_unlock(&pulse_lock);
2326 return S_OK;
2329 static HRESULT WINAPI AudioCaptureClient_GetNextPacketSize(
2330 IAudioCaptureClient *iface, UINT32 *frames)
2332 ACImpl *This = impl_from_IAudioCaptureClient(iface);
2334 TRACE("(%p)->(%p)\n", This, frames);
2335 if (!frames)
2336 return E_POINTER;
2338 pthread_mutex_lock(&pulse_lock);
2339 ACImpl_GetCapturePad(This, NULL);
2340 if (This->locked_ptr)
2341 *frames = This->capture_period / pa_frame_size(&This->ss);
2342 else
2343 *frames = 0;
2344 pthread_mutex_unlock(&pulse_lock);
2345 return S_OK;
2348 static const IAudioCaptureClientVtbl AudioCaptureClient_Vtbl =
2350 AudioCaptureClient_QueryInterface,
2351 AudioCaptureClient_AddRef,
2352 AudioCaptureClient_Release,
2353 AudioCaptureClient_GetBuffer,
2354 AudioCaptureClient_ReleaseBuffer,
2355 AudioCaptureClient_GetNextPacketSize
2358 static HRESULT WINAPI AudioClock_QueryInterface(IAudioClock *iface,
2359 REFIID riid, void **ppv)
2361 ACImpl *This = impl_from_IAudioClock(iface);
2363 TRACE("(%p)->(%s, %p)\n", iface, debugstr_guid(riid), ppv);
2365 if (!ppv)
2366 return E_POINTER;
2367 *ppv = NULL;
2369 if (IsEqualIID(riid, &IID_IUnknown) || IsEqualIID(riid, &IID_IAudioClock))
2370 *ppv = iface;
2371 else if (IsEqualIID(riid, &IID_IAudioClock2))
2372 *ppv = &This->IAudioClock2_iface;
2373 if (*ppv) {
2374 IUnknown_AddRef((IUnknown*)*ppv);
2375 return S_OK;
2378 if (IsEqualIID(riid, &IID_IMarshal))
2379 return IUnknown_QueryInterface(This->marshal, riid, ppv);
2381 WARN("Unknown interface %s\n", debugstr_guid(riid));
2382 return E_NOINTERFACE;
2385 static ULONG WINAPI AudioClock_AddRef(IAudioClock *iface)
2387 ACImpl *This = impl_from_IAudioClock(iface);
2388 return IAudioClient_AddRef(&This->IAudioClient_iface);
2391 static ULONG WINAPI AudioClock_Release(IAudioClock *iface)
2393 ACImpl *This = impl_from_IAudioClock(iface);
2394 return IAudioClient_Release(&This->IAudioClient_iface);
2397 static HRESULT WINAPI AudioClock_GetFrequency(IAudioClock *iface, UINT64 *freq)
2399 ACImpl *This = impl_from_IAudioClock(iface);
2400 HRESULT hr;
2402 TRACE("(%p)->(%p)\n", This, freq);
2404 pthread_mutex_lock(&pulse_lock);
2405 hr = pulse_stream_valid(This);
2406 if (SUCCEEDED(hr)) {
2407 *freq = This->ss.rate;
2408 if (This->share == AUDCLNT_SHAREMODE_SHARED)
2409 *freq *= pa_frame_size(&This->ss);
2411 pthread_mutex_unlock(&pulse_lock);
2412 return hr;
2415 static HRESULT WINAPI AudioClock_GetPosition(IAudioClock *iface, UINT64 *pos,
2416 UINT64 *qpctime)
2418 ACImpl *This = impl_from_IAudioClock(iface);
2419 HRESULT hr;
2421 TRACE("(%p)->(%p, %p)\n", This, pos, qpctime);
2423 if (!pos)
2424 return E_POINTER;
2426 pthread_mutex_lock(&pulse_lock);
2427 hr = pulse_stream_valid(This);
2428 if (FAILED(hr)) {
2429 pthread_mutex_unlock(&pulse_lock);
2430 return hr;
2433 *pos = This->clock_written;
2435 if (This->share == AUDCLNT_SHAREMODE_EXCLUSIVE)
2436 *pos /= pa_frame_size(&This->ss);
2438 /* Make time never go backwards */
2439 if (*pos < This->clock_lastpos)
2440 *pos = This->clock_lastpos;
2441 else
2442 This->clock_lastpos = *pos;
2443 pthread_mutex_unlock(&pulse_lock);
2445 TRACE("%p Position: %u\n", This, (unsigned)*pos);
2447 if (qpctime) {
2448 LARGE_INTEGER stamp, freq;
2449 QueryPerformanceCounter(&stamp);
2450 QueryPerformanceFrequency(&freq);
2451 *qpctime = (stamp.QuadPart * (INT64)10000000) / freq.QuadPart;
2454 return S_OK;
2457 static HRESULT WINAPI AudioClock_GetCharacteristics(IAudioClock *iface,
2458 DWORD *chars)
2460 ACImpl *This = impl_from_IAudioClock(iface);
2462 TRACE("(%p)->(%p)\n", This, chars);
2464 if (!chars)
2465 return E_POINTER;
2467 *chars = AUDIOCLOCK_CHARACTERISTIC_FIXED_FREQ;
2469 return S_OK;
2472 static const IAudioClockVtbl AudioClock_Vtbl =
2474 AudioClock_QueryInterface,
2475 AudioClock_AddRef,
2476 AudioClock_Release,
2477 AudioClock_GetFrequency,
2478 AudioClock_GetPosition,
2479 AudioClock_GetCharacteristics
2482 static HRESULT WINAPI AudioClock2_QueryInterface(IAudioClock2 *iface,
2483 REFIID riid, void **ppv)
2485 ACImpl *This = impl_from_IAudioClock2(iface);
2486 return IAudioClock_QueryInterface(&This->IAudioClock_iface, riid, ppv);
2489 static ULONG WINAPI AudioClock2_AddRef(IAudioClock2 *iface)
2491 ACImpl *This = impl_from_IAudioClock2(iface);
2492 return IAudioClient_AddRef(&This->IAudioClient_iface);
2495 static ULONG WINAPI AudioClock2_Release(IAudioClock2 *iface)
2497 ACImpl *This = impl_from_IAudioClock2(iface);
2498 return IAudioClient_Release(&This->IAudioClient_iface);
2501 static HRESULT WINAPI AudioClock2_GetDevicePosition(IAudioClock2 *iface,
2502 UINT64 *pos, UINT64 *qpctime)
2504 ACImpl *This = impl_from_IAudioClock2(iface);
2505 HRESULT hr = AudioClock_GetPosition(&This->IAudioClock_iface, pos, qpctime);
2506 if (SUCCEEDED(hr) && This->share == AUDCLNT_SHAREMODE_SHARED)
2507 *pos /= pa_frame_size(&This->ss);
2508 return hr;
2511 static const IAudioClock2Vtbl AudioClock2_Vtbl =
2513 AudioClock2_QueryInterface,
2514 AudioClock2_AddRef,
2515 AudioClock2_Release,
2516 AudioClock2_GetDevicePosition
2519 static HRESULT WINAPI AudioStreamVolume_QueryInterface(
2520 IAudioStreamVolume *iface, REFIID riid, void **ppv)
2522 ACImpl *This = impl_from_IAudioStreamVolume(iface);
2524 TRACE("(%p)->(%s, %p)\n", iface, debugstr_guid(riid), ppv);
2526 if (!ppv)
2527 return E_POINTER;
2528 *ppv = NULL;
2530 if (IsEqualIID(riid, &IID_IUnknown) ||
2531 IsEqualIID(riid, &IID_IAudioStreamVolume))
2532 *ppv = iface;
2533 if (*ppv) {
2534 IUnknown_AddRef((IUnknown*)*ppv);
2535 return S_OK;
2538 if (IsEqualIID(riid, &IID_IMarshal))
2539 return IUnknown_QueryInterface(This->marshal, riid, ppv);
2541 WARN("Unknown interface %s\n", debugstr_guid(riid));
2542 return E_NOINTERFACE;
2545 static ULONG WINAPI AudioStreamVolume_AddRef(IAudioStreamVolume *iface)
2547 ACImpl *This = impl_from_IAudioStreamVolume(iface);
2548 return IAudioClient_AddRef(&This->IAudioClient_iface);
2551 static ULONG WINAPI AudioStreamVolume_Release(IAudioStreamVolume *iface)
2553 ACImpl *This = impl_from_IAudioStreamVolume(iface);
2554 return IAudioClient_Release(&This->IAudioClient_iface);
2557 static HRESULT WINAPI AudioStreamVolume_GetChannelCount(
2558 IAudioStreamVolume *iface, UINT32 *out)
2560 ACImpl *This = impl_from_IAudioStreamVolume(iface);
2562 TRACE("(%p)->(%p)\n", This, out);
2564 if (!out)
2565 return E_POINTER;
2567 *out = This->ss.channels;
2569 return S_OK;
2572 struct pulse_info_cb_data {
2573 UINT32 n;
2574 float *levels;
2577 static HRESULT WINAPI AudioStreamVolume_SetAllVolumes(
2578 IAudioStreamVolume *iface, UINT32 count, const float *levels)
2580 ACImpl *This = impl_from_IAudioStreamVolume(iface);
2581 HRESULT hr;
2582 int i;
2584 TRACE("(%p)->(%d, %p)\n", This, count, levels);
2586 if (!levels)
2587 return E_POINTER;
2589 if (count != This->ss.channels)
2590 return E_INVALIDARG;
2592 pthread_mutex_lock(&pulse_lock);
2593 hr = pulse_stream_valid(This);
2594 if (FAILED(hr))
2595 goto out;
2597 for (i = 0; i < count; ++i)
2598 This->vol[i] = levels[i];
2600 out:
2601 pthread_mutex_unlock(&pulse_lock);
2602 return hr;
2605 static HRESULT WINAPI AudioStreamVolume_GetAllVolumes(
2606 IAudioStreamVolume *iface, UINT32 count, float *levels)
2608 ACImpl *This = impl_from_IAudioStreamVolume(iface);
2609 HRESULT hr;
2610 int i;
2612 TRACE("(%p)->(%d, %p)\n", This, count, levels);
2614 if (!levels)
2615 return E_POINTER;
2617 if (count != This->ss.channels)
2618 return E_INVALIDARG;
2620 pthread_mutex_lock(&pulse_lock);
2621 hr = pulse_stream_valid(This);
2622 if (FAILED(hr))
2623 goto out;
2625 for (i = 0; i < count; ++i)
2626 levels[i] = This->vol[i];
2628 out:
2629 pthread_mutex_unlock(&pulse_lock);
2630 return hr;
2633 static HRESULT WINAPI AudioStreamVolume_SetChannelVolume(
2634 IAudioStreamVolume *iface, UINT32 index, float level)
2636 ACImpl *This = impl_from_IAudioStreamVolume(iface);
2637 HRESULT hr;
2638 float volumes[PA_CHANNELS_MAX];
2640 TRACE("(%p)->(%d, %f)\n", This, index, level);
2642 if (level < 0.f || level > 1.f)
2643 return E_INVALIDARG;
2645 if (index >= This->ss.channels)
2646 return E_INVALIDARG;
2648 hr = AudioStreamVolume_GetAllVolumes(iface, This->ss.channels, volumes);
2649 volumes[index] = level;
2650 if (SUCCEEDED(hr))
2651 hr = AudioStreamVolume_SetAllVolumes(iface, This->ss.channels, volumes);
2652 return hr;
2655 static HRESULT WINAPI AudioStreamVolume_GetChannelVolume(
2656 IAudioStreamVolume *iface, UINT32 index, float *level)
2658 ACImpl *This = impl_from_IAudioStreamVolume(iface);
2659 float volumes[PA_CHANNELS_MAX];
2660 HRESULT hr;
2662 TRACE("(%p)->(%d, %p)\n", This, index, level);
2664 if (!level)
2665 return E_POINTER;
2667 if (index >= This->ss.channels)
2668 return E_INVALIDARG;
2670 hr = AudioStreamVolume_GetAllVolumes(iface, This->ss.channels, volumes);
2671 if (SUCCEEDED(hr))
2672 *level = volumes[index];
2673 return hr;
2676 static const IAudioStreamVolumeVtbl AudioStreamVolume_Vtbl =
2678 AudioStreamVolume_QueryInterface,
2679 AudioStreamVolume_AddRef,
2680 AudioStreamVolume_Release,
2681 AudioStreamVolume_GetChannelCount,
2682 AudioStreamVolume_SetChannelVolume,
2683 AudioStreamVolume_GetChannelVolume,
2684 AudioStreamVolume_SetAllVolumes,
2685 AudioStreamVolume_GetAllVolumes
2688 static AudioSessionWrapper *AudioSessionWrapper_Create(ACImpl *client)
2690 AudioSessionWrapper *ret;
2692 ret = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY,
2693 sizeof(AudioSessionWrapper));
2694 if (!ret)
2695 return NULL;
2697 ret->IAudioSessionControl2_iface.lpVtbl = &AudioSessionControl2_Vtbl;
2698 ret->ISimpleAudioVolume_iface.lpVtbl = &SimpleAudioVolume_Vtbl;
2699 ret->IChannelAudioVolume_iface.lpVtbl = &ChannelAudioVolume_Vtbl;
2701 ret->ref = !client;
2703 ret->client = client;
2704 if (client) {
2705 ret->session = client->session;
2706 AudioClient_AddRef(&client->IAudioClient_iface);
2709 return ret;
2712 static HRESULT WINAPI AudioSessionControl_QueryInterface(
2713 IAudioSessionControl2 *iface, REFIID riid, void **ppv)
2715 TRACE("(%p)->(%s, %p)\n", iface, debugstr_guid(riid), ppv);
2717 if (!ppv)
2718 return E_POINTER;
2719 *ppv = NULL;
2721 if (IsEqualIID(riid, &IID_IUnknown) ||
2722 IsEqualIID(riid, &IID_IAudioSessionControl) ||
2723 IsEqualIID(riid, &IID_IAudioSessionControl2))
2724 *ppv = iface;
2725 if (*ppv) {
2726 IUnknown_AddRef((IUnknown*)*ppv);
2727 return S_OK;
2730 WARN("Unknown interface %s\n", debugstr_guid(riid));
2731 return E_NOINTERFACE;
2734 static ULONG WINAPI AudioSessionControl_AddRef(IAudioSessionControl2 *iface)
2736 AudioSessionWrapper *This = impl_from_IAudioSessionControl2(iface);
2737 ULONG ref;
2738 ref = InterlockedIncrement(&This->ref);
2739 TRACE("(%p) Refcount now %u\n", This, ref);
2740 return ref;
2743 static ULONG WINAPI AudioSessionControl_Release(IAudioSessionControl2 *iface)
2745 AudioSessionWrapper *This = impl_from_IAudioSessionControl2(iface);
2746 ULONG ref;
2747 ref = InterlockedDecrement(&This->ref);
2748 TRACE("(%p) Refcount now %u\n", This, ref);
2749 if (!ref) {
2750 if (This->client) {
2751 This->client->session_wrapper = NULL;
2752 AudioClient_Release(&This->client->IAudioClient_iface);
2754 HeapFree(GetProcessHeap(), 0, This);
2756 return ref;
2759 static HRESULT WINAPI AudioSessionControl_GetState(IAudioSessionControl2 *iface,
2760 AudioSessionState *state)
2762 AudioSessionWrapper *This = impl_from_IAudioSessionControl2(iface);
2763 ACImpl *client;
2765 TRACE("(%p)->(%p)\n", This, state);
2767 if (!state)
2768 return NULL_PTR_ERR;
2770 pthread_mutex_lock(&pulse_lock);
2771 if (list_empty(&This->session->clients)) {
2772 *state = AudioSessionStateExpired;
2773 goto out;
2775 LIST_FOR_EACH_ENTRY(client, &This->session->clients, ACImpl, entry) {
2776 if (client->started) {
2777 *state = AudioSessionStateActive;
2778 goto out;
2781 *state = AudioSessionStateInactive;
2783 out:
2784 pthread_mutex_unlock(&pulse_lock);
2785 return S_OK;
2788 static HRESULT WINAPI AudioSessionControl_GetDisplayName(
2789 IAudioSessionControl2 *iface, WCHAR **name)
2791 AudioSessionWrapper *This = impl_from_IAudioSessionControl2(iface);
2793 FIXME("(%p)->(%p) - stub\n", This, name);
2795 return E_NOTIMPL;
2798 static HRESULT WINAPI AudioSessionControl_SetDisplayName(
2799 IAudioSessionControl2 *iface, const WCHAR *name, const GUID *session)
2801 AudioSessionWrapper *This = impl_from_IAudioSessionControl2(iface);
2803 FIXME("(%p)->(%p, %s) - stub\n", This, name, debugstr_guid(session));
2805 return E_NOTIMPL;
2808 static HRESULT WINAPI AudioSessionControl_GetIconPath(
2809 IAudioSessionControl2 *iface, WCHAR **path)
2811 AudioSessionWrapper *This = impl_from_IAudioSessionControl2(iface);
2813 FIXME("(%p)->(%p) - stub\n", This, path);
2815 return E_NOTIMPL;
2818 static HRESULT WINAPI AudioSessionControl_SetIconPath(
2819 IAudioSessionControl2 *iface, const WCHAR *path, const GUID *session)
2821 AudioSessionWrapper *This = impl_from_IAudioSessionControl2(iface);
2823 FIXME("(%p)->(%p, %s) - stub\n", This, path, debugstr_guid(session));
2825 return E_NOTIMPL;
2828 static HRESULT WINAPI AudioSessionControl_GetGroupingParam(
2829 IAudioSessionControl2 *iface, GUID *group)
2831 AudioSessionWrapper *This = impl_from_IAudioSessionControl2(iface);
2833 FIXME("(%p)->(%p) - stub\n", This, group);
2835 return E_NOTIMPL;
2838 static HRESULT WINAPI AudioSessionControl_SetGroupingParam(
2839 IAudioSessionControl2 *iface, const GUID *group, const GUID *session)
2841 AudioSessionWrapper *This = impl_from_IAudioSessionControl2(iface);
2843 FIXME("(%p)->(%s, %s) - stub\n", This, debugstr_guid(group),
2844 debugstr_guid(session));
2846 return E_NOTIMPL;
2849 static HRESULT WINAPI AudioSessionControl_RegisterAudioSessionNotification(
2850 IAudioSessionControl2 *iface, IAudioSessionEvents *events)
2852 AudioSessionWrapper *This = impl_from_IAudioSessionControl2(iface);
2854 FIXME("(%p)->(%p) - stub\n", This, events);
2856 return S_OK;
2859 static HRESULT WINAPI AudioSessionControl_UnregisterAudioSessionNotification(
2860 IAudioSessionControl2 *iface, IAudioSessionEvents *events)
2862 AudioSessionWrapper *This = impl_from_IAudioSessionControl2(iface);
2864 FIXME("(%p)->(%p) - stub\n", This, events);
2866 return S_OK;
2869 static HRESULT WINAPI AudioSessionControl_GetSessionIdentifier(
2870 IAudioSessionControl2 *iface, WCHAR **id)
2872 AudioSessionWrapper *This = impl_from_IAudioSessionControl2(iface);
2874 FIXME("(%p)->(%p) - stub\n", This, id);
2876 return E_NOTIMPL;
2879 static HRESULT WINAPI AudioSessionControl_GetSessionInstanceIdentifier(
2880 IAudioSessionControl2 *iface, WCHAR **id)
2882 AudioSessionWrapper *This = impl_from_IAudioSessionControl2(iface);
2884 FIXME("(%p)->(%p) - stub\n", This, id);
2886 return E_NOTIMPL;
2889 static HRESULT WINAPI AudioSessionControl_GetProcessId(
2890 IAudioSessionControl2 *iface, DWORD *pid)
2892 AudioSessionWrapper *This = impl_from_IAudioSessionControl2(iface);
2894 TRACE("(%p)->(%p)\n", This, pid);
2896 if (!pid)
2897 return E_POINTER;
2899 *pid = GetCurrentProcessId();
2901 return S_OK;
2904 static HRESULT WINAPI AudioSessionControl_IsSystemSoundsSession(
2905 IAudioSessionControl2 *iface)
2907 AudioSessionWrapper *This = impl_from_IAudioSessionControl2(iface);
2909 TRACE("(%p)\n", This);
2911 return S_FALSE;
2914 static HRESULT WINAPI AudioSessionControl_SetDuckingPreference(
2915 IAudioSessionControl2 *iface, BOOL optout)
2917 AudioSessionWrapper *This = impl_from_IAudioSessionControl2(iface);
2919 TRACE("(%p)->(%d)\n", This, optout);
2921 return S_OK;
2924 static const IAudioSessionControl2Vtbl AudioSessionControl2_Vtbl =
2926 AudioSessionControl_QueryInterface,
2927 AudioSessionControl_AddRef,
2928 AudioSessionControl_Release,
2929 AudioSessionControl_GetState,
2930 AudioSessionControl_GetDisplayName,
2931 AudioSessionControl_SetDisplayName,
2932 AudioSessionControl_GetIconPath,
2933 AudioSessionControl_SetIconPath,
2934 AudioSessionControl_GetGroupingParam,
2935 AudioSessionControl_SetGroupingParam,
2936 AudioSessionControl_RegisterAudioSessionNotification,
2937 AudioSessionControl_UnregisterAudioSessionNotification,
2938 AudioSessionControl_GetSessionIdentifier,
2939 AudioSessionControl_GetSessionInstanceIdentifier,
2940 AudioSessionControl_GetProcessId,
2941 AudioSessionControl_IsSystemSoundsSession,
2942 AudioSessionControl_SetDuckingPreference
2945 typedef struct _SessionMgr {
2946 IAudioSessionManager2 IAudioSessionManager2_iface;
2948 LONG ref;
2950 IMMDevice *device;
2951 } SessionMgr;
2953 static HRESULT WINAPI AudioSessionManager_QueryInterface(IAudioSessionManager2 *iface,
2954 REFIID riid, void **ppv)
2956 TRACE("(%p)->(%s, %p)\n", iface, debugstr_guid(riid), ppv);
2958 if (!ppv)
2959 return E_POINTER;
2960 *ppv = NULL;
2962 if (IsEqualIID(riid, &IID_IUnknown) ||
2963 IsEqualIID(riid, &IID_IAudioSessionManager) ||
2964 IsEqualIID(riid, &IID_IAudioSessionManager2))
2965 *ppv = iface;
2966 if (*ppv) {
2967 IUnknown_AddRef((IUnknown*)*ppv);
2968 return S_OK;
2971 WARN("Unknown interface %s\n", debugstr_guid(riid));
2972 return E_NOINTERFACE;
2975 static inline SessionMgr *impl_from_IAudioSessionManager2(IAudioSessionManager2 *iface)
2977 return CONTAINING_RECORD(iface, SessionMgr, IAudioSessionManager2_iface);
2980 static ULONG WINAPI AudioSessionManager_AddRef(IAudioSessionManager2 *iface)
2982 SessionMgr *This = impl_from_IAudioSessionManager2(iface);
2983 ULONG ref;
2984 ref = InterlockedIncrement(&This->ref);
2985 TRACE("(%p) Refcount now %u\n", This, ref);
2986 return ref;
2989 static ULONG WINAPI AudioSessionManager_Release(IAudioSessionManager2 *iface)
2991 SessionMgr *This = impl_from_IAudioSessionManager2(iface);
2992 ULONG ref;
2993 ref = InterlockedDecrement(&This->ref);
2994 TRACE("(%p) Refcount now %u\n", This, ref);
2995 if (!ref)
2996 HeapFree(GetProcessHeap(), 0, This);
2997 return ref;
3000 static HRESULT WINAPI AudioSessionManager_GetAudioSessionControl(
3001 IAudioSessionManager2 *iface, const GUID *session_guid, DWORD flags,
3002 IAudioSessionControl **out)
3004 SessionMgr *This = impl_from_IAudioSessionManager2(iface);
3005 AudioSession *session;
3006 AudioSessionWrapper *wrapper;
3007 HRESULT hr;
3009 TRACE("(%p)->(%s, %x, %p)\n", This, debugstr_guid(session_guid),
3010 flags, out);
3012 hr = get_audio_session(session_guid, This->device, 0, &session);
3013 if (FAILED(hr))
3014 return hr;
3016 wrapper = AudioSessionWrapper_Create(NULL);
3017 if (!wrapper)
3018 return E_OUTOFMEMORY;
3020 wrapper->session = session;
3022 *out = (IAudioSessionControl*)&wrapper->IAudioSessionControl2_iface;
3024 return S_OK;
3027 static HRESULT WINAPI AudioSessionManager_GetSimpleAudioVolume(
3028 IAudioSessionManager2 *iface, const GUID *session_guid, DWORD flags,
3029 ISimpleAudioVolume **out)
3031 SessionMgr *This = impl_from_IAudioSessionManager2(iface);
3032 AudioSession *session;
3033 AudioSessionWrapper *wrapper;
3034 HRESULT hr;
3036 TRACE("(%p)->(%s, %x, %p)\n", This, debugstr_guid(session_guid),
3037 flags, out);
3039 hr = get_audio_session(session_guid, This->device, 0, &session);
3040 if (FAILED(hr))
3041 return hr;
3043 wrapper = AudioSessionWrapper_Create(NULL);
3044 if (!wrapper)
3045 return E_OUTOFMEMORY;
3047 wrapper->session = session;
3049 *out = &wrapper->ISimpleAudioVolume_iface;
3051 return S_OK;
3054 static HRESULT WINAPI AudioSessionManager_GetSessionEnumerator(
3055 IAudioSessionManager2 *iface, IAudioSessionEnumerator **out)
3057 SessionMgr *This = impl_from_IAudioSessionManager2(iface);
3058 FIXME("(%p)->(%p) - stub\n", This, out);
3059 return E_NOTIMPL;
3062 static HRESULT WINAPI AudioSessionManager_RegisterSessionNotification(
3063 IAudioSessionManager2 *iface, IAudioSessionNotification *notification)
3065 SessionMgr *This = impl_from_IAudioSessionManager2(iface);
3066 FIXME("(%p)->(%p) - stub\n", This, notification);
3067 return E_NOTIMPL;
3070 static HRESULT WINAPI AudioSessionManager_UnregisterSessionNotification(
3071 IAudioSessionManager2 *iface, IAudioSessionNotification *notification)
3073 SessionMgr *This = impl_from_IAudioSessionManager2(iface);
3074 FIXME("(%p)->(%p) - stub\n", This, notification);
3075 return E_NOTIMPL;
3078 static HRESULT WINAPI AudioSessionManager_RegisterDuckNotification(
3079 IAudioSessionManager2 *iface, const WCHAR *session_id,
3080 IAudioVolumeDuckNotification *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_UnregisterDuckNotification(
3088 IAudioSessionManager2 *iface,
3089 IAudioVolumeDuckNotification *notification)
3091 SessionMgr *This = impl_from_IAudioSessionManager2(iface);
3092 FIXME("(%p)->(%p) - stub\n", This, notification);
3093 return E_NOTIMPL;
3096 static const IAudioSessionManager2Vtbl AudioSessionManager2_Vtbl =
3098 AudioSessionManager_QueryInterface,
3099 AudioSessionManager_AddRef,
3100 AudioSessionManager_Release,
3101 AudioSessionManager_GetAudioSessionControl,
3102 AudioSessionManager_GetSimpleAudioVolume,
3103 AudioSessionManager_GetSessionEnumerator,
3104 AudioSessionManager_RegisterSessionNotification,
3105 AudioSessionManager_UnregisterSessionNotification,
3106 AudioSessionManager_RegisterDuckNotification,
3107 AudioSessionManager_UnregisterDuckNotification
3110 static HRESULT WINAPI SimpleAudioVolume_QueryInterface(
3111 ISimpleAudioVolume *iface, REFIID riid, void **ppv)
3113 TRACE("(%p)->(%s, %p)\n", iface, debugstr_guid(riid), ppv);
3115 if (!ppv)
3116 return E_POINTER;
3117 *ppv = NULL;
3119 if (IsEqualIID(riid, &IID_IUnknown) ||
3120 IsEqualIID(riid, &IID_ISimpleAudioVolume))
3121 *ppv = iface;
3122 if (*ppv) {
3123 IUnknown_AddRef((IUnknown*)*ppv);
3124 return S_OK;
3127 WARN("Unknown interface %s\n", debugstr_guid(riid));
3128 return E_NOINTERFACE;
3131 static ULONG WINAPI SimpleAudioVolume_AddRef(ISimpleAudioVolume *iface)
3133 AudioSessionWrapper *This = impl_from_ISimpleAudioVolume(iface);
3134 return AudioSessionControl_AddRef(&This->IAudioSessionControl2_iface);
3137 static ULONG WINAPI SimpleAudioVolume_Release(ISimpleAudioVolume *iface)
3139 AudioSessionWrapper *This = impl_from_ISimpleAudioVolume(iface);
3140 return AudioSessionControl_Release(&This->IAudioSessionControl2_iface);
3143 static HRESULT WINAPI SimpleAudioVolume_SetMasterVolume(
3144 ISimpleAudioVolume *iface, float level, const GUID *context)
3146 AudioSessionWrapper *This = impl_from_ISimpleAudioVolume(iface);
3147 AudioSession *session = This->session;
3149 TRACE("(%p)->(%f, %s)\n", session, level, wine_dbgstr_guid(context));
3151 if (level < 0.f || level > 1.f)
3152 return E_INVALIDARG;
3154 if (context)
3155 FIXME("Notifications not supported yet\n");
3157 TRACE("PulseAudio does not support session volume control\n");
3159 pthread_mutex_lock(&pulse_lock);
3160 session->master_vol = level;
3161 pthread_mutex_unlock(&pulse_lock);
3163 return S_OK;
3166 static HRESULT WINAPI SimpleAudioVolume_GetMasterVolume(
3167 ISimpleAudioVolume *iface, float *level)
3169 AudioSessionWrapper *This = impl_from_ISimpleAudioVolume(iface);
3170 AudioSession *session = This->session;
3172 TRACE("(%p)->(%p)\n", session, level);
3174 if (!level)
3175 return NULL_PTR_ERR;
3177 *level = session->master_vol;
3179 return S_OK;
3182 static HRESULT WINAPI SimpleAudioVolume_SetMute(ISimpleAudioVolume *iface,
3183 BOOL mute, const GUID *context)
3185 AudioSessionWrapper *This = impl_from_ISimpleAudioVolume(iface);
3186 AudioSession *session = This->session;
3188 TRACE("(%p)->(%u, %p)\n", session, mute, context);
3190 if (context)
3191 FIXME("Notifications not supported yet\n");
3193 session->mute = mute;
3195 return S_OK;
3198 static HRESULT WINAPI SimpleAudioVolume_GetMute(ISimpleAudioVolume *iface,
3199 BOOL *mute)
3201 AudioSessionWrapper *This = impl_from_ISimpleAudioVolume(iface);
3202 AudioSession *session = This->session;
3204 TRACE("(%p)->(%p)\n", session, mute);
3206 if (!mute)
3207 return NULL_PTR_ERR;
3209 *mute = session->mute;
3211 return S_OK;
3214 static const ISimpleAudioVolumeVtbl SimpleAudioVolume_Vtbl =
3216 SimpleAudioVolume_QueryInterface,
3217 SimpleAudioVolume_AddRef,
3218 SimpleAudioVolume_Release,
3219 SimpleAudioVolume_SetMasterVolume,
3220 SimpleAudioVolume_GetMasterVolume,
3221 SimpleAudioVolume_SetMute,
3222 SimpleAudioVolume_GetMute
3225 static HRESULT WINAPI ChannelAudioVolume_QueryInterface(
3226 IChannelAudioVolume *iface, REFIID riid, void **ppv)
3228 TRACE("(%p)->(%s, %p)\n", iface, debugstr_guid(riid), ppv);
3230 if (!ppv)
3231 return E_POINTER;
3232 *ppv = NULL;
3234 if (IsEqualIID(riid, &IID_IUnknown) ||
3235 IsEqualIID(riid, &IID_IChannelAudioVolume))
3236 *ppv = iface;
3237 if (*ppv) {
3238 IUnknown_AddRef((IUnknown*)*ppv);
3239 return S_OK;
3242 WARN("Unknown interface %s\n", debugstr_guid(riid));
3243 return E_NOINTERFACE;
3246 static ULONG WINAPI ChannelAudioVolume_AddRef(IChannelAudioVolume *iface)
3248 AudioSessionWrapper *This = impl_from_IChannelAudioVolume(iface);
3249 return AudioSessionControl_AddRef(&This->IAudioSessionControl2_iface);
3252 static ULONG WINAPI ChannelAudioVolume_Release(IChannelAudioVolume *iface)
3254 AudioSessionWrapper *This = impl_from_IChannelAudioVolume(iface);
3255 return AudioSessionControl_Release(&This->IAudioSessionControl2_iface);
3258 static HRESULT WINAPI ChannelAudioVolume_GetChannelCount(
3259 IChannelAudioVolume *iface, UINT32 *out)
3261 AudioSessionWrapper *This = impl_from_IChannelAudioVolume(iface);
3262 AudioSession *session = This->session;
3264 TRACE("(%p)->(%p)\n", session, out);
3266 if (!out)
3267 return NULL_PTR_ERR;
3269 *out = session->channel_count;
3271 return S_OK;
3274 static HRESULT WINAPI ChannelAudioVolume_SetChannelVolume(
3275 IChannelAudioVolume *iface, UINT32 index, float level,
3276 const GUID *context)
3278 AudioSessionWrapper *This = impl_from_IChannelAudioVolume(iface);
3279 AudioSession *session = This->session;
3281 TRACE("(%p)->(%d, %f, %s)\n", session, index, level,
3282 wine_dbgstr_guid(context));
3284 if (level < 0.f || level > 1.f)
3285 return E_INVALIDARG;
3287 if (index >= session->channel_count)
3288 return E_INVALIDARG;
3290 if (context)
3291 FIXME("Notifications not supported yet\n");
3293 TRACE("PulseAudio does not support session volume control\n");
3295 pthread_mutex_lock(&pulse_lock);
3296 session->channel_vols[index] = level;
3297 pthread_mutex_unlock(&pulse_lock);
3299 return S_OK;
3302 static HRESULT WINAPI ChannelAudioVolume_GetChannelVolume(
3303 IChannelAudioVolume *iface, UINT32 index, float *level)
3305 AudioSessionWrapper *This = impl_from_IChannelAudioVolume(iface);
3306 AudioSession *session = This->session;
3308 TRACE("(%p)->(%d, %p)\n", session, index, level);
3310 if (!level)
3311 return NULL_PTR_ERR;
3313 if (index >= session->channel_count)
3314 return E_INVALIDARG;
3316 *level = session->channel_vols[index];
3318 return S_OK;
3321 static HRESULT WINAPI ChannelAudioVolume_SetAllVolumes(
3322 IChannelAudioVolume *iface, UINT32 count, const float *levels,
3323 const GUID *context)
3325 AudioSessionWrapper *This = impl_from_IChannelAudioVolume(iface);
3326 AudioSession *session = This->session;
3327 int i;
3329 TRACE("(%p)->(%d, %p, %s)\n", session, count, levels,
3330 wine_dbgstr_guid(context));
3332 if (!levels)
3333 return NULL_PTR_ERR;
3335 if (count != session->channel_count)
3336 return E_INVALIDARG;
3338 if (context)
3339 FIXME("Notifications not supported yet\n");
3341 TRACE("PulseAudio does not support session volume control\n");
3343 pthread_mutex_lock(&pulse_lock);
3344 for(i = 0; i < count; ++i)
3345 session->channel_vols[i] = levels[i];
3346 pthread_mutex_unlock(&pulse_lock);
3347 return S_OK;
3350 static HRESULT WINAPI ChannelAudioVolume_GetAllVolumes(
3351 IChannelAudioVolume *iface, UINT32 count, float *levels)
3353 AudioSessionWrapper *This = impl_from_IChannelAudioVolume(iface);
3354 AudioSession *session = This->session;
3355 int i;
3357 TRACE("(%p)->(%d, %p)\n", session, count, levels);
3359 if (!levels)
3360 return NULL_PTR_ERR;
3362 if (count != session->channel_count)
3363 return E_INVALIDARG;
3365 for(i = 0; i < count; ++i)
3366 levels[i] = session->channel_vols[i];
3368 return S_OK;
3371 static const IChannelAudioVolumeVtbl ChannelAudioVolume_Vtbl =
3373 ChannelAudioVolume_QueryInterface,
3374 ChannelAudioVolume_AddRef,
3375 ChannelAudioVolume_Release,
3376 ChannelAudioVolume_GetChannelCount,
3377 ChannelAudioVolume_SetChannelVolume,
3378 ChannelAudioVolume_GetChannelVolume,
3379 ChannelAudioVolume_SetAllVolumes,
3380 ChannelAudioVolume_GetAllVolumes
3383 HRESULT WINAPI AUDDRV_GetAudioSessionManager(IMMDevice *device,
3384 IAudioSessionManager2 **out)
3386 SessionMgr *This = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, sizeof(SessionMgr));
3387 *out = NULL;
3388 if (!This)
3389 return E_OUTOFMEMORY;
3390 This->IAudioSessionManager2_iface.lpVtbl = &AudioSessionManager2_Vtbl;
3391 This->device = device;
3392 This->ref = 1;
3393 *out = &This->IAudioSessionManager2_iface;
3394 return S_OK;
3397 HRESULT WINAPI AUDDRV_GetPropValue(GUID *guid, const PROPERTYKEY *prop, PROPVARIANT *out)
3399 TRACE("%s, (%s,%u), %p\n", wine_dbgstr_guid(guid), wine_dbgstr_guid(&prop->fmtid), prop->pid, out);
3401 if (IsEqualGUID(guid, &pulse_render_guid) && IsEqualPropertyKey(*prop, PKEY_AudioEndpoint_PhysicalSpeakers)) {
3402 out->vt = VT_UI4;
3403 out->u.ulVal = g_phys_speakers_mask;
3405 return out->u.ulVal ? S_OK : E_FAIL;
3408 return E_NOTIMPL;