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
)
417 pa_stream_unref(stream
);
420 pulse_def_period
[!render
] = pulse_min_period
[!render
] = pa_bytes_to_usec(10 * length
, &ss
);
422 if (pulse_min_period
[!render
] < MinimumPeriod
)
423 pulse_min_period
[!render
] = MinimumPeriod
;
425 if (pulse_def_period
[!render
] < DefaultPeriod
)
426 pulse_def_period
[!render
] = DefaultPeriod
;
428 wfx
->wFormatTag
= WAVE_FORMAT_EXTENSIBLE
;
429 wfx
->cbSize
= sizeof(WAVEFORMATEXTENSIBLE
) - sizeof(WAVEFORMATEX
);
430 wfx
->nChannels
= ss
.channels
;
431 wfx
->wBitsPerSample
= 8 * pa_sample_size_of_format(ss
.format
);
432 wfx
->nSamplesPerSec
= ss
.rate
;
433 wfx
->nBlockAlign
= pa_frame_size(&ss
);
434 wfx
->nAvgBytesPerSec
= wfx
->nSamplesPerSec
* wfx
->nBlockAlign
;
435 if (ss
.format
!= PA_SAMPLE_S24_32LE
)
436 fmt
->Samples
.wValidBitsPerSample
= wfx
->wBitsPerSample
;
438 fmt
->Samples
.wValidBitsPerSample
= 24;
439 if (ss
.format
== PA_SAMPLE_FLOAT32LE
)
440 fmt
->SubFormat
= KSDATAFORMAT_SUBTYPE_IEEE_FLOAT
;
442 fmt
->SubFormat
= KSDATAFORMAT_SUBTYPE_PCM
;
444 fmt
->dwChannelMask
= pulse_channel_map_to_channel_mask(&map
);
447 static HRESULT
pulse_connect(void)
450 WCHAR path
[MAX_PATH
], *name
;
455 if (!(pulse_thread
= CreateThread(NULL
, 0, pulse_mainloop_thread
, NULL
, 0, NULL
)))
457 ERR("Failed to create mainloop thread.\n");
460 SetThreadPriority(pulse_thread
, THREAD_PRIORITY_TIME_CRITICAL
);
461 pthread_cond_wait(&pulse_cond
, &pulse_lock
);
464 if (pulse_ctx
&& PA_CONTEXT_IS_GOOD(pa_context_get_state(pulse_ctx
)))
467 pa_context_unref(pulse_ctx
);
469 GetModuleFileNameW(NULL
, path
, sizeof(path
)/sizeof(*path
));
470 name
= strrchrW(path
, '\\');
475 len
= WideCharToMultiByte(CP_UNIXCP
, 0, name
, -1, NULL
, 0, NULL
, NULL
);
476 str
= pa_xmalloc(len
);
477 WideCharToMultiByte(CP_UNIXCP
, 0, name
, -1, str
, len
, NULL
, NULL
);
478 TRACE("Name: %s\n", str
);
479 pulse_ctx
= pa_context_new(pa_mainloop_get_api(pulse_ml
), str
);
482 ERR("Failed to create context\n");
486 pa_context_set_state_callback(pulse_ctx
, pulse_contextcallback
, NULL
);
488 TRACE("libpulse protocol version: %u. API Version %u\n", pa_context_get_protocol_version(pulse_ctx
), PA_API_VERSION
);
489 if (pa_context_connect(pulse_ctx
, NULL
, 0, NULL
) < 0)
492 /* Wait for connection */
493 while (pthread_cond_wait(&pulse_cond
, &pulse_lock
)) {
494 pa_context_state_t state
= pa_context_get_state(pulse_ctx
);
496 if (state
== PA_CONTEXT_FAILED
|| state
== PA_CONTEXT_TERMINATED
)
499 if (state
== PA_CONTEXT_READY
)
503 TRACE("Connected to server %s with protocol version: %i.\n",
504 pa_context_get_server(pulse_ctx
),
505 pa_context_get_server_protocol_version(pulse_ctx
));
509 pa_context_unref(pulse_ctx
);
514 /* For default PulseAudio render device, OR together all of the
515 * PKEY_AudioEndpoint_PhysicalSpeakers values of the sinks. */
516 static void pulse_phys_speakers_cb(pa_context
*c
, const pa_sink_info
*i
, int eol
, void *userdata
)
519 g_phys_speakers_mask
|= pulse_channel_map_to_channel_mask(&i
->channel_map
);
522 /* some poorly-behaved applications call audio functions during DllMain, so we
523 * have to do as much as possible without creating a new thread. this function
524 * sets up a synchronous connection to verify the server is running and query
526 static HRESULT
pulse_test_connect(void)
529 WCHAR path
[MAX_PATH
], *name
;
533 pulse_ml
= pa_mainloop_new();
535 pa_mainloop_set_poll_func(pulse_ml
, pulse_poll_func
, NULL
);
537 GetModuleFileNameW(NULL
, path
, sizeof(path
)/sizeof(*path
));
538 name
= strrchrW(path
, '\\');
543 len
= WideCharToMultiByte(CP_UNIXCP
, 0, name
, -1, NULL
, 0, NULL
, NULL
);
544 str
= pa_xmalloc(len
);
545 WideCharToMultiByte(CP_UNIXCP
, 0, name
, -1, str
, len
, NULL
, NULL
);
546 TRACE("Name: %s\n", str
);
547 pulse_ctx
= pa_context_new(pa_mainloop_get_api(pulse_ml
), str
);
550 ERR("Failed to create context\n");
551 pa_mainloop_free(pulse_ml
);
556 pa_context_set_state_callback(pulse_ctx
, pulse_contextcallback
, NULL
);
558 TRACE("libpulse protocol version: %u. API Version %u\n", pa_context_get_protocol_version(pulse_ctx
), PA_API_VERSION
);
559 if (pa_context_connect(pulse_ctx
, NULL
, 0, NULL
) < 0)
562 /* Wait for connection */
563 while (pa_mainloop_iterate(pulse_ml
, 1, &ret
) >= 0) {
564 pa_context_state_t state
= pa_context_get_state(pulse_ctx
);
566 if (state
== PA_CONTEXT_FAILED
|| state
== PA_CONTEXT_TERMINATED
)
569 if (state
== PA_CONTEXT_READY
)
573 TRACE("Test-connected to server %s with protocol version: %i.\n",
574 pa_context_get_server(pulse_ctx
),
575 pa_context_get_server_protocol_version(pulse_ctx
));
577 pulse_probe_settings(1, &pulse_fmt
[0]);
578 pulse_probe_settings(0, &pulse_fmt
[1]);
580 g_phys_speakers_mask
= 0;
581 o
= pa_context_get_sink_info_list(pulse_ctx
, &pulse_phys_speakers_cb
, NULL
);
583 while (pa_mainloop_iterate(pulse_ml
, 1, &ret
) >= 0 &&
584 pa_operation_get_state(o
) == PA_OPERATION_RUNNING
)
586 pa_operation_unref(o
);
589 pa_context_unref(pulse_ctx
);
591 pa_mainloop_free(pulse_ml
);
597 pa_context_unref(pulse_ctx
);
599 pa_mainloop_free(pulse_ml
);
605 static HRESULT
pulse_stream_valid(ACImpl
*This
) {
607 return AUDCLNT_E_NOT_INITIALIZED
;
608 if (!This
->stream
|| pa_stream_get_state(This
->stream
) != PA_STREAM_READY
)
609 return AUDCLNT_E_DEVICE_INVALIDATED
;
613 static void silence_buffer(pa_sample_format_t format
, BYTE
*buffer
, UINT32 bytes
)
615 memset(buffer
, format
== PA_SAMPLE_U8
? 0x80 : 0, bytes
);
618 static void dump_attr(const pa_buffer_attr
*attr
) {
619 TRACE("maxlength: %u\n", attr
->maxlength
);
620 TRACE("minreq: %u\n", attr
->minreq
);
621 TRACE("fragsize: %u\n", attr
->fragsize
);
622 TRACE("tlength: %u\n", attr
->tlength
);
623 TRACE("prebuf: %u\n", attr
->prebuf
);
626 static void pulse_op_cb(pa_stream
*s
, int success
, void *user
) {
627 TRACE("Success: %i\n", success
);
628 *(int*)user
= success
;
629 pthread_cond_signal(&pulse_cond
);
632 static void pulse_attr_update(pa_stream
*s
, void *user
) {
633 const pa_buffer_attr
*attr
= pa_stream_get_buffer_attr(s
);
634 TRACE("New attributes or device moved:\n");
638 static void pulse_wr_callback(pa_stream
*s
, size_t bytes
, void *userdata
)
640 ACImpl
*This
= userdata
;
641 UINT32 oldpad
= This
->pad
;
643 if (bytes
< This
->bufsize_bytes
)
644 This
->pad
= This
->bufsize_bytes
- bytes
;
648 if (oldpad
== This
->pad
)
651 assert(oldpad
> This
->pad
);
653 This
->clock_written
+= oldpad
- This
->pad
;
654 TRACE("New pad: %zu (-%zu)\n", This
->pad
/ pa_frame_size(&This
->ss
), (oldpad
- This
->pad
) / pa_frame_size(&This
->ss
));
657 SetEvent(This
->event
);
660 static void pulse_underflow_callback(pa_stream
*s
, void *userdata
)
665 /* Latency is periodically updated even when nothing is played,
666 * because of PA_STREAM_AUTO_TIMING_UPDATE so use it as timer
668 * Perfect for passing all tests :)
670 static void pulse_latency_callback(pa_stream
*s
, void *userdata
)
672 ACImpl
*This
= userdata
;
673 if (!This
->pad
&& This
->event
)
674 SetEvent(This
->event
);
677 static void pulse_started_callback(pa_stream
*s
, void *userdata
)
679 TRACE("(Re)started playing\n");
682 static void pulse_rd_loop(ACImpl
*This
, size_t bytes
)
684 while (bytes
>= This
->capture_period
) {
686 LARGE_INTEGER stamp
, freq
;
688 size_t src_len
, copy
, rem
= This
->capture_period
;
689 if (!(p
= (ACPacket
*)list_head(&This
->packet_free_head
))) {
690 p
= (ACPacket
*)list_head(&This
->packet_filled_head
);
692 next
= (ACPacket
*)p
->entry
.next
;
695 p
= (ACPacket
*)list_tail(&This
->packet_filled_head
);
696 assert(This
->pad
== This
->bufsize_bytes
);
698 assert(This
->pad
< This
->bufsize_bytes
);
699 This
->pad
+= This
->capture_period
;
700 assert(This
->pad
<= This
->bufsize_bytes
);
702 QueryPerformanceCounter(&stamp
);
703 QueryPerformanceFrequency(&freq
);
704 p
->qpcpos
= (stamp
.QuadPart
* (INT64
)10000000) / freq
.QuadPart
;
706 list_remove(&p
->entry
);
707 list_add_tail(&This
->packet_filled_head
, &p
->entry
);
711 pa_stream_peek(This
->stream
, (const void**)&src
, &src_len
);
713 assert(This
->peek_ofs
< src_len
);
714 src
+= This
->peek_ofs
;
715 src_len
-= This
->peek_ofs
;
716 assert(src_len
<= bytes
);
721 memcpy(dst
, src
, rem
);
729 pa_stream_drop(This
->stream
);
731 This
->peek_ofs
+= copy
;
733 bytes
-= This
->capture_period
;
737 static void pulse_rd_drop(ACImpl
*This
, size_t bytes
)
739 while (bytes
>= This
->capture_period
) {
740 size_t src_len
, copy
, rem
= This
->capture_period
;
743 pa_stream_peek(This
->stream
, &src
, &src_len
);
745 assert(This
->peek_ofs
< src_len
);
746 src_len
-= This
->peek_ofs
;
747 assert(src_len
<= bytes
);
758 pa_stream_drop(This
->stream
);
760 This
->peek_ofs
+= copy
;
766 static void pulse_rd_callback(pa_stream
*s
, size_t bytes
, void *userdata
)
768 ACImpl
*This
= userdata
;
770 TRACE("Readable total: %zu, fragsize: %u\n", bytes
, pa_stream_get_buffer_attr(s
)->fragsize
);
771 assert(bytes
>= This
->peek_ofs
);
772 bytes
-= This
->peek_ofs
;
773 if (bytes
< This
->capture_period
)
777 pulse_rd_loop(This
, bytes
);
779 pulse_rd_drop(This
, bytes
);
782 SetEvent(This
->event
);
785 static HRESULT
pulse_stream_connect(ACImpl
*This
, UINT32 period_bytes
) {
791 pa_stream_disconnect(This
->stream
);
792 while (pa_stream_get_state(This
->stream
) == PA_STREAM_READY
)
793 pthread_cond_wait(&pulse_cond
, &pulse_lock
);
794 pa_stream_unref(This
->stream
);
796 ret
= InterlockedIncrement(&number
);
797 sprintf(buffer
, "audio stream #%i", ret
);
798 This
->stream
= pa_stream_new(pulse_ctx
, buffer
, &This
->ss
, &This
->map
);
801 WARN("pa_stream_new returned error %i\n", pa_context_errno(pulse_ctx
));
802 return AUDCLNT_E_ENDPOINT_CREATE_FAILED
;
805 pa_stream_set_state_callback(This
->stream
, pulse_stream_state
, This
);
806 pa_stream_set_buffer_attr_callback(This
->stream
, pulse_attr_update
, This
);
807 pa_stream_set_moved_callback(This
->stream
, pulse_attr_update
, This
);
809 /* PulseAudio will fill in correct values */
810 attr
.minreq
= attr
.fragsize
= period_bytes
;
811 attr
.maxlength
= attr
.tlength
= This
->bufsize_bytes
;
812 attr
.prebuf
= pa_frame_size(&This
->ss
);
814 if (This
->dataflow
== eRender
)
815 ret
= pa_stream_connect_playback(This
->stream
, NULL
, &attr
,
816 PA_STREAM_START_CORKED
|PA_STREAM_START_UNMUTED
|PA_STREAM_AUTO_TIMING_UPDATE
|PA_STREAM_INTERPOLATE_TIMING
|PA_STREAM_EARLY_REQUESTS
, NULL
, NULL
);
818 ret
= pa_stream_connect_record(This
->stream
, NULL
, &attr
,
819 PA_STREAM_START_CORKED
|PA_STREAM_START_UNMUTED
|PA_STREAM_AUTO_TIMING_UPDATE
|PA_STREAM_INTERPOLATE_TIMING
|PA_STREAM_EARLY_REQUESTS
);
821 WARN("Returns %i\n", ret
);
822 return AUDCLNT_E_ENDPOINT_CREATE_FAILED
;
824 while (pa_stream_get_state(This
->stream
) == PA_STREAM_CREATING
)
825 pthread_cond_wait(&pulse_cond
, &pulse_lock
);
826 if (pa_stream_get_state(This
->stream
) != PA_STREAM_READY
)
827 return AUDCLNT_E_ENDPOINT_CREATE_FAILED
;
829 if (This
->dataflow
== eRender
) {
830 pa_stream_set_write_callback(This
->stream
, pulse_wr_callback
, This
);
831 pa_stream_set_underflow_callback(This
->stream
, pulse_underflow_callback
, This
);
832 pa_stream_set_started_callback(This
->stream
, pulse_started_callback
, This
);
834 pa_stream_set_read_callback(This
->stream
, pulse_rd_callback
, This
);
838 HRESULT WINAPI
AUDDRV_GetEndpointIDs(EDataFlow flow
, const WCHAR
***ids
, GUID
**keys
,
839 UINT
*num
, UINT
*def_index
)
843 TRACE("%d %p %p %p\n", flow
, ids
, num
, def_index
);
848 *ids
= HeapAlloc(GetProcessHeap(), 0, sizeof(**ids
));
851 return E_OUTOFMEMORY
;
853 (*ids
)[0] = id
= HeapAlloc(GetProcessHeap(), 0, sizeof(defaultW
));
854 *keys
= HeapAlloc(GetProcessHeap(), 0, sizeof(**keys
));
856 HeapFree(GetProcessHeap(), 0, id
);
857 HeapFree(GetProcessHeap(), 0, *keys
);
858 HeapFree(GetProcessHeap(), 0, *ids
);
861 return E_OUTOFMEMORY
;
863 memcpy(id
, defaultW
, sizeof(defaultW
));
866 (*keys
)[0] = pulse_render_guid
;
868 (*keys
)[0] = pulse_capture_guid
;
873 int WINAPI
AUDDRV_GetPriority(void)
876 pthread_mutex_lock(&pulse_lock
);
877 hr
= pulse_test_connect();
878 pthread_mutex_unlock(&pulse_lock
);
879 return SUCCEEDED(hr
) ? Priority_Preferred
: Priority_Unavailable
;
882 HRESULT WINAPI
AUDDRV_GetAudioEndpoint(GUID
*guid
, IMMDevice
*dev
, IAudioClient
**out
)
889 TRACE("%s %p %p\n", debugstr_guid(guid
), dev
, out
);
890 if (IsEqualGUID(guid
, &pulse_render_guid
))
892 else if (IsEqualGUID(guid
, &pulse_capture_guid
))
899 This
= HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY
, sizeof(*This
));
901 return E_OUTOFMEMORY
;
903 This
->IAudioClient_iface
.lpVtbl
= &AudioClient_Vtbl
;
904 This
->IAudioRenderClient_iface
.lpVtbl
= &AudioRenderClient_Vtbl
;
905 This
->IAudioCaptureClient_iface
.lpVtbl
= &AudioCaptureClient_Vtbl
;
906 This
->IAudioClock_iface
.lpVtbl
= &AudioClock_Vtbl
;
907 This
->IAudioClock2_iface
.lpVtbl
= &AudioClock2_Vtbl
;
908 This
->IAudioStreamVolume_iface
.lpVtbl
= &AudioStreamVolume_Vtbl
;
909 This
->dataflow
= dataflow
;
911 for (i
= 0; i
< PA_CHANNELS_MAX
; ++i
)
914 hr
= CoCreateFreeThreadedMarshaler((IUnknown
*)This
, &This
->marshal
);
916 HeapFree(GetProcessHeap(), 0, This
);
919 IMMDevice_AddRef(This
->parent
);
921 *out
= &This
->IAudioClient_iface
;
922 IAudioClient_AddRef(&This
->IAudioClient_iface
);
927 static HRESULT WINAPI
AudioClient_QueryInterface(IAudioClient
*iface
,
928 REFIID riid
, void **ppv
)
930 ACImpl
*This
= impl_from_IAudioClient(iface
);
932 TRACE("(%p)->(%s, %p)\n", iface
, debugstr_guid(riid
), ppv
);
938 if (IsEqualIID(riid
, &IID_IUnknown
) || IsEqualIID(riid
, &IID_IAudioClient
))
941 IUnknown_AddRef((IUnknown
*)*ppv
);
945 if (IsEqualIID(riid
, &IID_IMarshal
))
946 return IUnknown_QueryInterface(This
->marshal
, riid
, ppv
);
948 WARN("Unknown interface %s\n", debugstr_guid(riid
));
949 return E_NOINTERFACE
;
952 static ULONG WINAPI
AudioClient_AddRef(IAudioClient
*iface
)
954 ACImpl
*This
= impl_from_IAudioClient(iface
);
956 ref
= InterlockedIncrement(&This
->ref
);
957 TRACE("(%p) Refcount now %u\n", This
, ref
);
961 static ULONG WINAPI
AudioClient_Release(IAudioClient
*iface
)
963 ACImpl
*This
= impl_from_IAudioClient(iface
);
965 ref
= InterlockedDecrement(&This
->ref
);
966 TRACE("(%p) Refcount now %u\n", This
, ref
);
969 pthread_mutex_lock(&pulse_lock
);
970 if (PA_STREAM_IS_GOOD(pa_stream_get_state(This
->stream
))) {
971 pa_stream_disconnect(This
->stream
);
972 while (PA_STREAM_IS_GOOD(pa_stream_get_state(This
->stream
)))
973 pthread_cond_wait(&pulse_cond
, &pulse_lock
);
975 pa_stream_unref(This
->stream
);
977 list_remove(&This
->entry
);
978 pthread_mutex_unlock(&pulse_lock
);
980 IUnknown_Release(This
->marshal
);
981 IMMDevice_Release(This
->parent
);
982 HeapFree(GetProcessHeap(), 0, This
->tmp_buffer
);
983 HeapFree(GetProcessHeap(), 0, This
);
988 static void dump_fmt(const WAVEFORMATEX
*fmt
)
990 TRACE("wFormatTag: 0x%x (", fmt
->wFormatTag
);
991 switch(fmt
->wFormatTag
) {
992 case WAVE_FORMAT_PCM
:
993 TRACE("WAVE_FORMAT_PCM");
995 case WAVE_FORMAT_IEEE_FLOAT
:
996 TRACE("WAVE_FORMAT_IEEE_FLOAT");
998 case WAVE_FORMAT_EXTENSIBLE
:
999 TRACE("WAVE_FORMAT_EXTENSIBLE");
1007 TRACE("nChannels: %u\n", fmt
->nChannels
);
1008 TRACE("nSamplesPerSec: %u\n", fmt
->nSamplesPerSec
);
1009 TRACE("nAvgBytesPerSec: %u\n", fmt
->nAvgBytesPerSec
);
1010 TRACE("nBlockAlign: %u\n", fmt
->nBlockAlign
);
1011 TRACE("wBitsPerSample: %u\n", fmt
->wBitsPerSample
);
1012 TRACE("cbSize: %u\n", fmt
->cbSize
);
1014 if (fmt
->wFormatTag
== WAVE_FORMAT_EXTENSIBLE
) {
1015 WAVEFORMATEXTENSIBLE
*fmtex
= (void*)fmt
;
1016 TRACE("dwChannelMask: %08x\n", fmtex
->dwChannelMask
);
1017 TRACE("Samples: %04x\n", fmtex
->Samples
.wReserved
);
1018 TRACE("SubFormat: %s\n", wine_dbgstr_guid(&fmtex
->SubFormat
));
1022 static WAVEFORMATEX
*clone_format(const WAVEFORMATEX
*fmt
)
1027 if (fmt
->wFormatTag
== WAVE_FORMAT_EXTENSIBLE
)
1028 size
= sizeof(WAVEFORMATEXTENSIBLE
);
1030 size
= sizeof(WAVEFORMATEX
);
1032 ret
= CoTaskMemAlloc(size
);
1036 memcpy(ret
, fmt
, size
);
1038 ret
->cbSize
= size
- sizeof(WAVEFORMATEX
);
1043 static DWORD
get_channel_mask(unsigned int channels
)
1049 return KSAUDIO_SPEAKER_MONO
;
1051 return KSAUDIO_SPEAKER_STEREO
;
1053 return KSAUDIO_SPEAKER_STEREO
| SPEAKER_LOW_FREQUENCY
;
1055 return KSAUDIO_SPEAKER_QUAD
; /* not _SURROUND */
1057 return KSAUDIO_SPEAKER_QUAD
| SPEAKER_LOW_FREQUENCY
;
1059 return KSAUDIO_SPEAKER_5POINT1
; /* not 5POINT1_SURROUND */
1061 return KSAUDIO_SPEAKER_5POINT1
| SPEAKER_BACK_CENTER
;
1063 return KSAUDIO_SPEAKER_7POINT1_SURROUND
; /* Vista deprecates 7POINT1 */
1065 FIXME("Unknown speaker configuration: %u\n", channels
);
1069 static void session_init_vols(AudioSession
*session
, UINT channels
)
1071 if (session
->channel_count
< channels
) {
1074 if (session
->channel_vols
)
1075 session
->channel_vols
= HeapReAlloc(GetProcessHeap(), 0,
1076 session
->channel_vols
, sizeof(float) * channels
);
1078 session
->channel_vols
= HeapAlloc(GetProcessHeap(), 0,
1079 sizeof(float) * channels
);
1080 if (!session
->channel_vols
)
1083 for(i
= session
->channel_count
; i
< channels
; ++i
)
1084 session
->channel_vols
[i
] = 1.f
;
1086 session
->channel_count
= channels
;
1090 static AudioSession
*create_session(const GUID
*guid
, IMMDevice
*device
,
1095 ret
= HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY
, sizeof(AudioSession
));
1099 memcpy(&ret
->guid
, guid
, sizeof(GUID
));
1101 ret
->device
= device
;
1103 list_init(&ret
->clients
);
1105 list_add_head(&g_sessions
, &ret
->entry
);
1107 session_init_vols(ret
, num_channels
);
1109 ret
->master_vol
= 1.f
;
1114 /* if channels == 0, then this will return or create a session with
1115 * matching dataflow and GUID. otherwise, channels must also match */
1116 static HRESULT
get_audio_session(const GUID
*sessionguid
,
1117 IMMDevice
*device
, UINT channels
, AudioSession
**out
)
1119 AudioSession
*session
;
1121 if (!sessionguid
|| IsEqualGUID(sessionguid
, &GUID_NULL
)) {
1122 *out
= create_session(&GUID_NULL
, device
, channels
);
1124 return E_OUTOFMEMORY
;
1130 LIST_FOR_EACH_ENTRY(session
, &g_sessions
, AudioSession
, entry
) {
1131 if (session
->device
== device
&&
1132 IsEqualGUID(sessionguid
, &session
->guid
)) {
1133 session_init_vols(session
, channels
);
1140 *out
= create_session(sessionguid
, device
, channels
);
1142 return E_OUTOFMEMORY
;
1148 static HRESULT
pulse_spec_from_waveformat(ACImpl
*This
, const WAVEFORMATEX
*fmt
)
1150 pa_channel_map_init(&This
->map
);
1151 This
->ss
.rate
= fmt
->nSamplesPerSec
;
1152 This
->ss
.format
= PA_SAMPLE_INVALID
;
1154 switch(fmt
->wFormatTag
) {
1155 case WAVE_FORMAT_IEEE_FLOAT
:
1156 if (!fmt
->nChannels
|| fmt
->nChannels
> 2 || fmt
->wBitsPerSample
!= 32)
1158 This
->ss
.format
= PA_SAMPLE_FLOAT32LE
;
1159 pa_channel_map_init_auto(&This
->map
, fmt
->nChannels
, PA_CHANNEL_MAP_ALSA
);
1161 case WAVE_FORMAT_PCM
:
1162 if (!fmt
->nChannels
|| fmt
->nChannels
> 2)
1164 if (fmt
->wBitsPerSample
== 8)
1165 This
->ss
.format
= PA_SAMPLE_U8
;
1166 else if (fmt
->wBitsPerSample
== 16)
1167 This
->ss
.format
= PA_SAMPLE_S16LE
;
1169 return AUDCLNT_E_UNSUPPORTED_FORMAT
;
1170 pa_channel_map_init_auto(&This
->map
, fmt
->nChannels
, PA_CHANNEL_MAP_ALSA
);
1172 case WAVE_FORMAT_EXTENSIBLE
: {
1173 WAVEFORMATEXTENSIBLE
*wfe
= (WAVEFORMATEXTENSIBLE
*)fmt
;
1174 DWORD mask
= wfe
->dwChannelMask
;
1176 if (fmt
->cbSize
!= (sizeof(*wfe
) - sizeof(*fmt
)) && fmt
->cbSize
!= sizeof(*wfe
))
1178 if (IsEqualGUID(&wfe
->SubFormat
, &KSDATAFORMAT_SUBTYPE_IEEE_FLOAT
) &&
1179 (!wfe
->Samples
.wValidBitsPerSample
|| wfe
->Samples
.wValidBitsPerSample
== 32) &&
1180 fmt
->wBitsPerSample
== 32)
1181 This
->ss
.format
= PA_SAMPLE_FLOAT32LE
;
1182 else if (IsEqualGUID(&wfe
->SubFormat
, &KSDATAFORMAT_SUBTYPE_PCM
)) {
1183 DWORD valid
= wfe
->Samples
.wValidBitsPerSample
;
1185 valid
= fmt
->wBitsPerSample
;
1186 if (!valid
|| valid
> fmt
->wBitsPerSample
)
1188 switch (fmt
->wBitsPerSample
) {
1191 This
->ss
.format
= PA_SAMPLE_U8
;
1195 This
->ss
.format
= PA_SAMPLE_S16LE
;
1199 This
->ss
.format
= PA_SAMPLE_S24LE
;
1203 This
->ss
.format
= PA_SAMPLE_S24_32LE
;
1204 else if (valid
== 32)
1205 This
->ss
.format
= PA_SAMPLE_S32LE
;
1208 return AUDCLNT_E_UNSUPPORTED_FORMAT
;
1211 This
->map
.channels
= fmt
->nChannels
;
1212 if (!mask
|| (mask
& (SPEAKER_ALL
|SPEAKER_RESERVED
)))
1213 mask
= get_channel_mask(fmt
->nChannels
);
1214 for (j
= 0; j
< sizeof(pulse_pos_from_wfx
)/sizeof(*pulse_pos_from_wfx
) && i
< fmt
->nChannels
; ++j
) {
1215 if (mask
& (1 << j
))
1216 This
->map
.map
[i
++] = pulse_pos_from_wfx
[j
];
1219 /* Special case for mono since pulse appears to map it differently */
1220 if (mask
== SPEAKER_FRONT_CENTER
)
1221 This
->map
.map
[0] = PA_CHANNEL_POSITION_MONO
;
1223 if (i
< fmt
->nChannels
|| (mask
& SPEAKER_RESERVED
)) {
1224 This
->map
.channels
= 0;
1225 ERR("Invalid channel mask: %i/%i and %x(%x)\n", i
, fmt
->nChannels
, mask
, wfe
->dwChannelMask
);
1230 case WAVE_FORMAT_ALAW
:
1231 case WAVE_FORMAT_MULAW
:
1232 if (fmt
->wBitsPerSample
!= 8) {
1233 FIXME("Unsupported bpp %u for LAW\n", fmt
->wBitsPerSample
);
1234 return AUDCLNT_E_UNSUPPORTED_FORMAT
;
1236 if (fmt
->nChannels
!= 1 && fmt
->nChannels
!= 2) {
1237 FIXME("Unsupported channels %u for LAW\n", fmt
->nChannels
);
1238 return AUDCLNT_E_UNSUPPORTED_FORMAT
;
1240 This
->ss
.format
= fmt
->wFormatTag
== WAVE_FORMAT_MULAW
? PA_SAMPLE_ULAW
: PA_SAMPLE_ALAW
;
1241 pa_channel_map_init_auto(&This
->map
, fmt
->nChannels
, PA_CHANNEL_MAP_ALSA
);
1244 WARN("Unhandled tag %x\n", fmt
->wFormatTag
);
1245 return AUDCLNT_E_UNSUPPORTED_FORMAT
;
1247 This
->ss
.channels
= This
->map
.channels
;
1248 if (!pa_channel_map_valid(&This
->map
) || This
->ss
.format
== PA_SAMPLE_INVALID
) {
1249 ERR("Invalid format! Channel spec valid: %i, format: %i\n", pa_channel_map_valid(&This
->map
), This
->ss
.format
);
1250 return AUDCLNT_E_UNSUPPORTED_FORMAT
;
1255 static HRESULT WINAPI
AudioClient_Initialize(IAudioClient
*iface
,
1256 AUDCLNT_SHAREMODE mode
, DWORD flags
, REFERENCE_TIME duration
,
1257 REFERENCE_TIME period
, const WAVEFORMATEX
*fmt
,
1258 const GUID
*sessionguid
)
1260 ACImpl
*This
= impl_from_IAudioClient(iface
);
1264 TRACE("(%p)->(%x, %x, %s, %s, %p, %s)\n", This
, mode
, flags
,
1265 wine_dbgstr_longlong(duration
), wine_dbgstr_longlong(period
), fmt
, debugstr_guid(sessionguid
));
1270 if (mode
!= AUDCLNT_SHAREMODE_SHARED
&& mode
!= AUDCLNT_SHAREMODE_EXCLUSIVE
)
1271 return AUDCLNT_E_NOT_INITIALIZED
;
1272 if (mode
== AUDCLNT_SHAREMODE_EXCLUSIVE
)
1273 return AUDCLNT_E_EXCLUSIVE_MODE_NOT_ALLOWED
;
1275 if (flags
& ~(AUDCLNT_STREAMFLAGS_CROSSPROCESS
|
1276 AUDCLNT_STREAMFLAGS_LOOPBACK
|
1277 AUDCLNT_STREAMFLAGS_EVENTCALLBACK
|
1278 AUDCLNT_STREAMFLAGS_NOPERSIST
|
1279 AUDCLNT_STREAMFLAGS_RATEADJUST
|
1280 AUDCLNT_SESSIONFLAGS_EXPIREWHENUNOWNED
|
1281 AUDCLNT_SESSIONFLAGS_DISPLAY_HIDE
|
1282 AUDCLNT_SESSIONFLAGS_DISPLAY_HIDEWHENEXPIRED
)) {
1283 TRACE("Unknown flags: %08x\n", flags
);
1284 return E_INVALIDARG
;
1287 pthread_mutex_lock(&pulse_lock
);
1289 hr
= pulse_connect();
1291 pthread_mutex_unlock(&pulse_lock
);
1296 pthread_mutex_unlock(&pulse_lock
);
1297 return AUDCLNT_E_ALREADY_INITIALIZED
;
1300 hr
= pulse_spec_from_waveformat(This
, fmt
);
1301 TRACE("Obtaining format returns %08x\n", hr
);
1307 if (mode
== AUDCLNT_SHAREMODE_SHARED
) {
1308 REFERENCE_TIME def
= pulse_def_period
[This
->dataflow
== eCapture
];
1309 REFERENCE_TIME min
= pulse_min_period
[This
->dataflow
== eCapture
];
1311 /* Switch to low latency mode if below 2 default periods,
1312 * which is 20 ms by default, this will increase the amount
1313 * of interrupts but allows very low latency. In dsound I
1314 * managed to get a total latency of ~8ms, which is well below
1317 if (duration
< 2 * def
)
1321 if (duration
< 2 * period
)
1322 duration
= 2 * period
;
1324 /* Uh oh, really low latency requested.. */
1325 if (duration
<= 2 * period
)
1328 period_bytes
= pa_frame_size(&This
->ss
) * MulDiv(period
, This
->ss
.rate
, 10000000);
1330 if (duration
< 20000000)
1331 This
->bufsize_frames
= ceil((duration
/ 10000000.) * fmt
->nSamplesPerSec
);
1333 This
->bufsize_frames
= 2 * fmt
->nSamplesPerSec
;
1334 This
->bufsize_bytes
= This
->bufsize_frames
* pa_frame_size(&This
->ss
);
1337 This
->flags
= flags
;
1338 hr
= pulse_stream_connect(This
, period_bytes
);
1339 if (SUCCEEDED(hr
)) {
1341 const pa_buffer_attr
*attr
= pa_stream_get_buffer_attr(This
->stream
);
1342 /* Update frames according to new size */
1344 if (This
->dataflow
== eRender
) {
1345 if (attr
->tlength
< This
->bufsize_bytes
) {
1346 const char *latenv
= getenv("PULSE_LATENCY_MSEC");
1347 if (latenv
&& *latenv
)
1348 ERR_(winediag
)("PulseAudio buffer too small (%u < %u) - PULSE_LATENCY_MSEC is %s\n", attr
->tlength
, This
->bufsize_bytes
, latenv
);
1350 ERR_(winediag
)("PulseAudio buffer too small (%u < %u)\n", attr
->tlength
, This
->bufsize_bytes
);
1352 This
->bufsize_bytes
= attr
->tlength
;
1354 This
->capture_period
= period_bytes
= attr
->fragsize
;
1355 if ((unalign
= This
->bufsize_bytes
% period_bytes
))
1356 This
->bufsize_bytes
+= period_bytes
- unalign
;
1358 This
->bufsize_frames
= This
->bufsize_bytes
/ pa_frame_size(&This
->ss
);
1360 if (SUCCEEDED(hr
)) {
1361 UINT32 i
, capture_packets
= This
->capture_period
? This
->bufsize_bytes
/ This
->capture_period
: 0;
1362 This
->tmp_buffer
= HeapAlloc(GetProcessHeap(), 0, This
->bufsize_bytes
+ capture_packets
* sizeof(ACPacket
));
1363 if (!This
->tmp_buffer
)
1366 ACPacket
*cur_packet
= (ACPacket
*)((char*)This
->tmp_buffer
+ This
->bufsize_bytes
);
1367 BYTE
*data
= This
->tmp_buffer
;
1368 silence_buffer(This
->ss
.format
, This
->tmp_buffer
, This
->bufsize_bytes
);
1369 list_init(&This
->packet_free_head
);
1370 list_init(&This
->packet_filled_head
);
1371 for (i
= 0; i
< capture_packets
; ++i
, ++cur_packet
) {
1372 list_add_tail(&This
->packet_free_head
, &cur_packet
->entry
);
1373 cur_packet
->data
= data
;
1374 data
+= This
->capture_period
;
1376 assert(!This
->capture_period
|| This
->bufsize_bytes
== This
->capture_period
* capture_packets
);
1377 assert(!capture_packets
|| data
- This
->bufsize_bytes
== This
->tmp_buffer
);
1381 hr
= get_audio_session(sessionguid
, This
->parent
, fmt
->nChannels
, &This
->session
);
1383 list_add_tail(&This
->session
->clients
, &This
->entry
);
1387 HeapFree(GetProcessHeap(), 0, This
->tmp_buffer
);
1388 This
->tmp_buffer
= NULL
;
1390 pa_stream_disconnect(This
->stream
);
1391 pa_stream_unref(This
->stream
);
1392 This
->stream
= NULL
;
1395 pthread_mutex_unlock(&pulse_lock
);
1399 static HRESULT WINAPI
AudioClient_GetBufferSize(IAudioClient
*iface
,
1402 ACImpl
*This
= impl_from_IAudioClient(iface
);
1405 TRACE("(%p)->(%p)\n", This
, out
);
1410 pthread_mutex_lock(&pulse_lock
);
1411 hr
= pulse_stream_valid(This
);
1413 *out
= This
->bufsize_frames
;
1414 pthread_mutex_unlock(&pulse_lock
);
1419 static HRESULT WINAPI
AudioClient_GetStreamLatency(IAudioClient
*iface
,
1420 REFERENCE_TIME
*latency
)
1422 ACImpl
*This
= impl_from_IAudioClient(iface
);
1423 const pa_buffer_attr
*attr
;
1427 TRACE("(%p)->(%p)\n", This
, latency
);
1432 pthread_mutex_lock(&pulse_lock
);
1433 hr
= pulse_stream_valid(This
);
1435 pthread_mutex_unlock(&pulse_lock
);
1438 attr
= pa_stream_get_buffer_attr(This
->stream
);
1439 if (This
->dataflow
== eRender
){
1440 lat
= attr
->minreq
/ pa_frame_size(&This
->ss
);
1441 lat
+= pulse_def_period
[0];
1443 lat
= attr
->fragsize
/ pa_frame_size(&This
->ss
);
1444 *latency
= 10000000;
1446 *latency
/= This
->ss
.rate
;
1447 pthread_mutex_unlock(&pulse_lock
);
1448 TRACE("Latency: %u ms\n", (DWORD
)(*latency
/ 10000));
1452 static void ACImpl_GetRenderPad(ACImpl
*This
, UINT32
*out
)
1454 *out
= This
->pad
/ pa_frame_size(&This
->ss
);
1457 static void ACImpl_GetCapturePad(ACImpl
*This
, UINT32
*out
)
1459 ACPacket
*packet
= This
->locked_ptr
;
1460 if (!packet
&& !list_empty(&This
->packet_filled_head
)) {
1461 packet
= (ACPacket
*)list_head(&This
->packet_filled_head
);
1462 This
->locked_ptr
= packet
;
1463 list_remove(&packet
->entry
);
1466 *out
= This
->pad
/ pa_frame_size(&This
->ss
);
1469 static HRESULT WINAPI
AudioClient_GetCurrentPadding(IAudioClient
*iface
,
1472 ACImpl
*This
= impl_from_IAudioClient(iface
);
1475 TRACE("(%p)->(%p)\n", This
, out
);
1480 pthread_mutex_lock(&pulse_lock
);
1481 hr
= pulse_stream_valid(This
);
1483 pthread_mutex_unlock(&pulse_lock
);
1487 if (This
->dataflow
== eRender
)
1488 ACImpl_GetRenderPad(This
, out
);
1490 ACImpl_GetCapturePad(This
, out
);
1491 pthread_mutex_unlock(&pulse_lock
);
1493 TRACE("%p Pad: %u ms (%u)\n", This
, MulDiv(*out
, 1000, This
->ss
.rate
), *out
);
1497 static HRESULT WINAPI
AudioClient_IsFormatSupported(IAudioClient
*iface
,
1498 AUDCLNT_SHAREMODE mode
, const WAVEFORMATEX
*fmt
,
1501 ACImpl
*This
= impl_from_IAudioClient(iface
);
1503 WAVEFORMATEX
*closest
= NULL
;
1506 TRACE("(%p)->(%x, %p, %p)\n", This
, mode
, fmt
, out
);
1514 if (mode
== AUDCLNT_SHAREMODE_EXCLUSIVE
) {
1517 } else if (mode
== AUDCLNT_SHAREMODE_SHARED
) {
1522 return E_INVALIDARG
;
1524 if (fmt
->nChannels
== 0)
1525 return AUDCLNT_E_UNSUPPORTED_FORMAT
;
1527 closest
= clone_format(fmt
);
1529 return E_OUTOFMEMORY
;
1533 switch (fmt
->wFormatTag
) {
1534 case WAVE_FORMAT_EXTENSIBLE
: {
1535 WAVEFORMATEXTENSIBLE
*ext
= (WAVEFORMATEXTENSIBLE
*)closest
;
1537 if ((fmt
->cbSize
!= sizeof(WAVEFORMATEXTENSIBLE
) - sizeof(WAVEFORMATEX
) &&
1538 fmt
->cbSize
!= sizeof(WAVEFORMATEXTENSIBLE
)) ||
1539 fmt
->nBlockAlign
!= fmt
->wBitsPerSample
/ 8 * fmt
->nChannels
||
1540 ext
->Samples
.wValidBitsPerSample
> fmt
->wBitsPerSample
||
1541 fmt
->nAvgBytesPerSec
!= fmt
->nBlockAlign
* fmt
->nSamplesPerSec
) {
1547 UINT32 mask
= 0, i
, channels
= 0;
1549 if (!(ext
->dwChannelMask
& (SPEAKER_ALL
| SPEAKER_RESERVED
))) {
1550 for (i
= 1; !(i
& SPEAKER_RESERVED
); i
<<= 1) {
1551 if (i
& ext
->dwChannelMask
) {
1557 if (channels
!= fmt
->nChannels
|| (ext
->dwChannelMask
& ~mask
)) {
1558 hr
= AUDCLNT_E_UNSUPPORTED_FORMAT
;
1562 hr
= AUDCLNT_E_UNSUPPORTED_FORMAT
;
1567 if (IsEqualGUID(&ext
->SubFormat
, &KSDATAFORMAT_SUBTYPE_IEEE_FLOAT
)) {
1568 if (fmt
->wBitsPerSample
!= 32) {
1573 if (ext
->Samples
.wValidBitsPerSample
!= fmt
->wBitsPerSample
) {
1575 ext
->Samples
.wValidBitsPerSample
= fmt
->wBitsPerSample
;
1577 } else if (IsEqualGUID(&ext
->SubFormat
, &KSDATAFORMAT_SUBTYPE_PCM
)) {
1578 if (!fmt
->wBitsPerSample
|| fmt
->wBitsPerSample
> 32 || fmt
->wBitsPerSample
% 8) {
1583 if (ext
->Samples
.wValidBitsPerSample
!= fmt
->wBitsPerSample
&&
1584 !(fmt
->wBitsPerSample
== 32 &&
1585 ext
->Samples
.wValidBitsPerSample
== 24)) {
1587 ext
->Samples
.wValidBitsPerSample
= fmt
->wBitsPerSample
;
1591 hr
= AUDCLNT_E_UNSUPPORTED_FORMAT
;
1598 case WAVE_FORMAT_ALAW
:
1599 case WAVE_FORMAT_MULAW
:
1600 if (fmt
->wBitsPerSample
!= 8) {
1605 case WAVE_FORMAT_IEEE_FLOAT
:
1606 if (fmt
->wFormatTag
== WAVE_FORMAT_IEEE_FLOAT
&& fmt
->wBitsPerSample
!= 32) {
1611 case WAVE_FORMAT_PCM
:
1612 if (fmt
->wFormatTag
== WAVE_FORMAT_PCM
&&
1613 (!fmt
->wBitsPerSample
|| fmt
->wBitsPerSample
> 32 || fmt
->wBitsPerSample
% 8)) {
1618 if (fmt
->nChannels
> 2) {
1619 hr
= AUDCLNT_E_UNSUPPORTED_FORMAT
;
1623 * fmt->cbSize, fmt->nBlockAlign and fmt->nAvgBytesPerSec seem to be
1624 * ignored, invalid values are happily accepted.
1628 hr
= AUDCLNT_E_UNSUPPORTED_FORMAT
;
1632 if (exclusive
&& hr
!= S_OK
) {
1633 hr
= AUDCLNT_E_UNSUPPORTED_FORMAT
;
1634 CoTaskMemFree(closest
);
1635 } else if (hr
!= S_FALSE
)
1636 CoTaskMemFree(closest
);
1640 /* Winepulse does not currently support exclusive mode, if you know of an
1641 * application that uses it, I will correct this..
1643 if (hr
== S_OK
&& exclusive
)
1644 return This
->dataflow
== eCapture
? AUDCLNT_E_UNSUPPORTED_FORMAT
: AUDCLNT_E_EXCLUSIVE_MODE_NOT_ALLOWED
;
1646 TRACE("returning: %08x %p\n", hr
, out
? *out
: NULL
);
1650 static HRESULT WINAPI
AudioClient_GetMixFormat(IAudioClient
*iface
,
1651 WAVEFORMATEX
**pwfx
)
1653 ACImpl
*This
= impl_from_IAudioClient(iface
);
1654 WAVEFORMATEXTENSIBLE
*fmt
= &pulse_fmt
[This
->dataflow
== eCapture
];
1656 TRACE("(%p)->(%p)\n", This
, pwfx
);
1661 *pwfx
= clone_format(&fmt
->Format
);
1663 return E_OUTOFMEMORY
;
1668 static HRESULT WINAPI
AudioClient_GetDevicePeriod(IAudioClient
*iface
,
1669 REFERENCE_TIME
*defperiod
, REFERENCE_TIME
*minperiod
)
1671 ACImpl
*This
= impl_from_IAudioClient(iface
);
1673 TRACE("(%p)->(%p, %p)\n", This
, defperiod
, minperiod
);
1675 if (!defperiod
&& !minperiod
)
1679 *defperiod
= pulse_def_period
[This
->dataflow
== eCapture
];
1681 *minperiod
= pulse_min_period
[This
->dataflow
== eCapture
];
1686 static HRESULT WINAPI
AudioClient_Start(IAudioClient
*iface
)
1688 ACImpl
*This
= impl_from_IAudioClient(iface
);
1693 TRACE("(%p)\n", This
);
1695 pthread_mutex_lock(&pulse_lock
);
1696 hr
= pulse_stream_valid(This
);
1698 pthread_mutex_unlock(&pulse_lock
);
1702 if ((This
->flags
& AUDCLNT_STREAMFLAGS_EVENTCALLBACK
) && !This
->event
) {
1703 pthread_mutex_unlock(&pulse_lock
);
1704 return AUDCLNT_E_EVENTHANDLE_NOT_SET
;
1707 if (This
->started
) {
1708 pthread_mutex_unlock(&pulse_lock
);
1709 return AUDCLNT_E_NOT_STOPPED
;
1712 if (pa_stream_is_corked(This
->stream
)) {
1713 o
= pa_stream_cork(This
->stream
, 0, pulse_op_cb
, &success
);
1715 while(pa_operation_get_state(o
) == PA_OPERATION_RUNNING
)
1716 pthread_cond_wait(&pulse_cond
, &pulse_lock
);
1717 pa_operation_unref(o
);
1723 if (SUCCEEDED(hr
)) {
1724 This
->started
= TRUE
;
1725 if (This
->dataflow
== eRender
&& This
->event
)
1726 pa_stream_set_latency_update_callback(This
->stream
, pulse_latency_callback
, This
);
1728 pthread_mutex_unlock(&pulse_lock
);
1732 static HRESULT WINAPI
AudioClient_Stop(IAudioClient
*iface
)
1734 ACImpl
*This
= impl_from_IAudioClient(iface
);
1739 TRACE("(%p)\n", This
);
1741 pthread_mutex_lock(&pulse_lock
);
1742 hr
= pulse_stream_valid(This
);
1744 pthread_mutex_unlock(&pulse_lock
);
1748 if (!This
->started
) {
1749 pthread_mutex_unlock(&pulse_lock
);
1753 if (This
->dataflow
== eRender
) {
1754 o
= pa_stream_cork(This
->stream
, 1, pulse_op_cb
, &success
);
1756 while(pa_operation_get_state(o
) == PA_OPERATION_RUNNING
)
1757 pthread_cond_wait(&pulse_cond
, &pulse_lock
);
1758 pa_operation_unref(o
);
1764 if (SUCCEEDED(hr
)) {
1765 This
->started
= FALSE
;
1767 pthread_mutex_unlock(&pulse_lock
);
1771 static HRESULT WINAPI
AudioClient_Reset(IAudioClient
*iface
)
1773 ACImpl
*This
= impl_from_IAudioClient(iface
);
1776 TRACE("(%p)\n", This
);
1778 pthread_mutex_lock(&pulse_lock
);
1779 hr
= pulse_stream_valid(This
);
1781 pthread_mutex_unlock(&pulse_lock
);
1785 if (This
->started
) {
1786 pthread_mutex_unlock(&pulse_lock
);
1787 return AUDCLNT_E_NOT_STOPPED
;
1791 pthread_mutex_unlock(&pulse_lock
);
1792 return AUDCLNT_E_BUFFER_OPERATION_PENDING
;
1795 if (This
->dataflow
== eRender
) {
1796 /* If there is still data in the render buffer it needs to be removed from the server */
1799 pa_operation
*o
= pa_stream_flush(This
->stream
, pulse_op_cb
, &success
);
1801 while(pa_operation_get_state(o
) == PA_OPERATION_RUNNING
)
1802 pthread_cond_wait(&pulse_cond
, &pulse_lock
);
1803 pa_operation_unref(o
);
1806 if (success
|| !This
->pad
)
1807 This
->clock_lastpos
= This
->clock_written
= This
->pad
= 0;
1810 This
->clock_written
+= This
->pad
;
1813 if ((p
= This
->locked_ptr
)) {
1814 This
->locked_ptr
= NULL
;
1815 list_add_tail(&This
->packet_free_head
, &p
->entry
);
1817 list_move_tail(&This
->packet_free_head
, &This
->packet_filled_head
);
1819 pthread_mutex_unlock(&pulse_lock
);
1824 static HRESULT WINAPI
AudioClient_SetEventHandle(IAudioClient
*iface
,
1827 ACImpl
*This
= impl_from_IAudioClient(iface
);
1830 TRACE("(%p)->(%p)\n", This
, event
);
1833 return E_INVALIDARG
;
1835 pthread_mutex_lock(&pulse_lock
);
1836 hr
= pulse_stream_valid(This
);
1838 pthread_mutex_unlock(&pulse_lock
);
1842 if (!(This
->flags
& AUDCLNT_STREAMFLAGS_EVENTCALLBACK
))
1843 hr
= AUDCLNT_E_EVENTHANDLE_NOT_EXPECTED
;
1844 else if (This
->event
)
1845 hr
= HRESULT_FROM_WIN32(ERROR_INVALID_NAME
);
1847 This
->event
= event
;
1848 pthread_mutex_unlock(&pulse_lock
);
1852 static HRESULT WINAPI
AudioClient_GetService(IAudioClient
*iface
, REFIID riid
,
1855 ACImpl
*This
= impl_from_IAudioClient(iface
);
1858 TRACE("(%p)->(%s, %p)\n", This
, debugstr_guid(riid
), ppv
);
1864 pthread_mutex_lock(&pulse_lock
);
1865 hr
= pulse_stream_valid(This
);
1866 pthread_mutex_unlock(&pulse_lock
);
1870 if (IsEqualIID(riid
, &IID_IAudioRenderClient
)) {
1871 if (This
->dataflow
!= eRender
)
1872 return AUDCLNT_E_WRONG_ENDPOINT_TYPE
;
1873 *ppv
= &This
->IAudioRenderClient_iface
;
1874 } else if (IsEqualIID(riid
, &IID_IAudioCaptureClient
)) {
1875 if (This
->dataflow
!= eCapture
)
1876 return AUDCLNT_E_WRONG_ENDPOINT_TYPE
;
1877 *ppv
= &This
->IAudioCaptureClient_iface
;
1878 } else if (IsEqualIID(riid
, &IID_IAudioClock
)) {
1879 *ppv
= &This
->IAudioClock_iface
;
1880 } else if (IsEqualIID(riid
, &IID_IAudioStreamVolume
)) {
1881 *ppv
= &This
->IAudioStreamVolume_iface
;
1882 } else if (IsEqualIID(riid
, &IID_IAudioSessionControl
) ||
1883 IsEqualIID(riid
, &IID_IChannelAudioVolume
) ||
1884 IsEqualIID(riid
, &IID_ISimpleAudioVolume
)) {
1885 if (!This
->session_wrapper
) {
1886 This
->session_wrapper
= AudioSessionWrapper_Create(This
);
1887 if (!This
->session_wrapper
)
1888 return E_OUTOFMEMORY
;
1890 if (IsEqualIID(riid
, &IID_IAudioSessionControl
))
1891 *ppv
= &This
->session_wrapper
->IAudioSessionControl2_iface
;
1892 else if (IsEqualIID(riid
, &IID_IChannelAudioVolume
))
1893 *ppv
= &This
->session_wrapper
->IChannelAudioVolume_iface
;
1894 else if (IsEqualIID(riid
, &IID_ISimpleAudioVolume
))
1895 *ppv
= &This
->session_wrapper
->ISimpleAudioVolume_iface
;
1899 IUnknown_AddRef((IUnknown
*)*ppv
);
1903 FIXME("stub %s\n", debugstr_guid(riid
));
1904 return E_NOINTERFACE
;
1907 static const IAudioClientVtbl AudioClient_Vtbl
=
1909 AudioClient_QueryInterface
,
1911 AudioClient_Release
,
1912 AudioClient_Initialize
,
1913 AudioClient_GetBufferSize
,
1914 AudioClient_GetStreamLatency
,
1915 AudioClient_GetCurrentPadding
,
1916 AudioClient_IsFormatSupported
,
1917 AudioClient_GetMixFormat
,
1918 AudioClient_GetDevicePeriod
,
1922 AudioClient_SetEventHandle
,
1923 AudioClient_GetService
1926 static HRESULT WINAPI
AudioRenderClient_QueryInterface(
1927 IAudioRenderClient
*iface
, REFIID riid
, void **ppv
)
1929 ACImpl
*This
= impl_from_IAudioRenderClient(iface
);
1930 TRACE("(%p)->(%s, %p)\n", iface
, debugstr_guid(riid
), ppv
);
1936 if (IsEqualIID(riid
, &IID_IUnknown
) ||
1937 IsEqualIID(riid
, &IID_IAudioRenderClient
))
1940 IUnknown_AddRef((IUnknown
*)*ppv
);
1944 if (IsEqualIID(riid
, &IID_IMarshal
))
1945 return IUnknown_QueryInterface(This
->marshal
, riid
, ppv
);
1947 WARN("Unknown interface %s\n", debugstr_guid(riid
));
1948 return E_NOINTERFACE
;
1951 static ULONG WINAPI
AudioRenderClient_AddRef(IAudioRenderClient
*iface
)
1953 ACImpl
*This
= impl_from_IAudioRenderClient(iface
);
1954 return AudioClient_AddRef(&This
->IAudioClient_iface
);
1957 static ULONG WINAPI
AudioRenderClient_Release(IAudioRenderClient
*iface
)
1959 ACImpl
*This
= impl_from_IAudioRenderClient(iface
);
1960 return AudioClient_Release(&This
->IAudioClient_iface
);
1963 static HRESULT WINAPI
AudioRenderClient_GetBuffer(IAudioRenderClient
*iface
,
1964 UINT32 frames
, BYTE
**data
)
1966 ACImpl
*This
= impl_from_IAudioRenderClient(iface
);
1967 size_t avail
, req
, bytes
= frames
* pa_frame_size(&This
->ss
);
1972 TRACE("(%p)->(%u, %p)\n", This
, frames
, data
);
1978 pthread_mutex_lock(&pulse_lock
);
1979 hr
= pulse_stream_valid(This
);
1980 if (FAILED(hr
) || This
->locked
) {
1981 pthread_mutex_unlock(&pulse_lock
);
1982 return FAILED(hr
) ? hr
: AUDCLNT_E_OUT_OF_ORDER
;
1985 pthread_mutex_unlock(&pulse_lock
);
1989 ACImpl_GetRenderPad(This
, &pad
);
1990 avail
= This
->bufsize_frames
- pad
;
1991 if (avail
< frames
|| bytes
> This
->bufsize_bytes
) {
1992 pthread_mutex_unlock(&pulse_lock
);
1993 WARN("Wanted to write %u, but only %zu available\n", frames
, avail
);
1994 return AUDCLNT_E_BUFFER_TOO_LARGE
;
1997 This
->locked
= frames
;
1999 ret
= pa_stream_begin_write(This
->stream
, &This
->locked_ptr
, &req
);
2000 if (ret
< 0 || req
< bytes
) {
2001 FIXME("%p Not using pulse locked data: %i %zu/%u %u/%u\n", This
, ret
, req
/pa_frame_size(&This
->ss
), frames
, pad
, This
->bufsize_frames
);
2003 pa_stream_cancel_write(This
->stream
);
2004 *data
= This
->tmp_buffer
;
2005 This
->locked_ptr
= NULL
;
2007 *data
= This
->locked_ptr
;
2008 pthread_mutex_unlock(&pulse_lock
);
2012 static void pulse_free_noop(void *buf
)
2016 static HRESULT WINAPI
AudioRenderClient_ReleaseBuffer(
2017 IAudioRenderClient
*iface
, UINT32 written_frames
, DWORD flags
)
2019 ACImpl
*This
= impl_from_IAudioRenderClient(iface
);
2020 UINT32 written_bytes
= written_frames
* pa_frame_size(&This
->ss
);
2022 TRACE("(%p)->(%u, %x)\n", This
, written_frames
, flags
);
2024 pthread_mutex_lock(&pulse_lock
);
2025 if (!This
->locked
|| !written_frames
) {
2026 if (This
->locked_ptr
)
2027 pa_stream_cancel_write(This
->stream
);
2029 This
->locked_ptr
= NULL
;
2030 pthread_mutex_unlock(&pulse_lock
);
2031 return written_frames
? AUDCLNT_E_OUT_OF_ORDER
: S_OK
;
2034 if (This
->locked
< written_frames
) {
2035 pthread_mutex_unlock(&pulse_lock
);
2036 return AUDCLNT_E_INVALID_SIZE
;
2040 if (This
->locked_ptr
) {
2041 if (flags
& AUDCLNT_BUFFERFLAGS_SILENT
)
2042 silence_buffer(This
->ss
.format
, This
->locked_ptr
, written_bytes
);
2043 pa_stream_write(This
->stream
, This
->locked_ptr
, written_bytes
, NULL
, 0, PA_SEEK_RELATIVE
);
2045 if (flags
& AUDCLNT_BUFFERFLAGS_SILENT
)
2046 silence_buffer(This
->ss
.format
, This
->tmp_buffer
, written_bytes
);
2047 pa_stream_write(This
->stream
, This
->tmp_buffer
, written_bytes
, pulse_free_noop
, 0, PA_SEEK_RELATIVE
);
2050 This
->pad
+= written_bytes
;
2051 This
->locked_ptr
= NULL
;
2052 TRACE("Released %u, pad %zu\n", written_frames
, This
->pad
/ pa_frame_size(&This
->ss
));
2053 assert(This
->pad
<= This
->bufsize_bytes
);
2055 pthread_mutex_unlock(&pulse_lock
);
2059 static const IAudioRenderClientVtbl AudioRenderClient_Vtbl
= {
2060 AudioRenderClient_QueryInterface
,
2061 AudioRenderClient_AddRef
,
2062 AudioRenderClient_Release
,
2063 AudioRenderClient_GetBuffer
,
2064 AudioRenderClient_ReleaseBuffer
2067 static HRESULT WINAPI
AudioCaptureClient_QueryInterface(
2068 IAudioCaptureClient
*iface
, REFIID riid
, void **ppv
)
2070 ACImpl
*This
= impl_from_IAudioCaptureClient(iface
);
2071 TRACE("(%p)->(%s, %p)\n", iface
, debugstr_guid(riid
), ppv
);
2077 if (IsEqualIID(riid
, &IID_IUnknown
) ||
2078 IsEqualIID(riid
, &IID_IAudioCaptureClient
))
2081 IUnknown_AddRef((IUnknown
*)*ppv
);
2085 if (IsEqualIID(riid
, &IID_IMarshal
))
2086 return IUnknown_QueryInterface(This
->marshal
, riid
, ppv
);
2088 WARN("Unknown interface %s\n", debugstr_guid(riid
));
2089 return E_NOINTERFACE
;
2092 static ULONG WINAPI
AudioCaptureClient_AddRef(IAudioCaptureClient
*iface
)
2094 ACImpl
*This
= impl_from_IAudioCaptureClient(iface
);
2095 return IAudioClient_AddRef(&This
->IAudioClient_iface
);
2098 static ULONG WINAPI
AudioCaptureClient_Release(IAudioCaptureClient
*iface
)
2100 ACImpl
*This
= impl_from_IAudioCaptureClient(iface
);
2101 return IAudioClient_Release(&This
->IAudioClient_iface
);
2104 static HRESULT WINAPI
AudioCaptureClient_GetBuffer(IAudioCaptureClient
*iface
,
2105 BYTE
**data
, UINT32
*frames
, DWORD
*flags
, UINT64
*devpos
,
2108 ACImpl
*This
= impl_from_IAudioCaptureClient(iface
);
2112 TRACE("(%p)->(%p, %p, %p, %p, %p)\n", This
, data
, frames
, flags
,
2115 if (!data
|| !frames
|| !flags
)
2118 pthread_mutex_lock(&pulse_lock
);
2119 hr
= pulse_stream_valid(This
);
2120 if (FAILED(hr
) || This
->locked
) {
2121 pthread_mutex_unlock(&pulse_lock
);
2122 return FAILED(hr
) ? hr
: AUDCLNT_E_OUT_OF_ORDER
;
2125 ACImpl_GetCapturePad(This
, NULL
);
2126 if ((packet
= This
->locked_ptr
)) {
2127 *frames
= This
->capture_period
/ pa_frame_size(&This
->ss
);
2129 if (packet
->discont
)
2130 *flags
|= AUDCLNT_BUFFERFLAGS_DATA_DISCONTINUITY
;
2132 if (packet
->discont
)
2133 *devpos
= (This
->clock_written
+ This
->capture_period
) / pa_frame_size(&This
->ss
);
2135 *devpos
= This
->clock_written
/ pa_frame_size(&This
->ss
);
2138 *qpcpos
= packet
->qpcpos
;
2139 *data
= packet
->data
;
2143 This
->locked
= *frames
;
2144 pthread_mutex_unlock(&pulse_lock
);
2145 return *frames
? S_OK
: AUDCLNT_S_BUFFER_EMPTY
;
2148 static HRESULT WINAPI
AudioCaptureClient_ReleaseBuffer(
2149 IAudioCaptureClient
*iface
, UINT32 done
)
2151 ACImpl
*This
= impl_from_IAudioCaptureClient(iface
);
2153 TRACE("(%p)->(%u)\n", This
, done
);
2155 pthread_mutex_lock(&pulse_lock
);
2156 if (!This
->locked
&& done
) {
2157 pthread_mutex_unlock(&pulse_lock
);
2158 return AUDCLNT_E_OUT_OF_ORDER
;
2160 if (done
&& This
->locked
!= done
) {
2161 pthread_mutex_unlock(&pulse_lock
);
2162 return AUDCLNT_E_INVALID_SIZE
;
2165 ACPacket
*packet
= This
->locked_ptr
;
2166 This
->locked_ptr
= NULL
;
2167 This
->pad
-= This
->capture_period
;
2168 if (packet
->discont
)
2169 This
->clock_written
+= 2 * This
->capture_period
;
2171 This
->clock_written
+= This
->capture_period
;
2172 list_add_tail(&This
->packet_free_head
, &packet
->entry
);
2175 pthread_mutex_unlock(&pulse_lock
);
2179 static HRESULT WINAPI
AudioCaptureClient_GetNextPacketSize(
2180 IAudioCaptureClient
*iface
, UINT32
*frames
)
2182 ACImpl
*This
= impl_from_IAudioCaptureClient(iface
);
2184 TRACE("(%p)->(%p)\n", This
, frames
);
2188 pthread_mutex_lock(&pulse_lock
);
2189 ACImpl_GetCapturePad(This
, NULL
);
2190 if (This
->locked_ptr
)
2191 *frames
= This
->capture_period
/ pa_frame_size(&This
->ss
);
2194 pthread_mutex_unlock(&pulse_lock
);
2198 static const IAudioCaptureClientVtbl AudioCaptureClient_Vtbl
=
2200 AudioCaptureClient_QueryInterface
,
2201 AudioCaptureClient_AddRef
,
2202 AudioCaptureClient_Release
,
2203 AudioCaptureClient_GetBuffer
,
2204 AudioCaptureClient_ReleaseBuffer
,
2205 AudioCaptureClient_GetNextPacketSize
2208 static HRESULT WINAPI
AudioClock_QueryInterface(IAudioClock
*iface
,
2209 REFIID riid
, void **ppv
)
2211 ACImpl
*This
= impl_from_IAudioClock(iface
);
2213 TRACE("(%p)->(%s, %p)\n", iface
, debugstr_guid(riid
), ppv
);
2219 if (IsEqualIID(riid
, &IID_IUnknown
) || IsEqualIID(riid
, &IID_IAudioClock
))
2221 else if (IsEqualIID(riid
, &IID_IAudioClock2
))
2222 *ppv
= &This
->IAudioClock2_iface
;
2224 IUnknown_AddRef((IUnknown
*)*ppv
);
2228 if (IsEqualIID(riid
, &IID_IMarshal
))
2229 return IUnknown_QueryInterface(This
->marshal
, riid
, ppv
);
2231 WARN("Unknown interface %s\n", debugstr_guid(riid
));
2232 return E_NOINTERFACE
;
2235 static ULONG WINAPI
AudioClock_AddRef(IAudioClock
*iface
)
2237 ACImpl
*This
= impl_from_IAudioClock(iface
);
2238 return IAudioClient_AddRef(&This
->IAudioClient_iface
);
2241 static ULONG WINAPI
AudioClock_Release(IAudioClock
*iface
)
2243 ACImpl
*This
= impl_from_IAudioClock(iface
);
2244 return IAudioClient_Release(&This
->IAudioClient_iface
);
2247 static HRESULT WINAPI
AudioClock_GetFrequency(IAudioClock
*iface
, UINT64
*freq
)
2249 ACImpl
*This
= impl_from_IAudioClock(iface
);
2252 TRACE("(%p)->(%p)\n", This
, freq
);
2254 pthread_mutex_lock(&pulse_lock
);
2255 hr
= pulse_stream_valid(This
);
2256 if (SUCCEEDED(hr
)) {
2257 *freq
= This
->ss
.rate
;
2258 if (This
->share
== AUDCLNT_SHAREMODE_SHARED
)
2259 *freq
*= pa_frame_size(&This
->ss
);
2261 pthread_mutex_unlock(&pulse_lock
);
2265 static HRESULT WINAPI
AudioClock_GetPosition(IAudioClock
*iface
, UINT64
*pos
,
2268 ACImpl
*This
= impl_from_IAudioClock(iface
);
2271 TRACE("(%p)->(%p, %p)\n", This
, pos
, qpctime
);
2276 pthread_mutex_lock(&pulse_lock
);
2277 hr
= pulse_stream_valid(This
);
2279 pthread_mutex_unlock(&pulse_lock
);
2283 *pos
= This
->clock_written
;
2285 if (This
->share
== AUDCLNT_SHAREMODE_EXCLUSIVE
)
2286 *pos
/= pa_frame_size(&This
->ss
);
2288 /* Make time never go backwards */
2289 if (*pos
< This
->clock_lastpos
)
2290 *pos
= This
->clock_lastpos
;
2292 This
->clock_lastpos
= *pos
;
2293 pthread_mutex_unlock(&pulse_lock
);
2295 TRACE("%p Position: %u\n", This
, (unsigned)*pos
);
2298 LARGE_INTEGER stamp
, freq
;
2299 QueryPerformanceCounter(&stamp
);
2300 QueryPerformanceFrequency(&freq
);
2301 *qpctime
= (stamp
.QuadPart
* (INT64
)10000000) / freq
.QuadPart
;
2307 static HRESULT WINAPI
AudioClock_GetCharacteristics(IAudioClock
*iface
,
2310 ACImpl
*This
= impl_from_IAudioClock(iface
);
2312 TRACE("(%p)->(%p)\n", This
, chars
);
2317 *chars
= AUDIOCLOCK_CHARACTERISTIC_FIXED_FREQ
;
2322 static const IAudioClockVtbl AudioClock_Vtbl
=
2324 AudioClock_QueryInterface
,
2327 AudioClock_GetFrequency
,
2328 AudioClock_GetPosition
,
2329 AudioClock_GetCharacteristics
2332 static HRESULT WINAPI
AudioClock2_QueryInterface(IAudioClock2
*iface
,
2333 REFIID riid
, void **ppv
)
2335 ACImpl
*This
= impl_from_IAudioClock2(iface
);
2336 return IAudioClock_QueryInterface(&This
->IAudioClock_iface
, riid
, ppv
);
2339 static ULONG WINAPI
AudioClock2_AddRef(IAudioClock2
*iface
)
2341 ACImpl
*This
= impl_from_IAudioClock2(iface
);
2342 return IAudioClient_AddRef(&This
->IAudioClient_iface
);
2345 static ULONG WINAPI
AudioClock2_Release(IAudioClock2
*iface
)
2347 ACImpl
*This
= impl_from_IAudioClock2(iface
);
2348 return IAudioClient_Release(&This
->IAudioClient_iface
);
2351 static HRESULT WINAPI
AudioClock2_GetDevicePosition(IAudioClock2
*iface
,
2352 UINT64
*pos
, UINT64
*qpctime
)
2354 ACImpl
*This
= impl_from_IAudioClock2(iface
);
2355 HRESULT hr
= AudioClock_GetPosition(&This
->IAudioClock_iface
, pos
, qpctime
);
2356 if (SUCCEEDED(hr
) && This
->share
== AUDCLNT_SHAREMODE_SHARED
)
2357 *pos
/= pa_frame_size(&This
->ss
);
2361 static const IAudioClock2Vtbl AudioClock2_Vtbl
=
2363 AudioClock2_QueryInterface
,
2365 AudioClock2_Release
,
2366 AudioClock2_GetDevicePosition
2369 static HRESULT WINAPI
AudioStreamVolume_QueryInterface(
2370 IAudioStreamVolume
*iface
, REFIID riid
, void **ppv
)
2372 ACImpl
*This
= impl_from_IAudioStreamVolume(iface
);
2374 TRACE("(%p)->(%s, %p)\n", iface
, debugstr_guid(riid
), ppv
);
2380 if (IsEqualIID(riid
, &IID_IUnknown
) ||
2381 IsEqualIID(riid
, &IID_IAudioStreamVolume
))
2384 IUnknown_AddRef((IUnknown
*)*ppv
);
2388 if (IsEqualIID(riid
, &IID_IMarshal
))
2389 return IUnknown_QueryInterface(This
->marshal
, riid
, ppv
);
2391 WARN("Unknown interface %s\n", debugstr_guid(riid
));
2392 return E_NOINTERFACE
;
2395 static ULONG WINAPI
AudioStreamVolume_AddRef(IAudioStreamVolume
*iface
)
2397 ACImpl
*This
= impl_from_IAudioStreamVolume(iface
);
2398 return IAudioClient_AddRef(&This
->IAudioClient_iface
);
2401 static ULONG WINAPI
AudioStreamVolume_Release(IAudioStreamVolume
*iface
)
2403 ACImpl
*This
= impl_from_IAudioStreamVolume(iface
);
2404 return IAudioClient_Release(&This
->IAudioClient_iface
);
2407 static HRESULT WINAPI
AudioStreamVolume_GetChannelCount(
2408 IAudioStreamVolume
*iface
, UINT32
*out
)
2410 ACImpl
*This
= impl_from_IAudioStreamVolume(iface
);
2412 TRACE("(%p)->(%p)\n", This
, out
);
2417 *out
= This
->ss
.channels
;
2422 struct pulse_info_cb_data
{
2427 static HRESULT WINAPI
AudioStreamVolume_SetAllVolumes(
2428 IAudioStreamVolume
*iface
, UINT32 count
, const float *levels
)
2430 ACImpl
*This
= impl_from_IAudioStreamVolume(iface
);
2434 TRACE("(%p)->(%d, %p)\n", This
, count
, levels
);
2439 if (count
!= This
->ss
.channels
)
2440 return E_INVALIDARG
;
2442 pthread_mutex_lock(&pulse_lock
);
2443 hr
= pulse_stream_valid(This
);
2447 for (i
= 0; i
< count
; ++i
)
2448 This
->vol
[i
] = levels
[i
];
2451 pthread_mutex_unlock(&pulse_lock
);
2455 static HRESULT WINAPI
AudioStreamVolume_GetAllVolumes(
2456 IAudioStreamVolume
*iface
, UINT32 count
, float *levels
)
2458 ACImpl
*This
= impl_from_IAudioStreamVolume(iface
);
2462 TRACE("(%p)->(%d, %p)\n", This
, count
, levels
);
2467 if (count
!= This
->ss
.channels
)
2468 return E_INVALIDARG
;
2470 pthread_mutex_lock(&pulse_lock
);
2471 hr
= pulse_stream_valid(This
);
2475 for (i
= 0; i
< count
; ++i
)
2476 levels
[i
] = This
->vol
[i
];
2479 pthread_mutex_unlock(&pulse_lock
);
2483 static HRESULT WINAPI
AudioStreamVolume_SetChannelVolume(
2484 IAudioStreamVolume
*iface
, UINT32 index
, float level
)
2486 ACImpl
*This
= impl_from_IAudioStreamVolume(iface
);
2488 float volumes
[PA_CHANNELS_MAX
];
2490 TRACE("(%p)->(%d, %f)\n", This
, index
, level
);
2492 if (level
< 0.f
|| level
> 1.f
)
2493 return E_INVALIDARG
;
2495 if (index
>= This
->ss
.channels
)
2496 return E_INVALIDARG
;
2498 hr
= AudioStreamVolume_GetAllVolumes(iface
, This
->ss
.channels
, volumes
);
2499 volumes
[index
] = level
;
2501 hr
= AudioStreamVolume_SetAllVolumes(iface
, This
->ss
.channels
, volumes
);
2505 static HRESULT WINAPI
AudioStreamVolume_GetChannelVolume(
2506 IAudioStreamVolume
*iface
, UINT32 index
, float *level
)
2508 ACImpl
*This
= impl_from_IAudioStreamVolume(iface
);
2509 float volumes
[PA_CHANNELS_MAX
];
2512 TRACE("(%p)->(%d, %p)\n", This
, index
, level
);
2517 if (index
>= This
->ss
.channels
)
2518 return E_INVALIDARG
;
2520 hr
= AudioStreamVolume_GetAllVolumes(iface
, This
->ss
.channels
, volumes
);
2522 *level
= volumes
[index
];
2526 static const IAudioStreamVolumeVtbl AudioStreamVolume_Vtbl
=
2528 AudioStreamVolume_QueryInterface
,
2529 AudioStreamVolume_AddRef
,
2530 AudioStreamVolume_Release
,
2531 AudioStreamVolume_GetChannelCount
,
2532 AudioStreamVolume_SetChannelVolume
,
2533 AudioStreamVolume_GetChannelVolume
,
2534 AudioStreamVolume_SetAllVolumes
,
2535 AudioStreamVolume_GetAllVolumes
2538 static AudioSessionWrapper
*AudioSessionWrapper_Create(ACImpl
*client
)
2540 AudioSessionWrapper
*ret
;
2542 ret
= HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY
,
2543 sizeof(AudioSessionWrapper
));
2547 ret
->IAudioSessionControl2_iface
.lpVtbl
= &AudioSessionControl2_Vtbl
;
2548 ret
->ISimpleAudioVolume_iface
.lpVtbl
= &SimpleAudioVolume_Vtbl
;
2549 ret
->IChannelAudioVolume_iface
.lpVtbl
= &ChannelAudioVolume_Vtbl
;
2553 ret
->client
= client
;
2555 ret
->session
= client
->session
;
2556 AudioClient_AddRef(&client
->IAudioClient_iface
);
2562 static HRESULT WINAPI
AudioSessionControl_QueryInterface(
2563 IAudioSessionControl2
*iface
, REFIID riid
, void **ppv
)
2565 TRACE("(%p)->(%s, %p)\n", iface
, debugstr_guid(riid
), ppv
);
2571 if (IsEqualIID(riid
, &IID_IUnknown
) ||
2572 IsEqualIID(riid
, &IID_IAudioSessionControl
) ||
2573 IsEqualIID(riid
, &IID_IAudioSessionControl2
))
2576 IUnknown_AddRef((IUnknown
*)*ppv
);
2580 WARN("Unknown interface %s\n", debugstr_guid(riid
));
2581 return E_NOINTERFACE
;
2584 static ULONG WINAPI
AudioSessionControl_AddRef(IAudioSessionControl2
*iface
)
2586 AudioSessionWrapper
*This
= impl_from_IAudioSessionControl2(iface
);
2588 ref
= InterlockedIncrement(&This
->ref
);
2589 TRACE("(%p) Refcount now %u\n", This
, ref
);
2593 static ULONG WINAPI
AudioSessionControl_Release(IAudioSessionControl2
*iface
)
2595 AudioSessionWrapper
*This
= impl_from_IAudioSessionControl2(iface
);
2597 ref
= InterlockedDecrement(&This
->ref
);
2598 TRACE("(%p) Refcount now %u\n", This
, ref
);
2601 This
->client
->session_wrapper
= NULL
;
2602 AudioClient_Release(&This
->client
->IAudioClient_iface
);
2604 HeapFree(GetProcessHeap(), 0, This
);
2609 static HRESULT WINAPI
AudioSessionControl_GetState(IAudioSessionControl2
*iface
,
2610 AudioSessionState
*state
)
2612 AudioSessionWrapper
*This
= impl_from_IAudioSessionControl2(iface
);
2615 TRACE("(%p)->(%p)\n", This
, state
);
2618 return NULL_PTR_ERR
;
2620 pthread_mutex_lock(&pulse_lock
);
2621 if (list_empty(&This
->session
->clients
)) {
2622 *state
= AudioSessionStateExpired
;
2625 LIST_FOR_EACH_ENTRY(client
, &This
->session
->clients
, ACImpl
, entry
) {
2626 if (client
->started
) {
2627 *state
= AudioSessionStateActive
;
2631 *state
= AudioSessionStateInactive
;
2634 pthread_mutex_unlock(&pulse_lock
);
2638 static HRESULT WINAPI
AudioSessionControl_GetDisplayName(
2639 IAudioSessionControl2
*iface
, WCHAR
**name
)
2641 AudioSessionWrapper
*This
= impl_from_IAudioSessionControl2(iface
);
2643 FIXME("(%p)->(%p) - stub\n", This
, name
);
2648 static HRESULT WINAPI
AudioSessionControl_SetDisplayName(
2649 IAudioSessionControl2
*iface
, const WCHAR
*name
, const GUID
*session
)
2651 AudioSessionWrapper
*This
= impl_from_IAudioSessionControl2(iface
);
2653 FIXME("(%p)->(%p, %s) - stub\n", This
, name
, debugstr_guid(session
));
2658 static HRESULT WINAPI
AudioSessionControl_GetIconPath(
2659 IAudioSessionControl2
*iface
, WCHAR
**path
)
2661 AudioSessionWrapper
*This
= impl_from_IAudioSessionControl2(iface
);
2663 FIXME("(%p)->(%p) - stub\n", This
, path
);
2668 static HRESULT WINAPI
AudioSessionControl_SetIconPath(
2669 IAudioSessionControl2
*iface
, const WCHAR
*path
, const GUID
*session
)
2671 AudioSessionWrapper
*This
= impl_from_IAudioSessionControl2(iface
);
2673 FIXME("(%p)->(%p, %s) - stub\n", This
, path
, debugstr_guid(session
));
2678 static HRESULT WINAPI
AudioSessionControl_GetGroupingParam(
2679 IAudioSessionControl2
*iface
, GUID
*group
)
2681 AudioSessionWrapper
*This
= impl_from_IAudioSessionControl2(iface
);
2683 FIXME("(%p)->(%p) - stub\n", This
, group
);
2688 static HRESULT WINAPI
AudioSessionControl_SetGroupingParam(
2689 IAudioSessionControl2
*iface
, const GUID
*group
, const GUID
*session
)
2691 AudioSessionWrapper
*This
= impl_from_IAudioSessionControl2(iface
);
2693 FIXME("(%p)->(%s, %s) - stub\n", This
, debugstr_guid(group
),
2694 debugstr_guid(session
));
2699 static HRESULT WINAPI
AudioSessionControl_RegisterAudioSessionNotification(
2700 IAudioSessionControl2
*iface
, IAudioSessionEvents
*events
)
2702 AudioSessionWrapper
*This
= impl_from_IAudioSessionControl2(iface
);
2704 FIXME("(%p)->(%p) - stub\n", This
, events
);
2709 static HRESULT WINAPI
AudioSessionControl_UnregisterAudioSessionNotification(
2710 IAudioSessionControl2
*iface
, IAudioSessionEvents
*events
)
2712 AudioSessionWrapper
*This
= impl_from_IAudioSessionControl2(iface
);
2714 FIXME("(%p)->(%p) - stub\n", This
, events
);
2719 static HRESULT WINAPI
AudioSessionControl_GetSessionIdentifier(
2720 IAudioSessionControl2
*iface
, WCHAR
**id
)
2722 AudioSessionWrapper
*This
= impl_from_IAudioSessionControl2(iface
);
2724 FIXME("(%p)->(%p) - stub\n", This
, id
);
2729 static HRESULT WINAPI
AudioSessionControl_GetSessionInstanceIdentifier(
2730 IAudioSessionControl2
*iface
, WCHAR
**id
)
2732 AudioSessionWrapper
*This
= impl_from_IAudioSessionControl2(iface
);
2734 FIXME("(%p)->(%p) - stub\n", This
, id
);
2739 static HRESULT WINAPI
AudioSessionControl_GetProcessId(
2740 IAudioSessionControl2
*iface
, DWORD
*pid
)
2742 AudioSessionWrapper
*This
= impl_from_IAudioSessionControl2(iface
);
2744 TRACE("(%p)->(%p)\n", This
, pid
);
2749 *pid
= GetCurrentProcessId();
2754 static HRESULT WINAPI
AudioSessionControl_IsSystemSoundsSession(
2755 IAudioSessionControl2
*iface
)
2757 AudioSessionWrapper
*This
= impl_from_IAudioSessionControl2(iface
);
2759 TRACE("(%p)\n", This
);
2764 static HRESULT WINAPI
AudioSessionControl_SetDuckingPreference(
2765 IAudioSessionControl2
*iface
, BOOL optout
)
2767 AudioSessionWrapper
*This
= impl_from_IAudioSessionControl2(iface
);
2769 TRACE("(%p)->(%d)\n", This
, optout
);
2774 static const IAudioSessionControl2Vtbl AudioSessionControl2_Vtbl
=
2776 AudioSessionControl_QueryInterface
,
2777 AudioSessionControl_AddRef
,
2778 AudioSessionControl_Release
,
2779 AudioSessionControl_GetState
,
2780 AudioSessionControl_GetDisplayName
,
2781 AudioSessionControl_SetDisplayName
,
2782 AudioSessionControl_GetIconPath
,
2783 AudioSessionControl_SetIconPath
,
2784 AudioSessionControl_GetGroupingParam
,
2785 AudioSessionControl_SetGroupingParam
,
2786 AudioSessionControl_RegisterAudioSessionNotification
,
2787 AudioSessionControl_UnregisterAudioSessionNotification
,
2788 AudioSessionControl_GetSessionIdentifier
,
2789 AudioSessionControl_GetSessionInstanceIdentifier
,
2790 AudioSessionControl_GetProcessId
,
2791 AudioSessionControl_IsSystemSoundsSession
,
2792 AudioSessionControl_SetDuckingPreference
2795 typedef struct _SessionMgr
{
2796 IAudioSessionManager2 IAudioSessionManager2_iface
;
2803 static HRESULT WINAPI
AudioSessionManager_QueryInterface(IAudioSessionManager2
*iface
,
2804 REFIID riid
, void **ppv
)
2806 TRACE("(%p)->(%s, %p)\n", iface
, debugstr_guid(riid
), ppv
);
2812 if (IsEqualIID(riid
, &IID_IUnknown
) ||
2813 IsEqualIID(riid
, &IID_IAudioSessionManager
) ||
2814 IsEqualIID(riid
, &IID_IAudioSessionManager2
))
2817 IUnknown_AddRef((IUnknown
*)*ppv
);
2821 WARN("Unknown interface %s\n", debugstr_guid(riid
));
2822 return E_NOINTERFACE
;
2825 static inline SessionMgr
*impl_from_IAudioSessionManager2(IAudioSessionManager2
*iface
)
2827 return CONTAINING_RECORD(iface
, SessionMgr
, IAudioSessionManager2_iface
);
2830 static ULONG WINAPI
AudioSessionManager_AddRef(IAudioSessionManager2
*iface
)
2832 SessionMgr
*This
= impl_from_IAudioSessionManager2(iface
);
2834 ref
= InterlockedIncrement(&This
->ref
);
2835 TRACE("(%p) Refcount now %u\n", This
, ref
);
2839 static ULONG WINAPI
AudioSessionManager_Release(IAudioSessionManager2
*iface
)
2841 SessionMgr
*This
= impl_from_IAudioSessionManager2(iface
);
2843 ref
= InterlockedDecrement(&This
->ref
);
2844 TRACE("(%p) Refcount now %u\n", This
, ref
);
2846 HeapFree(GetProcessHeap(), 0, This
);
2850 static HRESULT WINAPI
AudioSessionManager_GetAudioSessionControl(
2851 IAudioSessionManager2
*iface
, const GUID
*session_guid
, DWORD flags
,
2852 IAudioSessionControl
**out
)
2854 SessionMgr
*This
= impl_from_IAudioSessionManager2(iface
);
2855 AudioSession
*session
;
2856 AudioSessionWrapper
*wrapper
;
2859 TRACE("(%p)->(%s, %x, %p)\n", This
, debugstr_guid(session_guid
),
2862 hr
= get_audio_session(session_guid
, This
->device
, 0, &session
);
2866 wrapper
= AudioSessionWrapper_Create(NULL
);
2868 return E_OUTOFMEMORY
;
2870 wrapper
->session
= session
;
2872 *out
= (IAudioSessionControl
*)&wrapper
->IAudioSessionControl2_iface
;
2877 static HRESULT WINAPI
AudioSessionManager_GetSimpleAudioVolume(
2878 IAudioSessionManager2
*iface
, const GUID
*session_guid
, DWORD flags
,
2879 ISimpleAudioVolume
**out
)
2881 SessionMgr
*This
= impl_from_IAudioSessionManager2(iface
);
2882 AudioSession
*session
;
2883 AudioSessionWrapper
*wrapper
;
2886 TRACE("(%p)->(%s, %x, %p)\n", This
, debugstr_guid(session_guid
),
2889 hr
= get_audio_session(session_guid
, This
->device
, 0, &session
);
2893 wrapper
= AudioSessionWrapper_Create(NULL
);
2895 return E_OUTOFMEMORY
;
2897 wrapper
->session
= session
;
2899 *out
= &wrapper
->ISimpleAudioVolume_iface
;
2904 static HRESULT WINAPI
AudioSessionManager_GetSessionEnumerator(
2905 IAudioSessionManager2
*iface
, IAudioSessionEnumerator
**out
)
2907 SessionMgr
*This
= impl_from_IAudioSessionManager2(iface
);
2908 FIXME("(%p)->(%p) - stub\n", This
, out
);
2912 static HRESULT WINAPI
AudioSessionManager_RegisterSessionNotification(
2913 IAudioSessionManager2
*iface
, IAudioSessionNotification
*notification
)
2915 SessionMgr
*This
= impl_from_IAudioSessionManager2(iface
);
2916 FIXME("(%p)->(%p) - stub\n", This
, notification
);
2920 static HRESULT WINAPI
AudioSessionManager_UnregisterSessionNotification(
2921 IAudioSessionManager2
*iface
, IAudioSessionNotification
*notification
)
2923 SessionMgr
*This
= impl_from_IAudioSessionManager2(iface
);
2924 FIXME("(%p)->(%p) - stub\n", This
, notification
);
2928 static HRESULT WINAPI
AudioSessionManager_RegisterDuckNotification(
2929 IAudioSessionManager2
*iface
, const WCHAR
*session_id
,
2930 IAudioVolumeDuckNotification
*notification
)
2932 SessionMgr
*This
= impl_from_IAudioSessionManager2(iface
);
2933 FIXME("(%p)->(%p) - stub\n", This
, notification
);
2937 static HRESULT WINAPI
AudioSessionManager_UnregisterDuckNotification(
2938 IAudioSessionManager2
*iface
,
2939 IAudioVolumeDuckNotification
*notification
)
2941 SessionMgr
*This
= impl_from_IAudioSessionManager2(iface
);
2942 FIXME("(%p)->(%p) - stub\n", This
, notification
);
2946 static const IAudioSessionManager2Vtbl AudioSessionManager2_Vtbl
=
2948 AudioSessionManager_QueryInterface
,
2949 AudioSessionManager_AddRef
,
2950 AudioSessionManager_Release
,
2951 AudioSessionManager_GetAudioSessionControl
,
2952 AudioSessionManager_GetSimpleAudioVolume
,
2953 AudioSessionManager_GetSessionEnumerator
,
2954 AudioSessionManager_RegisterSessionNotification
,
2955 AudioSessionManager_UnregisterSessionNotification
,
2956 AudioSessionManager_RegisterDuckNotification
,
2957 AudioSessionManager_UnregisterDuckNotification
2960 static HRESULT WINAPI
SimpleAudioVolume_QueryInterface(
2961 ISimpleAudioVolume
*iface
, REFIID riid
, void **ppv
)
2963 TRACE("(%p)->(%s, %p)\n", iface
, debugstr_guid(riid
), ppv
);
2969 if (IsEqualIID(riid
, &IID_IUnknown
) ||
2970 IsEqualIID(riid
, &IID_ISimpleAudioVolume
))
2973 IUnknown_AddRef((IUnknown
*)*ppv
);
2977 WARN("Unknown interface %s\n", debugstr_guid(riid
));
2978 return E_NOINTERFACE
;
2981 static ULONG WINAPI
SimpleAudioVolume_AddRef(ISimpleAudioVolume
*iface
)
2983 AudioSessionWrapper
*This
= impl_from_ISimpleAudioVolume(iface
);
2984 return AudioSessionControl_AddRef(&This
->IAudioSessionControl2_iface
);
2987 static ULONG WINAPI
SimpleAudioVolume_Release(ISimpleAudioVolume
*iface
)
2989 AudioSessionWrapper
*This
= impl_from_ISimpleAudioVolume(iface
);
2990 return AudioSessionControl_Release(&This
->IAudioSessionControl2_iface
);
2993 static HRESULT WINAPI
SimpleAudioVolume_SetMasterVolume(
2994 ISimpleAudioVolume
*iface
, float level
, const GUID
*context
)
2996 AudioSessionWrapper
*This
= impl_from_ISimpleAudioVolume(iface
);
2997 AudioSession
*session
= This
->session
;
2999 TRACE("(%p)->(%f, %s)\n", session
, level
, wine_dbgstr_guid(context
));
3001 if (level
< 0.f
|| level
> 1.f
)
3002 return E_INVALIDARG
;
3005 FIXME("Notifications not supported yet\n");
3007 TRACE("PulseAudio does not support session volume control\n");
3009 pthread_mutex_lock(&pulse_lock
);
3010 session
->master_vol
= level
;
3011 pthread_mutex_unlock(&pulse_lock
);
3016 static HRESULT WINAPI
SimpleAudioVolume_GetMasterVolume(
3017 ISimpleAudioVolume
*iface
, float *level
)
3019 AudioSessionWrapper
*This
= impl_from_ISimpleAudioVolume(iface
);
3020 AudioSession
*session
= This
->session
;
3022 TRACE("(%p)->(%p)\n", session
, level
);
3025 return NULL_PTR_ERR
;
3027 *level
= session
->master_vol
;
3032 static HRESULT WINAPI
SimpleAudioVolume_SetMute(ISimpleAudioVolume
*iface
,
3033 BOOL mute
, const GUID
*context
)
3035 AudioSessionWrapper
*This
= impl_from_ISimpleAudioVolume(iface
);
3036 AudioSession
*session
= This
->session
;
3038 TRACE("(%p)->(%u, %p)\n", session
, mute
, context
);
3041 FIXME("Notifications not supported yet\n");
3043 session
->mute
= mute
;
3048 static HRESULT WINAPI
SimpleAudioVolume_GetMute(ISimpleAudioVolume
*iface
,
3051 AudioSessionWrapper
*This
= impl_from_ISimpleAudioVolume(iface
);
3052 AudioSession
*session
= This
->session
;
3054 TRACE("(%p)->(%p)\n", session
, mute
);
3057 return NULL_PTR_ERR
;
3059 *mute
= session
->mute
;
3064 static const ISimpleAudioVolumeVtbl SimpleAudioVolume_Vtbl
=
3066 SimpleAudioVolume_QueryInterface
,
3067 SimpleAudioVolume_AddRef
,
3068 SimpleAudioVolume_Release
,
3069 SimpleAudioVolume_SetMasterVolume
,
3070 SimpleAudioVolume_GetMasterVolume
,
3071 SimpleAudioVolume_SetMute
,
3072 SimpleAudioVolume_GetMute
3075 static HRESULT WINAPI
ChannelAudioVolume_QueryInterface(
3076 IChannelAudioVolume
*iface
, REFIID riid
, void **ppv
)
3078 TRACE("(%p)->(%s, %p)\n", iface
, debugstr_guid(riid
), ppv
);
3084 if (IsEqualIID(riid
, &IID_IUnknown
) ||
3085 IsEqualIID(riid
, &IID_IChannelAudioVolume
))
3088 IUnknown_AddRef((IUnknown
*)*ppv
);
3092 WARN("Unknown interface %s\n", debugstr_guid(riid
));
3093 return E_NOINTERFACE
;
3096 static ULONG WINAPI
ChannelAudioVolume_AddRef(IChannelAudioVolume
*iface
)
3098 AudioSessionWrapper
*This
= impl_from_IChannelAudioVolume(iface
);
3099 return AudioSessionControl_AddRef(&This
->IAudioSessionControl2_iface
);
3102 static ULONG WINAPI
ChannelAudioVolume_Release(IChannelAudioVolume
*iface
)
3104 AudioSessionWrapper
*This
= impl_from_IChannelAudioVolume(iface
);
3105 return AudioSessionControl_Release(&This
->IAudioSessionControl2_iface
);
3108 static HRESULT WINAPI
ChannelAudioVolume_GetChannelCount(
3109 IChannelAudioVolume
*iface
, UINT32
*out
)
3111 AudioSessionWrapper
*This
= impl_from_IChannelAudioVolume(iface
);
3112 AudioSession
*session
= This
->session
;
3114 TRACE("(%p)->(%p)\n", session
, out
);
3117 return NULL_PTR_ERR
;
3119 *out
= session
->channel_count
;
3124 static HRESULT WINAPI
ChannelAudioVolume_SetChannelVolume(
3125 IChannelAudioVolume
*iface
, UINT32 index
, float level
,
3126 const GUID
*context
)
3128 AudioSessionWrapper
*This
= impl_from_IChannelAudioVolume(iface
);
3129 AudioSession
*session
= This
->session
;
3131 TRACE("(%p)->(%d, %f, %s)\n", session
, index
, level
,
3132 wine_dbgstr_guid(context
));
3134 if (level
< 0.f
|| level
> 1.f
)
3135 return E_INVALIDARG
;
3137 if (index
>= session
->channel_count
)
3138 return E_INVALIDARG
;
3141 FIXME("Notifications not supported yet\n");
3143 TRACE("PulseAudio does not support session volume control\n");
3145 pthread_mutex_lock(&pulse_lock
);
3146 session
->channel_vols
[index
] = level
;
3147 pthread_mutex_unlock(&pulse_lock
);
3152 static HRESULT WINAPI
ChannelAudioVolume_GetChannelVolume(
3153 IChannelAudioVolume
*iface
, UINT32 index
, float *level
)
3155 AudioSessionWrapper
*This
= impl_from_IChannelAudioVolume(iface
);
3156 AudioSession
*session
= This
->session
;
3158 TRACE("(%p)->(%d, %p)\n", session
, index
, level
);
3161 return NULL_PTR_ERR
;
3163 if (index
>= session
->channel_count
)
3164 return E_INVALIDARG
;
3166 *level
= session
->channel_vols
[index
];
3171 static HRESULT WINAPI
ChannelAudioVolume_SetAllVolumes(
3172 IChannelAudioVolume
*iface
, UINT32 count
, const float *levels
,
3173 const GUID
*context
)
3175 AudioSessionWrapper
*This
= impl_from_IChannelAudioVolume(iface
);
3176 AudioSession
*session
= This
->session
;
3179 TRACE("(%p)->(%d, %p, %s)\n", session
, count
, levels
,
3180 wine_dbgstr_guid(context
));
3183 return NULL_PTR_ERR
;
3185 if (count
!= session
->channel_count
)
3186 return E_INVALIDARG
;
3189 FIXME("Notifications not supported yet\n");
3191 TRACE("PulseAudio does not support session volume control\n");
3193 pthread_mutex_lock(&pulse_lock
);
3194 for(i
= 0; i
< count
; ++i
)
3195 session
->channel_vols
[i
] = levels
[i
];
3196 pthread_mutex_unlock(&pulse_lock
);
3200 static HRESULT WINAPI
ChannelAudioVolume_GetAllVolumes(
3201 IChannelAudioVolume
*iface
, UINT32 count
, float *levels
)
3203 AudioSessionWrapper
*This
= impl_from_IChannelAudioVolume(iface
);
3204 AudioSession
*session
= This
->session
;
3207 TRACE("(%p)->(%d, %p)\n", session
, count
, levels
);
3210 return NULL_PTR_ERR
;
3212 if (count
!= session
->channel_count
)
3213 return E_INVALIDARG
;
3215 for(i
= 0; i
< count
; ++i
)
3216 levels
[i
] = session
->channel_vols
[i
];
3221 static const IChannelAudioVolumeVtbl ChannelAudioVolume_Vtbl
=
3223 ChannelAudioVolume_QueryInterface
,
3224 ChannelAudioVolume_AddRef
,
3225 ChannelAudioVolume_Release
,
3226 ChannelAudioVolume_GetChannelCount
,
3227 ChannelAudioVolume_SetChannelVolume
,
3228 ChannelAudioVolume_GetChannelVolume
,
3229 ChannelAudioVolume_SetAllVolumes
,
3230 ChannelAudioVolume_GetAllVolumes
3233 HRESULT WINAPI
AUDDRV_GetAudioSessionManager(IMMDevice
*device
,
3234 IAudioSessionManager2
**out
)
3236 SessionMgr
*This
= HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY
, sizeof(SessionMgr
));
3239 return E_OUTOFMEMORY
;
3240 This
->IAudioSessionManager2_iface
.lpVtbl
= &AudioSessionManager2_Vtbl
;
3241 This
->device
= device
;
3243 *out
= &This
->IAudioSessionManager2_iface
;
3247 HRESULT WINAPI
AUDDRV_GetPropValue(GUID
*guid
, const PROPERTYKEY
*prop
, PROPVARIANT
*out
)
3249 TRACE("%s, (%s,%u), %p\n", wine_dbgstr_guid(guid
), wine_dbgstr_guid(&prop
->fmtid
), prop
->pid
, out
);
3251 if (IsEqualGUID(guid
, &pulse_render_guid
) && IsEqualPropertyKey(*prop
, PKEY_AudioEndpoint_PhysicalSpeakers
)) {
3253 out
->u
.ulVal
= g_phys_speakers_mask
;
3255 return out
->u
.ulVal
? S_OK
: E_FAIL
;