winepulse: Include a period in the stream latency.
[wine.git] / dlls / winepulse.drv / mmdevdrv.c
blob3f5e3d5e022fa7aa318875eac06f801aa7d39013
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);
60 WINE_DECLARE_DEBUG_CHANNEL(winediag);
62 #define NULL_PTR_ERR MAKE_HRESULT(SEVERITY_ERROR, FACILITY_WIN32, RPC_X_NULL_REF_POINTER)
64 /* From <dlls/mmdevapi/mmdevapi.h> */
65 enum DriverPriority {
66 Priority_Unavailable = 0,
67 Priority_Low,
68 Priority_Neutral,
69 Priority_Preferred
72 static const REFERENCE_TIME MinimumPeriod = 30000;
73 static const REFERENCE_TIME DefaultPeriod = 100000;
75 static pa_context *pulse_ctx;
76 static pa_mainloop *pulse_ml;
78 static HANDLE pulse_thread;
79 static pthread_mutex_t pulse_lock;
80 static pthread_cond_t pulse_cond = PTHREAD_COND_INITIALIZER;
81 static struct list g_sessions = LIST_INIT(g_sessions);
83 static UINT g_phys_speakers_mask = 0;
85 /* Mixer format + period times */
86 static WAVEFORMATEXTENSIBLE pulse_fmt[2];
87 static REFERENCE_TIME pulse_min_period[2], pulse_def_period[2];
89 static GUID pulse_render_guid =
90 { 0xfd47d9cc, 0x4218, 0x4135, { 0x9c, 0xe2, 0x0c, 0x19, 0x5c, 0x87, 0x40, 0x5b } };
91 static GUID pulse_capture_guid =
92 { 0x25da76d0, 0x033c, 0x4235, { 0x90, 0x02, 0x19, 0xf4, 0x88, 0x94, 0xac, 0x6f } };
94 BOOL WINAPI DllMain(HINSTANCE dll, DWORD reason, void *reserved)
96 if (reason == DLL_PROCESS_ATTACH) {
97 pthread_mutexattr_t attr;
99 DisableThreadLibraryCalls(dll);
101 pthread_mutexattr_init(&attr);
102 pthread_mutexattr_setprotocol(&attr, PTHREAD_PRIO_INHERIT);
104 if (pthread_mutex_init(&pulse_lock, &attr) != 0)
105 pthread_mutex_init(&pulse_lock, NULL);
106 } else if (reason == DLL_PROCESS_DETACH) {
107 if (pulse_thread)
108 SetThreadPriority(pulse_thread, 0);
109 if (pulse_ctx) {
110 pa_context_disconnect(pulse_ctx);
111 pa_context_unref(pulse_ctx);
113 if (pulse_ml)
114 pa_mainloop_quit(pulse_ml, 0);
115 if (pulse_thread) {
116 WaitForSingleObject(pulse_thread, INFINITE);
117 CloseHandle(pulse_thread);
120 return TRUE;
123 typedef struct ACImpl ACImpl;
125 typedef struct _AudioSession {
126 GUID guid;
127 struct list clients;
129 IMMDevice *device;
131 float master_vol;
132 UINT32 channel_count;
133 float *channel_vols;
134 BOOL mute;
136 struct list entry;
137 } AudioSession;
139 typedef struct _AudioSessionWrapper {
140 IAudioSessionControl2 IAudioSessionControl2_iface;
141 IChannelAudioVolume IChannelAudioVolume_iface;
142 ISimpleAudioVolume ISimpleAudioVolume_iface;
144 LONG ref;
146 ACImpl *client;
147 AudioSession *session;
148 } AudioSessionWrapper;
150 typedef struct _ACPacket {
151 struct list entry;
152 UINT64 qpcpos;
153 BYTE *data;
154 UINT32 discont;
155 } ACPacket;
157 struct ACImpl {
158 IAudioClient IAudioClient_iface;
159 IAudioRenderClient IAudioRenderClient_iface;
160 IAudioCaptureClient IAudioCaptureClient_iface;
161 IAudioClock IAudioClock_iface;
162 IAudioClock2 IAudioClock2_iface;
163 IAudioStreamVolume IAudioStreamVolume_iface;
164 IUnknown *marshal;
165 IMMDevice *parent;
166 struct list entry;
167 float vol[PA_CHANNELS_MAX];
169 LONG ref;
170 EDataFlow dataflow;
171 DWORD flags;
172 AUDCLNT_SHAREMODE share;
173 HANDLE event;
175 UINT32 bufsize_frames, bufsize_bytes, locked, capture_period, pad, started, peek_ofs;
176 void *locked_ptr, *tmp_buffer;
178 pa_stream *stream;
179 pa_sample_spec ss;
180 pa_channel_map map;
182 INT64 clock_lastpos, clock_written;
184 AudioSession *session;
185 AudioSessionWrapper *session_wrapper;
186 struct list packet_free_head;
187 struct list packet_filled_head;
190 static const WCHAR defaultW[] = {'P','u','l','s','e','a','u','d','i','o',0};
192 static const IAudioClientVtbl AudioClient_Vtbl;
193 static const IAudioRenderClientVtbl AudioRenderClient_Vtbl;
194 static const IAudioCaptureClientVtbl AudioCaptureClient_Vtbl;
195 static const IAudioSessionControl2Vtbl AudioSessionControl2_Vtbl;
196 static const ISimpleAudioVolumeVtbl SimpleAudioVolume_Vtbl;
197 static const IChannelAudioVolumeVtbl ChannelAudioVolume_Vtbl;
198 static const IAudioClockVtbl AudioClock_Vtbl;
199 static const IAudioClock2Vtbl AudioClock2_Vtbl;
200 static const IAudioStreamVolumeVtbl AudioStreamVolume_Vtbl;
202 static AudioSessionWrapper *AudioSessionWrapper_Create(ACImpl *client);
204 static inline ACImpl *impl_from_IAudioClient(IAudioClient *iface)
206 return CONTAINING_RECORD(iface, ACImpl, IAudioClient_iface);
209 static inline ACImpl *impl_from_IAudioRenderClient(IAudioRenderClient *iface)
211 return CONTAINING_RECORD(iface, ACImpl, IAudioRenderClient_iface);
214 static inline ACImpl *impl_from_IAudioCaptureClient(IAudioCaptureClient *iface)
216 return CONTAINING_RECORD(iface, ACImpl, IAudioCaptureClient_iface);
219 static inline AudioSessionWrapper *impl_from_IAudioSessionControl2(IAudioSessionControl2 *iface)
221 return CONTAINING_RECORD(iface, AudioSessionWrapper, IAudioSessionControl2_iface);
224 static inline AudioSessionWrapper *impl_from_ISimpleAudioVolume(ISimpleAudioVolume *iface)
226 return CONTAINING_RECORD(iface, AudioSessionWrapper, ISimpleAudioVolume_iface);
229 static inline AudioSessionWrapper *impl_from_IChannelAudioVolume(IChannelAudioVolume *iface)
231 return CONTAINING_RECORD(iface, AudioSessionWrapper, IChannelAudioVolume_iface);
234 static inline ACImpl *impl_from_IAudioClock(IAudioClock *iface)
236 return CONTAINING_RECORD(iface, ACImpl, IAudioClock_iface);
239 static inline ACImpl *impl_from_IAudioClock2(IAudioClock2 *iface)
241 return CONTAINING_RECORD(iface, ACImpl, IAudioClock2_iface);
244 static inline ACImpl *impl_from_IAudioStreamVolume(IAudioStreamVolume *iface)
246 return CONTAINING_RECORD(iface, ACImpl, IAudioStreamVolume_iface);
249 /* Following pulseaudio design here, mainloop has the lock taken whenever
250 * it is handling something for pulse, and the lock is required whenever
251 * doing any pa_* call that can affect the state in any way
253 * pa_cond_wait is used when waiting on results, because the mainloop needs
254 * the same lock taken to affect the state
256 * This is basically the same as the pa_threaded_mainloop implementation,
257 * but that cannot be used because it uses pthread_create directly
259 * pa_threaded_mainloop_(un)lock -> pthread_mutex_(un)lock
260 * pa_threaded_mainloop_signal -> pthread_cond_signal
261 * pa_threaded_mainloop_wait -> pthread_cond_wait
264 static int pulse_poll_func(struct pollfd *ufds, unsigned long nfds, int timeout, void *userdata) {
265 int r;
266 pthread_mutex_unlock(&pulse_lock);
267 r = poll(ufds, nfds, timeout);
268 pthread_mutex_lock(&pulse_lock);
269 return r;
272 static DWORD CALLBACK pulse_mainloop_thread(void *tmp) {
273 int ret;
274 pulse_ml = pa_mainloop_new();
275 pa_mainloop_set_poll_func(pulse_ml, pulse_poll_func, NULL);
276 pthread_mutex_lock(&pulse_lock);
277 pthread_cond_signal(&pulse_cond);
278 pa_mainloop_run(pulse_ml, &ret);
279 pthread_mutex_unlock(&pulse_lock);
280 pa_mainloop_free(pulse_ml);
281 return ret;
284 static void pulse_contextcallback(pa_context *c, void *userdata)
286 switch (pa_context_get_state(c)) {
287 default:
288 FIXME("Unhandled state: %i\n", pa_context_get_state(c));
289 return;
291 case PA_CONTEXT_CONNECTING:
292 case PA_CONTEXT_UNCONNECTED:
293 case PA_CONTEXT_AUTHORIZING:
294 case PA_CONTEXT_SETTING_NAME:
295 case PA_CONTEXT_TERMINATED:
296 TRACE("State change to %i\n", pa_context_get_state(c));
297 return;
299 case PA_CONTEXT_READY:
300 TRACE("Ready\n");
301 break;
303 case PA_CONTEXT_FAILED:
304 ERR("Context failed: %s\n", pa_strerror(pa_context_errno(c)));
305 break;
307 pthread_cond_signal(&pulse_cond);
310 static void pulse_stream_state(pa_stream *s, void *user)
312 pa_stream_state_t state = pa_stream_get_state(s);
313 TRACE("Stream state changed to %i\n", state);
314 pthread_cond_signal(&pulse_cond);
317 static const enum pa_channel_position pulse_pos_from_wfx[] = {
318 PA_CHANNEL_POSITION_FRONT_LEFT,
319 PA_CHANNEL_POSITION_FRONT_RIGHT,
320 PA_CHANNEL_POSITION_FRONT_CENTER,
321 PA_CHANNEL_POSITION_LFE,
322 PA_CHANNEL_POSITION_REAR_LEFT,
323 PA_CHANNEL_POSITION_REAR_RIGHT,
324 PA_CHANNEL_POSITION_FRONT_LEFT_OF_CENTER,
325 PA_CHANNEL_POSITION_FRONT_RIGHT_OF_CENTER,
326 PA_CHANNEL_POSITION_REAR_CENTER,
327 PA_CHANNEL_POSITION_SIDE_LEFT,
328 PA_CHANNEL_POSITION_SIDE_RIGHT,
329 PA_CHANNEL_POSITION_TOP_CENTER,
330 PA_CHANNEL_POSITION_TOP_FRONT_LEFT,
331 PA_CHANNEL_POSITION_TOP_FRONT_CENTER,
332 PA_CHANNEL_POSITION_TOP_FRONT_RIGHT,
333 PA_CHANNEL_POSITION_TOP_REAR_LEFT,
334 PA_CHANNEL_POSITION_TOP_REAR_CENTER,
335 PA_CHANNEL_POSITION_TOP_REAR_RIGHT
338 static DWORD pulse_channel_map_to_channel_mask(const pa_channel_map *map) {
339 int i;
340 DWORD mask = 0;
342 for (i = 0; i < map->channels; ++i)
343 switch (map->map[i]) {
344 default: FIXME("Unhandled channel %s\n", pa_channel_position_to_string(map->map[i])); break;
345 case PA_CHANNEL_POSITION_FRONT_LEFT: mask |= SPEAKER_FRONT_LEFT; break;
346 case PA_CHANNEL_POSITION_MONO:
347 case PA_CHANNEL_POSITION_FRONT_CENTER: mask |= SPEAKER_FRONT_CENTER; break;
348 case PA_CHANNEL_POSITION_FRONT_RIGHT: mask |= SPEAKER_FRONT_RIGHT; break;
349 case PA_CHANNEL_POSITION_REAR_LEFT: mask |= SPEAKER_BACK_LEFT; break;
350 case PA_CHANNEL_POSITION_REAR_CENTER: mask |= SPEAKER_BACK_CENTER; break;
351 case PA_CHANNEL_POSITION_REAR_RIGHT: mask |= SPEAKER_BACK_RIGHT; break;
352 case PA_CHANNEL_POSITION_LFE: mask |= SPEAKER_LOW_FREQUENCY; break;
353 case PA_CHANNEL_POSITION_SIDE_LEFT: mask |= SPEAKER_SIDE_LEFT; break;
354 case PA_CHANNEL_POSITION_SIDE_RIGHT: mask |= SPEAKER_SIDE_RIGHT; break;
355 case PA_CHANNEL_POSITION_TOP_CENTER: mask |= SPEAKER_TOP_CENTER; break;
356 case PA_CHANNEL_POSITION_TOP_FRONT_LEFT: mask |= SPEAKER_TOP_FRONT_LEFT; break;
357 case PA_CHANNEL_POSITION_TOP_FRONT_CENTER: mask |= SPEAKER_TOP_FRONT_CENTER; break;
358 case PA_CHANNEL_POSITION_TOP_FRONT_RIGHT: mask |= SPEAKER_TOP_FRONT_RIGHT; break;
359 case PA_CHANNEL_POSITION_TOP_REAR_LEFT: mask |= SPEAKER_TOP_BACK_LEFT; break;
360 case PA_CHANNEL_POSITION_TOP_REAR_CENTER: mask |= SPEAKER_TOP_BACK_CENTER; break;
361 case PA_CHANNEL_POSITION_TOP_REAR_RIGHT: mask |= SPEAKER_TOP_BACK_RIGHT; break;
362 case PA_CHANNEL_POSITION_FRONT_LEFT_OF_CENTER: mask |= SPEAKER_FRONT_LEFT_OF_CENTER; break;
363 case PA_CHANNEL_POSITION_FRONT_RIGHT_OF_CENTER: mask |= SPEAKER_FRONT_RIGHT_OF_CENTER; break;
366 return mask;
369 static void pulse_probe_settings(int render, WAVEFORMATEXTENSIBLE *fmt) {
370 WAVEFORMATEX *wfx = &fmt->Format;
371 pa_stream *stream;
372 pa_channel_map map;
373 pa_sample_spec ss;
374 pa_buffer_attr attr;
375 int ret;
376 unsigned int length = 0;
378 pa_channel_map_init_auto(&map, 2, PA_CHANNEL_MAP_ALSA);
379 ss.rate = 48000;
380 ss.format = PA_SAMPLE_FLOAT32LE;
381 ss.channels = map.channels;
383 attr.maxlength = -1;
384 attr.tlength = -1;
385 attr.minreq = attr.fragsize = pa_frame_size(&ss);
386 attr.prebuf = 0;
388 stream = pa_stream_new(pulse_ctx, "format test stream", &ss, &map);
389 if (stream)
390 pa_stream_set_state_callback(stream, pulse_stream_state, NULL);
391 if (!stream)
392 ret = -1;
393 else if (render)
394 ret = pa_stream_connect_playback(stream, NULL, &attr,
395 PA_STREAM_START_CORKED|PA_STREAM_FIX_RATE|PA_STREAM_FIX_CHANNELS|PA_STREAM_EARLY_REQUESTS, NULL, NULL);
396 else
397 ret = pa_stream_connect_record(stream, NULL, &attr, PA_STREAM_START_CORKED|PA_STREAM_FIX_RATE|PA_STREAM_FIX_CHANNELS|PA_STREAM_EARLY_REQUESTS);
398 if (ret >= 0) {
399 while (pa_mainloop_iterate(pulse_ml, 1, &ret) >= 0 &&
400 pa_stream_get_state(stream) == PA_STREAM_CREATING)
402 if (pa_stream_get_state(stream) == PA_STREAM_READY) {
403 ss = *pa_stream_get_sample_spec(stream);
404 map = *pa_stream_get_channel_map(stream);
405 if (render)
406 length = pa_stream_get_buffer_attr(stream)->minreq;
407 else
408 length = pa_stream_get_buffer_attr(stream)->fragsize;
409 pa_stream_disconnect(stream);
410 while (pa_mainloop_iterate(pulse_ml, 1, &ret) >= 0 &&
411 pa_stream_get_state(stream) == PA_STREAM_READY)
416 if (stream)
417 pa_stream_unref(stream);
419 if (length)
420 pulse_def_period[!render] = pulse_min_period[!render] = pa_bytes_to_usec(10 * length, &ss);
422 if (pulse_min_period[!render] < MinimumPeriod)
423 pulse_min_period[!render] = MinimumPeriod;
425 if (pulse_def_period[!render] < DefaultPeriod)
426 pulse_def_period[!render] = DefaultPeriod;
428 wfx->wFormatTag = WAVE_FORMAT_EXTENSIBLE;
429 wfx->cbSize = sizeof(WAVEFORMATEXTENSIBLE) - sizeof(WAVEFORMATEX);
430 wfx->nChannels = ss.channels;
431 wfx->wBitsPerSample = 8 * pa_sample_size_of_format(ss.format);
432 wfx->nSamplesPerSec = ss.rate;
433 wfx->nBlockAlign = pa_frame_size(&ss);
434 wfx->nAvgBytesPerSec = wfx->nSamplesPerSec * wfx->nBlockAlign;
435 if (ss.format != PA_SAMPLE_S24_32LE)
436 fmt->Samples.wValidBitsPerSample = wfx->wBitsPerSample;
437 else
438 fmt->Samples.wValidBitsPerSample = 24;
439 if (ss.format == PA_SAMPLE_FLOAT32LE)
440 fmt->SubFormat = KSDATAFORMAT_SUBTYPE_IEEE_FLOAT;
441 else
442 fmt->SubFormat = KSDATAFORMAT_SUBTYPE_PCM;
444 fmt->dwChannelMask = pulse_channel_map_to_channel_mask(&map);
447 static HRESULT pulse_connect(void)
449 int len;
450 WCHAR path[MAX_PATH], *name;
451 char *str;
453 if (!pulse_thread)
455 if (!(pulse_thread = CreateThread(NULL, 0, pulse_mainloop_thread, NULL, 0, NULL)))
457 ERR("Failed to create mainloop thread.\n");
458 return E_FAIL;
460 SetThreadPriority(pulse_thread, THREAD_PRIORITY_TIME_CRITICAL);
461 pthread_cond_wait(&pulse_cond, &pulse_lock);
464 if (pulse_ctx && PA_CONTEXT_IS_GOOD(pa_context_get_state(pulse_ctx)))
465 return S_OK;
466 if (pulse_ctx)
467 pa_context_unref(pulse_ctx);
469 GetModuleFileNameW(NULL, path, sizeof(path)/sizeof(*path));
470 name = strrchrW(path, '\\');
471 if (!name)
472 name = path;
473 else
474 name++;
475 len = WideCharToMultiByte(CP_UNIXCP, 0, name, -1, NULL, 0, NULL, NULL);
476 str = pa_xmalloc(len);
477 WideCharToMultiByte(CP_UNIXCP, 0, name, -1, str, len, NULL, NULL);
478 TRACE("Name: %s\n", str);
479 pulse_ctx = pa_context_new(pa_mainloop_get_api(pulse_ml), str);
480 pa_xfree(str);
481 if (!pulse_ctx) {
482 ERR("Failed to create context\n");
483 return E_FAIL;
486 pa_context_set_state_callback(pulse_ctx, pulse_contextcallback, NULL);
488 TRACE("libpulse protocol version: %u. API Version %u\n", pa_context_get_protocol_version(pulse_ctx), PA_API_VERSION);
489 if (pa_context_connect(pulse_ctx, NULL, 0, NULL) < 0)
490 goto fail;
492 /* Wait for connection */
493 while (pthread_cond_wait(&pulse_cond, &pulse_lock)) {
494 pa_context_state_t state = pa_context_get_state(pulse_ctx);
496 if (state == PA_CONTEXT_FAILED || state == PA_CONTEXT_TERMINATED)
497 goto fail;
499 if (state == PA_CONTEXT_READY)
500 break;
503 TRACE("Connected to server %s with protocol version: %i.\n",
504 pa_context_get_server(pulse_ctx),
505 pa_context_get_server_protocol_version(pulse_ctx));
506 return S_OK;
508 fail:
509 pa_context_unref(pulse_ctx);
510 pulse_ctx = NULL;
511 return E_FAIL;
514 /* For default PulseAudio render device, OR together all of the
515 * PKEY_AudioEndpoint_PhysicalSpeakers values of the sinks. */
516 static void pulse_phys_speakers_cb(pa_context *c, const pa_sink_info *i, int eol, void *userdata)
518 if (i)
519 g_phys_speakers_mask |= pulse_channel_map_to_channel_mask(&i->channel_map);
522 /* some poorly-behaved applications call audio functions during DllMain, so we
523 * have to do as much as possible without creating a new thread. this function
524 * sets up a synchronous connection to verify the server is running and query
525 * static data. */
526 static HRESULT pulse_test_connect(void)
528 int len, ret;
529 WCHAR path[MAX_PATH], *name;
530 char *str;
531 pa_operation *o;
533 pulse_ml = pa_mainloop_new();
535 pa_mainloop_set_poll_func(pulse_ml, pulse_poll_func, NULL);
537 GetModuleFileNameW(NULL, path, sizeof(path)/sizeof(*path));
538 name = strrchrW(path, '\\');
539 if (!name)
540 name = path;
541 else
542 name++;
543 len = WideCharToMultiByte(CP_UNIXCP, 0, name, -1, NULL, 0, NULL, NULL);
544 str = pa_xmalloc(len);
545 WideCharToMultiByte(CP_UNIXCP, 0, name, -1, str, len, NULL, NULL);
546 TRACE("Name: %s\n", str);
547 pulse_ctx = pa_context_new(pa_mainloop_get_api(pulse_ml), str);
548 pa_xfree(str);
549 if (!pulse_ctx) {
550 ERR("Failed to create context\n");
551 pa_mainloop_free(pulse_ml);
552 pulse_ml = NULL;
553 return E_FAIL;
556 pa_context_set_state_callback(pulse_ctx, pulse_contextcallback, NULL);
558 TRACE("libpulse protocol version: %u. API Version %u\n", pa_context_get_protocol_version(pulse_ctx), PA_API_VERSION);
559 if (pa_context_connect(pulse_ctx, NULL, 0, NULL) < 0)
560 goto fail;
562 /* Wait for connection */
563 while (pa_mainloop_iterate(pulse_ml, 1, &ret) >= 0) {
564 pa_context_state_t state = pa_context_get_state(pulse_ctx);
566 if (state == PA_CONTEXT_FAILED || state == PA_CONTEXT_TERMINATED)
567 goto fail;
569 if (state == PA_CONTEXT_READY)
570 break;
573 TRACE("Test-connected to server %s with protocol version: %i.\n",
574 pa_context_get_server(pulse_ctx),
575 pa_context_get_server_protocol_version(pulse_ctx));
577 pulse_probe_settings(1, &pulse_fmt[0]);
578 pulse_probe_settings(0, &pulse_fmt[1]);
580 g_phys_speakers_mask = 0;
581 o = pa_context_get_sink_info_list(pulse_ctx, &pulse_phys_speakers_cb, NULL);
582 if (o) {
583 while (pa_mainloop_iterate(pulse_ml, 1, &ret) >= 0 &&
584 pa_operation_get_state(o) == PA_OPERATION_RUNNING)
586 pa_operation_unref(o);
589 pa_context_unref(pulse_ctx);
590 pulse_ctx = NULL;
591 pa_mainloop_free(pulse_ml);
592 pulse_ml = NULL;
594 return S_OK;
596 fail:
597 pa_context_unref(pulse_ctx);
598 pulse_ctx = NULL;
599 pa_mainloop_free(pulse_ml);
600 pulse_ml = NULL;
602 return E_FAIL;
605 static HRESULT pulse_stream_valid(ACImpl *This) {
606 if (!This->stream)
607 return AUDCLNT_E_NOT_INITIALIZED;
608 if (!This->stream || pa_stream_get_state(This->stream) != PA_STREAM_READY)
609 return AUDCLNT_E_DEVICE_INVALIDATED;
610 return S_OK;
613 static void silence_buffer(pa_sample_format_t format, BYTE *buffer, UINT32 bytes)
615 memset(buffer, format == PA_SAMPLE_U8 ? 0x80 : 0, bytes);
618 static void dump_attr(const pa_buffer_attr *attr) {
619 TRACE("maxlength: %u\n", attr->maxlength);
620 TRACE("minreq: %u\n", attr->minreq);
621 TRACE("fragsize: %u\n", attr->fragsize);
622 TRACE("tlength: %u\n", attr->tlength);
623 TRACE("prebuf: %u\n", attr->prebuf);
626 static void pulse_op_cb(pa_stream *s, int success, void *user) {
627 TRACE("Success: %i\n", success);
628 *(int*)user = success;
629 pthread_cond_signal(&pulse_cond);
632 static void pulse_attr_update(pa_stream *s, void *user) {
633 const pa_buffer_attr *attr = pa_stream_get_buffer_attr(s);
634 TRACE("New attributes or device moved:\n");
635 dump_attr(attr);
638 static void pulse_wr_callback(pa_stream *s, size_t bytes, void *userdata)
640 ACImpl *This = userdata;
641 UINT32 oldpad = This->pad;
643 if (bytes < This->bufsize_bytes)
644 This->pad = This->bufsize_bytes - bytes;
645 else
646 This->pad = 0;
648 if (oldpad == This->pad)
649 return;
651 assert(oldpad > This->pad);
653 This->clock_written += oldpad - This->pad;
654 TRACE("New pad: %zu (-%zu)\n", This->pad / pa_frame_size(&This->ss), (oldpad - This->pad) / pa_frame_size(&This->ss));
656 if (This->event)
657 SetEvent(This->event);
660 static void pulse_underflow_callback(pa_stream *s, void *userdata)
662 WARN("Underflow\n");
665 /* Latency is periodically updated even when nothing is played,
666 * because of PA_STREAM_AUTO_TIMING_UPDATE so use it as timer
668 * Perfect for passing all tests :)
670 static void pulse_latency_callback(pa_stream *s, void *userdata)
672 ACImpl *This = userdata;
673 if (!This->pad && This->event)
674 SetEvent(This->event);
677 static void pulse_started_callback(pa_stream *s, void *userdata)
679 TRACE("(Re)started playing\n");
682 static void pulse_rd_loop(ACImpl *This, size_t bytes)
684 while (bytes >= This->capture_period) {
685 ACPacket *p, *next;
686 LARGE_INTEGER stamp, freq;
687 BYTE *dst, *src;
688 size_t src_len, copy, rem = This->capture_period;
689 if (!(p = (ACPacket*)list_head(&This->packet_free_head))) {
690 p = (ACPacket*)list_head(&This->packet_filled_head);
691 if (!p->discont) {
692 next = (ACPacket*)p->entry.next;
693 next->discont = 1;
694 } else
695 p = (ACPacket*)list_tail(&This->packet_filled_head);
696 assert(This->pad == This->bufsize_bytes);
697 } else {
698 assert(This->pad < This->bufsize_bytes);
699 This->pad += This->capture_period;
700 assert(This->pad <= This->bufsize_bytes);
702 QueryPerformanceCounter(&stamp);
703 QueryPerformanceFrequency(&freq);
704 p->qpcpos = (stamp.QuadPart * (INT64)10000000) / freq.QuadPart;
705 p->discont = 0;
706 list_remove(&p->entry);
707 list_add_tail(&This->packet_filled_head, &p->entry);
709 dst = p->data;
710 while (rem) {
711 pa_stream_peek(This->stream, (const void**)&src, &src_len);
712 assert(src_len);
713 assert(This->peek_ofs < src_len);
714 src += This->peek_ofs;
715 src_len -= This->peek_ofs;
716 assert(src_len <= bytes);
718 copy = rem;
719 if (copy > src_len)
720 copy = src_len;
721 memcpy(dst, src, rem);
722 src += copy;
723 src_len -= copy;
724 dst += copy;
725 rem -= copy;
727 if (!src_len) {
728 This->peek_ofs = 0;
729 pa_stream_drop(This->stream);
730 } else
731 This->peek_ofs += copy;
733 bytes -= This->capture_period;
737 static void pulse_rd_drop(ACImpl *This, size_t bytes)
739 while (bytes >= This->capture_period) {
740 size_t src_len, copy, rem = This->capture_period;
741 while (rem) {
742 const void *src;
743 pa_stream_peek(This->stream, &src, &src_len);
744 assert(src_len);
745 assert(This->peek_ofs < src_len);
746 src_len -= This->peek_ofs;
747 assert(src_len <= bytes);
749 copy = rem;
750 if (copy > src_len)
751 copy = src_len;
753 src_len -= copy;
754 rem -= copy;
756 if (!src_len) {
757 This->peek_ofs = 0;
758 pa_stream_drop(This->stream);
759 } else
760 This->peek_ofs += copy;
761 bytes -= copy;
766 static void pulse_rd_callback(pa_stream *s, size_t bytes, void *userdata)
768 ACImpl *This = userdata;
770 TRACE("Readable total: %zu, fragsize: %u\n", bytes, pa_stream_get_buffer_attr(s)->fragsize);
771 assert(bytes >= This->peek_ofs);
772 bytes -= This->peek_ofs;
773 if (bytes < This->capture_period)
774 return;
776 if (This->started)
777 pulse_rd_loop(This, bytes);
778 else
779 pulse_rd_drop(This, bytes);
781 if (This->event)
782 SetEvent(This->event);
785 static HRESULT pulse_stream_connect(ACImpl *This, UINT32 period_bytes) {
786 int ret;
787 char buffer[64];
788 static LONG number;
789 pa_buffer_attr attr;
790 if (This->stream) {
791 pa_stream_disconnect(This->stream);
792 while (pa_stream_get_state(This->stream) == PA_STREAM_READY)
793 pthread_cond_wait(&pulse_cond, &pulse_lock);
794 pa_stream_unref(This->stream);
796 ret = InterlockedIncrement(&number);
797 sprintf(buffer, "audio stream #%i", ret);
798 This->stream = pa_stream_new(pulse_ctx, buffer, &This->ss, &This->map);
800 if (!This->stream) {
801 WARN("pa_stream_new returned error %i\n", pa_context_errno(pulse_ctx));
802 return AUDCLNT_E_ENDPOINT_CREATE_FAILED;
805 pa_stream_set_state_callback(This->stream, pulse_stream_state, This);
806 pa_stream_set_buffer_attr_callback(This->stream, pulse_attr_update, This);
807 pa_stream_set_moved_callback(This->stream, pulse_attr_update, This);
809 /* PulseAudio will fill in correct values */
810 attr.minreq = attr.fragsize = period_bytes;
811 attr.maxlength = attr.tlength = This->bufsize_bytes;
812 attr.prebuf = pa_frame_size(&This->ss);
813 dump_attr(&attr);
814 if (This->dataflow == eRender)
815 ret = pa_stream_connect_playback(This->stream, NULL, &attr,
816 PA_STREAM_START_CORKED|PA_STREAM_START_UNMUTED|PA_STREAM_AUTO_TIMING_UPDATE|PA_STREAM_INTERPOLATE_TIMING|PA_STREAM_EARLY_REQUESTS, NULL, NULL);
817 else
818 ret = pa_stream_connect_record(This->stream, NULL, &attr,
819 PA_STREAM_START_CORKED|PA_STREAM_START_UNMUTED|PA_STREAM_AUTO_TIMING_UPDATE|PA_STREAM_INTERPOLATE_TIMING|PA_STREAM_EARLY_REQUESTS);
820 if (ret < 0) {
821 WARN("Returns %i\n", ret);
822 return AUDCLNT_E_ENDPOINT_CREATE_FAILED;
824 while (pa_stream_get_state(This->stream) == PA_STREAM_CREATING)
825 pthread_cond_wait(&pulse_cond, &pulse_lock);
826 if (pa_stream_get_state(This->stream) != PA_STREAM_READY)
827 return AUDCLNT_E_ENDPOINT_CREATE_FAILED;
829 if (This->dataflow == eRender) {
830 pa_stream_set_write_callback(This->stream, pulse_wr_callback, This);
831 pa_stream_set_underflow_callback(This->stream, pulse_underflow_callback, This);
832 pa_stream_set_started_callback(This->stream, pulse_started_callback, This);
833 } else
834 pa_stream_set_read_callback(This->stream, pulse_rd_callback, This);
835 return S_OK;
838 HRESULT WINAPI AUDDRV_GetEndpointIDs(EDataFlow flow, const WCHAR ***ids, GUID **keys,
839 UINT *num, UINT *def_index)
841 WCHAR *id;
843 TRACE("%d %p %p %p\n", flow, ids, num, def_index);
845 *num = 1;
846 *def_index = 0;
848 *ids = HeapAlloc(GetProcessHeap(), 0, sizeof(**ids));
849 *keys = NULL;
850 if (!*ids)
851 return E_OUTOFMEMORY;
853 (*ids)[0] = id = HeapAlloc(GetProcessHeap(), 0, sizeof(defaultW));
854 *keys = HeapAlloc(GetProcessHeap(), 0, sizeof(**keys));
855 if (!*keys || !id) {
856 HeapFree(GetProcessHeap(), 0, id);
857 HeapFree(GetProcessHeap(), 0, *keys);
858 HeapFree(GetProcessHeap(), 0, *ids);
859 *ids = NULL;
860 *keys = NULL;
861 return E_OUTOFMEMORY;
863 memcpy(id, defaultW, sizeof(defaultW));
865 if (flow == eRender)
866 (*keys)[0] = pulse_render_guid;
867 else
868 (*keys)[0] = pulse_capture_guid;
870 return S_OK;
873 int WINAPI AUDDRV_GetPriority(void)
875 HRESULT hr;
876 pthread_mutex_lock(&pulse_lock);
877 hr = pulse_test_connect();
878 pthread_mutex_unlock(&pulse_lock);
879 return SUCCEEDED(hr) ? Priority_Preferred : Priority_Unavailable;
882 HRESULT WINAPI AUDDRV_GetAudioEndpoint(GUID *guid, IMMDevice *dev, IAudioClient **out)
884 ACImpl *This;
885 int i;
886 EDataFlow dataflow;
887 HRESULT hr;
889 TRACE("%s %p %p\n", debugstr_guid(guid), dev, out);
890 if (IsEqualGUID(guid, &pulse_render_guid))
891 dataflow = eRender;
892 else if (IsEqualGUID(guid, &pulse_capture_guid))
893 dataflow = eCapture;
894 else
895 return E_UNEXPECTED;
897 *out = NULL;
899 This = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, sizeof(*This));
900 if (!This)
901 return E_OUTOFMEMORY;
903 This->IAudioClient_iface.lpVtbl = &AudioClient_Vtbl;
904 This->IAudioRenderClient_iface.lpVtbl = &AudioRenderClient_Vtbl;
905 This->IAudioCaptureClient_iface.lpVtbl = &AudioCaptureClient_Vtbl;
906 This->IAudioClock_iface.lpVtbl = &AudioClock_Vtbl;
907 This->IAudioClock2_iface.lpVtbl = &AudioClock2_Vtbl;
908 This->IAudioStreamVolume_iface.lpVtbl = &AudioStreamVolume_Vtbl;
909 This->dataflow = dataflow;
910 This->parent = dev;
911 for (i = 0; i < PA_CHANNELS_MAX; ++i)
912 This->vol[i] = 1.f;
914 hr = CoCreateFreeThreadedMarshaler((IUnknown*)This, &This->marshal);
915 if (hr) {
916 HeapFree(GetProcessHeap(), 0, This);
917 return hr;
919 IMMDevice_AddRef(This->parent);
921 *out = &This->IAudioClient_iface;
922 IAudioClient_AddRef(&This->IAudioClient_iface);
924 return S_OK;
927 static HRESULT WINAPI AudioClient_QueryInterface(IAudioClient *iface,
928 REFIID riid, void **ppv)
930 ACImpl *This = impl_from_IAudioClient(iface);
932 TRACE("(%p)->(%s, %p)\n", iface, debugstr_guid(riid), ppv);
934 if (!ppv)
935 return E_POINTER;
937 *ppv = NULL;
938 if (IsEqualIID(riid, &IID_IUnknown) || IsEqualIID(riid, &IID_IAudioClient))
939 *ppv = iface;
940 if (*ppv) {
941 IUnknown_AddRef((IUnknown*)*ppv);
942 return S_OK;
945 if (IsEqualIID(riid, &IID_IMarshal))
946 return IUnknown_QueryInterface(This->marshal, riid, ppv);
948 WARN("Unknown interface %s\n", debugstr_guid(riid));
949 return E_NOINTERFACE;
952 static ULONG WINAPI AudioClient_AddRef(IAudioClient *iface)
954 ACImpl *This = impl_from_IAudioClient(iface);
955 ULONG ref;
956 ref = InterlockedIncrement(&This->ref);
957 TRACE("(%p) Refcount now %u\n", This, ref);
958 return ref;
961 static ULONG WINAPI AudioClient_Release(IAudioClient *iface)
963 ACImpl *This = impl_from_IAudioClient(iface);
964 ULONG ref;
965 ref = InterlockedDecrement(&This->ref);
966 TRACE("(%p) Refcount now %u\n", This, ref);
967 if (!ref) {
968 if (This->stream) {
969 pthread_mutex_lock(&pulse_lock);
970 if (PA_STREAM_IS_GOOD(pa_stream_get_state(This->stream))) {
971 pa_stream_disconnect(This->stream);
972 while (PA_STREAM_IS_GOOD(pa_stream_get_state(This->stream)))
973 pthread_cond_wait(&pulse_cond, &pulse_lock);
975 pa_stream_unref(This->stream);
976 This->stream = NULL;
977 list_remove(&This->entry);
978 pthread_mutex_unlock(&pulse_lock);
980 IUnknown_Release(This->marshal);
981 IMMDevice_Release(This->parent);
982 HeapFree(GetProcessHeap(), 0, This->tmp_buffer);
983 HeapFree(GetProcessHeap(), 0, This);
985 return ref;
988 static void dump_fmt(const WAVEFORMATEX *fmt)
990 TRACE("wFormatTag: 0x%x (", fmt->wFormatTag);
991 switch(fmt->wFormatTag) {
992 case WAVE_FORMAT_PCM:
993 TRACE("WAVE_FORMAT_PCM");
994 break;
995 case WAVE_FORMAT_IEEE_FLOAT:
996 TRACE("WAVE_FORMAT_IEEE_FLOAT");
997 break;
998 case WAVE_FORMAT_EXTENSIBLE:
999 TRACE("WAVE_FORMAT_EXTENSIBLE");
1000 break;
1001 default:
1002 TRACE("Unknown");
1003 break;
1005 TRACE(")\n");
1007 TRACE("nChannels: %u\n", fmt->nChannels);
1008 TRACE("nSamplesPerSec: %u\n", fmt->nSamplesPerSec);
1009 TRACE("nAvgBytesPerSec: %u\n", fmt->nAvgBytesPerSec);
1010 TRACE("nBlockAlign: %u\n", fmt->nBlockAlign);
1011 TRACE("wBitsPerSample: %u\n", fmt->wBitsPerSample);
1012 TRACE("cbSize: %u\n", fmt->cbSize);
1014 if (fmt->wFormatTag == WAVE_FORMAT_EXTENSIBLE) {
1015 WAVEFORMATEXTENSIBLE *fmtex = (void*)fmt;
1016 TRACE("dwChannelMask: %08x\n", fmtex->dwChannelMask);
1017 TRACE("Samples: %04x\n", fmtex->Samples.wReserved);
1018 TRACE("SubFormat: %s\n", wine_dbgstr_guid(&fmtex->SubFormat));
1022 static WAVEFORMATEX *clone_format(const WAVEFORMATEX *fmt)
1024 WAVEFORMATEX *ret;
1025 size_t size;
1027 if (fmt->wFormatTag == WAVE_FORMAT_EXTENSIBLE)
1028 size = sizeof(WAVEFORMATEXTENSIBLE);
1029 else
1030 size = sizeof(WAVEFORMATEX);
1032 ret = CoTaskMemAlloc(size);
1033 if (!ret)
1034 return NULL;
1036 memcpy(ret, fmt, size);
1038 ret->cbSize = size - sizeof(WAVEFORMATEX);
1040 return ret;
1043 static DWORD get_channel_mask(unsigned int channels)
1045 switch(channels) {
1046 case 0:
1047 return 0;
1048 case 1:
1049 return KSAUDIO_SPEAKER_MONO;
1050 case 2:
1051 return KSAUDIO_SPEAKER_STEREO;
1052 case 3:
1053 return KSAUDIO_SPEAKER_STEREO | SPEAKER_LOW_FREQUENCY;
1054 case 4:
1055 return KSAUDIO_SPEAKER_QUAD; /* not _SURROUND */
1056 case 5:
1057 return KSAUDIO_SPEAKER_QUAD | SPEAKER_LOW_FREQUENCY;
1058 case 6:
1059 return KSAUDIO_SPEAKER_5POINT1; /* not 5POINT1_SURROUND */
1060 case 7:
1061 return KSAUDIO_SPEAKER_5POINT1 | SPEAKER_BACK_CENTER;
1062 case 8:
1063 return KSAUDIO_SPEAKER_7POINT1_SURROUND; /* Vista deprecates 7POINT1 */
1065 FIXME("Unknown speaker configuration: %u\n", channels);
1066 return 0;
1069 static void session_init_vols(AudioSession *session, UINT channels)
1071 if (session->channel_count < channels) {
1072 UINT i;
1074 if (session->channel_vols)
1075 session->channel_vols = HeapReAlloc(GetProcessHeap(), 0,
1076 session->channel_vols, sizeof(float) * channels);
1077 else
1078 session->channel_vols = HeapAlloc(GetProcessHeap(), 0,
1079 sizeof(float) * channels);
1080 if (!session->channel_vols)
1081 return;
1083 for(i = session->channel_count; i < channels; ++i)
1084 session->channel_vols[i] = 1.f;
1086 session->channel_count = channels;
1090 static AudioSession *create_session(const GUID *guid, IMMDevice *device,
1091 UINT num_channels)
1093 AudioSession *ret;
1095 ret = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, sizeof(AudioSession));
1096 if (!ret)
1097 return NULL;
1099 memcpy(&ret->guid, guid, sizeof(GUID));
1101 ret->device = device;
1103 list_init(&ret->clients);
1105 list_add_head(&g_sessions, &ret->entry);
1107 session_init_vols(ret, num_channels);
1109 ret->master_vol = 1.f;
1111 return ret;
1114 /* if channels == 0, then this will return or create a session with
1115 * matching dataflow and GUID. otherwise, channels must also match */
1116 static HRESULT get_audio_session(const GUID *sessionguid,
1117 IMMDevice *device, UINT channels, AudioSession **out)
1119 AudioSession *session;
1121 if (!sessionguid || IsEqualGUID(sessionguid, &GUID_NULL)) {
1122 *out = create_session(&GUID_NULL, device, channels);
1123 if (!*out)
1124 return E_OUTOFMEMORY;
1126 return S_OK;
1129 *out = NULL;
1130 LIST_FOR_EACH_ENTRY(session, &g_sessions, AudioSession, entry) {
1131 if (session->device == device &&
1132 IsEqualGUID(sessionguid, &session->guid)) {
1133 session_init_vols(session, channels);
1134 *out = session;
1135 break;
1139 if (!*out) {
1140 *out = create_session(sessionguid, device, channels);
1141 if (!*out)
1142 return E_OUTOFMEMORY;
1145 return S_OK;
1148 static HRESULT pulse_spec_from_waveformat(ACImpl *This, const WAVEFORMATEX *fmt)
1150 pa_channel_map_init(&This->map);
1151 This->ss.rate = fmt->nSamplesPerSec;
1152 This->ss.format = PA_SAMPLE_INVALID;
1154 switch(fmt->wFormatTag) {
1155 case WAVE_FORMAT_IEEE_FLOAT:
1156 if (!fmt->nChannels || fmt->nChannels > 2 || fmt->wBitsPerSample != 32)
1157 break;
1158 This->ss.format = PA_SAMPLE_FLOAT32LE;
1159 pa_channel_map_init_auto(&This->map, fmt->nChannels, PA_CHANNEL_MAP_ALSA);
1160 break;
1161 case WAVE_FORMAT_PCM:
1162 if (!fmt->nChannels || fmt->nChannels > 2)
1163 break;
1164 if (fmt->wBitsPerSample == 8)
1165 This->ss.format = PA_SAMPLE_U8;
1166 else if (fmt->wBitsPerSample == 16)
1167 This->ss.format = PA_SAMPLE_S16LE;
1168 else
1169 return AUDCLNT_E_UNSUPPORTED_FORMAT;
1170 pa_channel_map_init_auto(&This->map, fmt->nChannels, PA_CHANNEL_MAP_ALSA);
1171 break;
1172 case WAVE_FORMAT_EXTENSIBLE: {
1173 WAVEFORMATEXTENSIBLE *wfe = (WAVEFORMATEXTENSIBLE*)fmt;
1174 DWORD mask = wfe->dwChannelMask;
1175 DWORD i = 0, j;
1176 if (fmt->cbSize != (sizeof(*wfe) - sizeof(*fmt)) && fmt->cbSize != sizeof(*wfe))
1177 break;
1178 if (IsEqualGUID(&wfe->SubFormat, &KSDATAFORMAT_SUBTYPE_IEEE_FLOAT) &&
1179 (!wfe->Samples.wValidBitsPerSample || wfe->Samples.wValidBitsPerSample == 32) &&
1180 fmt->wBitsPerSample == 32)
1181 This->ss.format = PA_SAMPLE_FLOAT32LE;
1182 else if (IsEqualGUID(&wfe->SubFormat, &KSDATAFORMAT_SUBTYPE_PCM)) {
1183 DWORD valid = wfe->Samples.wValidBitsPerSample;
1184 if (!valid)
1185 valid = fmt->wBitsPerSample;
1186 if (!valid || valid > fmt->wBitsPerSample)
1187 break;
1188 switch (fmt->wBitsPerSample) {
1189 case 8:
1190 if (valid == 8)
1191 This->ss.format = PA_SAMPLE_U8;
1192 break;
1193 case 16:
1194 if (valid == 16)
1195 This->ss.format = PA_SAMPLE_S16LE;
1196 break;
1197 case 24:
1198 if (valid == 24)
1199 This->ss.format = PA_SAMPLE_S24LE;
1200 break;
1201 case 32:
1202 if (valid == 24)
1203 This->ss.format = PA_SAMPLE_S24_32LE;
1204 else if (valid == 32)
1205 This->ss.format = PA_SAMPLE_S32LE;
1206 break;
1207 default:
1208 return AUDCLNT_E_UNSUPPORTED_FORMAT;
1211 This->map.channels = fmt->nChannels;
1212 if (!mask || (mask & (SPEAKER_ALL|SPEAKER_RESERVED)))
1213 mask = get_channel_mask(fmt->nChannels);
1214 for (j = 0; j < sizeof(pulse_pos_from_wfx)/sizeof(*pulse_pos_from_wfx) && i < fmt->nChannels; ++j) {
1215 if (mask & (1 << j))
1216 This->map.map[i++] = pulse_pos_from_wfx[j];
1219 /* Special case for mono since pulse appears to map it differently */
1220 if (mask == SPEAKER_FRONT_CENTER)
1221 This->map.map[0] = PA_CHANNEL_POSITION_MONO;
1223 if (i < fmt->nChannels || (mask & SPEAKER_RESERVED)) {
1224 This->map.channels = 0;
1225 ERR("Invalid channel mask: %i/%i and %x(%x)\n", i, fmt->nChannels, mask, wfe->dwChannelMask);
1226 break;
1228 break;
1230 case WAVE_FORMAT_ALAW:
1231 case WAVE_FORMAT_MULAW:
1232 if (fmt->wBitsPerSample != 8) {
1233 FIXME("Unsupported bpp %u for LAW\n", fmt->wBitsPerSample);
1234 return AUDCLNT_E_UNSUPPORTED_FORMAT;
1236 if (fmt->nChannels != 1 && fmt->nChannels != 2) {
1237 FIXME("Unsupported channels %u for LAW\n", fmt->nChannels);
1238 return AUDCLNT_E_UNSUPPORTED_FORMAT;
1240 This->ss.format = fmt->wFormatTag == WAVE_FORMAT_MULAW ? PA_SAMPLE_ULAW : PA_SAMPLE_ALAW;
1241 pa_channel_map_init_auto(&This->map, fmt->nChannels, PA_CHANNEL_MAP_ALSA);
1242 break;
1243 default:
1244 WARN("Unhandled tag %x\n", fmt->wFormatTag);
1245 return AUDCLNT_E_UNSUPPORTED_FORMAT;
1247 This->ss.channels = This->map.channels;
1248 if (!pa_channel_map_valid(&This->map) || This->ss.format == PA_SAMPLE_INVALID) {
1249 ERR("Invalid format! Channel spec valid: %i, format: %i\n", pa_channel_map_valid(&This->map), This->ss.format);
1250 return AUDCLNT_E_UNSUPPORTED_FORMAT;
1252 return S_OK;
1255 static HRESULT WINAPI AudioClient_Initialize(IAudioClient *iface,
1256 AUDCLNT_SHAREMODE mode, DWORD flags, REFERENCE_TIME duration,
1257 REFERENCE_TIME period, const WAVEFORMATEX *fmt,
1258 const GUID *sessionguid)
1260 ACImpl *This = impl_from_IAudioClient(iface);
1261 HRESULT hr = S_OK;
1262 UINT period_bytes;
1264 TRACE("(%p)->(%x, %x, %s, %s, %p, %s)\n", This, mode, flags,
1265 wine_dbgstr_longlong(duration), wine_dbgstr_longlong(period), fmt, debugstr_guid(sessionguid));
1267 if (!fmt)
1268 return E_POINTER;
1270 if (mode != AUDCLNT_SHAREMODE_SHARED && mode != AUDCLNT_SHAREMODE_EXCLUSIVE)
1271 return AUDCLNT_E_NOT_INITIALIZED;
1272 if (mode == AUDCLNT_SHAREMODE_EXCLUSIVE)
1273 return AUDCLNT_E_EXCLUSIVE_MODE_NOT_ALLOWED;
1275 if (flags & ~(AUDCLNT_STREAMFLAGS_CROSSPROCESS |
1276 AUDCLNT_STREAMFLAGS_LOOPBACK |
1277 AUDCLNT_STREAMFLAGS_EVENTCALLBACK |
1278 AUDCLNT_STREAMFLAGS_NOPERSIST |
1279 AUDCLNT_STREAMFLAGS_RATEADJUST |
1280 AUDCLNT_SESSIONFLAGS_EXPIREWHENUNOWNED |
1281 AUDCLNT_SESSIONFLAGS_DISPLAY_HIDE |
1282 AUDCLNT_SESSIONFLAGS_DISPLAY_HIDEWHENEXPIRED)) {
1283 TRACE("Unknown flags: %08x\n", flags);
1284 return E_INVALIDARG;
1287 pthread_mutex_lock(&pulse_lock);
1289 hr = pulse_connect();
1290 if (FAILED(hr)) {
1291 pthread_mutex_unlock(&pulse_lock);
1292 return hr;
1295 if (This->stream) {
1296 pthread_mutex_unlock(&pulse_lock);
1297 return AUDCLNT_E_ALREADY_INITIALIZED;
1300 hr = pulse_spec_from_waveformat(This, fmt);
1301 TRACE("Obtaining format returns %08x\n", hr);
1302 dump_fmt(fmt);
1304 if (FAILED(hr))
1305 goto exit;
1307 if (mode == AUDCLNT_SHAREMODE_SHARED) {
1308 REFERENCE_TIME def = pulse_def_period[This->dataflow == eCapture];
1309 REFERENCE_TIME min = pulse_min_period[This->dataflow == eCapture];
1311 /* Switch to low latency mode if below 2 default periods,
1312 * which is 20 ms by default, this will increase the amount
1313 * of interrupts but allows very low latency. In dsound I
1314 * managed to get a total latency of ~8ms, which is well below
1315 * default
1317 if (duration < 2 * def)
1318 period = min;
1319 else
1320 period = def;
1321 if (duration < 2 * period)
1322 duration = 2 * period;
1324 /* Uh oh, really low latency requested.. */
1325 if (duration <= 2 * period)
1326 period /= 2;
1328 period_bytes = pa_frame_size(&This->ss) * MulDiv(period, This->ss.rate, 10000000);
1330 if (duration < 20000000)
1331 This->bufsize_frames = ceil((duration / 10000000.) * fmt->nSamplesPerSec);
1332 else
1333 This->bufsize_frames = 2 * fmt->nSamplesPerSec;
1334 This->bufsize_bytes = This->bufsize_frames * pa_frame_size(&This->ss);
1336 This->share = mode;
1337 This->flags = flags;
1338 hr = pulse_stream_connect(This, period_bytes);
1339 if (SUCCEEDED(hr)) {
1340 UINT32 unalign;
1341 const pa_buffer_attr *attr = pa_stream_get_buffer_attr(This->stream);
1342 /* Update frames according to new size */
1343 dump_attr(attr);
1344 if (This->dataflow == eRender) {
1345 if (attr->tlength < This->bufsize_bytes) {
1346 const char *latenv = getenv("PULSE_LATENCY_MSEC");
1347 if (latenv && *latenv)
1348 ERR_(winediag)("PulseAudio buffer too small (%u < %u) - PULSE_LATENCY_MSEC is %s\n", attr->tlength, This->bufsize_bytes, latenv);
1349 else
1350 ERR_(winediag)("PulseAudio buffer too small (%u < %u)\n", attr->tlength, This->bufsize_bytes);
1352 This->bufsize_bytes = attr->tlength;
1353 } else {
1354 This->capture_period = period_bytes = attr->fragsize;
1355 if ((unalign = This->bufsize_bytes % period_bytes))
1356 This->bufsize_bytes += period_bytes - unalign;
1358 This->bufsize_frames = This->bufsize_bytes / pa_frame_size(&This->ss);
1360 if (SUCCEEDED(hr)) {
1361 UINT32 i, capture_packets = This->capture_period ? This->bufsize_bytes / This->capture_period : 0;
1362 This->tmp_buffer = HeapAlloc(GetProcessHeap(), 0, This->bufsize_bytes + capture_packets * sizeof(ACPacket));
1363 if (!This->tmp_buffer)
1364 hr = E_OUTOFMEMORY;
1365 else {
1366 ACPacket *cur_packet = (ACPacket*)((char*)This->tmp_buffer + This->bufsize_bytes);
1367 BYTE *data = This->tmp_buffer;
1368 silence_buffer(This->ss.format, This->tmp_buffer, This->bufsize_bytes);
1369 list_init(&This->packet_free_head);
1370 list_init(&This->packet_filled_head);
1371 for (i = 0; i < capture_packets; ++i, ++cur_packet) {
1372 list_add_tail(&This->packet_free_head, &cur_packet->entry);
1373 cur_packet->data = data;
1374 data += This->capture_period;
1376 assert(!This->capture_period || This->bufsize_bytes == This->capture_period * capture_packets);
1377 assert(!capture_packets || data - This->bufsize_bytes == This->tmp_buffer);
1380 if (SUCCEEDED(hr))
1381 hr = get_audio_session(sessionguid, This->parent, fmt->nChannels, &This->session);
1382 if (SUCCEEDED(hr))
1383 list_add_tail(&This->session->clients, &This->entry);
1385 exit:
1386 if (FAILED(hr)) {
1387 HeapFree(GetProcessHeap(), 0, This->tmp_buffer);
1388 This->tmp_buffer = NULL;
1389 if (This->stream) {
1390 pa_stream_disconnect(This->stream);
1391 pa_stream_unref(This->stream);
1392 This->stream = NULL;
1395 pthread_mutex_unlock(&pulse_lock);
1396 return hr;
1399 static HRESULT WINAPI AudioClient_GetBufferSize(IAudioClient *iface,
1400 UINT32 *out)
1402 ACImpl *This = impl_from_IAudioClient(iface);
1403 HRESULT hr;
1405 TRACE("(%p)->(%p)\n", This, out);
1407 if (!out)
1408 return E_POINTER;
1410 pthread_mutex_lock(&pulse_lock);
1411 hr = pulse_stream_valid(This);
1412 if (SUCCEEDED(hr))
1413 *out = This->bufsize_frames;
1414 pthread_mutex_unlock(&pulse_lock);
1416 return hr;
1419 static HRESULT WINAPI AudioClient_GetStreamLatency(IAudioClient *iface,
1420 REFERENCE_TIME *latency)
1422 ACImpl *This = impl_from_IAudioClient(iface);
1423 const pa_buffer_attr *attr;
1424 REFERENCE_TIME lat;
1425 HRESULT hr;
1427 TRACE("(%p)->(%p)\n", This, latency);
1429 if (!latency)
1430 return E_POINTER;
1432 pthread_mutex_lock(&pulse_lock);
1433 hr = pulse_stream_valid(This);
1434 if (FAILED(hr)) {
1435 pthread_mutex_unlock(&pulse_lock);
1436 return hr;
1438 attr = pa_stream_get_buffer_attr(This->stream);
1439 if (This->dataflow == eRender){
1440 lat = attr->minreq / pa_frame_size(&This->ss);
1441 lat += pulse_def_period[0];
1442 }else
1443 lat = attr->fragsize / pa_frame_size(&This->ss);
1444 *latency = 10000000;
1445 *latency *= lat;
1446 *latency /= This->ss.rate;
1447 pthread_mutex_unlock(&pulse_lock);
1448 TRACE("Latency: %u ms\n", (DWORD)(*latency / 10000));
1449 return S_OK;
1452 static void ACImpl_GetRenderPad(ACImpl *This, UINT32 *out)
1454 *out = This->pad / pa_frame_size(&This->ss);
1457 static void ACImpl_GetCapturePad(ACImpl *This, UINT32 *out)
1459 ACPacket *packet = This->locked_ptr;
1460 if (!packet && !list_empty(&This->packet_filled_head)) {
1461 packet = (ACPacket*)list_head(&This->packet_filled_head);
1462 This->locked_ptr = packet;
1463 list_remove(&packet->entry);
1465 if (out)
1466 *out = This->pad / pa_frame_size(&This->ss);
1469 static HRESULT WINAPI AudioClient_GetCurrentPadding(IAudioClient *iface,
1470 UINT32 *out)
1472 ACImpl *This = impl_from_IAudioClient(iface);
1473 HRESULT hr;
1475 TRACE("(%p)->(%p)\n", This, out);
1477 if (!out)
1478 return E_POINTER;
1480 pthread_mutex_lock(&pulse_lock);
1481 hr = pulse_stream_valid(This);
1482 if (FAILED(hr)) {
1483 pthread_mutex_unlock(&pulse_lock);
1484 return hr;
1487 if (This->dataflow == eRender)
1488 ACImpl_GetRenderPad(This, out);
1489 else
1490 ACImpl_GetCapturePad(This, out);
1491 pthread_mutex_unlock(&pulse_lock);
1493 TRACE("%p Pad: %u ms (%u)\n", This, MulDiv(*out, 1000, This->ss.rate), *out);
1494 return S_OK;
1497 static HRESULT WINAPI AudioClient_IsFormatSupported(IAudioClient *iface,
1498 AUDCLNT_SHAREMODE mode, const WAVEFORMATEX *fmt,
1499 WAVEFORMATEX **out)
1501 ACImpl *This = impl_from_IAudioClient(iface);
1502 HRESULT hr = S_OK;
1503 WAVEFORMATEX *closest = NULL;
1504 BOOL exclusive;
1506 TRACE("(%p)->(%x, %p, %p)\n", This, mode, fmt, out);
1508 if (!fmt)
1509 return E_POINTER;
1511 if (out)
1512 *out = NULL;
1514 if (mode == AUDCLNT_SHAREMODE_EXCLUSIVE) {
1515 exclusive = 1;
1516 out = NULL;
1517 } else if (mode == AUDCLNT_SHAREMODE_SHARED) {
1518 exclusive = 0;
1519 if (!out)
1520 return E_POINTER;
1521 } else
1522 return E_INVALIDARG;
1524 if (fmt->nChannels == 0)
1525 return AUDCLNT_E_UNSUPPORTED_FORMAT;
1527 closest = clone_format(fmt);
1528 if (!closest)
1529 return E_OUTOFMEMORY;
1531 dump_fmt(fmt);
1533 switch (fmt->wFormatTag) {
1534 case WAVE_FORMAT_EXTENSIBLE: {
1535 WAVEFORMATEXTENSIBLE *ext = (WAVEFORMATEXTENSIBLE*)closest;
1537 if ((fmt->cbSize != sizeof(WAVEFORMATEXTENSIBLE) - sizeof(WAVEFORMATEX) &&
1538 fmt->cbSize != sizeof(WAVEFORMATEXTENSIBLE)) ||
1539 fmt->nBlockAlign != fmt->wBitsPerSample / 8 * fmt->nChannels ||
1540 ext->Samples.wValidBitsPerSample > fmt->wBitsPerSample ||
1541 fmt->nAvgBytesPerSec != fmt->nBlockAlign * fmt->nSamplesPerSec) {
1542 hr = E_INVALIDARG;
1543 break;
1546 if (exclusive) {
1547 UINT32 mask = 0, i, channels = 0;
1549 if (!(ext->dwChannelMask & (SPEAKER_ALL | SPEAKER_RESERVED))) {
1550 for (i = 1; !(i & SPEAKER_RESERVED); i <<= 1) {
1551 if (i & ext->dwChannelMask) {
1552 mask |= i;
1553 channels++;
1557 if (channels != fmt->nChannels || (ext->dwChannelMask & ~mask)) {
1558 hr = AUDCLNT_E_UNSUPPORTED_FORMAT;
1559 break;
1561 } else {
1562 hr = AUDCLNT_E_UNSUPPORTED_FORMAT;
1563 break;
1567 if (IsEqualGUID(&ext->SubFormat, &KSDATAFORMAT_SUBTYPE_IEEE_FLOAT)) {
1568 if (fmt->wBitsPerSample != 32) {
1569 hr = E_INVALIDARG;
1570 break;
1573 if (ext->Samples.wValidBitsPerSample != fmt->wBitsPerSample) {
1574 hr = S_FALSE;
1575 ext->Samples.wValidBitsPerSample = fmt->wBitsPerSample;
1577 } else if (IsEqualGUID(&ext->SubFormat, &KSDATAFORMAT_SUBTYPE_PCM)) {
1578 if (!fmt->wBitsPerSample || fmt->wBitsPerSample > 32 || fmt->wBitsPerSample % 8) {
1579 hr = E_INVALIDARG;
1580 break;
1583 if (ext->Samples.wValidBitsPerSample != fmt->wBitsPerSample &&
1584 !(fmt->wBitsPerSample == 32 &&
1585 ext->Samples.wValidBitsPerSample == 24)) {
1586 hr = S_FALSE;
1587 ext->Samples.wValidBitsPerSample = fmt->wBitsPerSample;
1588 break;
1590 } else {
1591 hr = AUDCLNT_E_UNSUPPORTED_FORMAT;
1592 break;
1595 break;
1598 case WAVE_FORMAT_ALAW:
1599 case WAVE_FORMAT_MULAW:
1600 if (fmt->wBitsPerSample != 8) {
1601 hr = E_INVALIDARG;
1602 break;
1604 /* Fall-through */
1605 case WAVE_FORMAT_IEEE_FLOAT:
1606 if (fmt->wFormatTag == WAVE_FORMAT_IEEE_FLOAT && fmt->wBitsPerSample != 32) {
1607 hr = E_INVALIDARG;
1608 break;
1610 /* Fall-through */
1611 case WAVE_FORMAT_PCM:
1612 if (fmt->wFormatTag == WAVE_FORMAT_PCM &&
1613 (!fmt->wBitsPerSample || fmt->wBitsPerSample > 32 || fmt->wBitsPerSample % 8)) {
1614 hr = E_INVALIDARG;
1615 break;
1618 if (fmt->nChannels > 2) {
1619 hr = AUDCLNT_E_UNSUPPORTED_FORMAT;
1620 break;
1623 * fmt->cbSize, fmt->nBlockAlign and fmt->nAvgBytesPerSec seem to be
1624 * ignored, invalid values are happily accepted.
1626 break;
1627 default:
1628 hr = AUDCLNT_E_UNSUPPORTED_FORMAT;
1629 break;
1632 if (exclusive && hr != S_OK) {
1633 hr = AUDCLNT_E_UNSUPPORTED_FORMAT;
1634 CoTaskMemFree(closest);
1635 } else if (hr != S_FALSE)
1636 CoTaskMemFree(closest);
1637 else
1638 *out = closest;
1640 /* Winepulse does not currently support exclusive mode, if you know of an
1641 * application that uses it, I will correct this..
1643 if (hr == S_OK && exclusive)
1644 return This->dataflow == eCapture ? AUDCLNT_E_UNSUPPORTED_FORMAT : AUDCLNT_E_EXCLUSIVE_MODE_NOT_ALLOWED;
1646 TRACE("returning: %08x %p\n", hr, out ? *out : NULL);
1647 return hr;
1650 static HRESULT WINAPI AudioClient_GetMixFormat(IAudioClient *iface,
1651 WAVEFORMATEX **pwfx)
1653 ACImpl *This = impl_from_IAudioClient(iface);
1654 WAVEFORMATEXTENSIBLE *fmt = &pulse_fmt[This->dataflow == eCapture];
1656 TRACE("(%p)->(%p)\n", This, pwfx);
1658 if (!pwfx)
1659 return E_POINTER;
1661 *pwfx = clone_format(&fmt->Format);
1662 if (!*pwfx)
1663 return E_OUTOFMEMORY;
1664 dump_fmt(*pwfx);
1665 return S_OK;
1668 static HRESULT WINAPI AudioClient_GetDevicePeriod(IAudioClient *iface,
1669 REFERENCE_TIME *defperiod, REFERENCE_TIME *minperiod)
1671 ACImpl *This = impl_from_IAudioClient(iface);
1673 TRACE("(%p)->(%p, %p)\n", This, defperiod, minperiod);
1675 if (!defperiod && !minperiod)
1676 return E_POINTER;
1678 if (defperiod)
1679 *defperiod = pulse_def_period[This->dataflow == eCapture];
1680 if (minperiod)
1681 *minperiod = pulse_min_period[This->dataflow == eCapture];
1683 return S_OK;
1686 static HRESULT WINAPI AudioClient_Start(IAudioClient *iface)
1688 ACImpl *This = impl_from_IAudioClient(iface);
1689 HRESULT hr = S_OK;
1690 int success;
1691 pa_operation *o;
1693 TRACE("(%p)\n", This);
1695 pthread_mutex_lock(&pulse_lock);
1696 hr = pulse_stream_valid(This);
1697 if (FAILED(hr)) {
1698 pthread_mutex_unlock(&pulse_lock);
1699 return hr;
1702 if ((This->flags & AUDCLNT_STREAMFLAGS_EVENTCALLBACK) && !This->event) {
1703 pthread_mutex_unlock(&pulse_lock);
1704 return AUDCLNT_E_EVENTHANDLE_NOT_SET;
1707 if (This->started) {
1708 pthread_mutex_unlock(&pulse_lock);
1709 return AUDCLNT_E_NOT_STOPPED;
1712 if (pa_stream_is_corked(This->stream)) {
1713 o = pa_stream_cork(This->stream, 0, pulse_op_cb, &success);
1714 if (o) {
1715 while(pa_operation_get_state(o) == PA_OPERATION_RUNNING)
1716 pthread_cond_wait(&pulse_cond, &pulse_lock);
1717 pa_operation_unref(o);
1718 } else
1719 success = 0;
1720 if (!success)
1721 hr = E_FAIL;
1723 if (SUCCEEDED(hr)) {
1724 This->started = TRUE;
1725 if (This->dataflow == eRender && This->event)
1726 pa_stream_set_latency_update_callback(This->stream, pulse_latency_callback, This);
1728 pthread_mutex_unlock(&pulse_lock);
1729 return hr;
1732 static HRESULT WINAPI AudioClient_Stop(IAudioClient *iface)
1734 ACImpl *This = impl_from_IAudioClient(iface);
1735 HRESULT hr = S_OK;
1736 pa_operation *o;
1737 int success;
1739 TRACE("(%p)\n", This);
1741 pthread_mutex_lock(&pulse_lock);
1742 hr = pulse_stream_valid(This);
1743 if (FAILED(hr)) {
1744 pthread_mutex_unlock(&pulse_lock);
1745 return hr;
1748 if (!This->started) {
1749 pthread_mutex_unlock(&pulse_lock);
1750 return S_FALSE;
1753 if (This->dataflow == eRender) {
1754 o = pa_stream_cork(This->stream, 1, pulse_op_cb, &success);
1755 if (o) {
1756 while(pa_operation_get_state(o) == PA_OPERATION_RUNNING)
1757 pthread_cond_wait(&pulse_cond, &pulse_lock);
1758 pa_operation_unref(o);
1759 } else
1760 success = 0;
1761 if (!success)
1762 hr = E_FAIL;
1764 if (SUCCEEDED(hr)) {
1765 This->started = FALSE;
1767 pthread_mutex_unlock(&pulse_lock);
1768 return hr;
1771 static HRESULT WINAPI AudioClient_Reset(IAudioClient *iface)
1773 ACImpl *This = impl_from_IAudioClient(iface);
1774 HRESULT hr = S_OK;
1776 TRACE("(%p)\n", This);
1778 pthread_mutex_lock(&pulse_lock);
1779 hr = pulse_stream_valid(This);
1780 if (FAILED(hr)) {
1781 pthread_mutex_unlock(&pulse_lock);
1782 return hr;
1785 if (This->started) {
1786 pthread_mutex_unlock(&pulse_lock);
1787 return AUDCLNT_E_NOT_STOPPED;
1790 if (This->locked) {
1791 pthread_mutex_unlock(&pulse_lock);
1792 return AUDCLNT_E_BUFFER_OPERATION_PENDING;
1795 if (This->dataflow == eRender) {
1796 /* If there is still data in the render buffer it needs to be removed from the server */
1797 int success = 0;
1798 if (This->pad) {
1799 pa_operation *o = pa_stream_flush(This->stream, pulse_op_cb, &success);
1800 if (o) {
1801 while(pa_operation_get_state(o) == PA_OPERATION_RUNNING)
1802 pthread_cond_wait(&pulse_cond, &pulse_lock);
1803 pa_operation_unref(o);
1806 if (success || !This->pad)
1807 This->clock_lastpos = This->clock_written = This->pad = 0;
1808 } else {
1809 ACPacket *p;
1810 This->clock_written += This->pad;
1811 This->pad = 0;
1813 if ((p = This->locked_ptr)) {
1814 This->locked_ptr = NULL;
1815 list_add_tail(&This->packet_free_head, &p->entry);
1817 list_move_tail(&This->packet_free_head, &This->packet_filled_head);
1819 pthread_mutex_unlock(&pulse_lock);
1821 return hr;
1824 static HRESULT WINAPI AudioClient_SetEventHandle(IAudioClient *iface,
1825 HANDLE event)
1827 ACImpl *This = impl_from_IAudioClient(iface);
1828 HRESULT hr;
1830 TRACE("(%p)->(%p)\n", This, event);
1832 if (!event)
1833 return E_INVALIDARG;
1835 pthread_mutex_lock(&pulse_lock);
1836 hr = pulse_stream_valid(This);
1837 if (FAILED(hr)) {
1838 pthread_mutex_unlock(&pulse_lock);
1839 return hr;
1842 if (!(This->flags & AUDCLNT_STREAMFLAGS_EVENTCALLBACK))
1843 hr = AUDCLNT_E_EVENTHANDLE_NOT_EXPECTED;
1844 else if (This->event)
1845 hr = HRESULT_FROM_WIN32(ERROR_INVALID_NAME);
1846 else
1847 This->event = event;
1848 pthread_mutex_unlock(&pulse_lock);
1849 return hr;
1852 static HRESULT WINAPI AudioClient_GetService(IAudioClient *iface, REFIID riid,
1853 void **ppv)
1855 ACImpl *This = impl_from_IAudioClient(iface);
1856 HRESULT hr;
1858 TRACE("(%p)->(%s, %p)\n", This, debugstr_guid(riid), ppv);
1860 if (!ppv)
1861 return E_POINTER;
1862 *ppv = NULL;
1864 pthread_mutex_lock(&pulse_lock);
1865 hr = pulse_stream_valid(This);
1866 pthread_mutex_unlock(&pulse_lock);
1867 if (FAILED(hr))
1868 return hr;
1870 if (IsEqualIID(riid, &IID_IAudioRenderClient)) {
1871 if (This->dataflow != eRender)
1872 return AUDCLNT_E_WRONG_ENDPOINT_TYPE;
1873 *ppv = &This->IAudioRenderClient_iface;
1874 } else if (IsEqualIID(riid, &IID_IAudioCaptureClient)) {
1875 if (This->dataflow != eCapture)
1876 return AUDCLNT_E_WRONG_ENDPOINT_TYPE;
1877 *ppv = &This->IAudioCaptureClient_iface;
1878 } else if (IsEqualIID(riid, &IID_IAudioClock)) {
1879 *ppv = &This->IAudioClock_iface;
1880 } else if (IsEqualIID(riid, &IID_IAudioStreamVolume)) {
1881 *ppv = &This->IAudioStreamVolume_iface;
1882 } else if (IsEqualIID(riid, &IID_IAudioSessionControl) ||
1883 IsEqualIID(riid, &IID_IChannelAudioVolume) ||
1884 IsEqualIID(riid, &IID_ISimpleAudioVolume)) {
1885 if (!This->session_wrapper) {
1886 This->session_wrapper = AudioSessionWrapper_Create(This);
1887 if (!This->session_wrapper)
1888 return E_OUTOFMEMORY;
1890 if (IsEqualIID(riid, &IID_IAudioSessionControl))
1891 *ppv = &This->session_wrapper->IAudioSessionControl2_iface;
1892 else if (IsEqualIID(riid, &IID_IChannelAudioVolume))
1893 *ppv = &This->session_wrapper->IChannelAudioVolume_iface;
1894 else if (IsEqualIID(riid, &IID_ISimpleAudioVolume))
1895 *ppv = &This->session_wrapper->ISimpleAudioVolume_iface;
1898 if (*ppv) {
1899 IUnknown_AddRef((IUnknown*)*ppv);
1900 return S_OK;
1903 FIXME("stub %s\n", debugstr_guid(riid));
1904 return E_NOINTERFACE;
1907 static const IAudioClientVtbl AudioClient_Vtbl =
1909 AudioClient_QueryInterface,
1910 AudioClient_AddRef,
1911 AudioClient_Release,
1912 AudioClient_Initialize,
1913 AudioClient_GetBufferSize,
1914 AudioClient_GetStreamLatency,
1915 AudioClient_GetCurrentPadding,
1916 AudioClient_IsFormatSupported,
1917 AudioClient_GetMixFormat,
1918 AudioClient_GetDevicePeriod,
1919 AudioClient_Start,
1920 AudioClient_Stop,
1921 AudioClient_Reset,
1922 AudioClient_SetEventHandle,
1923 AudioClient_GetService
1926 static HRESULT WINAPI AudioRenderClient_QueryInterface(
1927 IAudioRenderClient *iface, REFIID riid, void **ppv)
1929 ACImpl *This = impl_from_IAudioRenderClient(iface);
1930 TRACE("(%p)->(%s, %p)\n", iface, debugstr_guid(riid), ppv);
1932 if (!ppv)
1933 return E_POINTER;
1934 *ppv = NULL;
1936 if (IsEqualIID(riid, &IID_IUnknown) ||
1937 IsEqualIID(riid, &IID_IAudioRenderClient))
1938 *ppv = iface;
1939 if (*ppv) {
1940 IUnknown_AddRef((IUnknown*)*ppv);
1941 return S_OK;
1944 if (IsEqualIID(riid, &IID_IMarshal))
1945 return IUnknown_QueryInterface(This->marshal, riid, ppv);
1947 WARN("Unknown interface %s\n", debugstr_guid(riid));
1948 return E_NOINTERFACE;
1951 static ULONG WINAPI AudioRenderClient_AddRef(IAudioRenderClient *iface)
1953 ACImpl *This = impl_from_IAudioRenderClient(iface);
1954 return AudioClient_AddRef(&This->IAudioClient_iface);
1957 static ULONG WINAPI AudioRenderClient_Release(IAudioRenderClient *iface)
1959 ACImpl *This = impl_from_IAudioRenderClient(iface);
1960 return AudioClient_Release(&This->IAudioClient_iface);
1963 static HRESULT WINAPI AudioRenderClient_GetBuffer(IAudioRenderClient *iface,
1964 UINT32 frames, BYTE **data)
1966 ACImpl *This = impl_from_IAudioRenderClient(iface);
1967 size_t avail, req, bytes = frames * pa_frame_size(&This->ss);
1968 UINT32 pad;
1969 HRESULT hr = S_OK;
1970 int ret = -1;
1972 TRACE("(%p)->(%u, %p)\n", This, frames, data);
1974 if (!data)
1975 return E_POINTER;
1976 *data = NULL;
1978 pthread_mutex_lock(&pulse_lock);
1979 hr = pulse_stream_valid(This);
1980 if (FAILED(hr) || This->locked) {
1981 pthread_mutex_unlock(&pulse_lock);
1982 return FAILED(hr) ? hr : AUDCLNT_E_OUT_OF_ORDER;
1984 if (!frames) {
1985 pthread_mutex_unlock(&pulse_lock);
1986 return S_OK;
1989 ACImpl_GetRenderPad(This, &pad);
1990 avail = This->bufsize_frames - pad;
1991 if (avail < frames || bytes > This->bufsize_bytes) {
1992 pthread_mutex_unlock(&pulse_lock);
1993 WARN("Wanted to write %u, but only %zu available\n", frames, avail);
1994 return AUDCLNT_E_BUFFER_TOO_LARGE;
1997 This->locked = frames;
1998 req = bytes;
1999 ret = pa_stream_begin_write(This->stream, &This->locked_ptr, &req);
2000 if (ret < 0 || req < bytes) {
2001 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);
2002 if (ret >= 0)
2003 pa_stream_cancel_write(This->stream);
2004 *data = This->tmp_buffer;
2005 This->locked_ptr = NULL;
2006 } else
2007 *data = This->locked_ptr;
2008 pthread_mutex_unlock(&pulse_lock);
2009 return hr;
2012 static void pulse_free_noop(void *buf)
2016 static HRESULT WINAPI AudioRenderClient_ReleaseBuffer(
2017 IAudioRenderClient *iface, UINT32 written_frames, DWORD flags)
2019 ACImpl *This = impl_from_IAudioRenderClient(iface);
2020 UINT32 written_bytes = written_frames * pa_frame_size(&This->ss);
2022 TRACE("(%p)->(%u, %x)\n", This, written_frames, flags);
2024 pthread_mutex_lock(&pulse_lock);
2025 if (!This->locked || !written_frames) {
2026 if (This->locked_ptr)
2027 pa_stream_cancel_write(This->stream);
2028 This->locked = 0;
2029 This->locked_ptr = NULL;
2030 pthread_mutex_unlock(&pulse_lock);
2031 return written_frames ? AUDCLNT_E_OUT_OF_ORDER : S_OK;
2034 if (This->locked < written_frames) {
2035 pthread_mutex_unlock(&pulse_lock);
2036 return AUDCLNT_E_INVALID_SIZE;
2039 This->locked = 0;
2040 if (This->locked_ptr) {
2041 if (flags & AUDCLNT_BUFFERFLAGS_SILENT)
2042 silence_buffer(This->ss.format, This->locked_ptr, written_bytes);
2043 pa_stream_write(This->stream, This->locked_ptr, written_bytes, NULL, 0, PA_SEEK_RELATIVE);
2044 } else {
2045 if (flags & AUDCLNT_BUFFERFLAGS_SILENT)
2046 silence_buffer(This->ss.format, This->tmp_buffer, written_bytes);
2047 pa_stream_write(This->stream, This->tmp_buffer, written_bytes, pulse_free_noop, 0, PA_SEEK_RELATIVE);
2050 This->pad += written_bytes;
2051 This->locked_ptr = NULL;
2052 TRACE("Released %u, pad %zu\n", written_frames, This->pad / pa_frame_size(&This->ss));
2053 assert(This->pad <= This->bufsize_bytes);
2055 pthread_mutex_unlock(&pulse_lock);
2056 return S_OK;
2059 static const IAudioRenderClientVtbl AudioRenderClient_Vtbl = {
2060 AudioRenderClient_QueryInterface,
2061 AudioRenderClient_AddRef,
2062 AudioRenderClient_Release,
2063 AudioRenderClient_GetBuffer,
2064 AudioRenderClient_ReleaseBuffer
2067 static HRESULT WINAPI AudioCaptureClient_QueryInterface(
2068 IAudioCaptureClient *iface, REFIID riid, void **ppv)
2070 ACImpl *This = impl_from_IAudioCaptureClient(iface);
2071 TRACE("(%p)->(%s, %p)\n", iface, debugstr_guid(riid), ppv);
2073 if (!ppv)
2074 return E_POINTER;
2075 *ppv = NULL;
2077 if (IsEqualIID(riid, &IID_IUnknown) ||
2078 IsEqualIID(riid, &IID_IAudioCaptureClient))
2079 *ppv = iface;
2080 if (*ppv) {
2081 IUnknown_AddRef((IUnknown*)*ppv);
2082 return S_OK;
2085 if (IsEqualIID(riid, &IID_IMarshal))
2086 return IUnknown_QueryInterface(This->marshal, riid, ppv);
2088 WARN("Unknown interface %s\n", debugstr_guid(riid));
2089 return E_NOINTERFACE;
2092 static ULONG WINAPI AudioCaptureClient_AddRef(IAudioCaptureClient *iface)
2094 ACImpl *This = impl_from_IAudioCaptureClient(iface);
2095 return IAudioClient_AddRef(&This->IAudioClient_iface);
2098 static ULONG WINAPI AudioCaptureClient_Release(IAudioCaptureClient *iface)
2100 ACImpl *This = impl_from_IAudioCaptureClient(iface);
2101 return IAudioClient_Release(&This->IAudioClient_iface);
2104 static HRESULT WINAPI AudioCaptureClient_GetBuffer(IAudioCaptureClient *iface,
2105 BYTE **data, UINT32 *frames, DWORD *flags, UINT64 *devpos,
2106 UINT64 *qpcpos)
2108 ACImpl *This = impl_from_IAudioCaptureClient(iface);
2109 HRESULT hr;
2110 ACPacket *packet;
2112 TRACE("(%p)->(%p, %p, %p, %p, %p)\n", This, data, frames, flags,
2113 devpos, qpcpos);
2115 if (!data || !frames || !flags)
2116 return E_POINTER;
2118 pthread_mutex_lock(&pulse_lock);
2119 hr = pulse_stream_valid(This);
2120 if (FAILED(hr) || This->locked) {
2121 pthread_mutex_unlock(&pulse_lock);
2122 return FAILED(hr) ? hr : AUDCLNT_E_OUT_OF_ORDER;
2125 ACImpl_GetCapturePad(This, NULL);
2126 if ((packet = This->locked_ptr)) {
2127 *frames = This->capture_period / pa_frame_size(&This->ss);
2128 *flags = 0;
2129 if (packet->discont)
2130 *flags |= AUDCLNT_BUFFERFLAGS_DATA_DISCONTINUITY;
2131 if (devpos) {
2132 if (packet->discont)
2133 *devpos = (This->clock_written + This->capture_period) / pa_frame_size(&This->ss);
2134 else
2135 *devpos = This->clock_written / pa_frame_size(&This->ss);
2137 if (qpcpos)
2138 *qpcpos = packet->qpcpos;
2139 *data = packet->data;
2141 else
2142 *frames = 0;
2143 This->locked = *frames;
2144 pthread_mutex_unlock(&pulse_lock);
2145 return *frames ? S_OK : AUDCLNT_S_BUFFER_EMPTY;
2148 static HRESULT WINAPI AudioCaptureClient_ReleaseBuffer(
2149 IAudioCaptureClient *iface, UINT32 done)
2151 ACImpl *This = impl_from_IAudioCaptureClient(iface);
2153 TRACE("(%p)->(%u)\n", This, done);
2155 pthread_mutex_lock(&pulse_lock);
2156 if (!This->locked && done) {
2157 pthread_mutex_unlock(&pulse_lock);
2158 return AUDCLNT_E_OUT_OF_ORDER;
2160 if (done && This->locked != done) {
2161 pthread_mutex_unlock(&pulse_lock);
2162 return AUDCLNT_E_INVALID_SIZE;
2164 if (done) {
2165 ACPacket *packet = This->locked_ptr;
2166 This->locked_ptr = NULL;
2167 This->pad -= This->capture_period;
2168 if (packet->discont)
2169 This->clock_written += 2 * This->capture_period;
2170 else
2171 This->clock_written += This->capture_period;
2172 list_add_tail(&This->packet_free_head, &packet->entry);
2174 This->locked = 0;
2175 pthread_mutex_unlock(&pulse_lock);
2176 return S_OK;
2179 static HRESULT WINAPI AudioCaptureClient_GetNextPacketSize(
2180 IAudioCaptureClient *iface, UINT32 *frames)
2182 ACImpl *This = impl_from_IAudioCaptureClient(iface);
2184 TRACE("(%p)->(%p)\n", This, frames);
2185 if (!frames)
2186 return E_POINTER;
2188 pthread_mutex_lock(&pulse_lock);
2189 ACImpl_GetCapturePad(This, NULL);
2190 if (This->locked_ptr)
2191 *frames = This->capture_period / pa_frame_size(&This->ss);
2192 else
2193 *frames = 0;
2194 pthread_mutex_unlock(&pulse_lock);
2195 return S_OK;
2198 static const IAudioCaptureClientVtbl AudioCaptureClient_Vtbl =
2200 AudioCaptureClient_QueryInterface,
2201 AudioCaptureClient_AddRef,
2202 AudioCaptureClient_Release,
2203 AudioCaptureClient_GetBuffer,
2204 AudioCaptureClient_ReleaseBuffer,
2205 AudioCaptureClient_GetNextPacketSize
2208 static HRESULT WINAPI AudioClock_QueryInterface(IAudioClock *iface,
2209 REFIID riid, void **ppv)
2211 ACImpl *This = impl_from_IAudioClock(iface);
2213 TRACE("(%p)->(%s, %p)\n", iface, debugstr_guid(riid), ppv);
2215 if (!ppv)
2216 return E_POINTER;
2217 *ppv = NULL;
2219 if (IsEqualIID(riid, &IID_IUnknown) || IsEqualIID(riid, &IID_IAudioClock))
2220 *ppv = iface;
2221 else if (IsEqualIID(riid, &IID_IAudioClock2))
2222 *ppv = &This->IAudioClock2_iface;
2223 if (*ppv) {
2224 IUnknown_AddRef((IUnknown*)*ppv);
2225 return S_OK;
2228 if (IsEqualIID(riid, &IID_IMarshal))
2229 return IUnknown_QueryInterface(This->marshal, riid, ppv);
2231 WARN("Unknown interface %s\n", debugstr_guid(riid));
2232 return E_NOINTERFACE;
2235 static ULONG WINAPI AudioClock_AddRef(IAudioClock *iface)
2237 ACImpl *This = impl_from_IAudioClock(iface);
2238 return IAudioClient_AddRef(&This->IAudioClient_iface);
2241 static ULONG WINAPI AudioClock_Release(IAudioClock *iface)
2243 ACImpl *This = impl_from_IAudioClock(iface);
2244 return IAudioClient_Release(&This->IAudioClient_iface);
2247 static HRESULT WINAPI AudioClock_GetFrequency(IAudioClock *iface, UINT64 *freq)
2249 ACImpl *This = impl_from_IAudioClock(iface);
2250 HRESULT hr;
2252 TRACE("(%p)->(%p)\n", This, freq);
2254 pthread_mutex_lock(&pulse_lock);
2255 hr = pulse_stream_valid(This);
2256 if (SUCCEEDED(hr)) {
2257 *freq = This->ss.rate;
2258 if (This->share == AUDCLNT_SHAREMODE_SHARED)
2259 *freq *= pa_frame_size(&This->ss);
2261 pthread_mutex_unlock(&pulse_lock);
2262 return hr;
2265 static HRESULT WINAPI AudioClock_GetPosition(IAudioClock *iface, UINT64 *pos,
2266 UINT64 *qpctime)
2268 ACImpl *This = impl_from_IAudioClock(iface);
2269 HRESULT hr;
2271 TRACE("(%p)->(%p, %p)\n", This, pos, qpctime);
2273 if (!pos)
2274 return E_POINTER;
2276 pthread_mutex_lock(&pulse_lock);
2277 hr = pulse_stream_valid(This);
2278 if (FAILED(hr)) {
2279 pthread_mutex_unlock(&pulse_lock);
2280 return hr;
2283 *pos = This->clock_written;
2285 if (This->share == AUDCLNT_SHAREMODE_EXCLUSIVE)
2286 *pos /= pa_frame_size(&This->ss);
2288 /* Make time never go backwards */
2289 if (*pos < This->clock_lastpos)
2290 *pos = This->clock_lastpos;
2291 else
2292 This->clock_lastpos = *pos;
2293 pthread_mutex_unlock(&pulse_lock);
2295 TRACE("%p Position: %u\n", This, (unsigned)*pos);
2297 if (qpctime) {
2298 LARGE_INTEGER stamp, freq;
2299 QueryPerformanceCounter(&stamp);
2300 QueryPerformanceFrequency(&freq);
2301 *qpctime = (stamp.QuadPart * (INT64)10000000) / freq.QuadPart;
2304 return S_OK;
2307 static HRESULT WINAPI AudioClock_GetCharacteristics(IAudioClock *iface,
2308 DWORD *chars)
2310 ACImpl *This = impl_from_IAudioClock(iface);
2312 TRACE("(%p)->(%p)\n", This, chars);
2314 if (!chars)
2315 return E_POINTER;
2317 *chars = AUDIOCLOCK_CHARACTERISTIC_FIXED_FREQ;
2319 return S_OK;
2322 static const IAudioClockVtbl AudioClock_Vtbl =
2324 AudioClock_QueryInterface,
2325 AudioClock_AddRef,
2326 AudioClock_Release,
2327 AudioClock_GetFrequency,
2328 AudioClock_GetPosition,
2329 AudioClock_GetCharacteristics
2332 static HRESULT WINAPI AudioClock2_QueryInterface(IAudioClock2 *iface,
2333 REFIID riid, void **ppv)
2335 ACImpl *This = impl_from_IAudioClock2(iface);
2336 return IAudioClock_QueryInterface(&This->IAudioClock_iface, riid, ppv);
2339 static ULONG WINAPI AudioClock2_AddRef(IAudioClock2 *iface)
2341 ACImpl *This = impl_from_IAudioClock2(iface);
2342 return IAudioClient_AddRef(&This->IAudioClient_iface);
2345 static ULONG WINAPI AudioClock2_Release(IAudioClock2 *iface)
2347 ACImpl *This = impl_from_IAudioClock2(iface);
2348 return IAudioClient_Release(&This->IAudioClient_iface);
2351 static HRESULT WINAPI AudioClock2_GetDevicePosition(IAudioClock2 *iface,
2352 UINT64 *pos, UINT64 *qpctime)
2354 ACImpl *This = impl_from_IAudioClock2(iface);
2355 HRESULT hr = AudioClock_GetPosition(&This->IAudioClock_iface, pos, qpctime);
2356 if (SUCCEEDED(hr) && This->share == AUDCLNT_SHAREMODE_SHARED)
2357 *pos /= pa_frame_size(&This->ss);
2358 return hr;
2361 static const IAudioClock2Vtbl AudioClock2_Vtbl =
2363 AudioClock2_QueryInterface,
2364 AudioClock2_AddRef,
2365 AudioClock2_Release,
2366 AudioClock2_GetDevicePosition
2369 static HRESULT WINAPI AudioStreamVolume_QueryInterface(
2370 IAudioStreamVolume *iface, REFIID riid, void **ppv)
2372 ACImpl *This = impl_from_IAudioStreamVolume(iface);
2374 TRACE("(%p)->(%s, %p)\n", iface, debugstr_guid(riid), ppv);
2376 if (!ppv)
2377 return E_POINTER;
2378 *ppv = NULL;
2380 if (IsEqualIID(riid, &IID_IUnknown) ||
2381 IsEqualIID(riid, &IID_IAudioStreamVolume))
2382 *ppv = iface;
2383 if (*ppv) {
2384 IUnknown_AddRef((IUnknown*)*ppv);
2385 return S_OK;
2388 if (IsEqualIID(riid, &IID_IMarshal))
2389 return IUnknown_QueryInterface(This->marshal, riid, ppv);
2391 WARN("Unknown interface %s\n", debugstr_guid(riid));
2392 return E_NOINTERFACE;
2395 static ULONG WINAPI AudioStreamVolume_AddRef(IAudioStreamVolume *iface)
2397 ACImpl *This = impl_from_IAudioStreamVolume(iface);
2398 return IAudioClient_AddRef(&This->IAudioClient_iface);
2401 static ULONG WINAPI AudioStreamVolume_Release(IAudioStreamVolume *iface)
2403 ACImpl *This = impl_from_IAudioStreamVolume(iface);
2404 return IAudioClient_Release(&This->IAudioClient_iface);
2407 static HRESULT WINAPI AudioStreamVolume_GetChannelCount(
2408 IAudioStreamVolume *iface, UINT32 *out)
2410 ACImpl *This = impl_from_IAudioStreamVolume(iface);
2412 TRACE("(%p)->(%p)\n", This, out);
2414 if (!out)
2415 return E_POINTER;
2417 *out = This->ss.channels;
2419 return S_OK;
2422 struct pulse_info_cb_data {
2423 UINT32 n;
2424 float *levels;
2427 static HRESULT WINAPI AudioStreamVolume_SetAllVolumes(
2428 IAudioStreamVolume *iface, UINT32 count, const float *levels)
2430 ACImpl *This = impl_from_IAudioStreamVolume(iface);
2431 HRESULT hr;
2432 int i;
2434 TRACE("(%p)->(%d, %p)\n", This, count, levels);
2436 if (!levels)
2437 return E_POINTER;
2439 if (count != This->ss.channels)
2440 return E_INVALIDARG;
2442 pthread_mutex_lock(&pulse_lock);
2443 hr = pulse_stream_valid(This);
2444 if (FAILED(hr))
2445 goto out;
2447 for (i = 0; i < count; ++i)
2448 This->vol[i] = levels[i];
2450 out:
2451 pthread_mutex_unlock(&pulse_lock);
2452 return hr;
2455 static HRESULT WINAPI AudioStreamVolume_GetAllVolumes(
2456 IAudioStreamVolume *iface, UINT32 count, float *levels)
2458 ACImpl *This = impl_from_IAudioStreamVolume(iface);
2459 HRESULT hr;
2460 int i;
2462 TRACE("(%p)->(%d, %p)\n", This, count, levels);
2464 if (!levels)
2465 return E_POINTER;
2467 if (count != This->ss.channels)
2468 return E_INVALIDARG;
2470 pthread_mutex_lock(&pulse_lock);
2471 hr = pulse_stream_valid(This);
2472 if (FAILED(hr))
2473 goto out;
2475 for (i = 0; i < count; ++i)
2476 levels[i] = This->vol[i];
2478 out:
2479 pthread_mutex_unlock(&pulse_lock);
2480 return hr;
2483 static HRESULT WINAPI AudioStreamVolume_SetChannelVolume(
2484 IAudioStreamVolume *iface, UINT32 index, float level)
2486 ACImpl *This = impl_from_IAudioStreamVolume(iface);
2487 HRESULT hr;
2488 float volumes[PA_CHANNELS_MAX];
2490 TRACE("(%p)->(%d, %f)\n", This, index, level);
2492 if (level < 0.f || level > 1.f)
2493 return E_INVALIDARG;
2495 if (index >= This->ss.channels)
2496 return E_INVALIDARG;
2498 hr = AudioStreamVolume_GetAllVolumes(iface, This->ss.channels, volumes);
2499 volumes[index] = level;
2500 if (SUCCEEDED(hr))
2501 hr = AudioStreamVolume_SetAllVolumes(iface, This->ss.channels, volumes);
2502 return hr;
2505 static HRESULT WINAPI AudioStreamVolume_GetChannelVolume(
2506 IAudioStreamVolume *iface, UINT32 index, float *level)
2508 ACImpl *This = impl_from_IAudioStreamVolume(iface);
2509 float volumes[PA_CHANNELS_MAX];
2510 HRESULT hr;
2512 TRACE("(%p)->(%d, %p)\n", This, index, level);
2514 if (!level)
2515 return E_POINTER;
2517 if (index >= This->ss.channels)
2518 return E_INVALIDARG;
2520 hr = AudioStreamVolume_GetAllVolumes(iface, This->ss.channels, volumes);
2521 if (SUCCEEDED(hr))
2522 *level = volumes[index];
2523 return hr;
2526 static const IAudioStreamVolumeVtbl AudioStreamVolume_Vtbl =
2528 AudioStreamVolume_QueryInterface,
2529 AudioStreamVolume_AddRef,
2530 AudioStreamVolume_Release,
2531 AudioStreamVolume_GetChannelCount,
2532 AudioStreamVolume_SetChannelVolume,
2533 AudioStreamVolume_GetChannelVolume,
2534 AudioStreamVolume_SetAllVolumes,
2535 AudioStreamVolume_GetAllVolumes
2538 static AudioSessionWrapper *AudioSessionWrapper_Create(ACImpl *client)
2540 AudioSessionWrapper *ret;
2542 ret = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY,
2543 sizeof(AudioSessionWrapper));
2544 if (!ret)
2545 return NULL;
2547 ret->IAudioSessionControl2_iface.lpVtbl = &AudioSessionControl2_Vtbl;
2548 ret->ISimpleAudioVolume_iface.lpVtbl = &SimpleAudioVolume_Vtbl;
2549 ret->IChannelAudioVolume_iface.lpVtbl = &ChannelAudioVolume_Vtbl;
2551 ret->ref = !client;
2553 ret->client = client;
2554 if (client) {
2555 ret->session = client->session;
2556 AudioClient_AddRef(&client->IAudioClient_iface);
2559 return ret;
2562 static HRESULT WINAPI AudioSessionControl_QueryInterface(
2563 IAudioSessionControl2 *iface, REFIID riid, void **ppv)
2565 TRACE("(%p)->(%s, %p)\n", iface, debugstr_guid(riid), ppv);
2567 if (!ppv)
2568 return E_POINTER;
2569 *ppv = NULL;
2571 if (IsEqualIID(riid, &IID_IUnknown) ||
2572 IsEqualIID(riid, &IID_IAudioSessionControl) ||
2573 IsEqualIID(riid, &IID_IAudioSessionControl2))
2574 *ppv = iface;
2575 if (*ppv) {
2576 IUnknown_AddRef((IUnknown*)*ppv);
2577 return S_OK;
2580 WARN("Unknown interface %s\n", debugstr_guid(riid));
2581 return E_NOINTERFACE;
2584 static ULONG WINAPI AudioSessionControl_AddRef(IAudioSessionControl2 *iface)
2586 AudioSessionWrapper *This = impl_from_IAudioSessionControl2(iface);
2587 ULONG ref;
2588 ref = InterlockedIncrement(&This->ref);
2589 TRACE("(%p) Refcount now %u\n", This, ref);
2590 return ref;
2593 static ULONG WINAPI AudioSessionControl_Release(IAudioSessionControl2 *iface)
2595 AudioSessionWrapper *This = impl_from_IAudioSessionControl2(iface);
2596 ULONG ref;
2597 ref = InterlockedDecrement(&This->ref);
2598 TRACE("(%p) Refcount now %u\n", This, ref);
2599 if (!ref) {
2600 if (This->client) {
2601 This->client->session_wrapper = NULL;
2602 AudioClient_Release(&This->client->IAudioClient_iface);
2604 HeapFree(GetProcessHeap(), 0, This);
2606 return ref;
2609 static HRESULT WINAPI AudioSessionControl_GetState(IAudioSessionControl2 *iface,
2610 AudioSessionState *state)
2612 AudioSessionWrapper *This = impl_from_IAudioSessionControl2(iface);
2613 ACImpl *client;
2615 TRACE("(%p)->(%p)\n", This, state);
2617 if (!state)
2618 return NULL_PTR_ERR;
2620 pthread_mutex_lock(&pulse_lock);
2621 if (list_empty(&This->session->clients)) {
2622 *state = AudioSessionStateExpired;
2623 goto out;
2625 LIST_FOR_EACH_ENTRY(client, &This->session->clients, ACImpl, entry) {
2626 if (client->started) {
2627 *state = AudioSessionStateActive;
2628 goto out;
2631 *state = AudioSessionStateInactive;
2633 out:
2634 pthread_mutex_unlock(&pulse_lock);
2635 return S_OK;
2638 static HRESULT WINAPI AudioSessionControl_GetDisplayName(
2639 IAudioSessionControl2 *iface, WCHAR **name)
2641 AudioSessionWrapper *This = impl_from_IAudioSessionControl2(iface);
2643 FIXME("(%p)->(%p) - stub\n", This, name);
2645 return E_NOTIMPL;
2648 static HRESULT WINAPI AudioSessionControl_SetDisplayName(
2649 IAudioSessionControl2 *iface, const WCHAR *name, const GUID *session)
2651 AudioSessionWrapper *This = impl_from_IAudioSessionControl2(iface);
2653 FIXME("(%p)->(%p, %s) - stub\n", This, name, debugstr_guid(session));
2655 return E_NOTIMPL;
2658 static HRESULT WINAPI AudioSessionControl_GetIconPath(
2659 IAudioSessionControl2 *iface, WCHAR **path)
2661 AudioSessionWrapper *This = impl_from_IAudioSessionControl2(iface);
2663 FIXME("(%p)->(%p) - stub\n", This, path);
2665 return E_NOTIMPL;
2668 static HRESULT WINAPI AudioSessionControl_SetIconPath(
2669 IAudioSessionControl2 *iface, const WCHAR *path, const GUID *session)
2671 AudioSessionWrapper *This = impl_from_IAudioSessionControl2(iface);
2673 FIXME("(%p)->(%p, %s) - stub\n", This, path, debugstr_guid(session));
2675 return E_NOTIMPL;
2678 static HRESULT WINAPI AudioSessionControl_GetGroupingParam(
2679 IAudioSessionControl2 *iface, GUID *group)
2681 AudioSessionWrapper *This = impl_from_IAudioSessionControl2(iface);
2683 FIXME("(%p)->(%p) - stub\n", This, group);
2685 return E_NOTIMPL;
2688 static HRESULT WINAPI AudioSessionControl_SetGroupingParam(
2689 IAudioSessionControl2 *iface, const GUID *group, const GUID *session)
2691 AudioSessionWrapper *This = impl_from_IAudioSessionControl2(iface);
2693 FIXME("(%p)->(%s, %s) - stub\n", This, debugstr_guid(group),
2694 debugstr_guid(session));
2696 return E_NOTIMPL;
2699 static HRESULT WINAPI AudioSessionControl_RegisterAudioSessionNotification(
2700 IAudioSessionControl2 *iface, IAudioSessionEvents *events)
2702 AudioSessionWrapper *This = impl_from_IAudioSessionControl2(iface);
2704 FIXME("(%p)->(%p) - stub\n", This, events);
2706 return S_OK;
2709 static HRESULT WINAPI AudioSessionControl_UnregisterAudioSessionNotification(
2710 IAudioSessionControl2 *iface, IAudioSessionEvents *events)
2712 AudioSessionWrapper *This = impl_from_IAudioSessionControl2(iface);
2714 FIXME("(%p)->(%p) - stub\n", This, events);
2716 return S_OK;
2719 static HRESULT WINAPI AudioSessionControl_GetSessionIdentifier(
2720 IAudioSessionControl2 *iface, WCHAR **id)
2722 AudioSessionWrapper *This = impl_from_IAudioSessionControl2(iface);
2724 FIXME("(%p)->(%p) - stub\n", This, id);
2726 return E_NOTIMPL;
2729 static HRESULT WINAPI AudioSessionControl_GetSessionInstanceIdentifier(
2730 IAudioSessionControl2 *iface, WCHAR **id)
2732 AudioSessionWrapper *This = impl_from_IAudioSessionControl2(iface);
2734 FIXME("(%p)->(%p) - stub\n", This, id);
2736 return E_NOTIMPL;
2739 static HRESULT WINAPI AudioSessionControl_GetProcessId(
2740 IAudioSessionControl2 *iface, DWORD *pid)
2742 AudioSessionWrapper *This = impl_from_IAudioSessionControl2(iface);
2744 TRACE("(%p)->(%p)\n", This, pid);
2746 if (!pid)
2747 return E_POINTER;
2749 *pid = GetCurrentProcessId();
2751 return S_OK;
2754 static HRESULT WINAPI AudioSessionControl_IsSystemSoundsSession(
2755 IAudioSessionControl2 *iface)
2757 AudioSessionWrapper *This = impl_from_IAudioSessionControl2(iface);
2759 TRACE("(%p)\n", This);
2761 return S_FALSE;
2764 static HRESULT WINAPI AudioSessionControl_SetDuckingPreference(
2765 IAudioSessionControl2 *iface, BOOL optout)
2767 AudioSessionWrapper *This = impl_from_IAudioSessionControl2(iface);
2769 TRACE("(%p)->(%d)\n", This, optout);
2771 return S_OK;
2774 static const IAudioSessionControl2Vtbl AudioSessionControl2_Vtbl =
2776 AudioSessionControl_QueryInterface,
2777 AudioSessionControl_AddRef,
2778 AudioSessionControl_Release,
2779 AudioSessionControl_GetState,
2780 AudioSessionControl_GetDisplayName,
2781 AudioSessionControl_SetDisplayName,
2782 AudioSessionControl_GetIconPath,
2783 AudioSessionControl_SetIconPath,
2784 AudioSessionControl_GetGroupingParam,
2785 AudioSessionControl_SetGroupingParam,
2786 AudioSessionControl_RegisterAudioSessionNotification,
2787 AudioSessionControl_UnregisterAudioSessionNotification,
2788 AudioSessionControl_GetSessionIdentifier,
2789 AudioSessionControl_GetSessionInstanceIdentifier,
2790 AudioSessionControl_GetProcessId,
2791 AudioSessionControl_IsSystemSoundsSession,
2792 AudioSessionControl_SetDuckingPreference
2795 typedef struct _SessionMgr {
2796 IAudioSessionManager2 IAudioSessionManager2_iface;
2798 LONG ref;
2800 IMMDevice *device;
2801 } SessionMgr;
2803 static HRESULT WINAPI AudioSessionManager_QueryInterface(IAudioSessionManager2 *iface,
2804 REFIID riid, void **ppv)
2806 TRACE("(%p)->(%s, %p)\n", iface, debugstr_guid(riid), ppv);
2808 if (!ppv)
2809 return E_POINTER;
2810 *ppv = NULL;
2812 if (IsEqualIID(riid, &IID_IUnknown) ||
2813 IsEqualIID(riid, &IID_IAudioSessionManager) ||
2814 IsEqualIID(riid, &IID_IAudioSessionManager2))
2815 *ppv = iface;
2816 if (*ppv) {
2817 IUnknown_AddRef((IUnknown*)*ppv);
2818 return S_OK;
2821 WARN("Unknown interface %s\n", debugstr_guid(riid));
2822 return E_NOINTERFACE;
2825 static inline SessionMgr *impl_from_IAudioSessionManager2(IAudioSessionManager2 *iface)
2827 return CONTAINING_RECORD(iface, SessionMgr, IAudioSessionManager2_iface);
2830 static ULONG WINAPI AudioSessionManager_AddRef(IAudioSessionManager2 *iface)
2832 SessionMgr *This = impl_from_IAudioSessionManager2(iface);
2833 ULONG ref;
2834 ref = InterlockedIncrement(&This->ref);
2835 TRACE("(%p) Refcount now %u\n", This, ref);
2836 return ref;
2839 static ULONG WINAPI AudioSessionManager_Release(IAudioSessionManager2 *iface)
2841 SessionMgr *This = impl_from_IAudioSessionManager2(iface);
2842 ULONG ref;
2843 ref = InterlockedDecrement(&This->ref);
2844 TRACE("(%p) Refcount now %u\n", This, ref);
2845 if (!ref)
2846 HeapFree(GetProcessHeap(), 0, This);
2847 return ref;
2850 static HRESULT WINAPI AudioSessionManager_GetAudioSessionControl(
2851 IAudioSessionManager2 *iface, const GUID *session_guid, DWORD flags,
2852 IAudioSessionControl **out)
2854 SessionMgr *This = impl_from_IAudioSessionManager2(iface);
2855 AudioSession *session;
2856 AudioSessionWrapper *wrapper;
2857 HRESULT hr;
2859 TRACE("(%p)->(%s, %x, %p)\n", This, debugstr_guid(session_guid),
2860 flags, out);
2862 hr = get_audio_session(session_guid, This->device, 0, &session);
2863 if (FAILED(hr))
2864 return hr;
2866 wrapper = AudioSessionWrapper_Create(NULL);
2867 if (!wrapper)
2868 return E_OUTOFMEMORY;
2870 wrapper->session = session;
2872 *out = (IAudioSessionControl*)&wrapper->IAudioSessionControl2_iface;
2874 return S_OK;
2877 static HRESULT WINAPI AudioSessionManager_GetSimpleAudioVolume(
2878 IAudioSessionManager2 *iface, const GUID *session_guid, DWORD flags,
2879 ISimpleAudioVolume **out)
2881 SessionMgr *This = impl_from_IAudioSessionManager2(iface);
2882 AudioSession *session;
2883 AudioSessionWrapper *wrapper;
2884 HRESULT hr;
2886 TRACE("(%p)->(%s, %x, %p)\n", This, debugstr_guid(session_guid),
2887 flags, out);
2889 hr = get_audio_session(session_guid, This->device, 0, &session);
2890 if (FAILED(hr))
2891 return hr;
2893 wrapper = AudioSessionWrapper_Create(NULL);
2894 if (!wrapper)
2895 return E_OUTOFMEMORY;
2897 wrapper->session = session;
2899 *out = &wrapper->ISimpleAudioVolume_iface;
2901 return S_OK;
2904 static HRESULT WINAPI AudioSessionManager_GetSessionEnumerator(
2905 IAudioSessionManager2 *iface, IAudioSessionEnumerator **out)
2907 SessionMgr *This = impl_from_IAudioSessionManager2(iface);
2908 FIXME("(%p)->(%p) - stub\n", This, out);
2909 return E_NOTIMPL;
2912 static HRESULT WINAPI AudioSessionManager_RegisterSessionNotification(
2913 IAudioSessionManager2 *iface, IAudioSessionNotification *notification)
2915 SessionMgr *This = impl_from_IAudioSessionManager2(iface);
2916 FIXME("(%p)->(%p) - stub\n", This, notification);
2917 return E_NOTIMPL;
2920 static HRESULT WINAPI AudioSessionManager_UnregisterSessionNotification(
2921 IAudioSessionManager2 *iface, IAudioSessionNotification *notification)
2923 SessionMgr *This = impl_from_IAudioSessionManager2(iface);
2924 FIXME("(%p)->(%p) - stub\n", This, notification);
2925 return E_NOTIMPL;
2928 static HRESULT WINAPI AudioSessionManager_RegisterDuckNotification(
2929 IAudioSessionManager2 *iface, const WCHAR *session_id,
2930 IAudioVolumeDuckNotification *notification)
2932 SessionMgr *This = impl_from_IAudioSessionManager2(iface);
2933 FIXME("(%p)->(%p) - stub\n", This, notification);
2934 return E_NOTIMPL;
2937 static HRESULT WINAPI AudioSessionManager_UnregisterDuckNotification(
2938 IAudioSessionManager2 *iface,
2939 IAudioVolumeDuckNotification *notification)
2941 SessionMgr *This = impl_from_IAudioSessionManager2(iface);
2942 FIXME("(%p)->(%p) - stub\n", This, notification);
2943 return E_NOTIMPL;
2946 static const IAudioSessionManager2Vtbl AudioSessionManager2_Vtbl =
2948 AudioSessionManager_QueryInterface,
2949 AudioSessionManager_AddRef,
2950 AudioSessionManager_Release,
2951 AudioSessionManager_GetAudioSessionControl,
2952 AudioSessionManager_GetSimpleAudioVolume,
2953 AudioSessionManager_GetSessionEnumerator,
2954 AudioSessionManager_RegisterSessionNotification,
2955 AudioSessionManager_UnregisterSessionNotification,
2956 AudioSessionManager_RegisterDuckNotification,
2957 AudioSessionManager_UnregisterDuckNotification
2960 static HRESULT WINAPI SimpleAudioVolume_QueryInterface(
2961 ISimpleAudioVolume *iface, REFIID riid, void **ppv)
2963 TRACE("(%p)->(%s, %p)\n", iface, debugstr_guid(riid), ppv);
2965 if (!ppv)
2966 return E_POINTER;
2967 *ppv = NULL;
2969 if (IsEqualIID(riid, &IID_IUnknown) ||
2970 IsEqualIID(riid, &IID_ISimpleAudioVolume))
2971 *ppv = iface;
2972 if (*ppv) {
2973 IUnknown_AddRef((IUnknown*)*ppv);
2974 return S_OK;
2977 WARN("Unknown interface %s\n", debugstr_guid(riid));
2978 return E_NOINTERFACE;
2981 static ULONG WINAPI SimpleAudioVolume_AddRef(ISimpleAudioVolume *iface)
2983 AudioSessionWrapper *This = impl_from_ISimpleAudioVolume(iface);
2984 return AudioSessionControl_AddRef(&This->IAudioSessionControl2_iface);
2987 static ULONG WINAPI SimpleAudioVolume_Release(ISimpleAudioVolume *iface)
2989 AudioSessionWrapper *This = impl_from_ISimpleAudioVolume(iface);
2990 return AudioSessionControl_Release(&This->IAudioSessionControl2_iface);
2993 static HRESULT WINAPI SimpleAudioVolume_SetMasterVolume(
2994 ISimpleAudioVolume *iface, float level, const GUID *context)
2996 AudioSessionWrapper *This = impl_from_ISimpleAudioVolume(iface);
2997 AudioSession *session = This->session;
2999 TRACE("(%p)->(%f, %s)\n", session, level, wine_dbgstr_guid(context));
3001 if (level < 0.f || level > 1.f)
3002 return E_INVALIDARG;
3004 if (context)
3005 FIXME("Notifications not supported yet\n");
3007 TRACE("PulseAudio does not support session volume control\n");
3009 pthread_mutex_lock(&pulse_lock);
3010 session->master_vol = level;
3011 pthread_mutex_unlock(&pulse_lock);
3013 return S_OK;
3016 static HRESULT WINAPI SimpleAudioVolume_GetMasterVolume(
3017 ISimpleAudioVolume *iface, float *level)
3019 AudioSessionWrapper *This = impl_from_ISimpleAudioVolume(iface);
3020 AudioSession *session = This->session;
3022 TRACE("(%p)->(%p)\n", session, level);
3024 if (!level)
3025 return NULL_PTR_ERR;
3027 *level = session->master_vol;
3029 return S_OK;
3032 static HRESULT WINAPI SimpleAudioVolume_SetMute(ISimpleAudioVolume *iface,
3033 BOOL mute, const GUID *context)
3035 AudioSessionWrapper *This = impl_from_ISimpleAudioVolume(iface);
3036 AudioSession *session = This->session;
3038 TRACE("(%p)->(%u, %p)\n", session, mute, context);
3040 if (context)
3041 FIXME("Notifications not supported yet\n");
3043 session->mute = mute;
3045 return S_OK;
3048 static HRESULT WINAPI SimpleAudioVolume_GetMute(ISimpleAudioVolume *iface,
3049 BOOL *mute)
3051 AudioSessionWrapper *This = impl_from_ISimpleAudioVolume(iface);
3052 AudioSession *session = This->session;
3054 TRACE("(%p)->(%p)\n", session, mute);
3056 if (!mute)
3057 return NULL_PTR_ERR;
3059 *mute = session->mute;
3061 return S_OK;
3064 static const ISimpleAudioVolumeVtbl SimpleAudioVolume_Vtbl =
3066 SimpleAudioVolume_QueryInterface,
3067 SimpleAudioVolume_AddRef,
3068 SimpleAudioVolume_Release,
3069 SimpleAudioVolume_SetMasterVolume,
3070 SimpleAudioVolume_GetMasterVolume,
3071 SimpleAudioVolume_SetMute,
3072 SimpleAudioVolume_GetMute
3075 static HRESULT WINAPI ChannelAudioVolume_QueryInterface(
3076 IChannelAudioVolume *iface, REFIID riid, void **ppv)
3078 TRACE("(%p)->(%s, %p)\n", iface, debugstr_guid(riid), ppv);
3080 if (!ppv)
3081 return E_POINTER;
3082 *ppv = NULL;
3084 if (IsEqualIID(riid, &IID_IUnknown) ||
3085 IsEqualIID(riid, &IID_IChannelAudioVolume))
3086 *ppv = iface;
3087 if (*ppv) {
3088 IUnknown_AddRef((IUnknown*)*ppv);
3089 return S_OK;
3092 WARN("Unknown interface %s\n", debugstr_guid(riid));
3093 return E_NOINTERFACE;
3096 static ULONG WINAPI ChannelAudioVolume_AddRef(IChannelAudioVolume *iface)
3098 AudioSessionWrapper *This = impl_from_IChannelAudioVolume(iface);
3099 return AudioSessionControl_AddRef(&This->IAudioSessionControl2_iface);
3102 static ULONG WINAPI ChannelAudioVolume_Release(IChannelAudioVolume *iface)
3104 AudioSessionWrapper *This = impl_from_IChannelAudioVolume(iface);
3105 return AudioSessionControl_Release(&This->IAudioSessionControl2_iface);
3108 static HRESULT WINAPI ChannelAudioVolume_GetChannelCount(
3109 IChannelAudioVolume *iface, UINT32 *out)
3111 AudioSessionWrapper *This = impl_from_IChannelAudioVolume(iface);
3112 AudioSession *session = This->session;
3114 TRACE("(%p)->(%p)\n", session, out);
3116 if (!out)
3117 return NULL_PTR_ERR;
3119 *out = session->channel_count;
3121 return S_OK;
3124 static HRESULT WINAPI ChannelAudioVolume_SetChannelVolume(
3125 IChannelAudioVolume *iface, UINT32 index, float level,
3126 const GUID *context)
3128 AudioSessionWrapper *This = impl_from_IChannelAudioVolume(iface);
3129 AudioSession *session = This->session;
3131 TRACE("(%p)->(%d, %f, %s)\n", session, index, level,
3132 wine_dbgstr_guid(context));
3134 if (level < 0.f || level > 1.f)
3135 return E_INVALIDARG;
3137 if (index >= session->channel_count)
3138 return E_INVALIDARG;
3140 if (context)
3141 FIXME("Notifications not supported yet\n");
3143 TRACE("PulseAudio does not support session volume control\n");
3145 pthread_mutex_lock(&pulse_lock);
3146 session->channel_vols[index] = level;
3147 pthread_mutex_unlock(&pulse_lock);
3149 return S_OK;
3152 static HRESULT WINAPI ChannelAudioVolume_GetChannelVolume(
3153 IChannelAudioVolume *iface, UINT32 index, float *level)
3155 AudioSessionWrapper *This = impl_from_IChannelAudioVolume(iface);
3156 AudioSession *session = This->session;
3158 TRACE("(%p)->(%d, %p)\n", session, index, level);
3160 if (!level)
3161 return NULL_PTR_ERR;
3163 if (index >= session->channel_count)
3164 return E_INVALIDARG;
3166 *level = session->channel_vols[index];
3168 return S_OK;
3171 static HRESULT WINAPI ChannelAudioVolume_SetAllVolumes(
3172 IChannelAudioVolume *iface, UINT32 count, const float *levels,
3173 const GUID *context)
3175 AudioSessionWrapper *This = impl_from_IChannelAudioVolume(iface);
3176 AudioSession *session = This->session;
3177 int i;
3179 TRACE("(%p)->(%d, %p, %s)\n", session, count, levels,
3180 wine_dbgstr_guid(context));
3182 if (!levels)
3183 return NULL_PTR_ERR;
3185 if (count != session->channel_count)
3186 return E_INVALIDARG;
3188 if (context)
3189 FIXME("Notifications not supported yet\n");
3191 TRACE("PulseAudio does not support session volume control\n");
3193 pthread_mutex_lock(&pulse_lock);
3194 for(i = 0; i < count; ++i)
3195 session->channel_vols[i] = levels[i];
3196 pthread_mutex_unlock(&pulse_lock);
3197 return S_OK;
3200 static HRESULT WINAPI ChannelAudioVolume_GetAllVolumes(
3201 IChannelAudioVolume *iface, UINT32 count, float *levels)
3203 AudioSessionWrapper *This = impl_from_IChannelAudioVolume(iface);
3204 AudioSession *session = This->session;
3205 int i;
3207 TRACE("(%p)->(%d, %p)\n", session, count, levels);
3209 if (!levels)
3210 return NULL_PTR_ERR;
3212 if (count != session->channel_count)
3213 return E_INVALIDARG;
3215 for(i = 0; i < count; ++i)
3216 levels[i] = session->channel_vols[i];
3218 return S_OK;
3221 static const IChannelAudioVolumeVtbl ChannelAudioVolume_Vtbl =
3223 ChannelAudioVolume_QueryInterface,
3224 ChannelAudioVolume_AddRef,
3225 ChannelAudioVolume_Release,
3226 ChannelAudioVolume_GetChannelCount,
3227 ChannelAudioVolume_SetChannelVolume,
3228 ChannelAudioVolume_GetChannelVolume,
3229 ChannelAudioVolume_SetAllVolumes,
3230 ChannelAudioVolume_GetAllVolumes
3233 HRESULT WINAPI AUDDRV_GetAudioSessionManager(IMMDevice *device,
3234 IAudioSessionManager2 **out)
3236 SessionMgr *This = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, sizeof(SessionMgr));
3237 *out = NULL;
3238 if (!This)
3239 return E_OUTOFMEMORY;
3240 This->IAudioSessionManager2_iface.lpVtbl = &AudioSessionManager2_Vtbl;
3241 This->device = device;
3242 This->ref = 1;
3243 *out = &This->IAudioSessionManager2_iface;
3244 return S_OK;
3247 HRESULT WINAPI AUDDRV_GetPropValue(GUID *guid, const PROPERTYKEY *prop, PROPVARIANT *out)
3249 TRACE("%s, (%s,%u), %p\n", wine_dbgstr_guid(guid), wine_dbgstr_guid(&prop->fmtid), prop->pid, out);
3251 if (IsEqualGUID(guid, &pulse_render_guid) && IsEqualPropertyKey(*prop, PKEY_AudioEndpoint_PhysicalSpeakers)) {
3252 out->vt = VT_UI4;
3253 out->u.ulVal = g_phys_speakers_mask;
3255 return out->u.ulVal ? S_OK : E_FAIL;
3258 return E_NOTIMPL;