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
35 #include <pulse/pulseaudio.h>
41 #include "wine/debug.h"
42 #include "wine/unicode.h"
43 #include "wine/list.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> */
66 Priority_Unavailable
= 0,
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
) {
108 SetThreadPriority(pulse_thread
, 0);
110 pa_context_disconnect(pulse_ctx
);
111 pa_context_unref(pulse_ctx
);
114 pa_mainloop_quit(pulse_ml
, 0);
116 WaitForSingleObject(pulse_thread
, INFINITE
);
117 CloseHandle(pulse_thread
);
123 typedef struct ACImpl ACImpl
;
125 typedef struct _AudioSession
{
132 UINT32 channel_count
;
139 typedef struct _AudioSessionWrapper
{
140 IAudioSessionControl2 IAudioSessionControl2_iface
;
141 IChannelAudioVolume IChannelAudioVolume_iface
;
142 ISimpleAudioVolume ISimpleAudioVolume_iface
;
147 AudioSession
*session
;
148 } AudioSessionWrapper
;
150 typedef struct _ACPacket
{
158 IAudioClient IAudioClient_iface
;
159 IAudioRenderClient IAudioRenderClient_iface
;
160 IAudioCaptureClient IAudioCaptureClient_iface
;
161 IAudioClock IAudioClock_iface
;
162 IAudioClock2 IAudioClock2_iface
;
163 IAudioStreamVolume IAudioStreamVolume_iface
;
167 float vol
[PA_CHANNELS_MAX
];
172 AUDCLNT_SHAREMODE share
;
175 UINT32 bufsize_frames
, bufsize_bytes
, locked
, capture_period
, pad
, started
, peek_ofs
;
176 void *locked_ptr
, *tmp_buffer
;
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
) {
266 pthread_mutex_unlock(&pulse_lock
);
267 r
= poll(ufds
, nfds
, timeout
);
268 pthread_mutex_lock(&pulse_lock
);
272 static DWORD CALLBACK
pulse_mainloop_thread(void *tmp
) {
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
);
284 static void pulse_contextcallback(pa_context
*c
, void *userdata
)
286 switch (pa_context_get_state(c
)) {
288 FIXME("Unhandled state: %i\n", pa_context_get_state(c
));
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
));
299 case PA_CONTEXT_READY
:
303 case PA_CONTEXT_FAILED
:
304 ERR("Context failed: %s\n", pa_strerror(pa_context_errno(c
)));
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
) {
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;
369 static void pulse_probe_settings(int render
, WAVEFORMATEXTENSIBLE
*fmt
) {
370 WAVEFORMATEX
*wfx
= &fmt
->Format
;
376 unsigned int length
= 0;
378 pa_channel_map_init_auto(&map
, 2, PA_CHANNEL_MAP_ALSA
);
380 ss
.format
= PA_SAMPLE_FLOAT32LE
;
381 ss
.channels
= map
.channels
;
385 attr
.minreq
= attr
.fragsize
= pa_frame_size(&ss
);
388 stream
= pa_stream_new(pulse_ctx
, "format test stream", &ss
, &map
);
390 pa_stream_set_state_callback(stream
, pulse_stream_state
, NULL
);
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
);
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
);
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
);
406 length
= pa_stream_get_buffer_attr(stream
)->minreq
;
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 pa_stream_unref(stream
);
418 pulse_def_period
[!render
] = pulse_min_period
[!render
] = pa_bytes_to_usec(10 * length
, &ss
);
420 pulse_min_period
[!render
] = MinimumPeriod
;
421 if (pulse_def_period
[!render
] <= DefaultPeriod
)
422 pulse_def_period
[!render
] = DefaultPeriod
;
424 wfx
->wFormatTag
= WAVE_FORMAT_EXTENSIBLE
;
425 wfx
->cbSize
= sizeof(WAVEFORMATEXTENSIBLE
) - sizeof(WAVEFORMATEX
);
426 wfx
->nChannels
= ss
.channels
;
427 wfx
->wBitsPerSample
= 8 * pa_sample_size_of_format(ss
.format
);
428 wfx
->nSamplesPerSec
= ss
.rate
;
429 wfx
->nBlockAlign
= pa_frame_size(&ss
);
430 wfx
->nAvgBytesPerSec
= wfx
->nSamplesPerSec
* wfx
->nBlockAlign
;
431 if (ss
.format
!= PA_SAMPLE_S24_32LE
)
432 fmt
->Samples
.wValidBitsPerSample
= wfx
->wBitsPerSample
;
434 fmt
->Samples
.wValidBitsPerSample
= 24;
435 if (ss
.format
== PA_SAMPLE_FLOAT32LE
)
436 fmt
->SubFormat
= KSDATAFORMAT_SUBTYPE_IEEE_FLOAT
;
438 fmt
->SubFormat
= KSDATAFORMAT_SUBTYPE_PCM
;
440 fmt
->dwChannelMask
= pulse_channel_map_to_channel_mask(&map
);
443 static HRESULT
pulse_connect(void)
446 WCHAR path
[MAX_PATH
], *name
;
451 if (!(pulse_thread
= CreateThread(NULL
, 0, pulse_mainloop_thread
, NULL
, 0, NULL
)))
453 ERR("Failed to create mainloop thread.\n");
456 SetThreadPriority(pulse_thread
, THREAD_PRIORITY_TIME_CRITICAL
);
457 pthread_cond_wait(&pulse_cond
, &pulse_lock
);
460 if (pulse_ctx
&& PA_CONTEXT_IS_GOOD(pa_context_get_state(pulse_ctx
)))
463 pa_context_unref(pulse_ctx
);
465 GetModuleFileNameW(NULL
, path
, sizeof(path
)/sizeof(*path
));
466 name
= strrchrW(path
, '\\');
471 len
= WideCharToMultiByte(CP_UNIXCP
, 0, name
, -1, NULL
, 0, NULL
, NULL
);
472 str
= pa_xmalloc(len
);
473 WideCharToMultiByte(CP_UNIXCP
, 0, name
, -1, str
, len
, NULL
, NULL
);
474 TRACE("Name: %s\n", str
);
475 pulse_ctx
= pa_context_new(pa_mainloop_get_api(pulse_ml
), str
);
478 ERR("Failed to create context\n");
482 pa_context_set_state_callback(pulse_ctx
, pulse_contextcallback
, NULL
);
484 TRACE("libpulse protocol version: %u. API Version %u\n", pa_context_get_protocol_version(pulse_ctx
), PA_API_VERSION
);
485 if (pa_context_connect(pulse_ctx
, NULL
, 0, NULL
) < 0)
488 /* Wait for connection */
489 while (pthread_cond_wait(&pulse_cond
, &pulse_lock
)) {
490 pa_context_state_t state
= pa_context_get_state(pulse_ctx
);
492 if (state
== PA_CONTEXT_FAILED
|| state
== PA_CONTEXT_TERMINATED
)
495 if (state
== PA_CONTEXT_READY
)
499 TRACE("Connected to server %s with protocol version: %i.\n",
500 pa_context_get_server(pulse_ctx
),
501 pa_context_get_server_protocol_version(pulse_ctx
));
505 pa_context_unref(pulse_ctx
);
510 /* For default PulseAudio render device, OR together all of the
511 * PKEY_AudioEndpoint_PhysicalSpeakers values of the sinks. */
512 static void pulse_phys_speakers_cb(pa_context
*c
, const pa_sink_info
*i
, int eol
, void *userdata
)
515 g_phys_speakers_mask
|= pulse_channel_map_to_channel_mask(&i
->channel_map
);
518 /* some poorly-behaved applications call audio functions during DllMain, so we
519 * have to do as much as possible without creating a new thread. this function
520 * sets up a synchronous connection to verify the server is running and query
522 static HRESULT
pulse_test_connect(void)
525 WCHAR path
[MAX_PATH
], *name
;
529 pulse_ml
= pa_mainloop_new();
531 pa_mainloop_set_poll_func(pulse_ml
, pulse_poll_func
, NULL
);
533 GetModuleFileNameW(NULL
, path
, sizeof(path
)/sizeof(*path
));
534 name
= strrchrW(path
, '\\');
539 len
= WideCharToMultiByte(CP_UNIXCP
, 0, name
, -1, NULL
, 0, NULL
, NULL
);
540 str
= pa_xmalloc(len
);
541 WideCharToMultiByte(CP_UNIXCP
, 0, name
, -1, str
, len
, NULL
, NULL
);
542 TRACE("Name: %s\n", str
);
543 pulse_ctx
= pa_context_new(pa_mainloop_get_api(pulse_ml
), str
);
546 ERR("Failed to create context\n");
547 pa_mainloop_free(pulse_ml
);
552 pa_context_set_state_callback(pulse_ctx
, pulse_contextcallback
, NULL
);
554 TRACE("libpulse protocol version: %u. API Version %u\n", pa_context_get_protocol_version(pulse_ctx
), PA_API_VERSION
);
555 if (pa_context_connect(pulse_ctx
, NULL
, 0, NULL
) < 0)
558 /* Wait for connection */
559 while (pa_mainloop_iterate(pulse_ml
, 1, &ret
) >= 0) {
560 pa_context_state_t state
= pa_context_get_state(pulse_ctx
);
562 if (state
== PA_CONTEXT_FAILED
|| state
== PA_CONTEXT_TERMINATED
)
565 if (state
== PA_CONTEXT_READY
)
569 TRACE("Test-connected to server %s with protocol version: %i.\n",
570 pa_context_get_server(pulse_ctx
),
571 pa_context_get_server_protocol_version(pulse_ctx
));
573 pulse_probe_settings(1, &pulse_fmt
[0]);
574 pulse_probe_settings(0, &pulse_fmt
[1]);
576 g_phys_speakers_mask
= 0;
577 o
= pa_context_get_sink_info_list(pulse_ctx
, &pulse_phys_speakers_cb
, NULL
);
579 while (pa_mainloop_iterate(pulse_ml
, 1, &ret
) >= 0 &&
580 pa_operation_get_state(o
) == PA_OPERATION_RUNNING
)
582 pa_operation_unref(o
);
585 pa_context_unref(pulse_ctx
);
587 pa_mainloop_free(pulse_ml
);
593 pa_context_unref(pulse_ctx
);
595 pa_mainloop_free(pulse_ml
);
601 static HRESULT
pulse_stream_valid(ACImpl
*This
) {
603 return AUDCLNT_E_NOT_INITIALIZED
;
604 if (!This
->stream
|| pa_stream_get_state(This
->stream
) != PA_STREAM_READY
)
605 return AUDCLNT_E_DEVICE_INVALIDATED
;
609 static void silence_buffer(pa_sample_format_t format
, BYTE
*buffer
, UINT32 bytes
)
611 memset(buffer
, format
== PA_SAMPLE_U8
? 0x80 : 0, bytes
);
614 static void dump_attr(const pa_buffer_attr
*attr
) {
615 TRACE("maxlength: %u\n", attr
->maxlength
);
616 TRACE("minreq: %u\n", attr
->minreq
);
617 TRACE("fragsize: %u\n", attr
->fragsize
);
618 TRACE("tlength: %u\n", attr
->tlength
);
619 TRACE("prebuf: %u\n", attr
->prebuf
);
622 static void pulse_op_cb(pa_stream
*s
, int success
, void *user
) {
623 TRACE("Success: %i\n", success
);
624 *(int*)user
= success
;
625 pthread_cond_signal(&pulse_cond
);
628 static void pulse_attr_update(pa_stream
*s
, void *user
) {
629 const pa_buffer_attr
*attr
= pa_stream_get_buffer_attr(s
);
630 TRACE("New attributes or device moved:\n");
634 static void pulse_wr_callback(pa_stream
*s
, size_t bytes
, void *userdata
)
636 ACImpl
*This
= userdata
;
637 UINT32 oldpad
= This
->pad
;
639 if (bytes
< This
->bufsize_bytes
)
640 This
->pad
= This
->bufsize_bytes
- bytes
;
644 if (oldpad
== This
->pad
)
647 assert(oldpad
> This
->pad
);
649 This
->clock_written
+= oldpad
- This
->pad
;
650 TRACE("New pad: %zu (-%zu)\n", This
->pad
/ pa_frame_size(&This
->ss
), (oldpad
- This
->pad
) / pa_frame_size(&This
->ss
));
653 SetEvent(This
->event
);
656 static void pulse_underflow_callback(pa_stream
*s
, void *userdata
)
661 /* Latency is periodically updated even when nothing is played,
662 * because of PA_STREAM_AUTO_TIMING_UPDATE so use it as timer
664 * Perfect for passing all tests :)
666 static void pulse_latency_callback(pa_stream
*s
, void *userdata
)
668 ACImpl
*This
= userdata
;
669 if (!This
->pad
&& This
->event
)
670 SetEvent(This
->event
);
673 static void pulse_started_callback(pa_stream
*s
, void *userdata
)
675 TRACE("(Re)started playing\n");
678 static void pulse_rd_loop(ACImpl
*This
, size_t bytes
)
680 while (bytes
>= This
->capture_period
) {
682 LARGE_INTEGER stamp
, freq
;
684 size_t src_len
, copy
, rem
= This
->capture_period
;
685 if (!(p
= (ACPacket
*)list_head(&This
->packet_free_head
))) {
686 p
= (ACPacket
*)list_head(&This
->packet_filled_head
);
688 next
= (ACPacket
*)p
->entry
.next
;
691 p
= (ACPacket
*)list_tail(&This
->packet_filled_head
);
692 assert(This
->pad
== This
->bufsize_bytes
);
694 assert(This
->pad
< This
->bufsize_bytes
);
695 This
->pad
+= This
->capture_period
;
696 assert(This
->pad
<= This
->bufsize_bytes
);
698 QueryPerformanceCounter(&stamp
);
699 QueryPerformanceFrequency(&freq
);
700 p
->qpcpos
= (stamp
.QuadPart
* (INT64
)10000000) / freq
.QuadPart
;
702 list_remove(&p
->entry
);
703 list_add_tail(&This
->packet_filled_head
, &p
->entry
);
707 pa_stream_peek(This
->stream
, (const void**)&src
, &src_len
);
709 assert(This
->peek_ofs
< src_len
);
710 src
+= This
->peek_ofs
;
711 src_len
-= This
->peek_ofs
;
712 assert(src_len
<= bytes
);
717 memcpy(dst
, src
, rem
);
725 pa_stream_drop(This
->stream
);
727 This
->peek_ofs
+= copy
;
729 bytes
-= This
->capture_period
;
733 static void pulse_rd_drop(ACImpl
*This
, size_t bytes
)
735 while (bytes
>= This
->capture_period
) {
736 size_t src_len
, copy
, rem
= This
->capture_period
;
739 pa_stream_peek(This
->stream
, &src
, &src_len
);
741 assert(This
->peek_ofs
< src_len
);
742 src_len
-= This
->peek_ofs
;
743 assert(src_len
<= bytes
);
754 pa_stream_drop(This
->stream
);
756 This
->peek_ofs
+= copy
;
762 static void pulse_rd_callback(pa_stream
*s
, size_t bytes
, void *userdata
)
764 ACImpl
*This
= userdata
;
766 TRACE("Readable total: %zu, fragsize: %u\n", bytes
, pa_stream_get_buffer_attr(s
)->fragsize
);
767 assert(bytes
>= This
->peek_ofs
);
768 bytes
-= This
->peek_ofs
;
769 if (bytes
< This
->capture_period
)
773 pulse_rd_loop(This
, bytes
);
775 pulse_rd_drop(This
, bytes
);
778 SetEvent(This
->event
);
781 static HRESULT
pulse_stream_connect(ACImpl
*This
, UINT32 period_bytes
) {
787 pa_stream_disconnect(This
->stream
);
788 while (pa_stream_get_state(This
->stream
) == PA_STREAM_READY
)
789 pthread_cond_wait(&pulse_cond
, &pulse_lock
);
790 pa_stream_unref(This
->stream
);
792 ret
= InterlockedIncrement(&number
);
793 sprintf(buffer
, "audio stream #%i", ret
);
794 This
->stream
= pa_stream_new(pulse_ctx
, buffer
, &This
->ss
, &This
->map
);
797 WARN("pa_stream_new returned error %i\n", pa_context_errno(pulse_ctx
));
798 return AUDCLNT_E_ENDPOINT_CREATE_FAILED
;
801 pa_stream_set_state_callback(This
->stream
, pulse_stream_state
, This
);
802 pa_stream_set_buffer_attr_callback(This
->stream
, pulse_attr_update
, This
);
803 pa_stream_set_moved_callback(This
->stream
, pulse_attr_update
, This
);
805 /* PulseAudio will fill in correct values */
806 attr
.minreq
= attr
.fragsize
= period_bytes
;
807 attr
.maxlength
= attr
.tlength
= This
->bufsize_bytes
;
808 attr
.prebuf
= pa_frame_size(&This
->ss
);
810 if (This
->dataflow
== eRender
)
811 ret
= pa_stream_connect_playback(This
->stream
, NULL
, &attr
,
812 PA_STREAM_START_CORKED
|PA_STREAM_START_UNMUTED
|PA_STREAM_AUTO_TIMING_UPDATE
|PA_STREAM_INTERPOLATE_TIMING
|PA_STREAM_EARLY_REQUESTS
, NULL
, NULL
);
814 ret
= pa_stream_connect_record(This
->stream
, NULL
, &attr
,
815 PA_STREAM_START_CORKED
|PA_STREAM_START_UNMUTED
|PA_STREAM_AUTO_TIMING_UPDATE
|PA_STREAM_INTERPOLATE_TIMING
|PA_STREAM_EARLY_REQUESTS
);
817 WARN("Returns %i\n", ret
);
818 return AUDCLNT_E_ENDPOINT_CREATE_FAILED
;
820 while (pa_stream_get_state(This
->stream
) == PA_STREAM_CREATING
)
821 pthread_cond_wait(&pulse_cond
, &pulse_lock
);
822 if (pa_stream_get_state(This
->stream
) != PA_STREAM_READY
)
823 return AUDCLNT_E_ENDPOINT_CREATE_FAILED
;
825 if (This
->dataflow
== eRender
) {
826 pa_stream_set_write_callback(This
->stream
, pulse_wr_callback
, This
);
827 pa_stream_set_underflow_callback(This
->stream
, pulse_underflow_callback
, This
);
828 pa_stream_set_started_callback(This
->stream
, pulse_started_callback
, This
);
830 pa_stream_set_read_callback(This
->stream
, pulse_rd_callback
, This
);
834 HRESULT WINAPI
AUDDRV_GetEndpointIDs(EDataFlow flow
, const WCHAR
***ids
, GUID
**keys
,
835 UINT
*num
, UINT
*def_index
)
839 TRACE("%d %p %p %p\n", flow
, ids
, num
, def_index
);
844 *ids
= HeapAlloc(GetProcessHeap(), 0, sizeof(**ids
));
847 return E_OUTOFMEMORY
;
849 (*ids
)[0] = id
= HeapAlloc(GetProcessHeap(), 0, sizeof(defaultW
));
850 *keys
= HeapAlloc(GetProcessHeap(), 0, sizeof(**keys
));
852 HeapFree(GetProcessHeap(), 0, id
);
853 HeapFree(GetProcessHeap(), 0, *keys
);
854 HeapFree(GetProcessHeap(), 0, *ids
);
857 return E_OUTOFMEMORY
;
859 memcpy(id
, defaultW
, sizeof(defaultW
));
862 (*keys
)[0] = pulse_render_guid
;
864 (*keys
)[0] = pulse_capture_guid
;
869 int WINAPI
AUDDRV_GetPriority(void)
872 pthread_mutex_lock(&pulse_lock
);
873 hr
= pulse_test_connect();
874 pthread_mutex_unlock(&pulse_lock
);
875 return SUCCEEDED(hr
) ? Priority_Preferred
: Priority_Unavailable
;
878 HRESULT WINAPI
AUDDRV_GetAudioEndpoint(GUID
*guid
, IMMDevice
*dev
, IAudioClient
**out
)
885 TRACE("%s %p %p\n", debugstr_guid(guid
), dev
, out
);
886 if (IsEqualGUID(guid
, &pulse_render_guid
))
888 else if (IsEqualGUID(guid
, &pulse_capture_guid
))
895 This
= HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY
, sizeof(*This
));
897 return E_OUTOFMEMORY
;
899 This
->IAudioClient_iface
.lpVtbl
= &AudioClient_Vtbl
;
900 This
->IAudioRenderClient_iface
.lpVtbl
= &AudioRenderClient_Vtbl
;
901 This
->IAudioCaptureClient_iface
.lpVtbl
= &AudioCaptureClient_Vtbl
;
902 This
->IAudioClock_iface
.lpVtbl
= &AudioClock_Vtbl
;
903 This
->IAudioClock2_iface
.lpVtbl
= &AudioClock2_Vtbl
;
904 This
->IAudioStreamVolume_iface
.lpVtbl
= &AudioStreamVolume_Vtbl
;
905 This
->dataflow
= dataflow
;
907 for (i
= 0; i
< PA_CHANNELS_MAX
; ++i
)
910 hr
= CoCreateFreeThreadedMarshaler((IUnknown
*)This
, &This
->marshal
);
912 HeapFree(GetProcessHeap(), 0, This
);
915 IMMDevice_AddRef(This
->parent
);
917 *out
= &This
->IAudioClient_iface
;
918 IAudioClient_AddRef(&This
->IAudioClient_iface
);
923 static HRESULT WINAPI
AudioClient_QueryInterface(IAudioClient
*iface
,
924 REFIID riid
, void **ppv
)
926 ACImpl
*This
= impl_from_IAudioClient(iface
);
928 TRACE("(%p)->(%s, %p)\n", iface
, debugstr_guid(riid
), ppv
);
934 if (IsEqualIID(riid
, &IID_IUnknown
) || IsEqualIID(riid
, &IID_IAudioClient
))
937 IUnknown_AddRef((IUnknown
*)*ppv
);
941 if (IsEqualIID(riid
, &IID_IMarshal
))
942 return IUnknown_QueryInterface(This
->marshal
, riid
, ppv
);
944 WARN("Unknown interface %s\n", debugstr_guid(riid
));
945 return E_NOINTERFACE
;
948 static ULONG WINAPI
AudioClient_AddRef(IAudioClient
*iface
)
950 ACImpl
*This
= impl_from_IAudioClient(iface
);
952 ref
= InterlockedIncrement(&This
->ref
);
953 TRACE("(%p) Refcount now %u\n", This
, ref
);
957 static ULONG WINAPI
AudioClient_Release(IAudioClient
*iface
)
959 ACImpl
*This
= impl_from_IAudioClient(iface
);
961 ref
= InterlockedDecrement(&This
->ref
);
962 TRACE("(%p) Refcount now %u\n", This
, ref
);
965 pthread_mutex_lock(&pulse_lock
);
966 if (PA_STREAM_IS_GOOD(pa_stream_get_state(This
->stream
))) {
967 pa_stream_disconnect(This
->stream
);
968 while (PA_STREAM_IS_GOOD(pa_stream_get_state(This
->stream
)))
969 pthread_cond_wait(&pulse_cond
, &pulse_lock
);
971 pa_stream_unref(This
->stream
);
973 list_remove(&This
->entry
);
974 pthread_mutex_unlock(&pulse_lock
);
976 IUnknown_Release(This
->marshal
);
977 IMMDevice_Release(This
->parent
);
978 HeapFree(GetProcessHeap(), 0, This
->tmp_buffer
);
979 HeapFree(GetProcessHeap(), 0, This
);
984 static void dump_fmt(const WAVEFORMATEX
*fmt
)
986 TRACE("wFormatTag: 0x%x (", fmt
->wFormatTag
);
987 switch(fmt
->wFormatTag
) {
988 case WAVE_FORMAT_PCM
:
989 TRACE("WAVE_FORMAT_PCM");
991 case WAVE_FORMAT_IEEE_FLOAT
:
992 TRACE("WAVE_FORMAT_IEEE_FLOAT");
994 case WAVE_FORMAT_EXTENSIBLE
:
995 TRACE("WAVE_FORMAT_EXTENSIBLE");
1003 TRACE("nChannels: %u\n", fmt
->nChannels
);
1004 TRACE("nSamplesPerSec: %u\n", fmt
->nSamplesPerSec
);
1005 TRACE("nAvgBytesPerSec: %u\n", fmt
->nAvgBytesPerSec
);
1006 TRACE("nBlockAlign: %u\n", fmt
->nBlockAlign
);
1007 TRACE("wBitsPerSample: %u\n", fmt
->wBitsPerSample
);
1008 TRACE("cbSize: %u\n", fmt
->cbSize
);
1010 if (fmt
->wFormatTag
== WAVE_FORMAT_EXTENSIBLE
) {
1011 WAVEFORMATEXTENSIBLE
*fmtex
= (void*)fmt
;
1012 TRACE("dwChannelMask: %08x\n", fmtex
->dwChannelMask
);
1013 TRACE("Samples: %04x\n", fmtex
->Samples
.wReserved
);
1014 TRACE("SubFormat: %s\n", wine_dbgstr_guid(&fmtex
->SubFormat
));
1018 static WAVEFORMATEX
*clone_format(const WAVEFORMATEX
*fmt
)
1023 if (fmt
->wFormatTag
== WAVE_FORMAT_EXTENSIBLE
)
1024 size
= sizeof(WAVEFORMATEXTENSIBLE
);
1026 size
= sizeof(WAVEFORMATEX
);
1028 ret
= CoTaskMemAlloc(size
);
1032 memcpy(ret
, fmt
, size
);
1034 ret
->cbSize
= size
- sizeof(WAVEFORMATEX
);
1039 static DWORD
get_channel_mask(unsigned int channels
)
1045 return KSAUDIO_SPEAKER_MONO
;
1047 return KSAUDIO_SPEAKER_STEREO
;
1049 return KSAUDIO_SPEAKER_STEREO
| SPEAKER_LOW_FREQUENCY
;
1051 return KSAUDIO_SPEAKER_QUAD
; /* not _SURROUND */
1053 return KSAUDIO_SPEAKER_QUAD
| SPEAKER_LOW_FREQUENCY
;
1055 return KSAUDIO_SPEAKER_5POINT1
; /* not 5POINT1_SURROUND */
1057 return KSAUDIO_SPEAKER_5POINT1
| SPEAKER_BACK_CENTER
;
1059 return KSAUDIO_SPEAKER_7POINT1_SURROUND
; /* Vista deprecates 7POINT1 */
1061 FIXME("Unknown speaker configuration: %u\n", channels
);
1065 static void session_init_vols(AudioSession
*session
, UINT channels
)
1067 if (session
->channel_count
< channels
) {
1070 if (session
->channel_vols
)
1071 session
->channel_vols
= HeapReAlloc(GetProcessHeap(), 0,
1072 session
->channel_vols
, sizeof(float) * channels
);
1074 session
->channel_vols
= HeapAlloc(GetProcessHeap(), 0,
1075 sizeof(float) * channels
);
1076 if (!session
->channel_vols
)
1079 for(i
= session
->channel_count
; i
< channels
; ++i
)
1080 session
->channel_vols
[i
] = 1.f
;
1082 session
->channel_count
= channels
;
1086 static AudioSession
*create_session(const GUID
*guid
, IMMDevice
*device
,
1091 ret
= HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY
, sizeof(AudioSession
));
1095 memcpy(&ret
->guid
, guid
, sizeof(GUID
));
1097 ret
->device
= device
;
1099 list_init(&ret
->clients
);
1101 list_add_head(&g_sessions
, &ret
->entry
);
1103 session_init_vols(ret
, num_channels
);
1105 ret
->master_vol
= 1.f
;
1110 /* if channels == 0, then this will return or create a session with
1111 * matching dataflow and GUID. otherwise, channels must also match */
1112 static HRESULT
get_audio_session(const GUID
*sessionguid
,
1113 IMMDevice
*device
, UINT channels
, AudioSession
**out
)
1115 AudioSession
*session
;
1117 if (!sessionguid
|| IsEqualGUID(sessionguid
, &GUID_NULL
)) {
1118 *out
= create_session(&GUID_NULL
, device
, channels
);
1120 return E_OUTOFMEMORY
;
1126 LIST_FOR_EACH_ENTRY(session
, &g_sessions
, AudioSession
, entry
) {
1127 if (session
->device
== device
&&
1128 IsEqualGUID(sessionguid
, &session
->guid
)) {
1129 session_init_vols(session
, channels
);
1136 *out
= create_session(sessionguid
, device
, channels
);
1138 return E_OUTOFMEMORY
;
1144 static HRESULT
pulse_spec_from_waveformat(ACImpl
*This
, const WAVEFORMATEX
*fmt
)
1146 pa_channel_map_init(&This
->map
);
1147 This
->ss
.rate
= fmt
->nSamplesPerSec
;
1148 This
->ss
.format
= PA_SAMPLE_INVALID
;
1150 switch(fmt
->wFormatTag
) {
1151 case WAVE_FORMAT_IEEE_FLOAT
:
1152 if (!fmt
->nChannels
|| fmt
->nChannels
> 2 || fmt
->wBitsPerSample
!= 32)
1154 This
->ss
.format
= PA_SAMPLE_FLOAT32LE
;
1155 pa_channel_map_init_auto(&This
->map
, fmt
->nChannels
, PA_CHANNEL_MAP_ALSA
);
1157 case WAVE_FORMAT_PCM
:
1158 if (!fmt
->nChannels
|| fmt
->nChannels
> 2)
1160 if (fmt
->wBitsPerSample
== 8)
1161 This
->ss
.format
= PA_SAMPLE_U8
;
1162 else if (fmt
->wBitsPerSample
== 16)
1163 This
->ss
.format
= PA_SAMPLE_S16LE
;
1165 return AUDCLNT_E_UNSUPPORTED_FORMAT
;
1166 pa_channel_map_init_auto(&This
->map
, fmt
->nChannels
, PA_CHANNEL_MAP_ALSA
);
1168 case WAVE_FORMAT_EXTENSIBLE
: {
1169 WAVEFORMATEXTENSIBLE
*wfe
= (WAVEFORMATEXTENSIBLE
*)fmt
;
1170 DWORD mask
= wfe
->dwChannelMask
;
1172 if (fmt
->cbSize
!= (sizeof(*wfe
) - sizeof(*fmt
)) && fmt
->cbSize
!= sizeof(*wfe
))
1174 if (IsEqualGUID(&wfe
->SubFormat
, &KSDATAFORMAT_SUBTYPE_IEEE_FLOAT
) &&
1175 (!wfe
->Samples
.wValidBitsPerSample
|| wfe
->Samples
.wValidBitsPerSample
== 32) &&
1176 fmt
->wBitsPerSample
== 32)
1177 This
->ss
.format
= PA_SAMPLE_FLOAT32LE
;
1178 else if (IsEqualGUID(&wfe
->SubFormat
, &KSDATAFORMAT_SUBTYPE_PCM
)) {
1179 DWORD valid
= wfe
->Samples
.wValidBitsPerSample
;
1181 valid
= fmt
->wBitsPerSample
;
1182 if (!valid
|| valid
> fmt
->wBitsPerSample
)
1184 switch (fmt
->wBitsPerSample
) {
1187 This
->ss
.format
= PA_SAMPLE_U8
;
1191 This
->ss
.format
= PA_SAMPLE_S16LE
;
1195 This
->ss
.format
= PA_SAMPLE_S24LE
;
1199 This
->ss
.format
= PA_SAMPLE_S24_32LE
;
1200 else if (valid
== 32)
1201 This
->ss
.format
= PA_SAMPLE_S32LE
;
1204 return AUDCLNT_E_UNSUPPORTED_FORMAT
;
1207 This
->map
.channels
= fmt
->nChannels
;
1208 if (!mask
|| (mask
& (SPEAKER_ALL
|SPEAKER_RESERVED
)))
1209 mask
= get_channel_mask(fmt
->nChannels
);
1210 for (j
= 0; j
< sizeof(pulse_pos_from_wfx
)/sizeof(*pulse_pos_from_wfx
) && i
< fmt
->nChannels
; ++j
) {
1211 if (mask
& (1 << j
))
1212 This
->map
.map
[i
++] = pulse_pos_from_wfx
[j
];
1215 /* Special case for mono since pulse appears to map it differently */
1216 if (mask
== SPEAKER_FRONT_CENTER
)
1217 This
->map
.map
[0] = PA_CHANNEL_POSITION_MONO
;
1219 if (i
< fmt
->nChannels
|| (mask
& SPEAKER_RESERVED
)) {
1220 This
->map
.channels
= 0;
1221 ERR("Invalid channel mask: %i/%i and %x(%x)\n", i
, fmt
->nChannels
, mask
, wfe
->dwChannelMask
);
1226 case WAVE_FORMAT_ALAW
:
1227 case WAVE_FORMAT_MULAW
:
1228 if (fmt
->wBitsPerSample
!= 8) {
1229 FIXME("Unsupported bpp %u for LAW\n", fmt
->wBitsPerSample
);
1230 return AUDCLNT_E_UNSUPPORTED_FORMAT
;
1232 if (fmt
->nChannels
!= 1 && fmt
->nChannels
!= 2) {
1233 FIXME("Unsupported channels %u for LAW\n", fmt
->nChannels
);
1234 return AUDCLNT_E_UNSUPPORTED_FORMAT
;
1236 This
->ss
.format
= fmt
->wFormatTag
== WAVE_FORMAT_MULAW
? PA_SAMPLE_ULAW
: PA_SAMPLE_ALAW
;
1237 pa_channel_map_init_auto(&This
->map
, fmt
->nChannels
, PA_CHANNEL_MAP_ALSA
);
1240 WARN("Unhandled tag %x\n", fmt
->wFormatTag
);
1241 return AUDCLNT_E_UNSUPPORTED_FORMAT
;
1243 This
->ss
.channels
= This
->map
.channels
;
1244 if (!pa_channel_map_valid(&This
->map
) || This
->ss
.format
== PA_SAMPLE_INVALID
) {
1245 ERR("Invalid format! Channel spec valid: %i, format: %i\n", pa_channel_map_valid(&This
->map
), This
->ss
.format
);
1246 return AUDCLNT_E_UNSUPPORTED_FORMAT
;
1251 static HRESULT WINAPI
AudioClient_Initialize(IAudioClient
*iface
,
1252 AUDCLNT_SHAREMODE mode
, DWORD flags
, REFERENCE_TIME duration
,
1253 REFERENCE_TIME period
, const WAVEFORMATEX
*fmt
,
1254 const GUID
*sessionguid
)
1256 ACImpl
*This
= impl_from_IAudioClient(iface
);
1260 TRACE("(%p)->(%x, %x, %s, %s, %p, %s)\n", This
, mode
, flags
,
1261 wine_dbgstr_longlong(duration
), wine_dbgstr_longlong(period
), fmt
, debugstr_guid(sessionguid
));
1266 if (mode
!= AUDCLNT_SHAREMODE_SHARED
&& mode
!= AUDCLNT_SHAREMODE_EXCLUSIVE
)
1267 return AUDCLNT_E_NOT_INITIALIZED
;
1268 if (mode
== AUDCLNT_SHAREMODE_EXCLUSIVE
)
1269 return AUDCLNT_E_EXCLUSIVE_MODE_NOT_ALLOWED
;
1271 if (flags
& ~(AUDCLNT_STREAMFLAGS_CROSSPROCESS
|
1272 AUDCLNT_STREAMFLAGS_LOOPBACK
|
1273 AUDCLNT_STREAMFLAGS_EVENTCALLBACK
|
1274 AUDCLNT_STREAMFLAGS_NOPERSIST
|
1275 AUDCLNT_STREAMFLAGS_RATEADJUST
|
1276 AUDCLNT_SESSIONFLAGS_EXPIREWHENUNOWNED
|
1277 AUDCLNT_SESSIONFLAGS_DISPLAY_HIDE
|
1278 AUDCLNT_SESSIONFLAGS_DISPLAY_HIDEWHENEXPIRED
)) {
1279 TRACE("Unknown flags: %08x\n", flags
);
1280 return E_INVALIDARG
;
1283 pthread_mutex_lock(&pulse_lock
);
1285 hr
= pulse_connect();
1287 pthread_mutex_unlock(&pulse_lock
);
1292 pthread_mutex_unlock(&pulse_lock
);
1293 return AUDCLNT_E_ALREADY_INITIALIZED
;
1296 hr
= pulse_spec_from_waveformat(This
, fmt
);
1297 TRACE("Obtaining format returns %08x\n", hr
);
1303 if (mode
== AUDCLNT_SHAREMODE_SHARED
) {
1304 REFERENCE_TIME def
= pulse_def_period
[This
->dataflow
== eCapture
];
1305 REFERENCE_TIME min
= pulse_min_period
[This
->dataflow
== eCapture
];
1307 /* Switch to low latency mode if below 2 default periods,
1308 * which is 20 ms by default, this will increase the amount
1309 * of interrupts but allows very low latency. In dsound I
1310 * managed to get a total latency of ~8ms, which is well below
1313 if (duration
< 2 * def
)
1317 if (duration
< 2 * period
)
1318 duration
= 2 * period
;
1320 /* Uh oh, really low latency requested.. */
1321 if (duration
<= 2 * period
)
1324 period_bytes
= pa_frame_size(&This
->ss
) * MulDiv(period
, This
->ss
.rate
, 10000000);
1326 if (duration
< 20000000)
1327 This
->bufsize_frames
= ceil((duration
/ 10000000.) * fmt
->nSamplesPerSec
);
1329 This
->bufsize_frames
= 2 * fmt
->nSamplesPerSec
;
1330 This
->bufsize_bytes
= This
->bufsize_frames
* pa_frame_size(&This
->ss
);
1333 This
->flags
= flags
;
1334 hr
= pulse_stream_connect(This
, period_bytes
);
1335 if (SUCCEEDED(hr
)) {
1337 const pa_buffer_attr
*attr
= pa_stream_get_buffer_attr(This
->stream
);
1338 /* Update frames according to new size */
1340 if (This
->dataflow
== eRender
) {
1341 if (attr
->tlength
< This
->bufsize_bytes
) {
1342 const char *latenv
= getenv("PULSE_LATENCY_MSEC");
1343 if (latenv
&& *latenv
)
1344 ERR_(winediag
)("PulseAudio buffer too small (%u < %u) - PULSE_LATENCY_MSEC is %s\n", attr
->tlength
, This
->bufsize_bytes
, latenv
);
1346 ERR_(winediag
)("PulseAudio buffer too small (%u < %u)\n", attr
->tlength
, This
->bufsize_bytes
);
1348 This
->bufsize_bytes
= attr
->tlength
;
1350 This
->capture_period
= period_bytes
= attr
->fragsize
;
1351 if ((unalign
= This
->bufsize_bytes
% period_bytes
))
1352 This
->bufsize_bytes
+= period_bytes
- unalign
;
1354 This
->bufsize_frames
= This
->bufsize_bytes
/ pa_frame_size(&This
->ss
);
1356 if (SUCCEEDED(hr
)) {
1357 UINT32 i
, capture_packets
= This
->capture_period
? This
->bufsize_bytes
/ This
->capture_period
: 0;
1358 This
->tmp_buffer
= HeapAlloc(GetProcessHeap(), 0, This
->bufsize_bytes
+ capture_packets
* sizeof(ACPacket
));
1359 if (!This
->tmp_buffer
)
1362 ACPacket
*cur_packet
= (ACPacket
*)((char*)This
->tmp_buffer
+ This
->bufsize_bytes
);
1363 BYTE
*data
= This
->tmp_buffer
;
1364 silence_buffer(This
->ss
.format
, This
->tmp_buffer
, This
->bufsize_bytes
);
1365 list_init(&This
->packet_free_head
);
1366 list_init(&This
->packet_filled_head
);
1367 for (i
= 0; i
< capture_packets
; ++i
, ++cur_packet
) {
1368 list_add_tail(&This
->packet_free_head
, &cur_packet
->entry
);
1369 cur_packet
->data
= data
;
1370 data
+= This
->capture_period
;
1372 assert(!This
->capture_period
|| This
->bufsize_bytes
== This
->capture_period
* capture_packets
);
1373 assert(!capture_packets
|| data
- This
->bufsize_bytes
== This
->tmp_buffer
);
1377 hr
= get_audio_session(sessionguid
, This
->parent
, fmt
->nChannels
, &This
->session
);
1379 list_add_tail(&This
->session
->clients
, &This
->entry
);
1383 HeapFree(GetProcessHeap(), 0, This
->tmp_buffer
);
1384 This
->tmp_buffer
= NULL
;
1386 pa_stream_disconnect(This
->stream
);
1387 pa_stream_unref(This
->stream
);
1388 This
->stream
= NULL
;
1391 pthread_mutex_unlock(&pulse_lock
);
1395 static HRESULT WINAPI
AudioClient_GetBufferSize(IAudioClient
*iface
,
1398 ACImpl
*This
= impl_from_IAudioClient(iface
);
1401 TRACE("(%p)->(%p)\n", This
, out
);
1406 pthread_mutex_lock(&pulse_lock
);
1407 hr
= pulse_stream_valid(This
);
1409 *out
= This
->bufsize_frames
;
1410 pthread_mutex_unlock(&pulse_lock
);
1415 static HRESULT WINAPI
AudioClient_GetStreamLatency(IAudioClient
*iface
,
1416 REFERENCE_TIME
*latency
)
1418 ACImpl
*This
= impl_from_IAudioClient(iface
);
1419 const pa_buffer_attr
*attr
;
1423 TRACE("(%p)->(%p)\n", This
, latency
);
1428 pthread_mutex_lock(&pulse_lock
);
1429 hr
= pulse_stream_valid(This
);
1431 pthread_mutex_unlock(&pulse_lock
);
1434 attr
= pa_stream_get_buffer_attr(This
->stream
);
1435 if (This
->dataflow
== eRender
)
1436 lat
= attr
->minreq
/ pa_frame_size(&This
->ss
);
1438 lat
= attr
->fragsize
/ pa_frame_size(&This
->ss
);
1439 *latency
= 10000000;
1441 *latency
/= This
->ss
.rate
;
1442 pthread_mutex_unlock(&pulse_lock
);
1443 TRACE("Latency: %u ms\n", (DWORD
)(*latency
/ 10000));
1447 static void ACImpl_GetRenderPad(ACImpl
*This
, UINT32
*out
)
1449 *out
= This
->pad
/ pa_frame_size(&This
->ss
);
1452 static void ACImpl_GetCapturePad(ACImpl
*This
, UINT32
*out
)
1454 ACPacket
*packet
= This
->locked_ptr
;
1455 if (!packet
&& !list_empty(&This
->packet_filled_head
)) {
1456 packet
= (ACPacket
*)list_head(&This
->packet_filled_head
);
1457 This
->locked_ptr
= packet
;
1458 list_remove(&packet
->entry
);
1461 *out
= This
->pad
/ pa_frame_size(&This
->ss
);
1464 static HRESULT WINAPI
AudioClient_GetCurrentPadding(IAudioClient
*iface
,
1467 ACImpl
*This
= impl_from_IAudioClient(iface
);
1470 TRACE("(%p)->(%p)\n", This
, out
);
1475 pthread_mutex_lock(&pulse_lock
);
1476 hr
= pulse_stream_valid(This
);
1478 pthread_mutex_unlock(&pulse_lock
);
1482 if (This
->dataflow
== eRender
)
1483 ACImpl_GetRenderPad(This
, out
);
1485 ACImpl_GetCapturePad(This
, out
);
1486 pthread_mutex_unlock(&pulse_lock
);
1488 TRACE("%p Pad: %u ms (%u)\n", This
, MulDiv(*out
, 1000, This
->ss
.rate
), *out
);
1492 static HRESULT WINAPI
AudioClient_IsFormatSupported(IAudioClient
*iface
,
1493 AUDCLNT_SHAREMODE mode
, const WAVEFORMATEX
*fmt
,
1496 ACImpl
*This
= impl_from_IAudioClient(iface
);
1498 WAVEFORMATEX
*closest
= NULL
;
1501 TRACE("(%p)->(%x, %p, %p)\n", This
, mode
, fmt
, out
);
1509 if (mode
== AUDCLNT_SHAREMODE_EXCLUSIVE
) {
1512 } else if (mode
== AUDCLNT_SHAREMODE_SHARED
) {
1517 return E_INVALIDARG
;
1519 if (fmt
->nChannels
== 0)
1520 return AUDCLNT_E_UNSUPPORTED_FORMAT
;
1522 closest
= clone_format(fmt
);
1524 return E_OUTOFMEMORY
;
1528 switch (fmt
->wFormatTag
) {
1529 case WAVE_FORMAT_EXTENSIBLE
: {
1530 WAVEFORMATEXTENSIBLE
*ext
= (WAVEFORMATEXTENSIBLE
*)closest
;
1532 if ((fmt
->cbSize
!= sizeof(WAVEFORMATEXTENSIBLE
) - sizeof(WAVEFORMATEX
) &&
1533 fmt
->cbSize
!= sizeof(WAVEFORMATEXTENSIBLE
)) ||
1534 fmt
->nBlockAlign
!= fmt
->wBitsPerSample
/ 8 * fmt
->nChannels
||
1535 ext
->Samples
.wValidBitsPerSample
> fmt
->wBitsPerSample
||
1536 fmt
->nAvgBytesPerSec
!= fmt
->nBlockAlign
* fmt
->nSamplesPerSec
) {
1542 UINT32 mask
= 0, i
, channels
= 0;
1544 if (!(ext
->dwChannelMask
& (SPEAKER_ALL
| SPEAKER_RESERVED
))) {
1545 for (i
= 1; !(i
& SPEAKER_RESERVED
); i
<<= 1) {
1546 if (i
& ext
->dwChannelMask
) {
1552 if (channels
!= fmt
->nChannels
|| (ext
->dwChannelMask
& ~mask
)) {
1553 hr
= AUDCLNT_E_UNSUPPORTED_FORMAT
;
1557 hr
= AUDCLNT_E_UNSUPPORTED_FORMAT
;
1562 if (IsEqualGUID(&ext
->SubFormat
, &KSDATAFORMAT_SUBTYPE_IEEE_FLOAT
)) {
1563 if (fmt
->wBitsPerSample
!= 32) {
1568 if (ext
->Samples
.wValidBitsPerSample
!= fmt
->wBitsPerSample
) {
1570 ext
->Samples
.wValidBitsPerSample
= fmt
->wBitsPerSample
;
1572 } else if (IsEqualGUID(&ext
->SubFormat
, &KSDATAFORMAT_SUBTYPE_PCM
)) {
1573 if (!fmt
->wBitsPerSample
|| fmt
->wBitsPerSample
> 32 || fmt
->wBitsPerSample
% 8) {
1578 if (ext
->Samples
.wValidBitsPerSample
!= fmt
->wBitsPerSample
&&
1579 !(fmt
->wBitsPerSample
== 32 &&
1580 ext
->Samples
.wValidBitsPerSample
== 24)) {
1582 ext
->Samples
.wValidBitsPerSample
= fmt
->wBitsPerSample
;
1586 hr
= AUDCLNT_E_UNSUPPORTED_FORMAT
;
1593 case WAVE_FORMAT_ALAW
:
1594 case WAVE_FORMAT_MULAW
:
1595 if (fmt
->wBitsPerSample
!= 8) {
1600 case WAVE_FORMAT_IEEE_FLOAT
:
1601 if (fmt
->wFormatTag
== WAVE_FORMAT_IEEE_FLOAT
&& fmt
->wBitsPerSample
!= 32) {
1606 case WAVE_FORMAT_PCM
:
1607 if (fmt
->wFormatTag
== WAVE_FORMAT_PCM
&&
1608 (!fmt
->wBitsPerSample
|| fmt
->wBitsPerSample
> 32 || fmt
->wBitsPerSample
% 8)) {
1613 if (fmt
->nChannels
> 2) {
1614 hr
= AUDCLNT_E_UNSUPPORTED_FORMAT
;
1618 * fmt->cbSize, fmt->nBlockAlign and fmt->nAvgBytesPerSec seem to be
1619 * ignored, invalid values are happily accepted.
1623 hr
= AUDCLNT_E_UNSUPPORTED_FORMAT
;
1627 if (exclusive
&& hr
!= S_OK
) {
1628 hr
= AUDCLNT_E_UNSUPPORTED_FORMAT
;
1629 CoTaskMemFree(closest
);
1630 } else if (hr
!= S_FALSE
)
1631 CoTaskMemFree(closest
);
1635 /* Winepulse does not currently support exclusive mode, if you know of an
1636 * application that uses it, I will correct this..
1638 if (hr
== S_OK
&& exclusive
)
1639 return This
->dataflow
== eCapture
? AUDCLNT_E_UNSUPPORTED_FORMAT
: AUDCLNT_E_EXCLUSIVE_MODE_NOT_ALLOWED
;
1641 TRACE("returning: %08x %p\n", hr
, out
? *out
: NULL
);
1645 static HRESULT WINAPI
AudioClient_GetMixFormat(IAudioClient
*iface
,
1646 WAVEFORMATEX
**pwfx
)
1648 ACImpl
*This
= impl_from_IAudioClient(iface
);
1649 WAVEFORMATEXTENSIBLE
*fmt
= &pulse_fmt
[This
->dataflow
== eCapture
];
1651 TRACE("(%p)->(%p)\n", This
, pwfx
);
1656 *pwfx
= clone_format(&fmt
->Format
);
1658 return E_OUTOFMEMORY
;
1663 static HRESULT WINAPI
AudioClient_GetDevicePeriod(IAudioClient
*iface
,
1664 REFERENCE_TIME
*defperiod
, REFERENCE_TIME
*minperiod
)
1666 ACImpl
*This
= impl_from_IAudioClient(iface
);
1668 TRACE("(%p)->(%p, %p)\n", This
, defperiod
, minperiod
);
1670 if (!defperiod
&& !minperiod
)
1674 *defperiod
= pulse_def_period
[This
->dataflow
== eCapture
];
1676 *minperiod
= pulse_min_period
[This
->dataflow
== eCapture
];
1681 static HRESULT WINAPI
AudioClient_Start(IAudioClient
*iface
)
1683 ACImpl
*This
= impl_from_IAudioClient(iface
);
1688 TRACE("(%p)\n", This
);
1690 pthread_mutex_lock(&pulse_lock
);
1691 hr
= pulse_stream_valid(This
);
1693 pthread_mutex_unlock(&pulse_lock
);
1697 if ((This
->flags
& AUDCLNT_STREAMFLAGS_EVENTCALLBACK
) && !This
->event
) {
1698 pthread_mutex_unlock(&pulse_lock
);
1699 return AUDCLNT_E_EVENTHANDLE_NOT_SET
;
1702 if (This
->started
) {
1703 pthread_mutex_unlock(&pulse_lock
);
1704 return AUDCLNT_E_NOT_STOPPED
;
1707 if (pa_stream_is_corked(This
->stream
)) {
1708 o
= pa_stream_cork(This
->stream
, 0, pulse_op_cb
, &success
);
1710 while(pa_operation_get_state(o
) == PA_OPERATION_RUNNING
)
1711 pthread_cond_wait(&pulse_cond
, &pulse_lock
);
1712 pa_operation_unref(o
);
1718 if (SUCCEEDED(hr
)) {
1719 This
->started
= TRUE
;
1720 if (This
->dataflow
== eRender
&& This
->event
)
1721 pa_stream_set_latency_update_callback(This
->stream
, pulse_latency_callback
, This
);
1723 pthread_mutex_unlock(&pulse_lock
);
1727 static HRESULT WINAPI
AudioClient_Stop(IAudioClient
*iface
)
1729 ACImpl
*This
= impl_from_IAudioClient(iface
);
1734 TRACE("(%p)\n", This
);
1736 pthread_mutex_lock(&pulse_lock
);
1737 hr
= pulse_stream_valid(This
);
1739 pthread_mutex_unlock(&pulse_lock
);
1743 if (!This
->started
) {
1744 pthread_mutex_unlock(&pulse_lock
);
1748 if (This
->dataflow
== eRender
) {
1749 o
= pa_stream_cork(This
->stream
, 1, pulse_op_cb
, &success
);
1751 while(pa_operation_get_state(o
) == PA_OPERATION_RUNNING
)
1752 pthread_cond_wait(&pulse_cond
, &pulse_lock
);
1753 pa_operation_unref(o
);
1759 if (SUCCEEDED(hr
)) {
1760 This
->started
= FALSE
;
1762 pthread_mutex_unlock(&pulse_lock
);
1766 static HRESULT WINAPI
AudioClient_Reset(IAudioClient
*iface
)
1768 ACImpl
*This
= impl_from_IAudioClient(iface
);
1771 TRACE("(%p)\n", This
);
1773 pthread_mutex_lock(&pulse_lock
);
1774 hr
= pulse_stream_valid(This
);
1776 pthread_mutex_unlock(&pulse_lock
);
1780 if (This
->started
) {
1781 pthread_mutex_unlock(&pulse_lock
);
1782 return AUDCLNT_E_NOT_STOPPED
;
1786 pthread_mutex_unlock(&pulse_lock
);
1787 return AUDCLNT_E_BUFFER_OPERATION_PENDING
;
1790 if (This
->dataflow
== eRender
) {
1791 /* If there is still data in the render buffer it needs to be removed from the server */
1794 pa_operation
*o
= pa_stream_flush(This
->stream
, pulse_op_cb
, &success
);
1796 while(pa_operation_get_state(o
) == PA_OPERATION_RUNNING
)
1797 pthread_cond_wait(&pulse_cond
, &pulse_lock
);
1798 pa_operation_unref(o
);
1801 if (success
|| !This
->pad
)
1802 This
->clock_lastpos
= This
->clock_written
= This
->pad
= 0;
1805 This
->clock_written
+= This
->pad
;
1808 if ((p
= This
->locked_ptr
)) {
1809 This
->locked_ptr
= NULL
;
1810 list_add_tail(&This
->packet_free_head
, &p
->entry
);
1812 list_move_tail(&This
->packet_free_head
, &This
->packet_filled_head
);
1814 pthread_mutex_unlock(&pulse_lock
);
1819 static HRESULT WINAPI
AudioClient_SetEventHandle(IAudioClient
*iface
,
1822 ACImpl
*This
= impl_from_IAudioClient(iface
);
1825 TRACE("(%p)->(%p)\n", This
, event
);
1828 return E_INVALIDARG
;
1830 pthread_mutex_lock(&pulse_lock
);
1831 hr
= pulse_stream_valid(This
);
1833 pthread_mutex_unlock(&pulse_lock
);
1837 if (!(This
->flags
& AUDCLNT_STREAMFLAGS_EVENTCALLBACK
))
1838 hr
= AUDCLNT_E_EVENTHANDLE_NOT_EXPECTED
;
1839 else if (This
->event
)
1840 hr
= HRESULT_FROM_WIN32(ERROR_INVALID_NAME
);
1842 This
->event
= event
;
1843 pthread_mutex_unlock(&pulse_lock
);
1847 static HRESULT WINAPI
AudioClient_GetService(IAudioClient
*iface
, REFIID riid
,
1850 ACImpl
*This
= impl_from_IAudioClient(iface
);
1853 TRACE("(%p)->(%s, %p)\n", This
, debugstr_guid(riid
), ppv
);
1859 pthread_mutex_lock(&pulse_lock
);
1860 hr
= pulse_stream_valid(This
);
1861 pthread_mutex_unlock(&pulse_lock
);
1865 if (IsEqualIID(riid
, &IID_IAudioRenderClient
)) {
1866 if (This
->dataflow
!= eRender
)
1867 return AUDCLNT_E_WRONG_ENDPOINT_TYPE
;
1868 *ppv
= &This
->IAudioRenderClient_iface
;
1869 } else if (IsEqualIID(riid
, &IID_IAudioCaptureClient
)) {
1870 if (This
->dataflow
!= eCapture
)
1871 return AUDCLNT_E_WRONG_ENDPOINT_TYPE
;
1872 *ppv
= &This
->IAudioCaptureClient_iface
;
1873 } else if (IsEqualIID(riid
, &IID_IAudioClock
)) {
1874 *ppv
= &This
->IAudioClock_iface
;
1875 } else if (IsEqualIID(riid
, &IID_IAudioStreamVolume
)) {
1876 *ppv
= &This
->IAudioStreamVolume_iface
;
1877 } else if (IsEqualIID(riid
, &IID_IAudioSessionControl
) ||
1878 IsEqualIID(riid
, &IID_IChannelAudioVolume
) ||
1879 IsEqualIID(riid
, &IID_ISimpleAudioVolume
)) {
1880 if (!This
->session_wrapper
) {
1881 This
->session_wrapper
= AudioSessionWrapper_Create(This
);
1882 if (!This
->session_wrapper
)
1883 return E_OUTOFMEMORY
;
1885 if (IsEqualIID(riid
, &IID_IAudioSessionControl
))
1886 *ppv
= &This
->session_wrapper
->IAudioSessionControl2_iface
;
1887 else if (IsEqualIID(riid
, &IID_IChannelAudioVolume
))
1888 *ppv
= &This
->session_wrapper
->IChannelAudioVolume_iface
;
1889 else if (IsEqualIID(riid
, &IID_ISimpleAudioVolume
))
1890 *ppv
= &This
->session_wrapper
->ISimpleAudioVolume_iface
;
1894 IUnknown_AddRef((IUnknown
*)*ppv
);
1898 FIXME("stub %s\n", debugstr_guid(riid
));
1899 return E_NOINTERFACE
;
1902 static const IAudioClientVtbl AudioClient_Vtbl
=
1904 AudioClient_QueryInterface
,
1906 AudioClient_Release
,
1907 AudioClient_Initialize
,
1908 AudioClient_GetBufferSize
,
1909 AudioClient_GetStreamLatency
,
1910 AudioClient_GetCurrentPadding
,
1911 AudioClient_IsFormatSupported
,
1912 AudioClient_GetMixFormat
,
1913 AudioClient_GetDevicePeriod
,
1917 AudioClient_SetEventHandle
,
1918 AudioClient_GetService
1921 static HRESULT WINAPI
AudioRenderClient_QueryInterface(
1922 IAudioRenderClient
*iface
, REFIID riid
, void **ppv
)
1924 ACImpl
*This
= impl_from_IAudioRenderClient(iface
);
1925 TRACE("(%p)->(%s, %p)\n", iface
, debugstr_guid(riid
), ppv
);
1931 if (IsEqualIID(riid
, &IID_IUnknown
) ||
1932 IsEqualIID(riid
, &IID_IAudioRenderClient
))
1935 IUnknown_AddRef((IUnknown
*)*ppv
);
1939 if (IsEqualIID(riid
, &IID_IMarshal
))
1940 return IUnknown_QueryInterface(This
->marshal
, riid
, ppv
);
1942 WARN("Unknown interface %s\n", debugstr_guid(riid
));
1943 return E_NOINTERFACE
;
1946 static ULONG WINAPI
AudioRenderClient_AddRef(IAudioRenderClient
*iface
)
1948 ACImpl
*This
= impl_from_IAudioRenderClient(iface
);
1949 return AudioClient_AddRef(&This
->IAudioClient_iface
);
1952 static ULONG WINAPI
AudioRenderClient_Release(IAudioRenderClient
*iface
)
1954 ACImpl
*This
= impl_from_IAudioRenderClient(iface
);
1955 return AudioClient_Release(&This
->IAudioClient_iface
);
1958 static HRESULT WINAPI
AudioRenderClient_GetBuffer(IAudioRenderClient
*iface
,
1959 UINT32 frames
, BYTE
**data
)
1961 ACImpl
*This
= impl_from_IAudioRenderClient(iface
);
1962 size_t avail
, req
, bytes
= frames
* pa_frame_size(&This
->ss
);
1967 TRACE("(%p)->(%u, %p)\n", This
, frames
, data
);
1973 pthread_mutex_lock(&pulse_lock
);
1974 hr
= pulse_stream_valid(This
);
1975 if (FAILED(hr
) || This
->locked
) {
1976 pthread_mutex_unlock(&pulse_lock
);
1977 return FAILED(hr
) ? hr
: AUDCLNT_E_OUT_OF_ORDER
;
1980 pthread_mutex_unlock(&pulse_lock
);
1984 ACImpl_GetRenderPad(This
, &pad
);
1985 avail
= This
->bufsize_frames
- pad
;
1986 if (avail
< frames
|| bytes
> This
->bufsize_bytes
) {
1987 pthread_mutex_unlock(&pulse_lock
);
1988 WARN("Wanted to write %u, but only %zu available\n", frames
, avail
);
1989 return AUDCLNT_E_BUFFER_TOO_LARGE
;
1992 This
->locked
= frames
;
1994 ret
= pa_stream_begin_write(This
->stream
, &This
->locked_ptr
, &req
);
1995 if (ret
< 0 || req
< bytes
) {
1996 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
);
1998 pa_stream_cancel_write(This
->stream
);
1999 *data
= This
->tmp_buffer
;
2000 This
->locked_ptr
= NULL
;
2002 *data
= This
->locked_ptr
;
2003 pthread_mutex_unlock(&pulse_lock
);
2007 static void pulse_free_noop(void *buf
)
2011 static HRESULT WINAPI
AudioRenderClient_ReleaseBuffer(
2012 IAudioRenderClient
*iface
, UINT32 written_frames
, DWORD flags
)
2014 ACImpl
*This
= impl_from_IAudioRenderClient(iface
);
2015 UINT32 written_bytes
= written_frames
* pa_frame_size(&This
->ss
);
2017 TRACE("(%p)->(%u, %x)\n", This
, written_frames
, flags
);
2019 pthread_mutex_lock(&pulse_lock
);
2020 if (!This
->locked
|| !written_frames
) {
2021 if (This
->locked_ptr
)
2022 pa_stream_cancel_write(This
->stream
);
2024 This
->locked_ptr
= NULL
;
2025 pthread_mutex_unlock(&pulse_lock
);
2026 return written_frames
? AUDCLNT_E_OUT_OF_ORDER
: S_OK
;
2029 if (This
->locked
< written_frames
) {
2030 pthread_mutex_unlock(&pulse_lock
);
2031 return AUDCLNT_E_INVALID_SIZE
;
2035 if (This
->locked_ptr
) {
2036 if (flags
& AUDCLNT_BUFFERFLAGS_SILENT
)
2037 silence_buffer(This
->ss
.format
, This
->locked_ptr
, written_bytes
);
2038 pa_stream_write(This
->stream
, This
->locked_ptr
, written_bytes
, NULL
, 0, PA_SEEK_RELATIVE
);
2040 if (flags
& AUDCLNT_BUFFERFLAGS_SILENT
)
2041 silence_buffer(This
->ss
.format
, This
->tmp_buffer
, written_bytes
);
2042 pa_stream_write(This
->stream
, This
->tmp_buffer
, written_bytes
, pulse_free_noop
, 0, PA_SEEK_RELATIVE
);
2045 This
->pad
+= written_bytes
;
2046 This
->locked_ptr
= NULL
;
2047 TRACE("Released %u, pad %zu\n", written_frames
, This
->pad
/ pa_frame_size(&This
->ss
));
2048 assert(This
->pad
<= This
->bufsize_bytes
);
2050 pthread_mutex_unlock(&pulse_lock
);
2054 static const IAudioRenderClientVtbl AudioRenderClient_Vtbl
= {
2055 AudioRenderClient_QueryInterface
,
2056 AudioRenderClient_AddRef
,
2057 AudioRenderClient_Release
,
2058 AudioRenderClient_GetBuffer
,
2059 AudioRenderClient_ReleaseBuffer
2062 static HRESULT WINAPI
AudioCaptureClient_QueryInterface(
2063 IAudioCaptureClient
*iface
, REFIID riid
, void **ppv
)
2065 ACImpl
*This
= impl_from_IAudioCaptureClient(iface
);
2066 TRACE("(%p)->(%s, %p)\n", iface
, debugstr_guid(riid
), ppv
);
2072 if (IsEqualIID(riid
, &IID_IUnknown
) ||
2073 IsEqualIID(riid
, &IID_IAudioCaptureClient
))
2076 IUnknown_AddRef((IUnknown
*)*ppv
);
2080 if (IsEqualIID(riid
, &IID_IMarshal
))
2081 return IUnknown_QueryInterface(This
->marshal
, riid
, ppv
);
2083 WARN("Unknown interface %s\n", debugstr_guid(riid
));
2084 return E_NOINTERFACE
;
2087 static ULONG WINAPI
AudioCaptureClient_AddRef(IAudioCaptureClient
*iface
)
2089 ACImpl
*This
= impl_from_IAudioCaptureClient(iface
);
2090 return IAudioClient_AddRef(&This
->IAudioClient_iface
);
2093 static ULONG WINAPI
AudioCaptureClient_Release(IAudioCaptureClient
*iface
)
2095 ACImpl
*This
= impl_from_IAudioCaptureClient(iface
);
2096 return IAudioClient_Release(&This
->IAudioClient_iface
);
2099 static HRESULT WINAPI
AudioCaptureClient_GetBuffer(IAudioCaptureClient
*iface
,
2100 BYTE
**data
, UINT32
*frames
, DWORD
*flags
, UINT64
*devpos
,
2103 ACImpl
*This
= impl_from_IAudioCaptureClient(iface
);
2107 TRACE("(%p)->(%p, %p, %p, %p, %p)\n", This
, data
, frames
, flags
,
2110 if (!data
|| !frames
|| !flags
)
2113 pthread_mutex_lock(&pulse_lock
);
2114 hr
= pulse_stream_valid(This
);
2115 if (FAILED(hr
) || This
->locked
) {
2116 pthread_mutex_unlock(&pulse_lock
);
2117 return FAILED(hr
) ? hr
: AUDCLNT_E_OUT_OF_ORDER
;
2120 ACImpl_GetCapturePad(This
, NULL
);
2121 if ((packet
= This
->locked_ptr
)) {
2122 *frames
= This
->capture_period
/ pa_frame_size(&This
->ss
);
2124 if (packet
->discont
)
2125 *flags
|= AUDCLNT_BUFFERFLAGS_DATA_DISCONTINUITY
;
2127 if (packet
->discont
)
2128 *devpos
= (This
->clock_written
+ This
->capture_period
) / pa_frame_size(&This
->ss
);
2130 *devpos
= This
->clock_written
/ pa_frame_size(&This
->ss
);
2133 *qpcpos
= packet
->qpcpos
;
2134 *data
= packet
->data
;
2138 This
->locked
= *frames
;
2139 pthread_mutex_unlock(&pulse_lock
);
2140 return *frames
? S_OK
: AUDCLNT_S_BUFFER_EMPTY
;
2143 static HRESULT WINAPI
AudioCaptureClient_ReleaseBuffer(
2144 IAudioCaptureClient
*iface
, UINT32 done
)
2146 ACImpl
*This
= impl_from_IAudioCaptureClient(iface
);
2148 TRACE("(%p)->(%u)\n", This
, done
);
2150 pthread_mutex_lock(&pulse_lock
);
2151 if (!This
->locked
&& done
) {
2152 pthread_mutex_unlock(&pulse_lock
);
2153 return AUDCLNT_E_OUT_OF_ORDER
;
2155 if (done
&& This
->locked
!= done
) {
2156 pthread_mutex_unlock(&pulse_lock
);
2157 return AUDCLNT_E_INVALID_SIZE
;
2160 ACPacket
*packet
= This
->locked_ptr
;
2161 This
->locked_ptr
= NULL
;
2162 This
->pad
-= This
->capture_period
;
2163 if (packet
->discont
)
2164 This
->clock_written
+= 2 * This
->capture_period
;
2166 This
->clock_written
+= This
->capture_period
;
2167 list_add_tail(&This
->packet_free_head
, &packet
->entry
);
2170 pthread_mutex_unlock(&pulse_lock
);
2174 static HRESULT WINAPI
AudioCaptureClient_GetNextPacketSize(
2175 IAudioCaptureClient
*iface
, UINT32
*frames
)
2177 ACImpl
*This
= impl_from_IAudioCaptureClient(iface
);
2179 TRACE("(%p)->(%p)\n", This
, frames
);
2183 pthread_mutex_lock(&pulse_lock
);
2184 ACImpl_GetCapturePad(This
, NULL
);
2185 if (This
->locked_ptr
)
2186 *frames
= This
->capture_period
/ pa_frame_size(&This
->ss
);
2189 pthread_mutex_unlock(&pulse_lock
);
2193 static const IAudioCaptureClientVtbl AudioCaptureClient_Vtbl
=
2195 AudioCaptureClient_QueryInterface
,
2196 AudioCaptureClient_AddRef
,
2197 AudioCaptureClient_Release
,
2198 AudioCaptureClient_GetBuffer
,
2199 AudioCaptureClient_ReleaseBuffer
,
2200 AudioCaptureClient_GetNextPacketSize
2203 static HRESULT WINAPI
AudioClock_QueryInterface(IAudioClock
*iface
,
2204 REFIID riid
, void **ppv
)
2206 ACImpl
*This
= impl_from_IAudioClock(iface
);
2208 TRACE("(%p)->(%s, %p)\n", iface
, debugstr_guid(riid
), ppv
);
2214 if (IsEqualIID(riid
, &IID_IUnknown
) || IsEqualIID(riid
, &IID_IAudioClock
))
2216 else if (IsEqualIID(riid
, &IID_IAudioClock2
))
2217 *ppv
= &This
->IAudioClock2_iface
;
2219 IUnknown_AddRef((IUnknown
*)*ppv
);
2223 if (IsEqualIID(riid
, &IID_IMarshal
))
2224 return IUnknown_QueryInterface(This
->marshal
, riid
, ppv
);
2226 WARN("Unknown interface %s\n", debugstr_guid(riid
));
2227 return E_NOINTERFACE
;
2230 static ULONG WINAPI
AudioClock_AddRef(IAudioClock
*iface
)
2232 ACImpl
*This
= impl_from_IAudioClock(iface
);
2233 return IAudioClient_AddRef(&This
->IAudioClient_iface
);
2236 static ULONG WINAPI
AudioClock_Release(IAudioClock
*iface
)
2238 ACImpl
*This
= impl_from_IAudioClock(iface
);
2239 return IAudioClient_Release(&This
->IAudioClient_iface
);
2242 static HRESULT WINAPI
AudioClock_GetFrequency(IAudioClock
*iface
, UINT64
*freq
)
2244 ACImpl
*This
= impl_from_IAudioClock(iface
);
2247 TRACE("(%p)->(%p)\n", This
, freq
);
2249 pthread_mutex_lock(&pulse_lock
);
2250 hr
= pulse_stream_valid(This
);
2251 if (SUCCEEDED(hr
)) {
2252 *freq
= This
->ss
.rate
;
2253 if (This
->share
== AUDCLNT_SHAREMODE_SHARED
)
2254 *freq
*= pa_frame_size(&This
->ss
);
2256 pthread_mutex_unlock(&pulse_lock
);
2260 static HRESULT WINAPI
AudioClock_GetPosition(IAudioClock
*iface
, UINT64
*pos
,
2263 ACImpl
*This
= impl_from_IAudioClock(iface
);
2266 TRACE("(%p)->(%p, %p)\n", This
, pos
, qpctime
);
2271 pthread_mutex_lock(&pulse_lock
);
2272 hr
= pulse_stream_valid(This
);
2274 pthread_mutex_unlock(&pulse_lock
);
2278 *pos
= This
->clock_written
;
2280 if (This
->share
== AUDCLNT_SHAREMODE_EXCLUSIVE
)
2281 *pos
/= pa_frame_size(&This
->ss
);
2283 /* Make time never go backwards */
2284 if (*pos
< This
->clock_lastpos
)
2285 *pos
= This
->clock_lastpos
;
2287 This
->clock_lastpos
= *pos
;
2288 pthread_mutex_unlock(&pulse_lock
);
2290 TRACE("%p Position: %u\n", This
, (unsigned)*pos
);
2293 LARGE_INTEGER stamp
, freq
;
2294 QueryPerformanceCounter(&stamp
);
2295 QueryPerformanceFrequency(&freq
);
2296 *qpctime
= (stamp
.QuadPart
* (INT64
)10000000) / freq
.QuadPart
;
2302 static HRESULT WINAPI
AudioClock_GetCharacteristics(IAudioClock
*iface
,
2305 ACImpl
*This
= impl_from_IAudioClock(iface
);
2307 TRACE("(%p)->(%p)\n", This
, chars
);
2312 *chars
= AUDIOCLOCK_CHARACTERISTIC_FIXED_FREQ
;
2317 static const IAudioClockVtbl AudioClock_Vtbl
=
2319 AudioClock_QueryInterface
,
2322 AudioClock_GetFrequency
,
2323 AudioClock_GetPosition
,
2324 AudioClock_GetCharacteristics
2327 static HRESULT WINAPI
AudioClock2_QueryInterface(IAudioClock2
*iface
,
2328 REFIID riid
, void **ppv
)
2330 ACImpl
*This
= impl_from_IAudioClock2(iface
);
2331 return IAudioClock_QueryInterface(&This
->IAudioClock_iface
, riid
, ppv
);
2334 static ULONG WINAPI
AudioClock2_AddRef(IAudioClock2
*iface
)
2336 ACImpl
*This
= impl_from_IAudioClock2(iface
);
2337 return IAudioClient_AddRef(&This
->IAudioClient_iface
);
2340 static ULONG WINAPI
AudioClock2_Release(IAudioClock2
*iface
)
2342 ACImpl
*This
= impl_from_IAudioClock2(iface
);
2343 return IAudioClient_Release(&This
->IAudioClient_iface
);
2346 static HRESULT WINAPI
AudioClock2_GetDevicePosition(IAudioClock2
*iface
,
2347 UINT64
*pos
, UINT64
*qpctime
)
2349 ACImpl
*This
= impl_from_IAudioClock2(iface
);
2350 HRESULT hr
= AudioClock_GetPosition(&This
->IAudioClock_iface
, pos
, qpctime
);
2351 if (SUCCEEDED(hr
) && This
->share
== AUDCLNT_SHAREMODE_SHARED
)
2352 *pos
/= pa_frame_size(&This
->ss
);
2356 static const IAudioClock2Vtbl AudioClock2_Vtbl
=
2358 AudioClock2_QueryInterface
,
2360 AudioClock2_Release
,
2361 AudioClock2_GetDevicePosition
2364 static HRESULT WINAPI
AudioStreamVolume_QueryInterface(
2365 IAudioStreamVolume
*iface
, REFIID riid
, void **ppv
)
2367 ACImpl
*This
= impl_from_IAudioStreamVolume(iface
);
2369 TRACE("(%p)->(%s, %p)\n", iface
, debugstr_guid(riid
), ppv
);
2375 if (IsEqualIID(riid
, &IID_IUnknown
) ||
2376 IsEqualIID(riid
, &IID_IAudioStreamVolume
))
2379 IUnknown_AddRef((IUnknown
*)*ppv
);
2383 if (IsEqualIID(riid
, &IID_IMarshal
))
2384 return IUnknown_QueryInterface(This
->marshal
, riid
, ppv
);
2386 WARN("Unknown interface %s\n", debugstr_guid(riid
));
2387 return E_NOINTERFACE
;
2390 static ULONG WINAPI
AudioStreamVolume_AddRef(IAudioStreamVolume
*iface
)
2392 ACImpl
*This
= impl_from_IAudioStreamVolume(iface
);
2393 return IAudioClient_AddRef(&This
->IAudioClient_iface
);
2396 static ULONG WINAPI
AudioStreamVolume_Release(IAudioStreamVolume
*iface
)
2398 ACImpl
*This
= impl_from_IAudioStreamVolume(iface
);
2399 return IAudioClient_Release(&This
->IAudioClient_iface
);
2402 static HRESULT WINAPI
AudioStreamVolume_GetChannelCount(
2403 IAudioStreamVolume
*iface
, UINT32
*out
)
2405 ACImpl
*This
= impl_from_IAudioStreamVolume(iface
);
2407 TRACE("(%p)->(%p)\n", This
, out
);
2412 *out
= This
->ss
.channels
;
2417 struct pulse_info_cb_data
{
2422 static HRESULT WINAPI
AudioStreamVolume_SetAllVolumes(
2423 IAudioStreamVolume
*iface
, UINT32 count
, const float *levels
)
2425 ACImpl
*This
= impl_from_IAudioStreamVolume(iface
);
2429 TRACE("(%p)->(%d, %p)\n", This
, count
, levels
);
2434 if (count
!= This
->ss
.channels
)
2435 return E_INVALIDARG
;
2437 pthread_mutex_lock(&pulse_lock
);
2438 hr
= pulse_stream_valid(This
);
2442 for (i
= 0; i
< count
; ++i
)
2443 This
->vol
[i
] = levels
[i
];
2446 pthread_mutex_unlock(&pulse_lock
);
2450 static HRESULT WINAPI
AudioStreamVolume_GetAllVolumes(
2451 IAudioStreamVolume
*iface
, UINT32 count
, float *levels
)
2453 ACImpl
*This
= impl_from_IAudioStreamVolume(iface
);
2457 TRACE("(%p)->(%d, %p)\n", This
, count
, levels
);
2462 if (count
!= This
->ss
.channels
)
2463 return E_INVALIDARG
;
2465 pthread_mutex_lock(&pulse_lock
);
2466 hr
= pulse_stream_valid(This
);
2470 for (i
= 0; i
< count
; ++i
)
2471 levels
[i
] = This
->vol
[i
];
2474 pthread_mutex_unlock(&pulse_lock
);
2478 static HRESULT WINAPI
AudioStreamVolume_SetChannelVolume(
2479 IAudioStreamVolume
*iface
, UINT32 index
, float level
)
2481 ACImpl
*This
= impl_from_IAudioStreamVolume(iface
);
2483 float volumes
[PA_CHANNELS_MAX
];
2485 TRACE("(%p)->(%d, %f)\n", This
, index
, level
);
2487 if (level
< 0.f
|| level
> 1.f
)
2488 return E_INVALIDARG
;
2490 if (index
>= This
->ss
.channels
)
2491 return E_INVALIDARG
;
2493 hr
= AudioStreamVolume_GetAllVolumes(iface
, This
->ss
.channels
, volumes
);
2494 volumes
[index
] = level
;
2496 hr
= AudioStreamVolume_SetAllVolumes(iface
, This
->ss
.channels
, volumes
);
2500 static HRESULT WINAPI
AudioStreamVolume_GetChannelVolume(
2501 IAudioStreamVolume
*iface
, UINT32 index
, float *level
)
2503 ACImpl
*This
= impl_from_IAudioStreamVolume(iface
);
2504 float volumes
[PA_CHANNELS_MAX
];
2507 TRACE("(%p)->(%d, %p)\n", This
, index
, level
);
2512 if (index
>= This
->ss
.channels
)
2513 return E_INVALIDARG
;
2515 hr
= AudioStreamVolume_GetAllVolumes(iface
, This
->ss
.channels
, volumes
);
2517 *level
= volumes
[index
];
2521 static const IAudioStreamVolumeVtbl AudioStreamVolume_Vtbl
=
2523 AudioStreamVolume_QueryInterface
,
2524 AudioStreamVolume_AddRef
,
2525 AudioStreamVolume_Release
,
2526 AudioStreamVolume_GetChannelCount
,
2527 AudioStreamVolume_SetChannelVolume
,
2528 AudioStreamVolume_GetChannelVolume
,
2529 AudioStreamVolume_SetAllVolumes
,
2530 AudioStreamVolume_GetAllVolumes
2533 static AudioSessionWrapper
*AudioSessionWrapper_Create(ACImpl
*client
)
2535 AudioSessionWrapper
*ret
;
2537 ret
= HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY
,
2538 sizeof(AudioSessionWrapper
));
2542 ret
->IAudioSessionControl2_iface
.lpVtbl
= &AudioSessionControl2_Vtbl
;
2543 ret
->ISimpleAudioVolume_iface
.lpVtbl
= &SimpleAudioVolume_Vtbl
;
2544 ret
->IChannelAudioVolume_iface
.lpVtbl
= &ChannelAudioVolume_Vtbl
;
2548 ret
->client
= client
;
2550 ret
->session
= client
->session
;
2551 AudioClient_AddRef(&client
->IAudioClient_iface
);
2557 static HRESULT WINAPI
AudioSessionControl_QueryInterface(
2558 IAudioSessionControl2
*iface
, REFIID riid
, void **ppv
)
2560 TRACE("(%p)->(%s, %p)\n", iface
, debugstr_guid(riid
), ppv
);
2566 if (IsEqualIID(riid
, &IID_IUnknown
) ||
2567 IsEqualIID(riid
, &IID_IAudioSessionControl
) ||
2568 IsEqualIID(riid
, &IID_IAudioSessionControl2
))
2571 IUnknown_AddRef((IUnknown
*)*ppv
);
2575 WARN("Unknown interface %s\n", debugstr_guid(riid
));
2576 return E_NOINTERFACE
;
2579 static ULONG WINAPI
AudioSessionControl_AddRef(IAudioSessionControl2
*iface
)
2581 AudioSessionWrapper
*This
= impl_from_IAudioSessionControl2(iface
);
2583 ref
= InterlockedIncrement(&This
->ref
);
2584 TRACE("(%p) Refcount now %u\n", This
, ref
);
2588 static ULONG WINAPI
AudioSessionControl_Release(IAudioSessionControl2
*iface
)
2590 AudioSessionWrapper
*This
= impl_from_IAudioSessionControl2(iface
);
2592 ref
= InterlockedDecrement(&This
->ref
);
2593 TRACE("(%p) Refcount now %u\n", This
, ref
);
2596 This
->client
->session_wrapper
= NULL
;
2597 AudioClient_Release(&This
->client
->IAudioClient_iface
);
2599 HeapFree(GetProcessHeap(), 0, This
);
2604 static HRESULT WINAPI
AudioSessionControl_GetState(IAudioSessionControl2
*iface
,
2605 AudioSessionState
*state
)
2607 AudioSessionWrapper
*This
= impl_from_IAudioSessionControl2(iface
);
2610 TRACE("(%p)->(%p)\n", This
, state
);
2613 return NULL_PTR_ERR
;
2615 pthread_mutex_lock(&pulse_lock
);
2616 if (list_empty(&This
->session
->clients
)) {
2617 *state
= AudioSessionStateExpired
;
2620 LIST_FOR_EACH_ENTRY(client
, &This
->session
->clients
, ACImpl
, entry
) {
2621 if (client
->started
) {
2622 *state
= AudioSessionStateActive
;
2626 *state
= AudioSessionStateInactive
;
2629 pthread_mutex_unlock(&pulse_lock
);
2633 static HRESULT WINAPI
AudioSessionControl_GetDisplayName(
2634 IAudioSessionControl2
*iface
, WCHAR
**name
)
2636 AudioSessionWrapper
*This
= impl_from_IAudioSessionControl2(iface
);
2638 FIXME("(%p)->(%p) - stub\n", This
, name
);
2643 static HRESULT WINAPI
AudioSessionControl_SetDisplayName(
2644 IAudioSessionControl2
*iface
, const WCHAR
*name
, const GUID
*session
)
2646 AudioSessionWrapper
*This
= impl_from_IAudioSessionControl2(iface
);
2648 FIXME("(%p)->(%p, %s) - stub\n", This
, name
, debugstr_guid(session
));
2653 static HRESULT WINAPI
AudioSessionControl_GetIconPath(
2654 IAudioSessionControl2
*iface
, WCHAR
**path
)
2656 AudioSessionWrapper
*This
= impl_from_IAudioSessionControl2(iface
);
2658 FIXME("(%p)->(%p) - stub\n", This
, path
);
2663 static HRESULT WINAPI
AudioSessionControl_SetIconPath(
2664 IAudioSessionControl2
*iface
, const WCHAR
*path
, const GUID
*session
)
2666 AudioSessionWrapper
*This
= impl_from_IAudioSessionControl2(iface
);
2668 FIXME("(%p)->(%p, %s) - stub\n", This
, path
, debugstr_guid(session
));
2673 static HRESULT WINAPI
AudioSessionControl_GetGroupingParam(
2674 IAudioSessionControl2
*iface
, GUID
*group
)
2676 AudioSessionWrapper
*This
= impl_from_IAudioSessionControl2(iface
);
2678 FIXME("(%p)->(%p) - stub\n", This
, group
);
2683 static HRESULT WINAPI
AudioSessionControl_SetGroupingParam(
2684 IAudioSessionControl2
*iface
, const GUID
*group
, const GUID
*session
)
2686 AudioSessionWrapper
*This
= impl_from_IAudioSessionControl2(iface
);
2688 FIXME("(%p)->(%s, %s) - stub\n", This
, debugstr_guid(group
),
2689 debugstr_guid(session
));
2694 static HRESULT WINAPI
AudioSessionControl_RegisterAudioSessionNotification(
2695 IAudioSessionControl2
*iface
, IAudioSessionEvents
*events
)
2697 AudioSessionWrapper
*This
= impl_from_IAudioSessionControl2(iface
);
2699 FIXME("(%p)->(%p) - stub\n", This
, events
);
2704 static HRESULT WINAPI
AudioSessionControl_UnregisterAudioSessionNotification(
2705 IAudioSessionControl2
*iface
, IAudioSessionEvents
*events
)
2707 AudioSessionWrapper
*This
= impl_from_IAudioSessionControl2(iface
);
2709 FIXME("(%p)->(%p) - stub\n", This
, events
);
2714 static HRESULT WINAPI
AudioSessionControl_GetSessionIdentifier(
2715 IAudioSessionControl2
*iface
, WCHAR
**id
)
2717 AudioSessionWrapper
*This
= impl_from_IAudioSessionControl2(iface
);
2719 FIXME("(%p)->(%p) - stub\n", This
, id
);
2724 static HRESULT WINAPI
AudioSessionControl_GetSessionInstanceIdentifier(
2725 IAudioSessionControl2
*iface
, WCHAR
**id
)
2727 AudioSessionWrapper
*This
= impl_from_IAudioSessionControl2(iface
);
2729 FIXME("(%p)->(%p) - stub\n", This
, id
);
2734 static HRESULT WINAPI
AudioSessionControl_GetProcessId(
2735 IAudioSessionControl2
*iface
, DWORD
*pid
)
2737 AudioSessionWrapper
*This
= impl_from_IAudioSessionControl2(iface
);
2739 TRACE("(%p)->(%p)\n", This
, pid
);
2744 *pid
= GetCurrentProcessId();
2749 static HRESULT WINAPI
AudioSessionControl_IsSystemSoundsSession(
2750 IAudioSessionControl2
*iface
)
2752 AudioSessionWrapper
*This
= impl_from_IAudioSessionControl2(iface
);
2754 TRACE("(%p)\n", This
);
2759 static HRESULT WINAPI
AudioSessionControl_SetDuckingPreference(
2760 IAudioSessionControl2
*iface
, BOOL optout
)
2762 AudioSessionWrapper
*This
= impl_from_IAudioSessionControl2(iface
);
2764 TRACE("(%p)->(%d)\n", This
, optout
);
2769 static const IAudioSessionControl2Vtbl AudioSessionControl2_Vtbl
=
2771 AudioSessionControl_QueryInterface
,
2772 AudioSessionControl_AddRef
,
2773 AudioSessionControl_Release
,
2774 AudioSessionControl_GetState
,
2775 AudioSessionControl_GetDisplayName
,
2776 AudioSessionControl_SetDisplayName
,
2777 AudioSessionControl_GetIconPath
,
2778 AudioSessionControl_SetIconPath
,
2779 AudioSessionControl_GetGroupingParam
,
2780 AudioSessionControl_SetGroupingParam
,
2781 AudioSessionControl_RegisterAudioSessionNotification
,
2782 AudioSessionControl_UnregisterAudioSessionNotification
,
2783 AudioSessionControl_GetSessionIdentifier
,
2784 AudioSessionControl_GetSessionInstanceIdentifier
,
2785 AudioSessionControl_GetProcessId
,
2786 AudioSessionControl_IsSystemSoundsSession
,
2787 AudioSessionControl_SetDuckingPreference
2790 typedef struct _SessionMgr
{
2791 IAudioSessionManager2 IAudioSessionManager2_iface
;
2798 static HRESULT WINAPI
AudioSessionManager_QueryInterface(IAudioSessionManager2
*iface
,
2799 REFIID riid
, void **ppv
)
2801 TRACE("(%p)->(%s, %p)\n", iface
, debugstr_guid(riid
), ppv
);
2807 if (IsEqualIID(riid
, &IID_IUnknown
) ||
2808 IsEqualIID(riid
, &IID_IAudioSessionManager
) ||
2809 IsEqualIID(riid
, &IID_IAudioSessionManager2
))
2812 IUnknown_AddRef((IUnknown
*)*ppv
);
2816 WARN("Unknown interface %s\n", debugstr_guid(riid
));
2817 return E_NOINTERFACE
;
2820 static inline SessionMgr
*impl_from_IAudioSessionManager2(IAudioSessionManager2
*iface
)
2822 return CONTAINING_RECORD(iface
, SessionMgr
, IAudioSessionManager2_iface
);
2825 static ULONG WINAPI
AudioSessionManager_AddRef(IAudioSessionManager2
*iface
)
2827 SessionMgr
*This
= impl_from_IAudioSessionManager2(iface
);
2829 ref
= InterlockedIncrement(&This
->ref
);
2830 TRACE("(%p) Refcount now %u\n", This
, ref
);
2834 static ULONG WINAPI
AudioSessionManager_Release(IAudioSessionManager2
*iface
)
2836 SessionMgr
*This
= impl_from_IAudioSessionManager2(iface
);
2838 ref
= InterlockedDecrement(&This
->ref
);
2839 TRACE("(%p) Refcount now %u\n", This
, ref
);
2841 HeapFree(GetProcessHeap(), 0, This
);
2845 static HRESULT WINAPI
AudioSessionManager_GetAudioSessionControl(
2846 IAudioSessionManager2
*iface
, const GUID
*session_guid
, DWORD flags
,
2847 IAudioSessionControl
**out
)
2849 SessionMgr
*This
= impl_from_IAudioSessionManager2(iface
);
2850 AudioSession
*session
;
2851 AudioSessionWrapper
*wrapper
;
2854 TRACE("(%p)->(%s, %x, %p)\n", This
, debugstr_guid(session_guid
),
2857 hr
= get_audio_session(session_guid
, This
->device
, 0, &session
);
2861 wrapper
= AudioSessionWrapper_Create(NULL
);
2863 return E_OUTOFMEMORY
;
2865 wrapper
->session
= session
;
2867 *out
= (IAudioSessionControl
*)&wrapper
->IAudioSessionControl2_iface
;
2872 static HRESULT WINAPI
AudioSessionManager_GetSimpleAudioVolume(
2873 IAudioSessionManager2
*iface
, const GUID
*session_guid
, DWORD flags
,
2874 ISimpleAudioVolume
**out
)
2876 SessionMgr
*This
= impl_from_IAudioSessionManager2(iface
);
2877 AudioSession
*session
;
2878 AudioSessionWrapper
*wrapper
;
2881 TRACE("(%p)->(%s, %x, %p)\n", This
, debugstr_guid(session_guid
),
2884 hr
= get_audio_session(session_guid
, This
->device
, 0, &session
);
2888 wrapper
= AudioSessionWrapper_Create(NULL
);
2890 return E_OUTOFMEMORY
;
2892 wrapper
->session
= session
;
2894 *out
= &wrapper
->ISimpleAudioVolume_iface
;
2899 static HRESULT WINAPI
AudioSessionManager_GetSessionEnumerator(
2900 IAudioSessionManager2
*iface
, IAudioSessionEnumerator
**out
)
2902 SessionMgr
*This
= impl_from_IAudioSessionManager2(iface
);
2903 FIXME("(%p)->(%p) - stub\n", This
, out
);
2907 static HRESULT WINAPI
AudioSessionManager_RegisterSessionNotification(
2908 IAudioSessionManager2
*iface
, IAudioSessionNotification
*notification
)
2910 SessionMgr
*This
= impl_from_IAudioSessionManager2(iface
);
2911 FIXME("(%p)->(%p) - stub\n", This
, notification
);
2915 static HRESULT WINAPI
AudioSessionManager_UnregisterSessionNotification(
2916 IAudioSessionManager2
*iface
, IAudioSessionNotification
*notification
)
2918 SessionMgr
*This
= impl_from_IAudioSessionManager2(iface
);
2919 FIXME("(%p)->(%p) - stub\n", This
, notification
);
2923 static HRESULT WINAPI
AudioSessionManager_RegisterDuckNotification(
2924 IAudioSessionManager2
*iface
, const WCHAR
*session_id
,
2925 IAudioVolumeDuckNotification
*notification
)
2927 SessionMgr
*This
= impl_from_IAudioSessionManager2(iface
);
2928 FIXME("(%p)->(%p) - stub\n", This
, notification
);
2932 static HRESULT WINAPI
AudioSessionManager_UnregisterDuckNotification(
2933 IAudioSessionManager2
*iface
,
2934 IAudioVolumeDuckNotification
*notification
)
2936 SessionMgr
*This
= impl_from_IAudioSessionManager2(iface
);
2937 FIXME("(%p)->(%p) - stub\n", This
, notification
);
2941 static const IAudioSessionManager2Vtbl AudioSessionManager2_Vtbl
=
2943 AudioSessionManager_QueryInterface
,
2944 AudioSessionManager_AddRef
,
2945 AudioSessionManager_Release
,
2946 AudioSessionManager_GetAudioSessionControl
,
2947 AudioSessionManager_GetSimpleAudioVolume
,
2948 AudioSessionManager_GetSessionEnumerator
,
2949 AudioSessionManager_RegisterSessionNotification
,
2950 AudioSessionManager_UnregisterSessionNotification
,
2951 AudioSessionManager_RegisterDuckNotification
,
2952 AudioSessionManager_UnregisterDuckNotification
2955 static HRESULT WINAPI
SimpleAudioVolume_QueryInterface(
2956 ISimpleAudioVolume
*iface
, REFIID riid
, void **ppv
)
2958 TRACE("(%p)->(%s, %p)\n", iface
, debugstr_guid(riid
), ppv
);
2964 if (IsEqualIID(riid
, &IID_IUnknown
) ||
2965 IsEqualIID(riid
, &IID_ISimpleAudioVolume
))
2968 IUnknown_AddRef((IUnknown
*)*ppv
);
2972 WARN("Unknown interface %s\n", debugstr_guid(riid
));
2973 return E_NOINTERFACE
;
2976 static ULONG WINAPI
SimpleAudioVolume_AddRef(ISimpleAudioVolume
*iface
)
2978 AudioSessionWrapper
*This
= impl_from_ISimpleAudioVolume(iface
);
2979 return AudioSessionControl_AddRef(&This
->IAudioSessionControl2_iface
);
2982 static ULONG WINAPI
SimpleAudioVolume_Release(ISimpleAudioVolume
*iface
)
2984 AudioSessionWrapper
*This
= impl_from_ISimpleAudioVolume(iface
);
2985 return AudioSessionControl_Release(&This
->IAudioSessionControl2_iface
);
2988 static HRESULT WINAPI
SimpleAudioVolume_SetMasterVolume(
2989 ISimpleAudioVolume
*iface
, float level
, const GUID
*context
)
2991 AudioSessionWrapper
*This
= impl_from_ISimpleAudioVolume(iface
);
2992 AudioSession
*session
= This
->session
;
2994 TRACE("(%p)->(%f, %s)\n", session
, level
, wine_dbgstr_guid(context
));
2996 if (level
< 0.f
|| level
> 1.f
)
2997 return E_INVALIDARG
;
3000 FIXME("Notifications not supported yet\n");
3002 TRACE("PulseAudio does not support session volume control\n");
3004 pthread_mutex_lock(&pulse_lock
);
3005 session
->master_vol
= level
;
3006 pthread_mutex_unlock(&pulse_lock
);
3011 static HRESULT WINAPI
SimpleAudioVolume_GetMasterVolume(
3012 ISimpleAudioVolume
*iface
, float *level
)
3014 AudioSessionWrapper
*This
= impl_from_ISimpleAudioVolume(iface
);
3015 AudioSession
*session
= This
->session
;
3017 TRACE("(%p)->(%p)\n", session
, level
);
3020 return NULL_PTR_ERR
;
3022 *level
= session
->master_vol
;
3027 static HRESULT WINAPI
SimpleAudioVolume_SetMute(ISimpleAudioVolume
*iface
,
3028 BOOL mute
, const GUID
*context
)
3030 AudioSessionWrapper
*This
= impl_from_ISimpleAudioVolume(iface
);
3031 AudioSession
*session
= This
->session
;
3033 TRACE("(%p)->(%u, %p)\n", session
, mute
, context
);
3036 FIXME("Notifications not supported yet\n");
3038 session
->mute
= mute
;
3043 static HRESULT WINAPI
SimpleAudioVolume_GetMute(ISimpleAudioVolume
*iface
,
3046 AudioSessionWrapper
*This
= impl_from_ISimpleAudioVolume(iface
);
3047 AudioSession
*session
= This
->session
;
3049 TRACE("(%p)->(%p)\n", session
, mute
);
3052 return NULL_PTR_ERR
;
3054 *mute
= session
->mute
;
3059 static const ISimpleAudioVolumeVtbl SimpleAudioVolume_Vtbl
=
3061 SimpleAudioVolume_QueryInterface
,
3062 SimpleAudioVolume_AddRef
,
3063 SimpleAudioVolume_Release
,
3064 SimpleAudioVolume_SetMasterVolume
,
3065 SimpleAudioVolume_GetMasterVolume
,
3066 SimpleAudioVolume_SetMute
,
3067 SimpleAudioVolume_GetMute
3070 static HRESULT WINAPI
ChannelAudioVolume_QueryInterface(
3071 IChannelAudioVolume
*iface
, REFIID riid
, void **ppv
)
3073 TRACE("(%p)->(%s, %p)\n", iface
, debugstr_guid(riid
), ppv
);
3079 if (IsEqualIID(riid
, &IID_IUnknown
) ||
3080 IsEqualIID(riid
, &IID_IChannelAudioVolume
))
3083 IUnknown_AddRef((IUnknown
*)*ppv
);
3087 WARN("Unknown interface %s\n", debugstr_guid(riid
));
3088 return E_NOINTERFACE
;
3091 static ULONG WINAPI
ChannelAudioVolume_AddRef(IChannelAudioVolume
*iface
)
3093 AudioSessionWrapper
*This
= impl_from_IChannelAudioVolume(iface
);
3094 return AudioSessionControl_AddRef(&This
->IAudioSessionControl2_iface
);
3097 static ULONG WINAPI
ChannelAudioVolume_Release(IChannelAudioVolume
*iface
)
3099 AudioSessionWrapper
*This
= impl_from_IChannelAudioVolume(iface
);
3100 return AudioSessionControl_Release(&This
->IAudioSessionControl2_iface
);
3103 static HRESULT WINAPI
ChannelAudioVolume_GetChannelCount(
3104 IChannelAudioVolume
*iface
, UINT32
*out
)
3106 AudioSessionWrapper
*This
= impl_from_IChannelAudioVolume(iface
);
3107 AudioSession
*session
= This
->session
;
3109 TRACE("(%p)->(%p)\n", session
, out
);
3112 return NULL_PTR_ERR
;
3114 *out
= session
->channel_count
;
3119 static HRESULT WINAPI
ChannelAudioVolume_SetChannelVolume(
3120 IChannelAudioVolume
*iface
, UINT32 index
, float level
,
3121 const GUID
*context
)
3123 AudioSessionWrapper
*This
= impl_from_IChannelAudioVolume(iface
);
3124 AudioSession
*session
= This
->session
;
3126 TRACE("(%p)->(%d, %f, %s)\n", session
, index
, level
,
3127 wine_dbgstr_guid(context
));
3129 if (level
< 0.f
|| level
> 1.f
)
3130 return E_INVALIDARG
;
3132 if (index
>= session
->channel_count
)
3133 return E_INVALIDARG
;
3136 FIXME("Notifications not supported yet\n");
3138 TRACE("PulseAudio does not support session volume control\n");
3140 pthread_mutex_lock(&pulse_lock
);
3141 session
->channel_vols
[index
] = level
;
3142 pthread_mutex_unlock(&pulse_lock
);
3147 static HRESULT WINAPI
ChannelAudioVolume_GetChannelVolume(
3148 IChannelAudioVolume
*iface
, UINT32 index
, float *level
)
3150 AudioSessionWrapper
*This
= impl_from_IChannelAudioVolume(iface
);
3151 AudioSession
*session
= This
->session
;
3153 TRACE("(%p)->(%d, %p)\n", session
, index
, level
);
3156 return NULL_PTR_ERR
;
3158 if (index
>= session
->channel_count
)
3159 return E_INVALIDARG
;
3161 *level
= session
->channel_vols
[index
];
3166 static HRESULT WINAPI
ChannelAudioVolume_SetAllVolumes(
3167 IChannelAudioVolume
*iface
, UINT32 count
, const float *levels
,
3168 const GUID
*context
)
3170 AudioSessionWrapper
*This
= impl_from_IChannelAudioVolume(iface
);
3171 AudioSession
*session
= This
->session
;
3174 TRACE("(%p)->(%d, %p, %s)\n", session
, count
, levels
,
3175 wine_dbgstr_guid(context
));
3178 return NULL_PTR_ERR
;
3180 if (count
!= session
->channel_count
)
3181 return E_INVALIDARG
;
3184 FIXME("Notifications not supported yet\n");
3186 TRACE("PulseAudio does not support session volume control\n");
3188 pthread_mutex_lock(&pulse_lock
);
3189 for(i
= 0; i
< count
; ++i
)
3190 session
->channel_vols
[i
] = levels
[i
];
3191 pthread_mutex_unlock(&pulse_lock
);
3195 static HRESULT WINAPI
ChannelAudioVolume_GetAllVolumes(
3196 IChannelAudioVolume
*iface
, UINT32 count
, float *levels
)
3198 AudioSessionWrapper
*This
= impl_from_IChannelAudioVolume(iface
);
3199 AudioSession
*session
= This
->session
;
3202 TRACE("(%p)->(%d, %p)\n", session
, count
, levels
);
3205 return NULL_PTR_ERR
;
3207 if (count
!= session
->channel_count
)
3208 return E_INVALIDARG
;
3210 for(i
= 0; i
< count
; ++i
)
3211 levels
[i
] = session
->channel_vols
[i
];
3216 static const IChannelAudioVolumeVtbl ChannelAudioVolume_Vtbl
=
3218 ChannelAudioVolume_QueryInterface
,
3219 ChannelAudioVolume_AddRef
,
3220 ChannelAudioVolume_Release
,
3221 ChannelAudioVolume_GetChannelCount
,
3222 ChannelAudioVolume_SetChannelVolume
,
3223 ChannelAudioVolume_GetChannelVolume
,
3224 ChannelAudioVolume_SetAllVolumes
,
3225 ChannelAudioVolume_GetAllVolumes
3228 HRESULT WINAPI
AUDDRV_GetAudioSessionManager(IMMDevice
*device
,
3229 IAudioSessionManager2
**out
)
3231 SessionMgr
*This
= HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY
, sizeof(SessionMgr
));
3234 return E_OUTOFMEMORY
;
3235 This
->IAudioSessionManager2_iface
.lpVtbl
= &AudioSessionManager2_Vtbl
;
3236 This
->device
= device
;
3238 *out
= &This
->IAudioSessionManager2_iface
;
3242 HRESULT WINAPI
AUDDRV_GetPropValue(GUID
*guid
, const PROPERTYKEY
*prop
, PROPVARIANT
*out
)
3244 TRACE("%s, (%s,%u), %p\n", wine_dbgstr_guid(guid
), wine_dbgstr_guid(&prop
->fmtid
), prop
->pid
, out
);
3246 if (IsEqualGUID(guid
, &pulse_render_guid
) && IsEqualPropertyKey(*prop
, PKEY_AudioEndpoint_PhysicalSpeakers
)) {
3248 out
->u
.ulVal
= g_phys_speakers_mask
;
3250 return out
->u
.ulVal
? S_OK
: E_FAIL
;