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
);
61 #define NULL_PTR_ERR MAKE_HRESULT(SEVERITY_ERROR, FACILITY_WIN32, RPC_X_NULL_REF_POINTER)
63 /* From <dlls/mmdevapi/mmdevapi.h> */
65 Priority_Unavailable
= 0,
71 static const REFERENCE_TIME MinimumPeriod
= 30000;
72 static const REFERENCE_TIME DefaultPeriod
= 100000;
74 static pa_context
*pulse_ctx
;
75 static pa_mainloop
*pulse_ml
;
77 static HANDLE pulse_thread
;
78 static pthread_mutex_t pulse_lock
;
79 static pthread_cond_t pulse_cond
= PTHREAD_COND_INITIALIZER
;
80 static struct list g_sessions
= LIST_INIT(g_sessions
);
82 static UINT g_phys_speakers_mask
= 0;
84 /* Mixer format + period times */
85 static WAVEFORMATEXTENSIBLE pulse_fmt
[2];
86 static REFERENCE_TIME pulse_min_period
[2], pulse_def_period
[2];
88 static GUID pulse_render_guid
=
89 { 0xfd47d9cc, 0x4218, 0x4135, { 0x9c, 0xe2, 0x0c, 0x19, 0x5c, 0x87, 0x40, 0x5b } };
90 static GUID pulse_capture_guid
=
91 { 0x25da76d0, 0x033c, 0x4235, { 0x90, 0x02, 0x19, 0xf4, 0x88, 0x94, 0xac, 0x6f } };
93 BOOL WINAPI
DllMain(HINSTANCE dll
, DWORD reason
, void *reserved
)
95 if (reason
== DLL_PROCESS_ATTACH
) {
96 pthread_mutexattr_t attr
;
98 DisableThreadLibraryCalls(dll
);
100 pthread_mutexattr_init(&attr
);
101 pthread_mutexattr_setprotocol(&attr
, PTHREAD_PRIO_INHERIT
);
103 if (pthread_mutex_init(&pulse_lock
, &attr
) != 0)
104 pthread_mutex_init(&pulse_lock
, NULL
);
105 } else if (reason
== DLL_PROCESS_DETACH
) {
107 SetThreadPriority(pulse_thread
, 0);
109 pa_context_disconnect(pulse_ctx
);
110 pa_context_unref(pulse_ctx
);
113 pa_mainloop_quit(pulse_ml
, 0);
115 WaitForSingleObject(pulse_thread
, INFINITE
);
116 CloseHandle(pulse_thread
);
122 typedef struct ACImpl ACImpl
;
124 typedef struct _AudioSession
{
131 UINT32 channel_count
;
138 typedef struct _AudioSessionWrapper
{
139 IAudioSessionControl2 IAudioSessionControl2_iface
;
140 IChannelAudioVolume IChannelAudioVolume_iface
;
141 ISimpleAudioVolume ISimpleAudioVolume_iface
;
146 AudioSession
*session
;
147 } AudioSessionWrapper
;
149 typedef struct _ACPacket
{
157 IAudioClient IAudioClient_iface
;
158 IAudioRenderClient IAudioRenderClient_iface
;
159 IAudioCaptureClient IAudioCaptureClient_iface
;
160 IAudioClock IAudioClock_iface
;
161 IAudioClock2 IAudioClock2_iface
;
162 IAudioStreamVolume IAudioStreamVolume_iface
;
166 float vol
[PA_CHANNELS_MAX
];
171 AUDCLNT_SHAREMODE share
;
175 UINT32 bufsize_frames
, bufsize_bytes
, capture_period
, pad
, started
, peek_ofs
, wri_offs_bytes
, lcl_offs_bytes
;
176 UINT32 tmp_buffer_bytes
, held_bytes
, peek_len
, peek_buffer_len
;
177 BYTE
*local_buffer
, *tmp_buffer
, *peek_buffer
;
185 INT64 clock_lastpos
, clock_written
;
187 AudioSession
*session
;
188 AudioSessionWrapper
*session_wrapper
;
189 struct list packet_free_head
;
190 struct list packet_filled_head
;
193 static const WCHAR defaultW
[] = {'P','u','l','s','e','a','u','d','i','o',0};
195 static const IAudioClientVtbl AudioClient_Vtbl
;
196 static const IAudioRenderClientVtbl AudioRenderClient_Vtbl
;
197 static const IAudioCaptureClientVtbl AudioCaptureClient_Vtbl
;
198 static const IAudioSessionControl2Vtbl AudioSessionControl2_Vtbl
;
199 static const ISimpleAudioVolumeVtbl SimpleAudioVolume_Vtbl
;
200 static const IChannelAudioVolumeVtbl ChannelAudioVolume_Vtbl
;
201 static const IAudioClockVtbl AudioClock_Vtbl
;
202 static const IAudioClock2Vtbl AudioClock2_Vtbl
;
203 static const IAudioStreamVolumeVtbl AudioStreamVolume_Vtbl
;
205 static AudioSessionWrapper
*AudioSessionWrapper_Create(ACImpl
*client
);
207 static inline ACImpl
*impl_from_IAudioClient(IAudioClient
*iface
)
209 return CONTAINING_RECORD(iface
, ACImpl
, IAudioClient_iface
);
212 static inline ACImpl
*impl_from_IAudioRenderClient(IAudioRenderClient
*iface
)
214 return CONTAINING_RECORD(iface
, ACImpl
, IAudioRenderClient_iface
);
217 static inline ACImpl
*impl_from_IAudioCaptureClient(IAudioCaptureClient
*iface
)
219 return CONTAINING_RECORD(iface
, ACImpl
, IAudioCaptureClient_iface
);
222 static inline AudioSessionWrapper
*impl_from_IAudioSessionControl2(IAudioSessionControl2
*iface
)
224 return CONTAINING_RECORD(iface
, AudioSessionWrapper
, IAudioSessionControl2_iface
);
227 static inline AudioSessionWrapper
*impl_from_ISimpleAudioVolume(ISimpleAudioVolume
*iface
)
229 return CONTAINING_RECORD(iface
, AudioSessionWrapper
, ISimpleAudioVolume_iface
);
232 static inline AudioSessionWrapper
*impl_from_IChannelAudioVolume(IChannelAudioVolume
*iface
)
234 return CONTAINING_RECORD(iface
, AudioSessionWrapper
, IChannelAudioVolume_iface
);
237 static inline ACImpl
*impl_from_IAudioClock(IAudioClock
*iface
)
239 return CONTAINING_RECORD(iface
, ACImpl
, IAudioClock_iface
);
242 static inline ACImpl
*impl_from_IAudioClock2(IAudioClock2
*iface
)
244 return CONTAINING_RECORD(iface
, ACImpl
, IAudioClock2_iface
);
247 static inline ACImpl
*impl_from_IAudioStreamVolume(IAudioStreamVolume
*iface
)
249 return CONTAINING_RECORD(iface
, ACImpl
, IAudioStreamVolume_iface
);
252 /* Following pulseaudio design here, mainloop has the lock taken whenever
253 * it is handling something for pulse, and the lock is required whenever
254 * doing any pa_* call that can affect the state in any way
256 * pa_cond_wait is used when waiting on results, because the mainloop needs
257 * the same lock taken to affect the state
259 * This is basically the same as the pa_threaded_mainloop implementation,
260 * but that cannot be used because it uses pthread_create directly
262 * pa_threaded_mainloop_(un)lock -> pthread_mutex_(un)lock
263 * pa_threaded_mainloop_signal -> pthread_cond_signal
264 * pa_threaded_mainloop_wait -> pthread_cond_wait
267 static int pulse_poll_func(struct pollfd
*ufds
, unsigned long nfds
, int timeout
, void *userdata
) {
269 pthread_mutex_unlock(&pulse_lock
);
270 r
= poll(ufds
, nfds
, timeout
);
271 pthread_mutex_lock(&pulse_lock
);
275 static DWORD CALLBACK
pulse_mainloop_thread(void *tmp
) {
277 pulse_ml
= pa_mainloop_new();
278 pa_mainloop_set_poll_func(pulse_ml
, pulse_poll_func
, NULL
);
279 pthread_mutex_lock(&pulse_lock
);
280 pthread_cond_signal(&pulse_cond
);
281 pa_mainloop_run(pulse_ml
, &ret
);
282 pthread_mutex_unlock(&pulse_lock
);
283 pa_mainloop_free(pulse_ml
);
287 static void pulse_contextcallback(pa_context
*c
, void *userdata
)
289 switch (pa_context_get_state(c
)) {
291 FIXME("Unhandled state: %i\n", pa_context_get_state(c
));
294 case PA_CONTEXT_CONNECTING
:
295 case PA_CONTEXT_UNCONNECTED
:
296 case PA_CONTEXT_AUTHORIZING
:
297 case PA_CONTEXT_SETTING_NAME
:
298 case PA_CONTEXT_TERMINATED
:
299 TRACE("State change to %i\n", pa_context_get_state(c
));
302 case PA_CONTEXT_READY
:
306 case PA_CONTEXT_FAILED
:
307 WARN("Context failed: %s\n", pa_strerror(pa_context_errno(c
)));
310 pthread_cond_signal(&pulse_cond
);
313 static void pulse_stream_state(pa_stream
*s
, void *user
)
315 pa_stream_state_t state
= pa_stream_get_state(s
);
316 TRACE("Stream state changed to %i\n", state
);
317 pthread_cond_signal(&pulse_cond
);
320 static const enum pa_channel_position pulse_pos_from_wfx
[] = {
321 PA_CHANNEL_POSITION_FRONT_LEFT
,
322 PA_CHANNEL_POSITION_FRONT_RIGHT
,
323 PA_CHANNEL_POSITION_FRONT_CENTER
,
324 PA_CHANNEL_POSITION_LFE
,
325 PA_CHANNEL_POSITION_REAR_LEFT
,
326 PA_CHANNEL_POSITION_REAR_RIGHT
,
327 PA_CHANNEL_POSITION_FRONT_LEFT_OF_CENTER
,
328 PA_CHANNEL_POSITION_FRONT_RIGHT_OF_CENTER
,
329 PA_CHANNEL_POSITION_REAR_CENTER
,
330 PA_CHANNEL_POSITION_SIDE_LEFT
,
331 PA_CHANNEL_POSITION_SIDE_RIGHT
,
332 PA_CHANNEL_POSITION_TOP_CENTER
,
333 PA_CHANNEL_POSITION_TOP_FRONT_LEFT
,
334 PA_CHANNEL_POSITION_TOP_FRONT_CENTER
,
335 PA_CHANNEL_POSITION_TOP_FRONT_RIGHT
,
336 PA_CHANNEL_POSITION_TOP_REAR_LEFT
,
337 PA_CHANNEL_POSITION_TOP_REAR_CENTER
,
338 PA_CHANNEL_POSITION_TOP_REAR_RIGHT
341 static DWORD
pulse_channel_map_to_channel_mask(const pa_channel_map
*map
) {
345 for (i
= 0; i
< map
->channels
; ++i
)
346 switch (map
->map
[i
]) {
347 default: FIXME("Unhandled channel %s\n", pa_channel_position_to_string(map
->map
[i
])); break;
348 case PA_CHANNEL_POSITION_FRONT_LEFT
: mask
|= SPEAKER_FRONT_LEFT
; break;
349 case PA_CHANNEL_POSITION_MONO
:
350 case PA_CHANNEL_POSITION_FRONT_CENTER
: mask
|= SPEAKER_FRONT_CENTER
; break;
351 case PA_CHANNEL_POSITION_FRONT_RIGHT
: mask
|= SPEAKER_FRONT_RIGHT
; break;
352 case PA_CHANNEL_POSITION_REAR_LEFT
: mask
|= SPEAKER_BACK_LEFT
; break;
353 case PA_CHANNEL_POSITION_REAR_CENTER
: mask
|= SPEAKER_BACK_CENTER
; break;
354 case PA_CHANNEL_POSITION_REAR_RIGHT
: mask
|= SPEAKER_BACK_RIGHT
; break;
355 case PA_CHANNEL_POSITION_LFE
: mask
|= SPEAKER_LOW_FREQUENCY
; break;
356 case PA_CHANNEL_POSITION_SIDE_LEFT
: mask
|= SPEAKER_SIDE_LEFT
; break;
357 case PA_CHANNEL_POSITION_SIDE_RIGHT
: mask
|= SPEAKER_SIDE_RIGHT
; break;
358 case PA_CHANNEL_POSITION_TOP_CENTER
: mask
|= SPEAKER_TOP_CENTER
; break;
359 case PA_CHANNEL_POSITION_TOP_FRONT_LEFT
: mask
|= SPEAKER_TOP_FRONT_LEFT
; break;
360 case PA_CHANNEL_POSITION_TOP_FRONT_CENTER
: mask
|= SPEAKER_TOP_FRONT_CENTER
; break;
361 case PA_CHANNEL_POSITION_TOP_FRONT_RIGHT
: mask
|= SPEAKER_TOP_FRONT_RIGHT
; break;
362 case PA_CHANNEL_POSITION_TOP_REAR_LEFT
: mask
|= SPEAKER_TOP_BACK_LEFT
; break;
363 case PA_CHANNEL_POSITION_TOP_REAR_CENTER
: mask
|= SPEAKER_TOP_BACK_CENTER
; break;
364 case PA_CHANNEL_POSITION_TOP_REAR_RIGHT
: mask
|= SPEAKER_TOP_BACK_RIGHT
; break;
365 case PA_CHANNEL_POSITION_FRONT_LEFT_OF_CENTER
: mask
|= SPEAKER_FRONT_LEFT_OF_CENTER
; break;
366 case PA_CHANNEL_POSITION_FRONT_RIGHT_OF_CENTER
: mask
|= SPEAKER_FRONT_RIGHT_OF_CENTER
; break;
372 static void pulse_probe_settings(int render
, WAVEFORMATEXTENSIBLE
*fmt
) {
373 WAVEFORMATEX
*wfx
= &fmt
->Format
;
379 unsigned int length
= 0;
381 pa_channel_map_init_auto(&map
, 2, PA_CHANNEL_MAP_ALSA
);
383 ss
.format
= PA_SAMPLE_FLOAT32LE
;
384 ss
.channels
= map
.channels
;
388 attr
.minreq
= attr
.fragsize
= pa_frame_size(&ss
);
391 stream
= pa_stream_new(pulse_ctx
, "format test stream", &ss
, &map
);
393 pa_stream_set_state_callback(stream
, pulse_stream_state
, NULL
);
397 ret
= pa_stream_connect_playback(stream
, NULL
, &attr
,
398 PA_STREAM_START_CORKED
|PA_STREAM_FIX_RATE
|PA_STREAM_FIX_CHANNELS
|PA_STREAM_EARLY_REQUESTS
, NULL
, NULL
);
400 ret
= pa_stream_connect_record(stream
, NULL
, &attr
, PA_STREAM_START_CORKED
|PA_STREAM_FIX_RATE
|PA_STREAM_FIX_CHANNELS
|PA_STREAM_EARLY_REQUESTS
);
402 while (pa_mainloop_iterate(pulse_ml
, 1, &ret
) >= 0 &&
403 pa_stream_get_state(stream
) == PA_STREAM_CREATING
)
405 if (pa_stream_get_state(stream
) == PA_STREAM_READY
) {
406 ss
= *pa_stream_get_sample_spec(stream
);
407 map
= *pa_stream_get_channel_map(stream
);
409 length
= pa_stream_get_buffer_attr(stream
)->minreq
;
411 length
= pa_stream_get_buffer_attr(stream
)->fragsize
;
412 pa_stream_disconnect(stream
);
413 while (pa_mainloop_iterate(pulse_ml
, 1, &ret
) >= 0 &&
414 pa_stream_get_state(stream
) == PA_STREAM_READY
)
420 pa_stream_unref(stream
);
423 pulse_def_period
[!render
] = pulse_min_period
[!render
] = pa_bytes_to_usec(10 * length
, &ss
);
425 if (pulse_min_period
[!render
] < MinimumPeriod
)
426 pulse_min_period
[!render
] = MinimumPeriod
;
428 if (pulse_def_period
[!render
] < DefaultPeriod
)
429 pulse_def_period
[!render
] = DefaultPeriod
;
431 wfx
->wFormatTag
= WAVE_FORMAT_EXTENSIBLE
;
432 wfx
->cbSize
= sizeof(WAVEFORMATEXTENSIBLE
) - sizeof(WAVEFORMATEX
);
433 wfx
->nChannels
= ss
.channels
;
434 wfx
->wBitsPerSample
= 8 * pa_sample_size_of_format(ss
.format
);
435 wfx
->nSamplesPerSec
= ss
.rate
;
436 wfx
->nBlockAlign
= pa_frame_size(&ss
);
437 wfx
->nAvgBytesPerSec
= wfx
->nSamplesPerSec
* wfx
->nBlockAlign
;
438 if (ss
.format
!= PA_SAMPLE_S24_32LE
)
439 fmt
->Samples
.wValidBitsPerSample
= wfx
->wBitsPerSample
;
441 fmt
->Samples
.wValidBitsPerSample
= 24;
442 if (ss
.format
== PA_SAMPLE_FLOAT32LE
)
443 fmt
->SubFormat
= KSDATAFORMAT_SUBTYPE_IEEE_FLOAT
;
445 fmt
->SubFormat
= KSDATAFORMAT_SUBTYPE_PCM
;
447 fmt
->dwChannelMask
= pulse_channel_map_to_channel_mask(&map
);
450 static HRESULT
pulse_connect(void)
453 WCHAR path
[MAX_PATH
], *name
;
458 if (!(pulse_thread
= CreateThread(NULL
, 0, pulse_mainloop_thread
, NULL
, 0, NULL
)))
460 ERR("Failed to create mainloop thread.\n");
463 SetThreadPriority(pulse_thread
, THREAD_PRIORITY_TIME_CRITICAL
);
464 pthread_cond_wait(&pulse_cond
, &pulse_lock
);
467 if (pulse_ctx
&& PA_CONTEXT_IS_GOOD(pa_context_get_state(pulse_ctx
)))
470 pa_context_unref(pulse_ctx
);
472 GetModuleFileNameW(NULL
, path
, sizeof(path
)/sizeof(*path
));
473 name
= strrchrW(path
, '\\');
478 len
= WideCharToMultiByte(CP_UNIXCP
, 0, name
, -1, NULL
, 0, NULL
, NULL
);
479 str
= pa_xmalloc(len
);
480 WideCharToMultiByte(CP_UNIXCP
, 0, name
, -1, str
, len
, NULL
, NULL
);
481 TRACE("Name: %s\n", str
);
482 pulse_ctx
= pa_context_new(pa_mainloop_get_api(pulse_ml
), str
);
485 ERR("Failed to create context\n");
489 pa_context_set_state_callback(pulse_ctx
, pulse_contextcallback
, NULL
);
491 TRACE("libpulse protocol version: %u. API Version %u\n", pa_context_get_protocol_version(pulse_ctx
), PA_API_VERSION
);
492 if (pa_context_connect(pulse_ctx
, NULL
, 0, NULL
) < 0)
495 /* Wait for connection */
496 while (pthread_cond_wait(&pulse_cond
, &pulse_lock
)) {
497 pa_context_state_t state
= pa_context_get_state(pulse_ctx
);
499 if (state
== PA_CONTEXT_FAILED
|| state
== PA_CONTEXT_TERMINATED
)
502 if (state
== PA_CONTEXT_READY
)
506 TRACE("Connected to server %s with protocol version: %i.\n",
507 pa_context_get_server(pulse_ctx
),
508 pa_context_get_server_protocol_version(pulse_ctx
));
512 pa_context_unref(pulse_ctx
);
517 /* For default PulseAudio render device, OR together all of the
518 * PKEY_AudioEndpoint_PhysicalSpeakers values of the sinks. */
519 static void pulse_phys_speakers_cb(pa_context
*c
, const pa_sink_info
*i
, int eol
, void *userdata
)
522 g_phys_speakers_mask
|= pulse_channel_map_to_channel_mask(&i
->channel_map
);
525 /* some poorly-behaved applications call audio functions during DllMain, so we
526 * have to do as much as possible without creating a new thread. this function
527 * sets up a synchronous connection to verify the server is running and query
529 static HRESULT
pulse_test_connect(void)
532 WCHAR path
[MAX_PATH
], *name
;
536 pulse_ml
= pa_mainloop_new();
538 pa_mainloop_set_poll_func(pulse_ml
, pulse_poll_func
, NULL
);
540 GetModuleFileNameW(NULL
, path
, sizeof(path
)/sizeof(*path
));
541 name
= strrchrW(path
, '\\');
546 len
= WideCharToMultiByte(CP_UNIXCP
, 0, name
, -1, NULL
, 0, NULL
, NULL
);
547 str
= pa_xmalloc(len
);
548 WideCharToMultiByte(CP_UNIXCP
, 0, name
, -1, str
, len
, NULL
, NULL
);
549 TRACE("Name: %s\n", str
);
550 pulse_ctx
= pa_context_new(pa_mainloop_get_api(pulse_ml
), str
);
553 ERR("Failed to create context\n");
554 pa_mainloop_free(pulse_ml
);
559 pa_context_set_state_callback(pulse_ctx
, pulse_contextcallback
, NULL
);
561 TRACE("libpulse protocol version: %u. API Version %u\n", pa_context_get_protocol_version(pulse_ctx
), PA_API_VERSION
);
562 if (pa_context_connect(pulse_ctx
, NULL
, 0, NULL
) < 0)
565 /* Wait for connection */
566 while (pa_mainloop_iterate(pulse_ml
, 1, &ret
) >= 0) {
567 pa_context_state_t state
= pa_context_get_state(pulse_ctx
);
569 if (state
== PA_CONTEXT_FAILED
|| state
== PA_CONTEXT_TERMINATED
)
572 if (state
== PA_CONTEXT_READY
)
576 if (pa_context_get_state(pulse_ctx
) != PA_CONTEXT_READY
)
579 TRACE("Test-connected to server %s with protocol version: %i.\n",
580 pa_context_get_server(pulse_ctx
),
581 pa_context_get_server_protocol_version(pulse_ctx
));
583 pulse_probe_settings(1, &pulse_fmt
[0]);
584 pulse_probe_settings(0, &pulse_fmt
[1]);
586 g_phys_speakers_mask
= 0;
587 o
= pa_context_get_sink_info_list(pulse_ctx
, &pulse_phys_speakers_cb
, NULL
);
589 while (pa_mainloop_iterate(pulse_ml
, 1, &ret
) >= 0 &&
590 pa_operation_get_state(o
) == PA_OPERATION_RUNNING
)
592 pa_operation_unref(o
);
595 pa_context_unref(pulse_ctx
);
597 pa_mainloop_free(pulse_ml
);
603 pa_context_unref(pulse_ctx
);
605 pa_mainloop_free(pulse_ml
);
611 static HRESULT
pulse_stream_valid(ACImpl
*This
) {
613 return AUDCLNT_E_NOT_INITIALIZED
;
614 if (pa_stream_get_state(This
->stream
) != PA_STREAM_READY
)
615 return AUDCLNT_E_DEVICE_INVALIDATED
;
619 static void silence_buffer(pa_sample_format_t format
, BYTE
*buffer
, UINT32 bytes
)
621 memset(buffer
, format
== PA_SAMPLE_U8
? 0x80 : 0, bytes
);
624 static void dump_attr(const pa_buffer_attr
*attr
) {
625 TRACE("maxlength: %u\n", attr
->maxlength
);
626 TRACE("minreq: %u\n", attr
->minreq
);
627 TRACE("fragsize: %u\n", attr
->fragsize
);
628 TRACE("tlength: %u\n", attr
->tlength
);
629 TRACE("prebuf: %u\n", attr
->prebuf
);
632 static void pulse_op_cb(pa_stream
*s
, int success
, void *user
) {
633 TRACE("Success: %i\n", success
);
634 *(int*)user
= success
;
635 pthread_cond_signal(&pulse_cond
);
638 static void pulse_attr_update(pa_stream
*s
, void *user
) {
639 const pa_buffer_attr
*attr
= pa_stream_get_buffer_attr(s
);
640 TRACE("New attributes or device moved:\n");
644 /* Here's the buffer setup:
646 * vvvvvvvv sent to HW already
647 * vvvvvvvv in Pulse buffer but rewindable
648 * [dddddddddddddddd] Pulse buffer
649 * [dddddddddddddddd--------] mmdevapi buffer
650 * ^^^^^^^^^^^^^^^^ pad
652 * ^^^^^^^^^ held_bytes
655 * GetCurrentPadding is pad
657 * During pulse_wr_callback, we decrement pad, fill Pulse buffer, and move
660 * During Stop, we flush the Pulse buffer
662 static void pulse_wr_callback(pa_stream
*s
, size_t bytes
, void *userdata
)
664 ACImpl
*This
= userdata
;
665 UINT32 oldpad
= This
->pad
;
667 if(This
->local_buffer
){
669 BYTE
*buf
= This
->local_buffer
+ This
->lcl_offs_bytes
;
671 if(This
->pad
> bytes
){
672 This
->clock_written
+= bytes
;
675 This
->clock_written
+= This
->pad
;
679 bytes
= min(bytes
, This
->held_bytes
);
681 if(This
->lcl_offs_bytes
+ bytes
> This
->bufsize_bytes
){
682 to_write
= This
->bufsize_bytes
- This
->lcl_offs_bytes
;
683 TRACE("writing small chunk of %u bytes\n", to_write
);
684 pa_stream_write(This
->stream
, buf
, to_write
, NULL
, 0, PA_SEEK_RELATIVE
);
685 This
->held_bytes
-= to_write
;
686 to_write
= bytes
- to_write
;
687 This
->lcl_offs_bytes
= 0;
688 buf
= This
->local_buffer
;
692 TRACE("writing main chunk of %u bytes\n", to_write
);
693 pa_stream_write(This
->stream
, buf
, to_write
, NULL
, 0, PA_SEEK_RELATIVE
);
694 This
->lcl_offs_bytes
+= to_write
;
695 This
->lcl_offs_bytes
%= This
->bufsize_bytes
;
696 This
->held_bytes
-= to_write
;
698 if (bytes
< This
->bufsize_bytes
)
699 This
->pad
= This
->bufsize_bytes
- bytes
;
703 if (oldpad
== This
->pad
)
706 assert(oldpad
> This
->pad
);
708 This
->clock_written
+= oldpad
- This
->pad
;
709 TRACE("New pad: %zu (-%zu)\n", This
->pad
/ pa_frame_size(&This
->ss
), (oldpad
- This
->pad
) / pa_frame_size(&This
->ss
));
713 SetEvent(This
->event
);
716 static void pulse_underflow_callback(pa_stream
*s
, void *userdata
)
721 /* Latency is periodically updated even when nothing is played,
722 * because of PA_STREAM_AUTO_TIMING_UPDATE so use it as timer
724 * Perfect for passing all tests :)
726 static void pulse_latency_callback(pa_stream
*s
, void *userdata
)
728 ACImpl
*This
= userdata
;
729 if (!This
->pad
&& This
->event
)
730 SetEvent(This
->event
);
733 static void pulse_started_callback(pa_stream
*s
, void *userdata
)
735 TRACE("(Re)started playing\n");
738 static void pulse_rd_loop(ACImpl
*This
, size_t bytes
)
740 while (bytes
>= This
->capture_period
) {
742 LARGE_INTEGER stamp
, freq
;
744 size_t src_len
, copy
, rem
= This
->capture_period
;
745 if (!(p
= (ACPacket
*)list_head(&This
->packet_free_head
))) {
746 p
= (ACPacket
*)list_head(&This
->packet_filled_head
);
748 next
= (ACPacket
*)p
->entry
.next
;
751 p
= (ACPacket
*)list_tail(&This
->packet_filled_head
);
752 assert(This
->pad
== This
->bufsize_bytes
);
754 assert(This
->pad
< This
->bufsize_bytes
);
755 This
->pad
+= This
->capture_period
;
756 assert(This
->pad
<= This
->bufsize_bytes
);
758 QueryPerformanceCounter(&stamp
);
759 QueryPerformanceFrequency(&freq
);
760 p
->qpcpos
= (stamp
.QuadPart
* (INT64
)10000000) / freq
.QuadPart
;
762 list_remove(&p
->entry
);
763 list_add_tail(&This
->packet_filled_head
, &p
->entry
);
767 if (This
->peek_len
) {
768 copy
= min(rem
, This
->peek_len
- This
->peek_ofs
);
770 memcpy(dst
, This
->peek_buffer
+ This
->peek_ofs
, copy
);
774 This
->peek_ofs
+= copy
;
775 if(This
->peek_len
== This
->peek_ofs
)
778 pa_stream_peek(This
->stream
, (const void**)&src
, &src_len
);
780 copy
= min(rem
, src_len
);
782 memcpy(dst
, src
, rem
);
787 if (copy
< src_len
) {
788 if (src_len
> This
->peek_buffer_len
) {
789 HeapFree(GetProcessHeap(), 0, This
->peek_buffer
);
790 This
->peek_buffer
= HeapAlloc(GetProcessHeap(), 0, src_len
);
791 This
->peek_buffer_len
= src_len
;
794 memcpy(This
->peek_buffer
, src
+ copy
, src_len
- copy
);
795 This
->peek_len
= src_len
- copy
;
799 pa_stream_drop(This
->stream
);
803 bytes
-= This
->capture_period
;
807 static void pulse_rd_drop(ACImpl
*This
, size_t bytes
)
809 while (bytes
>= This
->capture_period
) {
810 size_t src_len
, copy
, rem
= This
->capture_period
;
813 pa_stream_peek(This
->stream
, &src
, &src_len
);
815 assert(This
->peek_ofs
< src_len
);
816 src_len
-= This
->peek_ofs
;
817 assert(src_len
<= bytes
);
828 pa_stream_drop(This
->stream
);
830 This
->peek_ofs
+= copy
;
836 static void pulse_rd_callback(pa_stream
*s
, size_t bytes
, void *userdata
)
838 ACImpl
*This
= userdata
;
840 TRACE("Readable total: %zu, fragsize: %u\n", bytes
, pa_stream_get_buffer_attr(s
)->fragsize
);
841 assert(bytes
>= This
->peek_ofs
);
842 bytes
-= This
->peek_ofs
;
843 if (bytes
< This
->capture_period
)
847 pulse_rd_loop(This
, bytes
);
849 pulse_rd_drop(This
, bytes
);
852 SetEvent(This
->event
);
855 static HRESULT
pulse_stream_connect(ACImpl
*This
, UINT32 period_bytes
) {
861 pa_stream_disconnect(This
->stream
);
862 while (pa_stream_get_state(This
->stream
) == PA_STREAM_READY
)
863 pthread_cond_wait(&pulse_cond
, &pulse_lock
);
864 pa_stream_unref(This
->stream
);
866 ret
= InterlockedIncrement(&number
);
867 sprintf(buffer
, "audio stream #%i", ret
);
868 This
->stream
= pa_stream_new(pulse_ctx
, buffer
, &This
->ss
, &This
->map
);
871 WARN("pa_stream_new returned error %i\n", pa_context_errno(pulse_ctx
));
872 return AUDCLNT_E_ENDPOINT_CREATE_FAILED
;
875 pa_stream_set_state_callback(This
->stream
, pulse_stream_state
, This
);
876 pa_stream_set_buffer_attr_callback(This
->stream
, pulse_attr_update
, This
);
877 pa_stream_set_moved_callback(This
->stream
, pulse_attr_update
, This
);
879 /* PulseAudio will fill in correct values */
880 attr
.minreq
= attr
.fragsize
= period_bytes
;
881 attr
.maxlength
= attr
.tlength
= This
->bufsize_bytes
;
882 attr
.prebuf
= pa_frame_size(&This
->ss
);
884 if (This
->dataflow
== eRender
)
885 ret
= pa_stream_connect_playback(This
->stream
, NULL
, &attr
,
886 PA_STREAM_START_CORKED
|PA_STREAM_START_UNMUTED
|PA_STREAM_AUTO_TIMING_UPDATE
|PA_STREAM_INTERPOLATE_TIMING
|PA_STREAM_EARLY_REQUESTS
, NULL
, NULL
);
888 ret
= pa_stream_connect_record(This
->stream
, NULL
, &attr
,
889 PA_STREAM_START_CORKED
|PA_STREAM_START_UNMUTED
|PA_STREAM_AUTO_TIMING_UPDATE
|PA_STREAM_INTERPOLATE_TIMING
|PA_STREAM_EARLY_REQUESTS
);
891 WARN("Returns %i\n", ret
);
892 return AUDCLNT_E_ENDPOINT_CREATE_FAILED
;
894 while (pa_stream_get_state(This
->stream
) == PA_STREAM_CREATING
)
895 pthread_cond_wait(&pulse_cond
, &pulse_lock
);
896 if (pa_stream_get_state(This
->stream
) != PA_STREAM_READY
)
897 return AUDCLNT_E_ENDPOINT_CREATE_FAILED
;
899 if (This
->dataflow
== eRender
) {
900 pa_stream_set_write_callback(This
->stream
, pulse_wr_callback
, This
);
901 pa_stream_set_underflow_callback(This
->stream
, pulse_underflow_callback
, This
);
902 pa_stream_set_started_callback(This
->stream
, pulse_started_callback
, This
);
904 pa_stream_set_read_callback(This
->stream
, pulse_rd_callback
, This
);
908 HRESULT WINAPI
AUDDRV_GetEndpointIDs(EDataFlow flow
, const WCHAR
***ids
, GUID
**keys
,
909 UINT
*num
, UINT
*def_index
)
913 TRACE("%d %p %p %p\n", flow
, ids
, num
, def_index
);
918 *ids
= HeapAlloc(GetProcessHeap(), 0, sizeof(**ids
));
921 return E_OUTOFMEMORY
;
923 (*ids
)[0] = id
= HeapAlloc(GetProcessHeap(), 0, sizeof(defaultW
));
924 *keys
= HeapAlloc(GetProcessHeap(), 0, sizeof(**keys
));
926 HeapFree(GetProcessHeap(), 0, id
);
927 HeapFree(GetProcessHeap(), 0, *keys
);
928 HeapFree(GetProcessHeap(), 0, *ids
);
931 return E_OUTOFMEMORY
;
933 memcpy(id
, defaultW
, sizeof(defaultW
));
936 (*keys
)[0] = pulse_render_guid
;
938 (*keys
)[0] = pulse_capture_guid
;
943 int WINAPI
AUDDRV_GetPriority(void)
946 pthread_mutex_lock(&pulse_lock
);
947 hr
= pulse_test_connect();
948 pthread_mutex_unlock(&pulse_lock
);
949 return SUCCEEDED(hr
) ? Priority_Preferred
: Priority_Unavailable
;
952 HRESULT WINAPI
AUDDRV_GetAudioEndpoint(GUID
*guid
, IMMDevice
*dev
, IAudioClient
**out
)
959 TRACE("%s %p %p\n", debugstr_guid(guid
), dev
, out
);
960 if (IsEqualGUID(guid
, &pulse_render_guid
))
962 else if (IsEqualGUID(guid
, &pulse_capture_guid
))
969 This
= HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY
, sizeof(*This
));
971 return E_OUTOFMEMORY
;
973 This
->IAudioClient_iface
.lpVtbl
= &AudioClient_Vtbl
;
974 This
->IAudioRenderClient_iface
.lpVtbl
= &AudioRenderClient_Vtbl
;
975 This
->IAudioCaptureClient_iface
.lpVtbl
= &AudioCaptureClient_Vtbl
;
976 This
->IAudioClock_iface
.lpVtbl
= &AudioClock_Vtbl
;
977 This
->IAudioClock2_iface
.lpVtbl
= &AudioClock2_Vtbl
;
978 This
->IAudioStreamVolume_iface
.lpVtbl
= &AudioStreamVolume_Vtbl
;
979 This
->dataflow
= dataflow
;
981 for (i
= 0; i
< PA_CHANNELS_MAX
; ++i
)
984 hr
= CoCreateFreeThreadedMarshaler((IUnknown
*)&This
->IAudioClient_iface
, &This
->marshal
);
986 HeapFree(GetProcessHeap(), 0, This
);
989 IMMDevice_AddRef(This
->parent
);
991 *out
= &This
->IAudioClient_iface
;
992 IAudioClient_AddRef(&This
->IAudioClient_iface
);
997 static HRESULT WINAPI
AudioClient_QueryInterface(IAudioClient
*iface
,
998 REFIID riid
, void **ppv
)
1000 ACImpl
*This
= impl_from_IAudioClient(iface
);
1002 TRACE("(%p)->(%s, %p)\n", iface
, debugstr_guid(riid
), ppv
);
1008 if (IsEqualIID(riid
, &IID_IUnknown
) || IsEqualIID(riid
, &IID_IAudioClient
))
1011 IUnknown_AddRef((IUnknown
*)*ppv
);
1015 if (IsEqualIID(riid
, &IID_IMarshal
))
1016 return IUnknown_QueryInterface(This
->marshal
, riid
, ppv
);
1018 WARN("Unknown interface %s\n", debugstr_guid(riid
));
1019 return E_NOINTERFACE
;
1022 static ULONG WINAPI
AudioClient_AddRef(IAudioClient
*iface
)
1024 ACImpl
*This
= impl_from_IAudioClient(iface
);
1026 ref
= InterlockedIncrement(&This
->ref
);
1027 TRACE("(%p) Refcount now %u\n", This
, ref
);
1031 static ULONG WINAPI
AudioClient_Release(IAudioClient
*iface
)
1033 ACImpl
*This
= impl_from_IAudioClient(iface
);
1035 ref
= InterlockedDecrement(&This
->ref
);
1036 TRACE("(%p) Refcount now %u\n", This
, ref
);
1039 pthread_mutex_lock(&pulse_lock
);
1040 if (PA_STREAM_IS_GOOD(pa_stream_get_state(This
->stream
))) {
1041 pa_stream_disconnect(This
->stream
);
1042 while (PA_STREAM_IS_GOOD(pa_stream_get_state(This
->stream
)))
1043 pthread_cond_wait(&pulse_cond
, &pulse_lock
);
1045 pa_stream_unref(This
->stream
);
1046 This
->stream
= NULL
;
1047 list_remove(&This
->entry
);
1048 pthread_mutex_unlock(&pulse_lock
);
1050 IUnknown_Release(This
->marshal
);
1051 IMMDevice_Release(This
->parent
);
1052 HeapFree(GetProcessHeap(), 0, This
->tmp_buffer
);
1053 HeapFree(GetProcessHeap(), 0, This
->peek_buffer
);
1054 HeapFree(GetProcessHeap(), 0, This
->local_buffer
);
1055 HeapFree(GetProcessHeap(), 0, This
);
1060 static void dump_fmt(const WAVEFORMATEX
*fmt
)
1062 TRACE("wFormatTag: 0x%x (", fmt
->wFormatTag
);
1063 switch(fmt
->wFormatTag
) {
1064 case WAVE_FORMAT_PCM
:
1065 TRACE("WAVE_FORMAT_PCM");
1067 case WAVE_FORMAT_IEEE_FLOAT
:
1068 TRACE("WAVE_FORMAT_IEEE_FLOAT");
1070 case WAVE_FORMAT_EXTENSIBLE
:
1071 TRACE("WAVE_FORMAT_EXTENSIBLE");
1079 TRACE("nChannels: %u\n", fmt
->nChannels
);
1080 TRACE("nSamplesPerSec: %u\n", fmt
->nSamplesPerSec
);
1081 TRACE("nAvgBytesPerSec: %u\n", fmt
->nAvgBytesPerSec
);
1082 TRACE("nBlockAlign: %u\n", fmt
->nBlockAlign
);
1083 TRACE("wBitsPerSample: %u\n", fmt
->wBitsPerSample
);
1084 TRACE("cbSize: %u\n", fmt
->cbSize
);
1086 if (fmt
->wFormatTag
== WAVE_FORMAT_EXTENSIBLE
) {
1087 WAVEFORMATEXTENSIBLE
*fmtex
= (void*)fmt
;
1088 TRACE("dwChannelMask: %08x\n", fmtex
->dwChannelMask
);
1089 TRACE("Samples: %04x\n", fmtex
->Samples
.wReserved
);
1090 TRACE("SubFormat: %s\n", wine_dbgstr_guid(&fmtex
->SubFormat
));
1094 static WAVEFORMATEX
*clone_format(const WAVEFORMATEX
*fmt
)
1099 if (fmt
->wFormatTag
== WAVE_FORMAT_EXTENSIBLE
)
1100 size
= sizeof(WAVEFORMATEXTENSIBLE
);
1102 size
= sizeof(WAVEFORMATEX
);
1104 ret
= CoTaskMemAlloc(size
);
1108 memcpy(ret
, fmt
, size
);
1110 ret
->cbSize
= size
- sizeof(WAVEFORMATEX
);
1115 static DWORD
get_channel_mask(unsigned int channels
)
1121 return KSAUDIO_SPEAKER_MONO
;
1123 return KSAUDIO_SPEAKER_STEREO
;
1125 return KSAUDIO_SPEAKER_STEREO
| SPEAKER_LOW_FREQUENCY
;
1127 return KSAUDIO_SPEAKER_QUAD
; /* not _SURROUND */
1129 return KSAUDIO_SPEAKER_QUAD
| SPEAKER_LOW_FREQUENCY
;
1131 return KSAUDIO_SPEAKER_5POINT1
; /* not 5POINT1_SURROUND */
1133 return KSAUDIO_SPEAKER_5POINT1
| SPEAKER_BACK_CENTER
;
1135 return KSAUDIO_SPEAKER_7POINT1_SURROUND
; /* Vista deprecates 7POINT1 */
1137 FIXME("Unknown speaker configuration: %u\n", channels
);
1141 static void session_init_vols(AudioSession
*session
, UINT channels
)
1143 if (session
->channel_count
< channels
) {
1146 if (session
->channel_vols
)
1147 session
->channel_vols
= HeapReAlloc(GetProcessHeap(), 0,
1148 session
->channel_vols
, sizeof(float) * channels
);
1150 session
->channel_vols
= HeapAlloc(GetProcessHeap(), 0,
1151 sizeof(float) * channels
);
1152 if (!session
->channel_vols
)
1155 for(i
= session
->channel_count
; i
< channels
; ++i
)
1156 session
->channel_vols
[i
] = 1.f
;
1158 session
->channel_count
= channels
;
1162 static AudioSession
*create_session(const GUID
*guid
, IMMDevice
*device
,
1167 ret
= HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY
, sizeof(AudioSession
));
1171 memcpy(&ret
->guid
, guid
, sizeof(GUID
));
1173 ret
->device
= device
;
1175 list_init(&ret
->clients
);
1177 list_add_head(&g_sessions
, &ret
->entry
);
1179 session_init_vols(ret
, num_channels
);
1181 ret
->master_vol
= 1.f
;
1186 /* if channels == 0, then this will return or create a session with
1187 * matching dataflow and GUID. otherwise, channels must also match */
1188 static HRESULT
get_audio_session(const GUID
*sessionguid
,
1189 IMMDevice
*device
, UINT channels
, AudioSession
**out
)
1191 AudioSession
*session
;
1193 if (!sessionguid
|| IsEqualGUID(sessionguid
, &GUID_NULL
)) {
1194 *out
= create_session(&GUID_NULL
, device
, channels
);
1196 return E_OUTOFMEMORY
;
1202 LIST_FOR_EACH_ENTRY(session
, &g_sessions
, AudioSession
, entry
) {
1203 if (session
->device
== device
&&
1204 IsEqualGUID(sessionguid
, &session
->guid
)) {
1205 session_init_vols(session
, channels
);
1212 *out
= create_session(sessionguid
, device
, channels
);
1214 return E_OUTOFMEMORY
;
1220 static HRESULT
pulse_spec_from_waveformat(ACImpl
*This
, const WAVEFORMATEX
*fmt
)
1222 pa_channel_map_init(&This
->map
);
1223 This
->ss
.rate
= fmt
->nSamplesPerSec
;
1224 This
->ss
.format
= PA_SAMPLE_INVALID
;
1226 switch(fmt
->wFormatTag
) {
1227 case WAVE_FORMAT_IEEE_FLOAT
:
1228 if (!fmt
->nChannels
|| fmt
->nChannels
> 2 || fmt
->wBitsPerSample
!= 32)
1230 This
->ss
.format
= PA_SAMPLE_FLOAT32LE
;
1231 pa_channel_map_init_auto(&This
->map
, fmt
->nChannels
, PA_CHANNEL_MAP_ALSA
);
1233 case WAVE_FORMAT_PCM
:
1234 if (!fmt
->nChannels
|| fmt
->nChannels
> 2)
1236 if (fmt
->wBitsPerSample
== 8)
1237 This
->ss
.format
= PA_SAMPLE_U8
;
1238 else if (fmt
->wBitsPerSample
== 16)
1239 This
->ss
.format
= PA_SAMPLE_S16LE
;
1241 return AUDCLNT_E_UNSUPPORTED_FORMAT
;
1242 pa_channel_map_init_auto(&This
->map
, fmt
->nChannels
, PA_CHANNEL_MAP_ALSA
);
1244 case WAVE_FORMAT_EXTENSIBLE
: {
1245 WAVEFORMATEXTENSIBLE
*wfe
= (WAVEFORMATEXTENSIBLE
*)fmt
;
1246 DWORD mask
= wfe
->dwChannelMask
;
1248 if (fmt
->cbSize
!= (sizeof(*wfe
) - sizeof(*fmt
)) && fmt
->cbSize
!= sizeof(*wfe
))
1250 if (IsEqualGUID(&wfe
->SubFormat
, &KSDATAFORMAT_SUBTYPE_IEEE_FLOAT
) &&
1251 (!wfe
->Samples
.wValidBitsPerSample
|| wfe
->Samples
.wValidBitsPerSample
== 32) &&
1252 fmt
->wBitsPerSample
== 32)
1253 This
->ss
.format
= PA_SAMPLE_FLOAT32LE
;
1254 else if (IsEqualGUID(&wfe
->SubFormat
, &KSDATAFORMAT_SUBTYPE_PCM
)) {
1255 DWORD valid
= wfe
->Samples
.wValidBitsPerSample
;
1257 valid
= fmt
->wBitsPerSample
;
1258 if (!valid
|| valid
> fmt
->wBitsPerSample
)
1260 switch (fmt
->wBitsPerSample
) {
1263 This
->ss
.format
= PA_SAMPLE_U8
;
1267 This
->ss
.format
= PA_SAMPLE_S16LE
;
1271 This
->ss
.format
= PA_SAMPLE_S24LE
;
1275 This
->ss
.format
= PA_SAMPLE_S24_32LE
;
1276 else if (valid
== 32)
1277 This
->ss
.format
= PA_SAMPLE_S32LE
;
1280 return AUDCLNT_E_UNSUPPORTED_FORMAT
;
1283 This
->map
.channels
= fmt
->nChannels
;
1284 if (!mask
|| (mask
& (SPEAKER_ALL
|SPEAKER_RESERVED
)))
1285 mask
= get_channel_mask(fmt
->nChannels
);
1286 for (j
= 0; j
< sizeof(pulse_pos_from_wfx
)/sizeof(*pulse_pos_from_wfx
) && i
< fmt
->nChannels
; ++j
) {
1287 if (mask
& (1 << j
))
1288 This
->map
.map
[i
++] = pulse_pos_from_wfx
[j
];
1291 /* Special case for mono since pulse appears to map it differently */
1292 if (mask
== SPEAKER_FRONT_CENTER
)
1293 This
->map
.map
[0] = PA_CHANNEL_POSITION_MONO
;
1295 if (i
< fmt
->nChannels
|| (mask
& SPEAKER_RESERVED
)) {
1296 This
->map
.channels
= 0;
1297 ERR("Invalid channel mask: %i/%i and %x(%x)\n", i
, fmt
->nChannels
, mask
, wfe
->dwChannelMask
);
1302 case WAVE_FORMAT_ALAW
:
1303 case WAVE_FORMAT_MULAW
:
1304 if (fmt
->wBitsPerSample
!= 8) {
1305 FIXME("Unsupported bpp %u for LAW\n", fmt
->wBitsPerSample
);
1306 return AUDCLNT_E_UNSUPPORTED_FORMAT
;
1308 if (fmt
->nChannels
!= 1 && fmt
->nChannels
!= 2) {
1309 FIXME("Unsupported channels %u for LAW\n", fmt
->nChannels
);
1310 return AUDCLNT_E_UNSUPPORTED_FORMAT
;
1312 This
->ss
.format
= fmt
->wFormatTag
== WAVE_FORMAT_MULAW
? PA_SAMPLE_ULAW
: PA_SAMPLE_ALAW
;
1313 pa_channel_map_init_auto(&This
->map
, fmt
->nChannels
, PA_CHANNEL_MAP_ALSA
);
1316 WARN("Unhandled tag %x\n", fmt
->wFormatTag
);
1317 return AUDCLNT_E_UNSUPPORTED_FORMAT
;
1319 This
->ss
.channels
= This
->map
.channels
;
1320 if (!pa_channel_map_valid(&This
->map
) || This
->ss
.format
== PA_SAMPLE_INVALID
) {
1321 ERR("Invalid format! Channel spec valid: %i, format: %i\n", pa_channel_map_valid(&This
->map
), This
->ss
.format
);
1322 return AUDCLNT_E_UNSUPPORTED_FORMAT
;
1327 static HRESULT WINAPI
AudioClient_Initialize(IAudioClient
*iface
,
1328 AUDCLNT_SHAREMODE mode
, DWORD flags
, REFERENCE_TIME duration
,
1329 REFERENCE_TIME period
, const WAVEFORMATEX
*fmt
,
1330 const GUID
*sessionguid
)
1332 ACImpl
*This
= impl_from_IAudioClient(iface
);
1336 TRACE("(%p)->(%x, %x, %s, %s, %p, %s)\n", This
, mode
, flags
,
1337 wine_dbgstr_longlong(duration
), wine_dbgstr_longlong(period
), fmt
, debugstr_guid(sessionguid
));
1342 if (mode
!= AUDCLNT_SHAREMODE_SHARED
&& mode
!= AUDCLNT_SHAREMODE_EXCLUSIVE
)
1343 return AUDCLNT_E_NOT_INITIALIZED
;
1344 if (mode
== AUDCLNT_SHAREMODE_EXCLUSIVE
)
1345 return AUDCLNT_E_EXCLUSIVE_MODE_NOT_ALLOWED
;
1347 if (flags
& ~(AUDCLNT_STREAMFLAGS_CROSSPROCESS
|
1348 AUDCLNT_STREAMFLAGS_LOOPBACK
|
1349 AUDCLNT_STREAMFLAGS_EVENTCALLBACK
|
1350 AUDCLNT_STREAMFLAGS_NOPERSIST
|
1351 AUDCLNT_STREAMFLAGS_RATEADJUST
|
1352 AUDCLNT_SESSIONFLAGS_EXPIREWHENUNOWNED
|
1353 AUDCLNT_SESSIONFLAGS_DISPLAY_HIDE
|
1354 AUDCLNT_SESSIONFLAGS_DISPLAY_HIDEWHENEXPIRED
)) {
1355 TRACE("Unknown flags: %08x\n", flags
);
1356 return E_INVALIDARG
;
1359 pthread_mutex_lock(&pulse_lock
);
1361 hr
= pulse_connect();
1363 pthread_mutex_unlock(&pulse_lock
);
1368 pthread_mutex_unlock(&pulse_lock
);
1369 return AUDCLNT_E_ALREADY_INITIALIZED
;
1372 hr
= pulse_spec_from_waveformat(This
, fmt
);
1373 TRACE("Obtaining format returns %08x\n", hr
);
1379 if (mode
== AUDCLNT_SHAREMODE_SHARED
) {
1380 REFERENCE_TIME def
= pulse_def_period
[This
->dataflow
== eCapture
];
1381 REFERENCE_TIME min
= pulse_min_period
[This
->dataflow
== eCapture
];
1383 /* Switch to low latency mode if below 2 default periods,
1384 * which is 20 ms by default, this will increase the amount
1385 * of interrupts but allows very low latency. In dsound I
1386 * managed to get a total latency of ~8ms, which is well below
1389 if (duration
< 2 * def
)
1393 if (duration
< 2 * period
)
1394 duration
= 2 * period
;
1396 /* Uh oh, really low latency requested.. */
1397 if (duration
<= 2 * period
)
1400 period_bytes
= pa_frame_size(&This
->ss
) * MulDiv(period
, This
->ss
.rate
, 10000000);
1402 if (duration
< 20000000)
1403 This
->bufsize_frames
= ceil((duration
/ 10000000.) * fmt
->nSamplesPerSec
);
1405 This
->bufsize_frames
= 2 * fmt
->nSamplesPerSec
;
1406 This
->bufsize_bytes
= This
->bufsize_frames
* pa_frame_size(&This
->ss
);
1409 This
->flags
= flags
;
1410 hr
= pulse_stream_connect(This
, period_bytes
);
1411 if (SUCCEEDED(hr
)) {
1413 const pa_buffer_attr
*attr
= pa_stream_get_buffer_attr(This
->stream
);
1415 /* Update frames according to new size */
1417 if (This
->dataflow
== eRender
) {
1418 if (attr
->tlength
< This
->bufsize_bytes
) {
1419 TRACE("PulseAudio buffer too small (%u < %u), using tmp buffer\n", attr
->tlength
, This
->bufsize_bytes
);
1421 This
->local_buffer
= HeapAlloc(GetProcessHeap(), 0, This
->bufsize_bytes
);
1422 if(!This
->local_buffer
)
1426 UINT32 i
, capture_packets
;
1428 This
->capture_period
= period_bytes
= attr
->fragsize
;
1429 if ((unalign
= This
->bufsize_bytes
% period_bytes
))
1430 This
->bufsize_bytes
+= period_bytes
- unalign
;
1431 This
->bufsize_frames
= This
->bufsize_bytes
/ pa_frame_size(&This
->ss
);
1433 capture_packets
= This
->bufsize_bytes
/ This
->capture_period
;
1435 This
->local_buffer
= HeapAlloc(GetProcessHeap(), 0, This
->bufsize_bytes
+ capture_packets
* sizeof(ACPacket
));
1436 if (!This
->local_buffer
)
1439 ACPacket
*cur_packet
= (ACPacket
*)((char*)This
->local_buffer
+ This
->bufsize_bytes
);
1440 BYTE
*data
= This
->local_buffer
;
1441 silence_buffer(This
->ss
.format
, This
->local_buffer
, This
->bufsize_bytes
);
1442 list_init(&This
->packet_free_head
);
1443 list_init(&This
->packet_filled_head
);
1444 for (i
= 0; i
< capture_packets
; ++i
, ++cur_packet
) {
1445 list_add_tail(&This
->packet_free_head
, &cur_packet
->entry
);
1446 cur_packet
->data
= data
;
1447 data
+= This
->capture_period
;
1449 assert(!This
->capture_period
|| This
->bufsize_bytes
== This
->capture_period
* capture_packets
);
1450 assert(!capture_packets
|| data
- This
->bufsize_bytes
== This
->local_buffer
);
1455 hr
= get_audio_session(sessionguid
, This
->parent
, fmt
->nChannels
, &This
->session
);
1457 list_add_tail(&This
->session
->clients
, &This
->entry
);
1461 HeapFree(GetProcessHeap(), 0, This
->local_buffer
);
1462 This
->local_buffer
= NULL
;
1464 pa_stream_disconnect(This
->stream
);
1465 pa_stream_unref(This
->stream
);
1466 This
->stream
= NULL
;
1469 pthread_mutex_unlock(&pulse_lock
);
1473 static HRESULT WINAPI
AudioClient_GetBufferSize(IAudioClient
*iface
,
1476 ACImpl
*This
= impl_from_IAudioClient(iface
);
1479 TRACE("(%p)->(%p)\n", This
, out
);
1484 pthread_mutex_lock(&pulse_lock
);
1485 hr
= pulse_stream_valid(This
);
1487 *out
= This
->bufsize_frames
;
1488 pthread_mutex_unlock(&pulse_lock
);
1493 static HRESULT WINAPI
AudioClient_GetStreamLatency(IAudioClient
*iface
,
1494 REFERENCE_TIME
*latency
)
1496 ACImpl
*This
= impl_from_IAudioClient(iface
);
1497 const pa_buffer_attr
*attr
;
1501 TRACE("(%p)->(%p)\n", This
, latency
);
1506 pthread_mutex_lock(&pulse_lock
);
1507 hr
= pulse_stream_valid(This
);
1509 pthread_mutex_unlock(&pulse_lock
);
1512 attr
= pa_stream_get_buffer_attr(This
->stream
);
1513 if (This
->dataflow
== eRender
){
1514 lat
= attr
->minreq
/ pa_frame_size(&This
->ss
);
1515 lat
+= pulse_def_period
[0];
1517 lat
= attr
->fragsize
/ pa_frame_size(&This
->ss
);
1518 *latency
= 10000000;
1520 *latency
/= This
->ss
.rate
;
1521 pthread_mutex_unlock(&pulse_lock
);
1522 TRACE("Latency: %u ms\n", (DWORD
)(*latency
/ 10000));
1526 static void ACImpl_GetRenderPad(ACImpl
*This
, UINT32
*out
)
1528 *out
= This
->pad
/ pa_frame_size(&This
->ss
);
1531 static void ACImpl_GetCapturePad(ACImpl
*This
, UINT32
*out
)
1533 ACPacket
*packet
= This
->locked_ptr
;
1534 if (!packet
&& !list_empty(&This
->packet_filled_head
)) {
1535 packet
= (ACPacket
*)list_head(&This
->packet_filled_head
);
1536 This
->locked_ptr
= packet
;
1537 list_remove(&packet
->entry
);
1540 *out
= This
->pad
/ pa_frame_size(&This
->ss
);
1543 static HRESULT WINAPI
AudioClient_GetCurrentPadding(IAudioClient
*iface
,
1546 ACImpl
*This
= impl_from_IAudioClient(iface
);
1549 TRACE("(%p)->(%p)\n", This
, out
);
1554 pthread_mutex_lock(&pulse_lock
);
1555 hr
= pulse_stream_valid(This
);
1557 pthread_mutex_unlock(&pulse_lock
);
1561 if (This
->dataflow
== eRender
)
1562 ACImpl_GetRenderPad(This
, out
);
1564 ACImpl_GetCapturePad(This
, out
);
1565 pthread_mutex_unlock(&pulse_lock
);
1567 TRACE("%p Pad: %u ms (%u)\n", This
, MulDiv(*out
, 1000, This
->ss
.rate
), *out
);
1571 static HRESULT WINAPI
AudioClient_IsFormatSupported(IAudioClient
*iface
,
1572 AUDCLNT_SHAREMODE mode
, const WAVEFORMATEX
*fmt
,
1575 ACImpl
*This
= impl_from_IAudioClient(iface
);
1577 WAVEFORMATEX
*closest
= NULL
;
1580 TRACE("(%p)->(%x, %p, %p)\n", This
, mode
, fmt
, out
);
1588 if (mode
== AUDCLNT_SHAREMODE_EXCLUSIVE
) {
1591 } else if (mode
== AUDCLNT_SHAREMODE_SHARED
) {
1596 return E_INVALIDARG
;
1598 if (fmt
->nChannels
== 0)
1599 return AUDCLNT_E_UNSUPPORTED_FORMAT
;
1601 closest
= clone_format(fmt
);
1603 return E_OUTOFMEMORY
;
1607 switch (fmt
->wFormatTag
) {
1608 case WAVE_FORMAT_EXTENSIBLE
: {
1609 WAVEFORMATEXTENSIBLE
*ext
= (WAVEFORMATEXTENSIBLE
*)closest
;
1611 if ((fmt
->cbSize
!= sizeof(WAVEFORMATEXTENSIBLE
) - sizeof(WAVEFORMATEX
) &&
1612 fmt
->cbSize
!= sizeof(WAVEFORMATEXTENSIBLE
)) ||
1613 fmt
->nBlockAlign
!= fmt
->wBitsPerSample
/ 8 * fmt
->nChannels
||
1614 ext
->Samples
.wValidBitsPerSample
> fmt
->wBitsPerSample
||
1615 fmt
->nAvgBytesPerSec
!= fmt
->nBlockAlign
* fmt
->nSamplesPerSec
) {
1621 UINT32 mask
= 0, i
, channels
= 0;
1623 if (!(ext
->dwChannelMask
& (SPEAKER_ALL
| SPEAKER_RESERVED
))) {
1624 for (i
= 1; !(i
& SPEAKER_RESERVED
); i
<<= 1) {
1625 if (i
& ext
->dwChannelMask
) {
1631 if (channels
!= fmt
->nChannels
|| (ext
->dwChannelMask
& ~mask
)) {
1632 hr
= AUDCLNT_E_UNSUPPORTED_FORMAT
;
1636 hr
= AUDCLNT_E_UNSUPPORTED_FORMAT
;
1641 if (IsEqualGUID(&ext
->SubFormat
, &KSDATAFORMAT_SUBTYPE_IEEE_FLOAT
)) {
1642 if (fmt
->wBitsPerSample
!= 32) {
1647 if (ext
->Samples
.wValidBitsPerSample
!= fmt
->wBitsPerSample
) {
1649 ext
->Samples
.wValidBitsPerSample
= fmt
->wBitsPerSample
;
1651 } else if (IsEqualGUID(&ext
->SubFormat
, &KSDATAFORMAT_SUBTYPE_PCM
)) {
1652 if (!fmt
->wBitsPerSample
|| fmt
->wBitsPerSample
> 32 || fmt
->wBitsPerSample
% 8) {
1657 if (ext
->Samples
.wValidBitsPerSample
!= fmt
->wBitsPerSample
&&
1658 !(fmt
->wBitsPerSample
== 32 &&
1659 ext
->Samples
.wValidBitsPerSample
== 24)) {
1661 ext
->Samples
.wValidBitsPerSample
= fmt
->wBitsPerSample
;
1665 hr
= AUDCLNT_E_UNSUPPORTED_FORMAT
;
1672 case WAVE_FORMAT_ALAW
:
1673 case WAVE_FORMAT_MULAW
:
1674 if (fmt
->wBitsPerSample
!= 8) {
1679 case WAVE_FORMAT_IEEE_FLOAT
:
1680 if (fmt
->wFormatTag
== WAVE_FORMAT_IEEE_FLOAT
&& fmt
->wBitsPerSample
!= 32) {
1685 case WAVE_FORMAT_PCM
:
1686 if (fmt
->wFormatTag
== WAVE_FORMAT_PCM
&&
1687 (!fmt
->wBitsPerSample
|| fmt
->wBitsPerSample
> 32 || fmt
->wBitsPerSample
% 8)) {
1692 if (fmt
->nChannels
> 2) {
1693 hr
= AUDCLNT_E_UNSUPPORTED_FORMAT
;
1697 * fmt->cbSize, fmt->nBlockAlign and fmt->nAvgBytesPerSec seem to be
1698 * ignored, invalid values are happily accepted.
1702 hr
= AUDCLNT_E_UNSUPPORTED_FORMAT
;
1706 if (exclusive
&& hr
!= S_OK
) {
1707 hr
= AUDCLNT_E_UNSUPPORTED_FORMAT
;
1708 CoTaskMemFree(closest
);
1709 } else if (hr
!= S_FALSE
)
1710 CoTaskMemFree(closest
);
1714 /* Winepulse does not currently support exclusive mode, if you know of an
1715 * application that uses it, I will correct this..
1717 if (hr
== S_OK
&& exclusive
)
1718 return This
->dataflow
== eCapture
? AUDCLNT_E_UNSUPPORTED_FORMAT
: AUDCLNT_E_EXCLUSIVE_MODE_NOT_ALLOWED
;
1720 TRACE("returning: %08x %p\n", hr
, out
? *out
: NULL
);
1724 static HRESULT WINAPI
AudioClient_GetMixFormat(IAudioClient
*iface
,
1725 WAVEFORMATEX
**pwfx
)
1727 ACImpl
*This
= impl_from_IAudioClient(iface
);
1728 WAVEFORMATEXTENSIBLE
*fmt
= &pulse_fmt
[This
->dataflow
== eCapture
];
1730 TRACE("(%p)->(%p)\n", This
, pwfx
);
1735 *pwfx
= clone_format(&fmt
->Format
);
1737 return E_OUTOFMEMORY
;
1742 static HRESULT WINAPI
AudioClient_GetDevicePeriod(IAudioClient
*iface
,
1743 REFERENCE_TIME
*defperiod
, REFERENCE_TIME
*minperiod
)
1745 ACImpl
*This
= impl_from_IAudioClient(iface
);
1747 TRACE("(%p)->(%p, %p)\n", This
, defperiod
, minperiod
);
1749 if (!defperiod
&& !minperiod
)
1753 *defperiod
= pulse_def_period
[This
->dataflow
== eCapture
];
1755 *minperiod
= pulse_min_period
[This
->dataflow
== eCapture
];
1760 static HRESULT WINAPI
AudioClient_Start(IAudioClient
*iface
)
1762 ACImpl
*This
= impl_from_IAudioClient(iface
);
1767 TRACE("(%p)\n", This
);
1769 pthread_mutex_lock(&pulse_lock
);
1770 hr
= pulse_stream_valid(This
);
1772 pthread_mutex_unlock(&pulse_lock
);
1776 if ((This
->flags
& AUDCLNT_STREAMFLAGS_EVENTCALLBACK
) && !This
->event
) {
1777 pthread_mutex_unlock(&pulse_lock
);
1778 return AUDCLNT_E_EVENTHANDLE_NOT_SET
;
1781 if (This
->started
) {
1782 pthread_mutex_unlock(&pulse_lock
);
1783 return AUDCLNT_E_NOT_STOPPED
;
1786 if (pa_stream_is_corked(This
->stream
)) {
1787 o
= pa_stream_cork(This
->stream
, 0, pulse_op_cb
, &success
);
1789 while(pa_operation_get_state(o
) == PA_OPERATION_RUNNING
)
1790 pthread_cond_wait(&pulse_cond
, &pulse_lock
);
1791 pa_operation_unref(o
);
1798 if (SUCCEEDED(hr
)) {
1799 This
->started
= TRUE
;
1800 if (This
->dataflow
== eRender
&& This
->event
)
1801 pa_stream_set_latency_update_callback(This
->stream
, pulse_latency_callback
, This
);
1803 pthread_mutex_unlock(&pulse_lock
);
1807 static HRESULT WINAPI
AudioClient_Stop(IAudioClient
*iface
)
1809 ACImpl
*This
= impl_from_IAudioClient(iface
);
1814 TRACE("(%p)\n", This
);
1816 pthread_mutex_lock(&pulse_lock
);
1817 hr
= pulse_stream_valid(This
);
1819 pthread_mutex_unlock(&pulse_lock
);
1823 if (!This
->started
) {
1824 pthread_mutex_unlock(&pulse_lock
);
1828 if (This
->dataflow
== eRender
) {
1829 o
= pa_stream_cork(This
->stream
, 1, pulse_op_cb
, &success
);
1831 while(pa_operation_get_state(o
) == PA_OPERATION_RUNNING
)
1832 pthread_cond_wait(&pulse_cond
, &pulse_lock
);
1833 pa_operation_unref(o
);
1839 if (SUCCEEDED(hr
)) {
1840 This
->started
= FALSE
;
1842 pthread_mutex_unlock(&pulse_lock
);
1846 static HRESULT WINAPI
AudioClient_Reset(IAudioClient
*iface
)
1848 ACImpl
*This
= impl_from_IAudioClient(iface
);
1851 TRACE("(%p)\n", This
);
1853 pthread_mutex_lock(&pulse_lock
);
1854 hr
= pulse_stream_valid(This
);
1856 pthread_mutex_unlock(&pulse_lock
);
1860 if (This
->started
) {
1861 pthread_mutex_unlock(&pulse_lock
);
1862 return AUDCLNT_E_NOT_STOPPED
;
1866 pthread_mutex_unlock(&pulse_lock
);
1867 return AUDCLNT_E_BUFFER_OPERATION_PENDING
;
1870 if (This
->dataflow
== eRender
) {
1871 /* If there is still data in the render buffer it needs to be removed from the server */
1874 pa_operation
*o
= pa_stream_flush(This
->stream
, pulse_op_cb
, &success
);
1876 while(pa_operation_get_state(o
) == PA_OPERATION_RUNNING
)
1877 pthread_cond_wait(&pulse_cond
, &pulse_lock
);
1878 pa_operation_unref(o
);
1881 if (success
|| !This
->pad
){
1882 This
->clock_lastpos
= This
->clock_written
= This
->pad
= 0;
1883 This
->wri_offs_bytes
= This
->lcl_offs_bytes
= This
->held_bytes
= 0;
1887 This
->clock_written
+= This
->pad
;
1890 if ((p
= This
->locked_ptr
)) {
1891 This
->locked_ptr
= NULL
;
1892 list_add_tail(&This
->packet_free_head
, &p
->entry
);
1894 list_move_tail(&This
->packet_free_head
, &This
->packet_filled_head
);
1896 pthread_mutex_unlock(&pulse_lock
);
1901 static HRESULT WINAPI
AudioClient_SetEventHandle(IAudioClient
*iface
,
1904 ACImpl
*This
= impl_from_IAudioClient(iface
);
1907 TRACE("(%p)->(%p)\n", This
, event
);
1910 return E_INVALIDARG
;
1912 pthread_mutex_lock(&pulse_lock
);
1913 hr
= pulse_stream_valid(This
);
1915 pthread_mutex_unlock(&pulse_lock
);
1919 if (!(This
->flags
& AUDCLNT_STREAMFLAGS_EVENTCALLBACK
))
1920 hr
= AUDCLNT_E_EVENTHANDLE_NOT_EXPECTED
;
1921 else if (This
->event
)
1922 hr
= HRESULT_FROM_WIN32(ERROR_INVALID_NAME
);
1924 This
->event
= event
;
1925 pthread_mutex_unlock(&pulse_lock
);
1929 static HRESULT WINAPI
AudioClient_GetService(IAudioClient
*iface
, REFIID riid
,
1932 ACImpl
*This
= impl_from_IAudioClient(iface
);
1935 TRACE("(%p)->(%s, %p)\n", This
, debugstr_guid(riid
), ppv
);
1941 pthread_mutex_lock(&pulse_lock
);
1942 hr
= pulse_stream_valid(This
);
1943 pthread_mutex_unlock(&pulse_lock
);
1947 if (IsEqualIID(riid
, &IID_IAudioRenderClient
)) {
1948 if (This
->dataflow
!= eRender
)
1949 return AUDCLNT_E_WRONG_ENDPOINT_TYPE
;
1950 *ppv
= &This
->IAudioRenderClient_iface
;
1951 } else if (IsEqualIID(riid
, &IID_IAudioCaptureClient
)) {
1952 if (This
->dataflow
!= eCapture
)
1953 return AUDCLNT_E_WRONG_ENDPOINT_TYPE
;
1954 *ppv
= &This
->IAudioCaptureClient_iface
;
1955 } else if (IsEqualIID(riid
, &IID_IAudioClock
)) {
1956 *ppv
= &This
->IAudioClock_iface
;
1957 } else if (IsEqualIID(riid
, &IID_IAudioStreamVolume
)) {
1958 *ppv
= &This
->IAudioStreamVolume_iface
;
1959 } else if (IsEqualIID(riid
, &IID_IAudioSessionControl
) ||
1960 IsEqualIID(riid
, &IID_IChannelAudioVolume
) ||
1961 IsEqualIID(riid
, &IID_ISimpleAudioVolume
)) {
1962 if (!This
->session_wrapper
) {
1963 This
->session_wrapper
= AudioSessionWrapper_Create(This
);
1964 if (!This
->session_wrapper
)
1965 return E_OUTOFMEMORY
;
1967 if (IsEqualIID(riid
, &IID_IAudioSessionControl
))
1968 *ppv
= &This
->session_wrapper
->IAudioSessionControl2_iface
;
1969 else if (IsEqualIID(riid
, &IID_IChannelAudioVolume
))
1970 *ppv
= &This
->session_wrapper
->IChannelAudioVolume_iface
;
1971 else if (IsEqualIID(riid
, &IID_ISimpleAudioVolume
))
1972 *ppv
= &This
->session_wrapper
->ISimpleAudioVolume_iface
;
1976 IUnknown_AddRef((IUnknown
*)*ppv
);
1980 FIXME("stub %s\n", debugstr_guid(riid
));
1981 return E_NOINTERFACE
;
1984 static const IAudioClientVtbl AudioClient_Vtbl
=
1986 AudioClient_QueryInterface
,
1988 AudioClient_Release
,
1989 AudioClient_Initialize
,
1990 AudioClient_GetBufferSize
,
1991 AudioClient_GetStreamLatency
,
1992 AudioClient_GetCurrentPadding
,
1993 AudioClient_IsFormatSupported
,
1994 AudioClient_GetMixFormat
,
1995 AudioClient_GetDevicePeriod
,
1999 AudioClient_SetEventHandle
,
2000 AudioClient_GetService
2003 static HRESULT WINAPI
AudioRenderClient_QueryInterface(
2004 IAudioRenderClient
*iface
, REFIID riid
, void **ppv
)
2006 ACImpl
*This
= impl_from_IAudioRenderClient(iface
);
2007 TRACE("(%p)->(%s, %p)\n", iface
, debugstr_guid(riid
), ppv
);
2013 if (IsEqualIID(riid
, &IID_IUnknown
) ||
2014 IsEqualIID(riid
, &IID_IAudioRenderClient
))
2017 IUnknown_AddRef((IUnknown
*)*ppv
);
2021 if (IsEqualIID(riid
, &IID_IMarshal
))
2022 return IUnknown_QueryInterface(This
->marshal
, riid
, ppv
);
2024 WARN("Unknown interface %s\n", debugstr_guid(riid
));
2025 return E_NOINTERFACE
;
2028 static ULONG WINAPI
AudioRenderClient_AddRef(IAudioRenderClient
*iface
)
2030 ACImpl
*This
= impl_from_IAudioRenderClient(iface
);
2031 return AudioClient_AddRef(&This
->IAudioClient_iface
);
2034 static ULONG WINAPI
AudioRenderClient_Release(IAudioRenderClient
*iface
)
2036 ACImpl
*This
= impl_from_IAudioRenderClient(iface
);
2037 return AudioClient_Release(&This
->IAudioClient_iface
);
2040 static void alloc_tmp_buffer(ACImpl
*This
, UINT32 bytes
)
2042 if(This
->tmp_buffer_bytes
>= bytes
)
2045 HeapFree(GetProcessHeap(), 0, This
->tmp_buffer
);
2046 This
->tmp_buffer
= HeapAlloc(GetProcessHeap(), 0, bytes
);
2047 This
->tmp_buffer_bytes
= bytes
;
2050 static HRESULT WINAPI
AudioRenderClient_GetBuffer(IAudioRenderClient
*iface
,
2051 UINT32 frames
, BYTE
**data
)
2053 ACImpl
*This
= impl_from_IAudioRenderClient(iface
);
2054 size_t avail
, req
, bytes
= frames
* pa_frame_size(&This
->ss
);
2059 TRACE("(%p)->(%u, %p)\n", This
, frames
, data
);
2065 pthread_mutex_lock(&pulse_lock
);
2066 hr
= pulse_stream_valid(This
);
2067 if (FAILED(hr
) || This
->locked
) {
2068 pthread_mutex_unlock(&pulse_lock
);
2069 return FAILED(hr
) ? hr
: AUDCLNT_E_OUT_OF_ORDER
;
2072 pthread_mutex_unlock(&pulse_lock
);
2076 ACImpl_GetRenderPad(This
, &pad
);
2077 avail
= This
->bufsize_frames
- pad
;
2078 if (avail
< frames
|| bytes
> This
->bufsize_bytes
) {
2079 pthread_mutex_unlock(&pulse_lock
);
2080 WARN("Wanted to write %u, but only %zu available\n", frames
, avail
);
2081 return AUDCLNT_E_BUFFER_TOO_LARGE
;
2084 if(This
->local_buffer
){
2085 if(This
->wri_offs_bytes
+ bytes
> This
->bufsize_bytes
){
2086 alloc_tmp_buffer(This
, bytes
);
2087 *data
= This
->tmp_buffer
;
2088 This
->locked
= -frames
;
2090 *data
= This
->local_buffer
+ This
->wri_offs_bytes
;
2091 This
->locked
= frames
;
2095 ret
= pa_stream_begin_write(This
->stream
, &This
->locked_ptr
, &req
);
2096 if (ret
< 0 || req
< bytes
) {
2097 FIXME("%p Not using pulse locked data: %i %zu/%u %u/%u\n", This
, ret
, req
/pa_frame_size(&This
->ss
), frames
, pad
, This
->bufsize_frames
);
2099 pa_stream_cancel_write(This
->stream
);
2100 alloc_tmp_buffer(This
, bytes
);
2101 *data
= This
->tmp_buffer
;
2102 This
->locked_ptr
= NULL
;
2104 *data
= This
->locked_ptr
;
2106 This
->locked
= frames
;
2109 silence_buffer(This
->ss
.format
, *data
, bytes
);
2111 pthread_mutex_unlock(&pulse_lock
);
2116 static void pulse_wrap_buffer(ACImpl
*This
, BYTE
*buffer
, UINT32 written_bytes
)
2118 UINT32 chunk_bytes
= This
->bufsize_bytes
- This
->wri_offs_bytes
;
2120 if(written_bytes
<= chunk_bytes
){
2121 memcpy(This
->local_buffer
+ This
->wri_offs_bytes
, buffer
, written_bytes
);
2123 memcpy(This
->local_buffer
+ This
->wri_offs_bytes
, buffer
, chunk_bytes
);
2124 memcpy(This
->local_buffer
, buffer
+ chunk_bytes
,
2125 written_bytes
- chunk_bytes
);
2129 static void pulse_free_noop(void *buf
)
2133 static HRESULT WINAPI
AudioRenderClient_ReleaseBuffer(
2134 IAudioRenderClient
*iface
, UINT32 written_frames
, DWORD flags
)
2136 ACImpl
*This
= impl_from_IAudioRenderClient(iface
);
2137 UINT32 written_bytes
= written_frames
* pa_frame_size(&This
->ss
);
2139 TRACE("(%p)->(%u, %x)\n", This
, written_frames
, flags
);
2141 pthread_mutex_lock(&pulse_lock
);
2142 if (!This
->locked
|| !written_frames
) {
2143 if (This
->locked_ptr
)
2144 pa_stream_cancel_write(This
->stream
);
2146 This
->locked_ptr
= NULL
;
2147 pthread_mutex_unlock(&pulse_lock
);
2148 return written_frames
? AUDCLNT_E_OUT_OF_ORDER
: S_OK
;
2151 if (This
->locked
< written_frames
) {
2152 pthread_mutex_unlock(&pulse_lock
);
2153 return AUDCLNT_E_INVALID_SIZE
;
2156 if(This
->local_buffer
){
2159 if(This
->locked
>= 0)
2160 buffer
= This
->local_buffer
+ This
->wri_offs_bytes
;
2162 buffer
= This
->tmp_buffer
;
2164 if(flags
& AUDCLNT_BUFFERFLAGS_SILENT
)
2165 silence_buffer(This
->ss
.format
, buffer
, written_bytes
);
2167 if(This
->locked
< 0)
2168 pulse_wrap_buffer(This
, buffer
, written_bytes
);
2170 This
->wri_offs_bytes
+= written_bytes
;
2171 This
->wri_offs_bytes
%= This
->bufsize_bytes
;
2173 This
->pad
+= written_bytes
;
2174 This
->held_bytes
+= written_bytes
;
2176 if(This
->held_bytes
== This
->pad
){
2178 UINT32 to_write
= min(This
->attr
.tlength
, written_bytes
);
2180 /* nothing in PA, so send data immediately */
2182 TRACE("pre-writing %u bytes\n", to_write
);
2184 e
= pa_stream_write(This
->stream
, buffer
, to_write
, NULL
, 0, PA_SEEK_RELATIVE
);
2186 ERR("pa_stream_write failed: 0x%x\n", e
);
2188 This
->lcl_offs_bytes
+= to_write
;
2189 This
->lcl_offs_bytes
%= This
->bufsize_bytes
;
2190 This
->held_bytes
-= to_write
;
2194 if (This
->locked_ptr
) {
2195 if (flags
& AUDCLNT_BUFFERFLAGS_SILENT
)
2196 silence_buffer(This
->ss
.format
, This
->locked_ptr
, written_bytes
);
2197 pa_stream_write(This
->stream
, This
->locked_ptr
, written_bytes
, NULL
, 0, PA_SEEK_RELATIVE
);
2199 if (flags
& AUDCLNT_BUFFERFLAGS_SILENT
)
2200 silence_buffer(This
->ss
.format
, This
->tmp_buffer
, written_bytes
);
2201 pa_stream_write(This
->stream
, This
->tmp_buffer
, written_bytes
, pulse_free_noop
, 0, PA_SEEK_RELATIVE
);
2203 This
->pad
+= written_bytes
;
2206 if (!pa_stream_is_corked(This
->stream
)) {
2209 o
= pa_stream_trigger(This
->stream
, pulse_op_cb
, &success
);
2211 while(pa_operation_get_state(o
) == PA_OPERATION_RUNNING
)
2212 pthread_cond_wait(&pulse_cond
, &pulse_lock
);
2213 pa_operation_unref(o
);
2218 This
->locked_ptr
= NULL
;
2219 TRACE("Released %u, pad %zu\n", written_frames
, This
->pad
/ pa_frame_size(&This
->ss
));
2220 assert(This
->pad
<= This
->bufsize_bytes
);
2222 pthread_mutex_unlock(&pulse_lock
);
2226 static const IAudioRenderClientVtbl AudioRenderClient_Vtbl
= {
2227 AudioRenderClient_QueryInterface
,
2228 AudioRenderClient_AddRef
,
2229 AudioRenderClient_Release
,
2230 AudioRenderClient_GetBuffer
,
2231 AudioRenderClient_ReleaseBuffer
2234 static HRESULT WINAPI
AudioCaptureClient_QueryInterface(
2235 IAudioCaptureClient
*iface
, REFIID riid
, void **ppv
)
2237 ACImpl
*This
= impl_from_IAudioCaptureClient(iface
);
2238 TRACE("(%p)->(%s, %p)\n", iface
, debugstr_guid(riid
), ppv
);
2244 if (IsEqualIID(riid
, &IID_IUnknown
) ||
2245 IsEqualIID(riid
, &IID_IAudioCaptureClient
))
2248 IUnknown_AddRef((IUnknown
*)*ppv
);
2252 if (IsEqualIID(riid
, &IID_IMarshal
))
2253 return IUnknown_QueryInterface(This
->marshal
, riid
, ppv
);
2255 WARN("Unknown interface %s\n", debugstr_guid(riid
));
2256 return E_NOINTERFACE
;
2259 static ULONG WINAPI
AudioCaptureClient_AddRef(IAudioCaptureClient
*iface
)
2261 ACImpl
*This
= impl_from_IAudioCaptureClient(iface
);
2262 return IAudioClient_AddRef(&This
->IAudioClient_iface
);
2265 static ULONG WINAPI
AudioCaptureClient_Release(IAudioCaptureClient
*iface
)
2267 ACImpl
*This
= impl_from_IAudioCaptureClient(iface
);
2268 return IAudioClient_Release(&This
->IAudioClient_iface
);
2271 static HRESULT WINAPI
AudioCaptureClient_GetBuffer(IAudioCaptureClient
*iface
,
2272 BYTE
**data
, UINT32
*frames
, DWORD
*flags
, UINT64
*devpos
,
2275 ACImpl
*This
= impl_from_IAudioCaptureClient(iface
);
2279 TRACE("(%p)->(%p, %p, %p, %p, %p)\n", This
, data
, frames
, flags
,
2282 if (!data
|| !frames
|| !flags
)
2285 pthread_mutex_lock(&pulse_lock
);
2286 hr
= pulse_stream_valid(This
);
2287 if (FAILED(hr
) || This
->locked
) {
2288 pthread_mutex_unlock(&pulse_lock
);
2289 return FAILED(hr
) ? hr
: AUDCLNT_E_OUT_OF_ORDER
;
2292 ACImpl_GetCapturePad(This
, NULL
);
2293 if ((packet
= This
->locked_ptr
)) {
2294 *frames
= This
->capture_period
/ pa_frame_size(&This
->ss
);
2296 if (packet
->discont
)
2297 *flags
|= AUDCLNT_BUFFERFLAGS_DATA_DISCONTINUITY
;
2299 if (packet
->discont
)
2300 *devpos
= (This
->clock_written
+ This
->capture_period
) / pa_frame_size(&This
->ss
);
2302 *devpos
= This
->clock_written
/ pa_frame_size(&This
->ss
);
2305 *qpcpos
= packet
->qpcpos
;
2306 *data
= packet
->data
;
2310 This
->locked
= *frames
;
2311 pthread_mutex_unlock(&pulse_lock
);
2312 return *frames
? S_OK
: AUDCLNT_S_BUFFER_EMPTY
;
2315 static HRESULT WINAPI
AudioCaptureClient_ReleaseBuffer(
2316 IAudioCaptureClient
*iface
, UINT32 done
)
2318 ACImpl
*This
= impl_from_IAudioCaptureClient(iface
);
2320 TRACE("(%p)->(%u)\n", This
, done
);
2322 pthread_mutex_lock(&pulse_lock
);
2323 if (!This
->locked
&& done
) {
2324 pthread_mutex_unlock(&pulse_lock
);
2325 return AUDCLNT_E_OUT_OF_ORDER
;
2327 if (done
&& This
->locked
!= done
) {
2328 pthread_mutex_unlock(&pulse_lock
);
2329 return AUDCLNT_E_INVALID_SIZE
;
2332 ACPacket
*packet
= This
->locked_ptr
;
2333 This
->locked_ptr
= NULL
;
2334 This
->pad
-= This
->capture_period
;
2335 if (packet
->discont
)
2336 This
->clock_written
+= 2 * This
->capture_period
;
2338 This
->clock_written
+= This
->capture_period
;
2339 list_add_tail(&This
->packet_free_head
, &packet
->entry
);
2342 pthread_mutex_unlock(&pulse_lock
);
2346 static HRESULT WINAPI
AudioCaptureClient_GetNextPacketSize(
2347 IAudioCaptureClient
*iface
, UINT32
*frames
)
2349 ACImpl
*This
= impl_from_IAudioCaptureClient(iface
);
2351 TRACE("(%p)->(%p)\n", This
, frames
);
2355 pthread_mutex_lock(&pulse_lock
);
2356 ACImpl_GetCapturePad(This
, NULL
);
2357 if (This
->locked_ptr
)
2358 *frames
= This
->capture_period
/ pa_frame_size(&This
->ss
);
2361 pthread_mutex_unlock(&pulse_lock
);
2365 static const IAudioCaptureClientVtbl AudioCaptureClient_Vtbl
=
2367 AudioCaptureClient_QueryInterface
,
2368 AudioCaptureClient_AddRef
,
2369 AudioCaptureClient_Release
,
2370 AudioCaptureClient_GetBuffer
,
2371 AudioCaptureClient_ReleaseBuffer
,
2372 AudioCaptureClient_GetNextPacketSize
2375 static HRESULT WINAPI
AudioClock_QueryInterface(IAudioClock
*iface
,
2376 REFIID riid
, void **ppv
)
2378 ACImpl
*This
= impl_from_IAudioClock(iface
);
2380 TRACE("(%p)->(%s, %p)\n", iface
, debugstr_guid(riid
), ppv
);
2386 if (IsEqualIID(riid
, &IID_IUnknown
) || IsEqualIID(riid
, &IID_IAudioClock
))
2388 else if (IsEqualIID(riid
, &IID_IAudioClock2
))
2389 *ppv
= &This
->IAudioClock2_iface
;
2391 IUnknown_AddRef((IUnknown
*)*ppv
);
2395 if (IsEqualIID(riid
, &IID_IMarshal
))
2396 return IUnknown_QueryInterface(This
->marshal
, riid
, ppv
);
2398 WARN("Unknown interface %s\n", debugstr_guid(riid
));
2399 return E_NOINTERFACE
;
2402 static ULONG WINAPI
AudioClock_AddRef(IAudioClock
*iface
)
2404 ACImpl
*This
= impl_from_IAudioClock(iface
);
2405 return IAudioClient_AddRef(&This
->IAudioClient_iface
);
2408 static ULONG WINAPI
AudioClock_Release(IAudioClock
*iface
)
2410 ACImpl
*This
= impl_from_IAudioClock(iface
);
2411 return IAudioClient_Release(&This
->IAudioClient_iface
);
2414 static HRESULT WINAPI
AudioClock_GetFrequency(IAudioClock
*iface
, UINT64
*freq
)
2416 ACImpl
*This
= impl_from_IAudioClock(iface
);
2419 TRACE("(%p)->(%p)\n", This
, freq
);
2421 pthread_mutex_lock(&pulse_lock
);
2422 hr
= pulse_stream_valid(This
);
2423 if (SUCCEEDED(hr
)) {
2424 *freq
= This
->ss
.rate
;
2425 if (This
->share
== AUDCLNT_SHAREMODE_SHARED
)
2426 *freq
*= pa_frame_size(&This
->ss
);
2428 pthread_mutex_unlock(&pulse_lock
);
2432 static HRESULT WINAPI
AudioClock_GetPosition(IAudioClock
*iface
, UINT64
*pos
,
2435 ACImpl
*This
= impl_from_IAudioClock(iface
);
2438 TRACE("(%p)->(%p, %p)\n", This
, pos
, qpctime
);
2443 pthread_mutex_lock(&pulse_lock
);
2444 hr
= pulse_stream_valid(This
);
2446 pthread_mutex_unlock(&pulse_lock
);
2450 *pos
= This
->clock_written
;
2452 if (This
->share
== AUDCLNT_SHAREMODE_EXCLUSIVE
)
2453 *pos
/= pa_frame_size(&This
->ss
);
2455 /* Make time never go backwards */
2456 if (*pos
< This
->clock_lastpos
)
2457 *pos
= This
->clock_lastpos
;
2459 This
->clock_lastpos
= *pos
;
2460 pthread_mutex_unlock(&pulse_lock
);
2462 TRACE("%p Position: %u\n", This
, (unsigned)*pos
);
2465 LARGE_INTEGER stamp
, freq
;
2466 QueryPerformanceCounter(&stamp
);
2467 QueryPerformanceFrequency(&freq
);
2468 *qpctime
= (stamp
.QuadPart
* (INT64
)10000000) / freq
.QuadPart
;
2474 static HRESULT WINAPI
AudioClock_GetCharacteristics(IAudioClock
*iface
,
2477 ACImpl
*This
= impl_from_IAudioClock(iface
);
2479 TRACE("(%p)->(%p)\n", This
, chars
);
2484 *chars
= AUDIOCLOCK_CHARACTERISTIC_FIXED_FREQ
;
2489 static const IAudioClockVtbl AudioClock_Vtbl
=
2491 AudioClock_QueryInterface
,
2494 AudioClock_GetFrequency
,
2495 AudioClock_GetPosition
,
2496 AudioClock_GetCharacteristics
2499 static HRESULT WINAPI
AudioClock2_QueryInterface(IAudioClock2
*iface
,
2500 REFIID riid
, void **ppv
)
2502 ACImpl
*This
= impl_from_IAudioClock2(iface
);
2503 return IAudioClock_QueryInterface(&This
->IAudioClock_iface
, riid
, ppv
);
2506 static ULONG WINAPI
AudioClock2_AddRef(IAudioClock2
*iface
)
2508 ACImpl
*This
= impl_from_IAudioClock2(iface
);
2509 return IAudioClient_AddRef(&This
->IAudioClient_iface
);
2512 static ULONG WINAPI
AudioClock2_Release(IAudioClock2
*iface
)
2514 ACImpl
*This
= impl_from_IAudioClock2(iface
);
2515 return IAudioClient_Release(&This
->IAudioClient_iface
);
2518 static HRESULT WINAPI
AudioClock2_GetDevicePosition(IAudioClock2
*iface
,
2519 UINT64
*pos
, UINT64
*qpctime
)
2521 ACImpl
*This
= impl_from_IAudioClock2(iface
);
2522 HRESULT hr
= AudioClock_GetPosition(&This
->IAudioClock_iface
, pos
, qpctime
);
2523 if (SUCCEEDED(hr
) && This
->share
== AUDCLNT_SHAREMODE_SHARED
)
2524 *pos
/= pa_frame_size(&This
->ss
);
2528 static const IAudioClock2Vtbl AudioClock2_Vtbl
=
2530 AudioClock2_QueryInterface
,
2532 AudioClock2_Release
,
2533 AudioClock2_GetDevicePosition
2536 static HRESULT WINAPI
AudioStreamVolume_QueryInterface(
2537 IAudioStreamVolume
*iface
, REFIID riid
, void **ppv
)
2539 ACImpl
*This
= impl_from_IAudioStreamVolume(iface
);
2541 TRACE("(%p)->(%s, %p)\n", iface
, debugstr_guid(riid
), ppv
);
2547 if (IsEqualIID(riid
, &IID_IUnknown
) ||
2548 IsEqualIID(riid
, &IID_IAudioStreamVolume
))
2551 IUnknown_AddRef((IUnknown
*)*ppv
);
2555 if (IsEqualIID(riid
, &IID_IMarshal
))
2556 return IUnknown_QueryInterface(This
->marshal
, riid
, ppv
);
2558 WARN("Unknown interface %s\n", debugstr_guid(riid
));
2559 return E_NOINTERFACE
;
2562 static ULONG WINAPI
AudioStreamVolume_AddRef(IAudioStreamVolume
*iface
)
2564 ACImpl
*This
= impl_from_IAudioStreamVolume(iface
);
2565 return IAudioClient_AddRef(&This
->IAudioClient_iface
);
2568 static ULONG WINAPI
AudioStreamVolume_Release(IAudioStreamVolume
*iface
)
2570 ACImpl
*This
= impl_from_IAudioStreamVolume(iface
);
2571 return IAudioClient_Release(&This
->IAudioClient_iface
);
2574 static HRESULT WINAPI
AudioStreamVolume_GetChannelCount(
2575 IAudioStreamVolume
*iface
, UINT32
*out
)
2577 ACImpl
*This
= impl_from_IAudioStreamVolume(iface
);
2579 TRACE("(%p)->(%p)\n", This
, out
);
2584 *out
= This
->ss
.channels
;
2589 struct pulse_info_cb_data
{
2594 static HRESULT WINAPI
AudioStreamVolume_SetAllVolumes(
2595 IAudioStreamVolume
*iface
, UINT32 count
, const float *levels
)
2597 ACImpl
*This
= impl_from_IAudioStreamVolume(iface
);
2601 TRACE("(%p)->(%d, %p)\n", This
, count
, levels
);
2606 if (count
!= This
->ss
.channels
)
2607 return E_INVALIDARG
;
2609 pthread_mutex_lock(&pulse_lock
);
2610 hr
= pulse_stream_valid(This
);
2614 for (i
= 0; i
< count
; ++i
)
2615 This
->vol
[i
] = levels
[i
];
2618 pthread_mutex_unlock(&pulse_lock
);
2622 static HRESULT WINAPI
AudioStreamVolume_GetAllVolumes(
2623 IAudioStreamVolume
*iface
, UINT32 count
, float *levels
)
2625 ACImpl
*This
= impl_from_IAudioStreamVolume(iface
);
2629 TRACE("(%p)->(%d, %p)\n", This
, count
, levels
);
2634 if (count
!= This
->ss
.channels
)
2635 return E_INVALIDARG
;
2637 pthread_mutex_lock(&pulse_lock
);
2638 hr
= pulse_stream_valid(This
);
2642 for (i
= 0; i
< count
; ++i
)
2643 levels
[i
] = This
->vol
[i
];
2646 pthread_mutex_unlock(&pulse_lock
);
2650 static HRESULT WINAPI
AudioStreamVolume_SetChannelVolume(
2651 IAudioStreamVolume
*iface
, UINT32 index
, float level
)
2653 ACImpl
*This
= impl_from_IAudioStreamVolume(iface
);
2655 float volumes
[PA_CHANNELS_MAX
];
2657 TRACE("(%p)->(%d, %f)\n", This
, index
, level
);
2659 if (level
< 0.f
|| level
> 1.f
)
2660 return E_INVALIDARG
;
2662 if (index
>= This
->ss
.channels
)
2663 return E_INVALIDARG
;
2665 hr
= AudioStreamVolume_GetAllVolumes(iface
, This
->ss
.channels
, volumes
);
2666 volumes
[index
] = level
;
2668 hr
= AudioStreamVolume_SetAllVolumes(iface
, This
->ss
.channels
, volumes
);
2672 static HRESULT WINAPI
AudioStreamVolume_GetChannelVolume(
2673 IAudioStreamVolume
*iface
, UINT32 index
, float *level
)
2675 ACImpl
*This
= impl_from_IAudioStreamVolume(iface
);
2676 float volumes
[PA_CHANNELS_MAX
];
2679 TRACE("(%p)->(%d, %p)\n", This
, index
, level
);
2684 if (index
>= This
->ss
.channels
)
2685 return E_INVALIDARG
;
2687 hr
= AudioStreamVolume_GetAllVolumes(iface
, This
->ss
.channels
, volumes
);
2689 *level
= volumes
[index
];
2693 static const IAudioStreamVolumeVtbl AudioStreamVolume_Vtbl
=
2695 AudioStreamVolume_QueryInterface
,
2696 AudioStreamVolume_AddRef
,
2697 AudioStreamVolume_Release
,
2698 AudioStreamVolume_GetChannelCount
,
2699 AudioStreamVolume_SetChannelVolume
,
2700 AudioStreamVolume_GetChannelVolume
,
2701 AudioStreamVolume_SetAllVolumes
,
2702 AudioStreamVolume_GetAllVolumes
2705 static AudioSessionWrapper
*AudioSessionWrapper_Create(ACImpl
*client
)
2707 AudioSessionWrapper
*ret
;
2709 ret
= HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY
,
2710 sizeof(AudioSessionWrapper
));
2714 ret
->IAudioSessionControl2_iface
.lpVtbl
= &AudioSessionControl2_Vtbl
;
2715 ret
->ISimpleAudioVolume_iface
.lpVtbl
= &SimpleAudioVolume_Vtbl
;
2716 ret
->IChannelAudioVolume_iface
.lpVtbl
= &ChannelAudioVolume_Vtbl
;
2720 ret
->client
= client
;
2722 ret
->session
= client
->session
;
2723 AudioClient_AddRef(&client
->IAudioClient_iface
);
2729 static HRESULT WINAPI
AudioSessionControl_QueryInterface(
2730 IAudioSessionControl2
*iface
, REFIID riid
, void **ppv
)
2732 TRACE("(%p)->(%s, %p)\n", iface
, debugstr_guid(riid
), ppv
);
2738 if (IsEqualIID(riid
, &IID_IUnknown
) ||
2739 IsEqualIID(riid
, &IID_IAudioSessionControl
) ||
2740 IsEqualIID(riid
, &IID_IAudioSessionControl2
))
2743 IUnknown_AddRef((IUnknown
*)*ppv
);
2747 WARN("Unknown interface %s\n", debugstr_guid(riid
));
2748 return E_NOINTERFACE
;
2751 static ULONG WINAPI
AudioSessionControl_AddRef(IAudioSessionControl2
*iface
)
2753 AudioSessionWrapper
*This
= impl_from_IAudioSessionControl2(iface
);
2755 ref
= InterlockedIncrement(&This
->ref
);
2756 TRACE("(%p) Refcount now %u\n", This
, ref
);
2760 static ULONG WINAPI
AudioSessionControl_Release(IAudioSessionControl2
*iface
)
2762 AudioSessionWrapper
*This
= impl_from_IAudioSessionControl2(iface
);
2764 ref
= InterlockedDecrement(&This
->ref
);
2765 TRACE("(%p) Refcount now %u\n", This
, ref
);
2768 This
->client
->session_wrapper
= NULL
;
2769 AudioClient_Release(&This
->client
->IAudioClient_iface
);
2771 HeapFree(GetProcessHeap(), 0, This
);
2776 static HRESULT WINAPI
AudioSessionControl_GetState(IAudioSessionControl2
*iface
,
2777 AudioSessionState
*state
)
2779 AudioSessionWrapper
*This
= impl_from_IAudioSessionControl2(iface
);
2782 TRACE("(%p)->(%p)\n", This
, state
);
2785 return NULL_PTR_ERR
;
2787 pthread_mutex_lock(&pulse_lock
);
2788 if (list_empty(&This
->session
->clients
)) {
2789 *state
= AudioSessionStateExpired
;
2792 LIST_FOR_EACH_ENTRY(client
, &This
->session
->clients
, ACImpl
, entry
) {
2793 if (client
->started
) {
2794 *state
= AudioSessionStateActive
;
2798 *state
= AudioSessionStateInactive
;
2801 pthread_mutex_unlock(&pulse_lock
);
2805 static HRESULT WINAPI
AudioSessionControl_GetDisplayName(
2806 IAudioSessionControl2
*iface
, WCHAR
**name
)
2808 AudioSessionWrapper
*This
= impl_from_IAudioSessionControl2(iface
);
2810 FIXME("(%p)->(%p) - stub\n", This
, name
);
2815 static HRESULT WINAPI
AudioSessionControl_SetDisplayName(
2816 IAudioSessionControl2
*iface
, const WCHAR
*name
, const GUID
*session
)
2818 AudioSessionWrapper
*This
= impl_from_IAudioSessionControl2(iface
);
2820 FIXME("(%p)->(%p, %s) - stub\n", This
, name
, debugstr_guid(session
));
2825 static HRESULT WINAPI
AudioSessionControl_GetIconPath(
2826 IAudioSessionControl2
*iface
, WCHAR
**path
)
2828 AudioSessionWrapper
*This
= impl_from_IAudioSessionControl2(iface
);
2830 FIXME("(%p)->(%p) - stub\n", This
, path
);
2835 static HRESULT WINAPI
AudioSessionControl_SetIconPath(
2836 IAudioSessionControl2
*iface
, const WCHAR
*path
, const GUID
*session
)
2838 AudioSessionWrapper
*This
= impl_from_IAudioSessionControl2(iface
);
2840 FIXME("(%p)->(%p, %s) - stub\n", This
, path
, debugstr_guid(session
));
2845 static HRESULT WINAPI
AudioSessionControl_GetGroupingParam(
2846 IAudioSessionControl2
*iface
, GUID
*group
)
2848 AudioSessionWrapper
*This
= impl_from_IAudioSessionControl2(iface
);
2850 FIXME("(%p)->(%p) - stub\n", This
, group
);
2855 static HRESULT WINAPI
AudioSessionControl_SetGroupingParam(
2856 IAudioSessionControl2
*iface
, const GUID
*group
, const GUID
*session
)
2858 AudioSessionWrapper
*This
= impl_from_IAudioSessionControl2(iface
);
2860 FIXME("(%p)->(%s, %s) - stub\n", This
, debugstr_guid(group
),
2861 debugstr_guid(session
));
2866 static HRESULT WINAPI
AudioSessionControl_RegisterAudioSessionNotification(
2867 IAudioSessionControl2
*iface
, IAudioSessionEvents
*events
)
2869 AudioSessionWrapper
*This
= impl_from_IAudioSessionControl2(iface
);
2871 FIXME("(%p)->(%p) - stub\n", This
, events
);
2876 static HRESULT WINAPI
AudioSessionControl_UnregisterAudioSessionNotification(
2877 IAudioSessionControl2
*iface
, IAudioSessionEvents
*events
)
2879 AudioSessionWrapper
*This
= impl_from_IAudioSessionControl2(iface
);
2881 FIXME("(%p)->(%p) - stub\n", This
, events
);
2886 static HRESULT WINAPI
AudioSessionControl_GetSessionIdentifier(
2887 IAudioSessionControl2
*iface
, WCHAR
**id
)
2889 AudioSessionWrapper
*This
= impl_from_IAudioSessionControl2(iface
);
2891 FIXME("(%p)->(%p) - stub\n", This
, id
);
2896 static HRESULT WINAPI
AudioSessionControl_GetSessionInstanceIdentifier(
2897 IAudioSessionControl2
*iface
, WCHAR
**id
)
2899 AudioSessionWrapper
*This
= impl_from_IAudioSessionControl2(iface
);
2901 FIXME("(%p)->(%p) - stub\n", This
, id
);
2906 static HRESULT WINAPI
AudioSessionControl_GetProcessId(
2907 IAudioSessionControl2
*iface
, DWORD
*pid
)
2909 AudioSessionWrapper
*This
= impl_from_IAudioSessionControl2(iface
);
2911 TRACE("(%p)->(%p)\n", This
, pid
);
2916 *pid
= GetCurrentProcessId();
2921 static HRESULT WINAPI
AudioSessionControl_IsSystemSoundsSession(
2922 IAudioSessionControl2
*iface
)
2924 AudioSessionWrapper
*This
= impl_from_IAudioSessionControl2(iface
);
2926 TRACE("(%p)\n", This
);
2931 static HRESULT WINAPI
AudioSessionControl_SetDuckingPreference(
2932 IAudioSessionControl2
*iface
, BOOL optout
)
2934 AudioSessionWrapper
*This
= impl_from_IAudioSessionControl2(iface
);
2936 TRACE("(%p)->(%d)\n", This
, optout
);
2941 static const IAudioSessionControl2Vtbl AudioSessionControl2_Vtbl
=
2943 AudioSessionControl_QueryInterface
,
2944 AudioSessionControl_AddRef
,
2945 AudioSessionControl_Release
,
2946 AudioSessionControl_GetState
,
2947 AudioSessionControl_GetDisplayName
,
2948 AudioSessionControl_SetDisplayName
,
2949 AudioSessionControl_GetIconPath
,
2950 AudioSessionControl_SetIconPath
,
2951 AudioSessionControl_GetGroupingParam
,
2952 AudioSessionControl_SetGroupingParam
,
2953 AudioSessionControl_RegisterAudioSessionNotification
,
2954 AudioSessionControl_UnregisterAudioSessionNotification
,
2955 AudioSessionControl_GetSessionIdentifier
,
2956 AudioSessionControl_GetSessionInstanceIdentifier
,
2957 AudioSessionControl_GetProcessId
,
2958 AudioSessionControl_IsSystemSoundsSession
,
2959 AudioSessionControl_SetDuckingPreference
2962 typedef struct _SessionMgr
{
2963 IAudioSessionManager2 IAudioSessionManager2_iface
;
2970 static HRESULT WINAPI
AudioSessionManager_QueryInterface(IAudioSessionManager2
*iface
,
2971 REFIID riid
, void **ppv
)
2973 TRACE("(%p)->(%s, %p)\n", iface
, debugstr_guid(riid
), ppv
);
2979 if (IsEqualIID(riid
, &IID_IUnknown
) ||
2980 IsEqualIID(riid
, &IID_IAudioSessionManager
) ||
2981 IsEqualIID(riid
, &IID_IAudioSessionManager2
))
2984 IUnknown_AddRef((IUnknown
*)*ppv
);
2988 WARN("Unknown interface %s\n", debugstr_guid(riid
));
2989 return E_NOINTERFACE
;
2992 static inline SessionMgr
*impl_from_IAudioSessionManager2(IAudioSessionManager2
*iface
)
2994 return CONTAINING_RECORD(iface
, SessionMgr
, IAudioSessionManager2_iface
);
2997 static ULONG WINAPI
AudioSessionManager_AddRef(IAudioSessionManager2
*iface
)
2999 SessionMgr
*This
= impl_from_IAudioSessionManager2(iface
);
3001 ref
= InterlockedIncrement(&This
->ref
);
3002 TRACE("(%p) Refcount now %u\n", This
, ref
);
3006 static ULONG WINAPI
AudioSessionManager_Release(IAudioSessionManager2
*iface
)
3008 SessionMgr
*This
= impl_from_IAudioSessionManager2(iface
);
3010 ref
= InterlockedDecrement(&This
->ref
);
3011 TRACE("(%p) Refcount now %u\n", This
, ref
);
3013 HeapFree(GetProcessHeap(), 0, This
);
3017 static HRESULT WINAPI
AudioSessionManager_GetAudioSessionControl(
3018 IAudioSessionManager2
*iface
, const GUID
*session_guid
, DWORD flags
,
3019 IAudioSessionControl
**out
)
3021 SessionMgr
*This
= impl_from_IAudioSessionManager2(iface
);
3022 AudioSession
*session
;
3023 AudioSessionWrapper
*wrapper
;
3026 TRACE("(%p)->(%s, %x, %p)\n", This
, debugstr_guid(session_guid
),
3029 hr
= get_audio_session(session_guid
, This
->device
, 0, &session
);
3033 wrapper
= AudioSessionWrapper_Create(NULL
);
3035 return E_OUTOFMEMORY
;
3037 wrapper
->session
= session
;
3039 *out
= (IAudioSessionControl
*)&wrapper
->IAudioSessionControl2_iface
;
3044 static HRESULT WINAPI
AudioSessionManager_GetSimpleAudioVolume(
3045 IAudioSessionManager2
*iface
, const GUID
*session_guid
, DWORD flags
,
3046 ISimpleAudioVolume
**out
)
3048 SessionMgr
*This
= impl_from_IAudioSessionManager2(iface
);
3049 AudioSession
*session
;
3050 AudioSessionWrapper
*wrapper
;
3053 TRACE("(%p)->(%s, %x, %p)\n", This
, debugstr_guid(session_guid
),
3056 hr
= get_audio_session(session_guid
, This
->device
, 0, &session
);
3060 wrapper
= AudioSessionWrapper_Create(NULL
);
3062 return E_OUTOFMEMORY
;
3064 wrapper
->session
= session
;
3066 *out
= &wrapper
->ISimpleAudioVolume_iface
;
3071 static HRESULT WINAPI
AudioSessionManager_GetSessionEnumerator(
3072 IAudioSessionManager2
*iface
, IAudioSessionEnumerator
**out
)
3074 SessionMgr
*This
= impl_from_IAudioSessionManager2(iface
);
3075 FIXME("(%p)->(%p) - stub\n", This
, out
);
3079 static HRESULT WINAPI
AudioSessionManager_RegisterSessionNotification(
3080 IAudioSessionManager2
*iface
, IAudioSessionNotification
*notification
)
3082 SessionMgr
*This
= impl_from_IAudioSessionManager2(iface
);
3083 FIXME("(%p)->(%p) - stub\n", This
, notification
);
3087 static HRESULT WINAPI
AudioSessionManager_UnregisterSessionNotification(
3088 IAudioSessionManager2
*iface
, IAudioSessionNotification
*notification
)
3090 SessionMgr
*This
= impl_from_IAudioSessionManager2(iface
);
3091 FIXME("(%p)->(%p) - stub\n", This
, notification
);
3095 static HRESULT WINAPI
AudioSessionManager_RegisterDuckNotification(
3096 IAudioSessionManager2
*iface
, const WCHAR
*session_id
,
3097 IAudioVolumeDuckNotification
*notification
)
3099 SessionMgr
*This
= impl_from_IAudioSessionManager2(iface
);
3100 FIXME("(%p)->(%p) - stub\n", This
, notification
);
3104 static HRESULT WINAPI
AudioSessionManager_UnregisterDuckNotification(
3105 IAudioSessionManager2
*iface
,
3106 IAudioVolumeDuckNotification
*notification
)
3108 SessionMgr
*This
= impl_from_IAudioSessionManager2(iface
);
3109 FIXME("(%p)->(%p) - stub\n", This
, notification
);
3113 static const IAudioSessionManager2Vtbl AudioSessionManager2_Vtbl
=
3115 AudioSessionManager_QueryInterface
,
3116 AudioSessionManager_AddRef
,
3117 AudioSessionManager_Release
,
3118 AudioSessionManager_GetAudioSessionControl
,
3119 AudioSessionManager_GetSimpleAudioVolume
,
3120 AudioSessionManager_GetSessionEnumerator
,
3121 AudioSessionManager_RegisterSessionNotification
,
3122 AudioSessionManager_UnregisterSessionNotification
,
3123 AudioSessionManager_RegisterDuckNotification
,
3124 AudioSessionManager_UnregisterDuckNotification
3127 static HRESULT WINAPI
SimpleAudioVolume_QueryInterface(
3128 ISimpleAudioVolume
*iface
, REFIID riid
, void **ppv
)
3130 TRACE("(%p)->(%s, %p)\n", iface
, debugstr_guid(riid
), ppv
);
3136 if (IsEqualIID(riid
, &IID_IUnknown
) ||
3137 IsEqualIID(riid
, &IID_ISimpleAudioVolume
))
3140 IUnknown_AddRef((IUnknown
*)*ppv
);
3144 WARN("Unknown interface %s\n", debugstr_guid(riid
));
3145 return E_NOINTERFACE
;
3148 static ULONG WINAPI
SimpleAudioVolume_AddRef(ISimpleAudioVolume
*iface
)
3150 AudioSessionWrapper
*This
= impl_from_ISimpleAudioVolume(iface
);
3151 return AudioSessionControl_AddRef(&This
->IAudioSessionControl2_iface
);
3154 static ULONG WINAPI
SimpleAudioVolume_Release(ISimpleAudioVolume
*iface
)
3156 AudioSessionWrapper
*This
= impl_from_ISimpleAudioVolume(iface
);
3157 return AudioSessionControl_Release(&This
->IAudioSessionControl2_iface
);
3160 static HRESULT WINAPI
SimpleAudioVolume_SetMasterVolume(
3161 ISimpleAudioVolume
*iface
, float level
, const GUID
*context
)
3163 AudioSessionWrapper
*This
= impl_from_ISimpleAudioVolume(iface
);
3164 AudioSession
*session
= This
->session
;
3166 TRACE("(%p)->(%f, %s)\n", session
, level
, wine_dbgstr_guid(context
));
3168 if (level
< 0.f
|| level
> 1.f
)
3169 return E_INVALIDARG
;
3172 FIXME("Notifications not supported yet\n");
3174 TRACE("PulseAudio does not support session volume control\n");
3176 pthread_mutex_lock(&pulse_lock
);
3177 session
->master_vol
= level
;
3178 pthread_mutex_unlock(&pulse_lock
);
3183 static HRESULT WINAPI
SimpleAudioVolume_GetMasterVolume(
3184 ISimpleAudioVolume
*iface
, float *level
)
3186 AudioSessionWrapper
*This
= impl_from_ISimpleAudioVolume(iface
);
3187 AudioSession
*session
= This
->session
;
3189 TRACE("(%p)->(%p)\n", session
, level
);
3192 return NULL_PTR_ERR
;
3194 *level
= session
->master_vol
;
3199 static HRESULT WINAPI
SimpleAudioVolume_SetMute(ISimpleAudioVolume
*iface
,
3200 BOOL mute
, const GUID
*context
)
3202 AudioSessionWrapper
*This
= impl_from_ISimpleAudioVolume(iface
);
3203 AudioSession
*session
= This
->session
;
3205 TRACE("(%p)->(%u, %s)\n", session
, mute
, debugstr_guid(context
));
3208 FIXME("Notifications not supported yet\n");
3210 session
->mute
= mute
;
3215 static HRESULT WINAPI
SimpleAudioVolume_GetMute(ISimpleAudioVolume
*iface
,
3218 AudioSessionWrapper
*This
= impl_from_ISimpleAudioVolume(iface
);
3219 AudioSession
*session
= This
->session
;
3221 TRACE("(%p)->(%p)\n", session
, mute
);
3224 return NULL_PTR_ERR
;
3226 *mute
= session
->mute
;
3231 static const ISimpleAudioVolumeVtbl SimpleAudioVolume_Vtbl
=
3233 SimpleAudioVolume_QueryInterface
,
3234 SimpleAudioVolume_AddRef
,
3235 SimpleAudioVolume_Release
,
3236 SimpleAudioVolume_SetMasterVolume
,
3237 SimpleAudioVolume_GetMasterVolume
,
3238 SimpleAudioVolume_SetMute
,
3239 SimpleAudioVolume_GetMute
3242 static HRESULT WINAPI
ChannelAudioVolume_QueryInterface(
3243 IChannelAudioVolume
*iface
, REFIID riid
, void **ppv
)
3245 TRACE("(%p)->(%s, %p)\n", iface
, debugstr_guid(riid
), ppv
);
3251 if (IsEqualIID(riid
, &IID_IUnknown
) ||
3252 IsEqualIID(riid
, &IID_IChannelAudioVolume
))
3255 IUnknown_AddRef((IUnknown
*)*ppv
);
3259 WARN("Unknown interface %s\n", debugstr_guid(riid
));
3260 return E_NOINTERFACE
;
3263 static ULONG WINAPI
ChannelAudioVolume_AddRef(IChannelAudioVolume
*iface
)
3265 AudioSessionWrapper
*This
= impl_from_IChannelAudioVolume(iface
);
3266 return AudioSessionControl_AddRef(&This
->IAudioSessionControl2_iface
);
3269 static ULONG WINAPI
ChannelAudioVolume_Release(IChannelAudioVolume
*iface
)
3271 AudioSessionWrapper
*This
= impl_from_IChannelAudioVolume(iface
);
3272 return AudioSessionControl_Release(&This
->IAudioSessionControl2_iface
);
3275 static HRESULT WINAPI
ChannelAudioVolume_GetChannelCount(
3276 IChannelAudioVolume
*iface
, UINT32
*out
)
3278 AudioSessionWrapper
*This
= impl_from_IChannelAudioVolume(iface
);
3279 AudioSession
*session
= This
->session
;
3281 TRACE("(%p)->(%p)\n", session
, out
);
3284 return NULL_PTR_ERR
;
3286 *out
= session
->channel_count
;
3291 static HRESULT WINAPI
ChannelAudioVolume_SetChannelVolume(
3292 IChannelAudioVolume
*iface
, UINT32 index
, float level
,
3293 const GUID
*context
)
3295 AudioSessionWrapper
*This
= impl_from_IChannelAudioVolume(iface
);
3296 AudioSession
*session
= This
->session
;
3298 TRACE("(%p)->(%d, %f, %s)\n", session
, index
, level
,
3299 wine_dbgstr_guid(context
));
3301 if (level
< 0.f
|| level
> 1.f
)
3302 return E_INVALIDARG
;
3304 if (index
>= session
->channel_count
)
3305 return E_INVALIDARG
;
3308 FIXME("Notifications not supported yet\n");
3310 TRACE("PulseAudio does not support session volume control\n");
3312 pthread_mutex_lock(&pulse_lock
);
3313 session
->channel_vols
[index
] = level
;
3314 pthread_mutex_unlock(&pulse_lock
);
3319 static HRESULT WINAPI
ChannelAudioVolume_GetChannelVolume(
3320 IChannelAudioVolume
*iface
, UINT32 index
, float *level
)
3322 AudioSessionWrapper
*This
= impl_from_IChannelAudioVolume(iface
);
3323 AudioSession
*session
= This
->session
;
3325 TRACE("(%p)->(%d, %p)\n", session
, index
, level
);
3328 return NULL_PTR_ERR
;
3330 if (index
>= session
->channel_count
)
3331 return E_INVALIDARG
;
3333 *level
= session
->channel_vols
[index
];
3338 static HRESULT WINAPI
ChannelAudioVolume_SetAllVolumes(
3339 IChannelAudioVolume
*iface
, UINT32 count
, const float *levels
,
3340 const GUID
*context
)
3342 AudioSessionWrapper
*This
= impl_from_IChannelAudioVolume(iface
);
3343 AudioSession
*session
= This
->session
;
3346 TRACE("(%p)->(%d, %p, %s)\n", session
, count
, levels
,
3347 wine_dbgstr_guid(context
));
3350 return NULL_PTR_ERR
;
3352 if (count
!= session
->channel_count
)
3353 return E_INVALIDARG
;
3356 FIXME("Notifications not supported yet\n");
3358 TRACE("PulseAudio does not support session volume control\n");
3360 pthread_mutex_lock(&pulse_lock
);
3361 for(i
= 0; i
< count
; ++i
)
3362 session
->channel_vols
[i
] = levels
[i
];
3363 pthread_mutex_unlock(&pulse_lock
);
3367 static HRESULT WINAPI
ChannelAudioVolume_GetAllVolumes(
3368 IChannelAudioVolume
*iface
, UINT32 count
, float *levels
)
3370 AudioSessionWrapper
*This
= impl_from_IChannelAudioVolume(iface
);
3371 AudioSession
*session
= This
->session
;
3374 TRACE("(%p)->(%d, %p)\n", session
, count
, levels
);
3377 return NULL_PTR_ERR
;
3379 if (count
!= session
->channel_count
)
3380 return E_INVALIDARG
;
3382 for(i
= 0; i
< count
; ++i
)
3383 levels
[i
] = session
->channel_vols
[i
];
3388 static const IChannelAudioVolumeVtbl ChannelAudioVolume_Vtbl
=
3390 ChannelAudioVolume_QueryInterface
,
3391 ChannelAudioVolume_AddRef
,
3392 ChannelAudioVolume_Release
,
3393 ChannelAudioVolume_GetChannelCount
,
3394 ChannelAudioVolume_SetChannelVolume
,
3395 ChannelAudioVolume_GetChannelVolume
,
3396 ChannelAudioVolume_SetAllVolumes
,
3397 ChannelAudioVolume_GetAllVolumes
3400 HRESULT WINAPI
AUDDRV_GetAudioSessionManager(IMMDevice
*device
,
3401 IAudioSessionManager2
**out
)
3403 SessionMgr
*This
= HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY
, sizeof(SessionMgr
));
3406 return E_OUTOFMEMORY
;
3407 This
->IAudioSessionManager2_iface
.lpVtbl
= &AudioSessionManager2_Vtbl
;
3408 This
->device
= device
;
3410 *out
= &This
->IAudioSessionManager2_iface
;
3414 HRESULT WINAPI
AUDDRV_GetPropValue(GUID
*guid
, const PROPERTYKEY
*prop
, PROPVARIANT
*out
)
3416 TRACE("%s, (%s,%u), %p\n", wine_dbgstr_guid(guid
), wine_dbgstr_guid(&prop
->fmtid
), prop
->pid
, out
);
3418 if (IsEqualGUID(guid
, &pulse_render_guid
) && IsEqualPropertyKey(*prop
, PKEY_AudioEndpoint_PhysicalSpeakers
)) {
3420 out
->u
.ulVal
= g_phys_speakers_mask
;
3422 return out
->u
.ulVal
? S_OK
: E_FAIL
;