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
;
177 BYTE
*local_buffer
, *tmp_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 ERR("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 TRACE("Test-connected to server %s with protocol version: %i.\n",
577 pa_context_get_server(pulse_ctx
),
578 pa_context_get_server_protocol_version(pulse_ctx
));
580 pulse_probe_settings(1, &pulse_fmt
[0]);
581 pulse_probe_settings(0, &pulse_fmt
[1]);
583 g_phys_speakers_mask
= 0;
584 o
= pa_context_get_sink_info_list(pulse_ctx
, &pulse_phys_speakers_cb
, NULL
);
586 while (pa_mainloop_iterate(pulse_ml
, 1, &ret
) >= 0 &&
587 pa_operation_get_state(o
) == PA_OPERATION_RUNNING
)
589 pa_operation_unref(o
);
592 pa_context_unref(pulse_ctx
);
594 pa_mainloop_free(pulse_ml
);
600 pa_context_unref(pulse_ctx
);
602 pa_mainloop_free(pulse_ml
);
608 static HRESULT
pulse_stream_valid(ACImpl
*This
) {
610 return AUDCLNT_E_NOT_INITIALIZED
;
611 if (!This
->stream
|| pa_stream_get_state(This
->stream
) != PA_STREAM_READY
)
612 return AUDCLNT_E_DEVICE_INVALIDATED
;
616 static void silence_buffer(pa_sample_format_t format
, BYTE
*buffer
, UINT32 bytes
)
618 memset(buffer
, format
== PA_SAMPLE_U8
? 0x80 : 0, bytes
);
621 static void dump_attr(const pa_buffer_attr
*attr
) {
622 TRACE("maxlength: %u\n", attr
->maxlength
);
623 TRACE("minreq: %u\n", attr
->minreq
);
624 TRACE("fragsize: %u\n", attr
->fragsize
);
625 TRACE("tlength: %u\n", attr
->tlength
);
626 TRACE("prebuf: %u\n", attr
->prebuf
);
629 static void pulse_op_cb(pa_stream
*s
, int success
, void *user
) {
630 TRACE("Success: %i\n", success
);
631 *(int*)user
= success
;
632 pthread_cond_signal(&pulse_cond
);
635 static void pulse_attr_update(pa_stream
*s
, void *user
) {
636 const pa_buffer_attr
*attr
= pa_stream_get_buffer_attr(s
);
637 TRACE("New attributes or device moved:\n");
641 /* Here's the buffer setup:
643 * vvvvvvvv sent to HW already
644 * vvvvvvvv in Pulse buffer but rewindable
645 * [dddddddddddddddd] Pulse buffer
646 * [dddddddddddddddd--------] mmdevapi buffer
647 * ^^^^^^^^^^^^^^^^ pad
649 * ^^^^^^^^^ held_bytes
652 * GetCurrentPadding is pad
654 * During pulse_wr_callback, we decrement pad, fill Pulse buffer, and move
657 * During Stop, we flush the Pulse buffer
659 static void pulse_wr_callback(pa_stream
*s
, size_t bytes
, void *userdata
)
661 ACImpl
*This
= userdata
;
662 UINT32 oldpad
= This
->pad
;
664 if(This
->local_buffer
){
666 BYTE
*buf
= This
->local_buffer
+ This
->lcl_offs_bytes
;
668 if(This
->pad
> bytes
){
669 This
->clock_written
+= bytes
;
672 This
->clock_written
+= This
->pad
;
676 bytes
= min(bytes
, This
->held_bytes
);
678 if(This
->lcl_offs_bytes
+ bytes
> This
->bufsize_bytes
){
679 to_write
= This
->bufsize_bytes
- This
->lcl_offs_bytes
;
680 TRACE("writing small chunk of %u bytes\n", to_write
);
681 pa_stream_write(This
->stream
, buf
, to_write
, NULL
, 0, PA_SEEK_RELATIVE
);
682 This
->held_bytes
-= to_write
;
683 to_write
= bytes
- to_write
;
684 This
->lcl_offs_bytes
= 0;
685 buf
= This
->local_buffer
;
689 TRACE("writing main chunk of %u bytes\n", to_write
);
690 pa_stream_write(This
->stream
, buf
, to_write
, NULL
, 0, PA_SEEK_RELATIVE
);
691 This
->lcl_offs_bytes
+= to_write
;
692 This
->lcl_offs_bytes
%= This
->bufsize_bytes
;
693 This
->held_bytes
-= to_write
;
695 if (bytes
< This
->bufsize_bytes
)
696 This
->pad
= This
->bufsize_bytes
- bytes
;
700 if (oldpad
== This
->pad
)
703 assert(oldpad
> This
->pad
);
705 This
->clock_written
+= oldpad
- This
->pad
;
706 TRACE("New pad: %zu (-%zu)\n", This
->pad
/ pa_frame_size(&This
->ss
), (oldpad
- This
->pad
) / pa_frame_size(&This
->ss
));
710 SetEvent(This
->event
);
713 static void pulse_underflow_callback(pa_stream
*s
, void *userdata
)
718 /* Latency is periodically updated even when nothing is played,
719 * because of PA_STREAM_AUTO_TIMING_UPDATE so use it as timer
721 * Perfect for passing all tests :)
723 static void pulse_latency_callback(pa_stream
*s
, void *userdata
)
725 ACImpl
*This
= userdata
;
726 if (!This
->pad
&& This
->event
)
727 SetEvent(This
->event
);
730 static void pulse_started_callback(pa_stream
*s
, void *userdata
)
732 TRACE("(Re)started playing\n");
735 static void pulse_rd_loop(ACImpl
*This
, size_t bytes
)
737 while (bytes
>= This
->capture_period
) {
739 LARGE_INTEGER stamp
, freq
;
741 size_t src_len
, copy
, rem
= This
->capture_period
;
742 if (!(p
= (ACPacket
*)list_head(&This
->packet_free_head
))) {
743 p
= (ACPacket
*)list_head(&This
->packet_filled_head
);
745 next
= (ACPacket
*)p
->entry
.next
;
748 p
= (ACPacket
*)list_tail(&This
->packet_filled_head
);
749 assert(This
->pad
== This
->bufsize_bytes
);
751 assert(This
->pad
< This
->bufsize_bytes
);
752 This
->pad
+= This
->capture_period
;
753 assert(This
->pad
<= This
->bufsize_bytes
);
755 QueryPerformanceCounter(&stamp
);
756 QueryPerformanceFrequency(&freq
);
757 p
->qpcpos
= (stamp
.QuadPart
* (INT64
)10000000) / freq
.QuadPart
;
759 list_remove(&p
->entry
);
760 list_add_tail(&This
->packet_filled_head
, &p
->entry
);
764 pa_stream_peek(This
->stream
, (const void**)&src
, &src_len
);
766 assert(This
->peek_ofs
< src_len
);
767 src
+= This
->peek_ofs
;
768 src_len
-= This
->peek_ofs
;
769 assert(src_len
<= bytes
);
774 memcpy(dst
, src
, rem
);
782 pa_stream_drop(This
->stream
);
784 This
->peek_ofs
+= copy
;
786 bytes
-= This
->capture_period
;
790 static void pulse_rd_drop(ACImpl
*This
, size_t bytes
)
792 while (bytes
>= This
->capture_period
) {
793 size_t src_len
, copy
, rem
= This
->capture_period
;
796 pa_stream_peek(This
->stream
, &src
, &src_len
);
798 assert(This
->peek_ofs
< src_len
);
799 src_len
-= This
->peek_ofs
;
800 assert(src_len
<= bytes
);
811 pa_stream_drop(This
->stream
);
813 This
->peek_ofs
+= copy
;
819 static void pulse_rd_callback(pa_stream
*s
, size_t bytes
, void *userdata
)
821 ACImpl
*This
= userdata
;
823 TRACE("Readable total: %zu, fragsize: %u\n", bytes
, pa_stream_get_buffer_attr(s
)->fragsize
);
824 assert(bytes
>= This
->peek_ofs
);
825 bytes
-= This
->peek_ofs
;
826 if (bytes
< This
->capture_period
)
830 pulse_rd_loop(This
, bytes
);
832 pulse_rd_drop(This
, bytes
);
835 SetEvent(This
->event
);
838 static HRESULT
pulse_stream_connect(ACImpl
*This
, UINT32 period_bytes
) {
844 pa_stream_disconnect(This
->stream
);
845 while (pa_stream_get_state(This
->stream
) == PA_STREAM_READY
)
846 pthread_cond_wait(&pulse_cond
, &pulse_lock
);
847 pa_stream_unref(This
->stream
);
849 ret
= InterlockedIncrement(&number
);
850 sprintf(buffer
, "audio stream #%i", ret
);
851 This
->stream
= pa_stream_new(pulse_ctx
, buffer
, &This
->ss
, &This
->map
);
854 WARN("pa_stream_new returned error %i\n", pa_context_errno(pulse_ctx
));
855 return AUDCLNT_E_ENDPOINT_CREATE_FAILED
;
858 pa_stream_set_state_callback(This
->stream
, pulse_stream_state
, This
);
859 pa_stream_set_buffer_attr_callback(This
->stream
, pulse_attr_update
, This
);
860 pa_stream_set_moved_callback(This
->stream
, pulse_attr_update
, This
);
862 /* PulseAudio will fill in correct values */
863 attr
.minreq
= attr
.fragsize
= period_bytes
;
864 attr
.maxlength
= attr
.tlength
= This
->bufsize_bytes
;
865 attr
.prebuf
= pa_frame_size(&This
->ss
);
867 if (This
->dataflow
== eRender
)
868 ret
= pa_stream_connect_playback(This
->stream
, NULL
, &attr
,
869 PA_STREAM_START_CORKED
|PA_STREAM_START_UNMUTED
|PA_STREAM_AUTO_TIMING_UPDATE
|PA_STREAM_INTERPOLATE_TIMING
|PA_STREAM_EARLY_REQUESTS
, NULL
, NULL
);
871 ret
= pa_stream_connect_record(This
->stream
, NULL
, &attr
,
872 PA_STREAM_START_CORKED
|PA_STREAM_START_UNMUTED
|PA_STREAM_AUTO_TIMING_UPDATE
|PA_STREAM_INTERPOLATE_TIMING
|PA_STREAM_EARLY_REQUESTS
);
874 WARN("Returns %i\n", ret
);
875 return AUDCLNT_E_ENDPOINT_CREATE_FAILED
;
877 while (pa_stream_get_state(This
->stream
) == PA_STREAM_CREATING
)
878 pthread_cond_wait(&pulse_cond
, &pulse_lock
);
879 if (pa_stream_get_state(This
->stream
) != PA_STREAM_READY
)
880 return AUDCLNT_E_ENDPOINT_CREATE_FAILED
;
882 if (This
->dataflow
== eRender
) {
883 pa_stream_set_write_callback(This
->stream
, pulse_wr_callback
, This
);
884 pa_stream_set_underflow_callback(This
->stream
, pulse_underflow_callback
, This
);
885 pa_stream_set_started_callback(This
->stream
, pulse_started_callback
, This
);
887 pa_stream_set_read_callback(This
->stream
, pulse_rd_callback
, This
);
891 HRESULT WINAPI
AUDDRV_GetEndpointIDs(EDataFlow flow
, const WCHAR
***ids
, GUID
**keys
,
892 UINT
*num
, UINT
*def_index
)
896 TRACE("%d %p %p %p\n", flow
, ids
, num
, def_index
);
901 *ids
= HeapAlloc(GetProcessHeap(), 0, sizeof(**ids
));
904 return E_OUTOFMEMORY
;
906 (*ids
)[0] = id
= HeapAlloc(GetProcessHeap(), 0, sizeof(defaultW
));
907 *keys
= HeapAlloc(GetProcessHeap(), 0, sizeof(**keys
));
909 HeapFree(GetProcessHeap(), 0, id
);
910 HeapFree(GetProcessHeap(), 0, *keys
);
911 HeapFree(GetProcessHeap(), 0, *ids
);
914 return E_OUTOFMEMORY
;
916 memcpy(id
, defaultW
, sizeof(defaultW
));
919 (*keys
)[0] = pulse_render_guid
;
921 (*keys
)[0] = pulse_capture_guid
;
926 int WINAPI
AUDDRV_GetPriority(void)
929 pthread_mutex_lock(&pulse_lock
);
930 hr
= pulse_test_connect();
931 pthread_mutex_unlock(&pulse_lock
);
932 return SUCCEEDED(hr
) ? Priority_Preferred
: Priority_Unavailable
;
935 HRESULT WINAPI
AUDDRV_GetAudioEndpoint(GUID
*guid
, IMMDevice
*dev
, IAudioClient
**out
)
942 TRACE("%s %p %p\n", debugstr_guid(guid
), dev
, out
);
943 if (IsEqualGUID(guid
, &pulse_render_guid
))
945 else if (IsEqualGUID(guid
, &pulse_capture_guid
))
952 This
= HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY
, sizeof(*This
));
954 return E_OUTOFMEMORY
;
956 This
->IAudioClient_iface
.lpVtbl
= &AudioClient_Vtbl
;
957 This
->IAudioRenderClient_iface
.lpVtbl
= &AudioRenderClient_Vtbl
;
958 This
->IAudioCaptureClient_iface
.lpVtbl
= &AudioCaptureClient_Vtbl
;
959 This
->IAudioClock_iface
.lpVtbl
= &AudioClock_Vtbl
;
960 This
->IAudioClock2_iface
.lpVtbl
= &AudioClock2_Vtbl
;
961 This
->IAudioStreamVolume_iface
.lpVtbl
= &AudioStreamVolume_Vtbl
;
962 This
->dataflow
= dataflow
;
964 for (i
= 0; i
< PA_CHANNELS_MAX
; ++i
)
967 hr
= CoCreateFreeThreadedMarshaler((IUnknown
*)This
, &This
->marshal
);
969 HeapFree(GetProcessHeap(), 0, This
);
972 IMMDevice_AddRef(This
->parent
);
974 *out
= &This
->IAudioClient_iface
;
975 IAudioClient_AddRef(&This
->IAudioClient_iface
);
980 static HRESULT WINAPI
AudioClient_QueryInterface(IAudioClient
*iface
,
981 REFIID riid
, void **ppv
)
983 ACImpl
*This
= impl_from_IAudioClient(iface
);
985 TRACE("(%p)->(%s, %p)\n", iface
, debugstr_guid(riid
), ppv
);
991 if (IsEqualIID(riid
, &IID_IUnknown
) || IsEqualIID(riid
, &IID_IAudioClient
))
994 IUnknown_AddRef((IUnknown
*)*ppv
);
998 if (IsEqualIID(riid
, &IID_IMarshal
))
999 return IUnknown_QueryInterface(This
->marshal
, riid
, ppv
);
1001 WARN("Unknown interface %s\n", debugstr_guid(riid
));
1002 return E_NOINTERFACE
;
1005 static ULONG WINAPI
AudioClient_AddRef(IAudioClient
*iface
)
1007 ACImpl
*This
= impl_from_IAudioClient(iface
);
1009 ref
= InterlockedIncrement(&This
->ref
);
1010 TRACE("(%p) Refcount now %u\n", This
, ref
);
1014 static ULONG WINAPI
AudioClient_Release(IAudioClient
*iface
)
1016 ACImpl
*This
= impl_from_IAudioClient(iface
);
1018 ref
= InterlockedDecrement(&This
->ref
);
1019 TRACE("(%p) Refcount now %u\n", This
, ref
);
1022 pthread_mutex_lock(&pulse_lock
);
1023 if (PA_STREAM_IS_GOOD(pa_stream_get_state(This
->stream
))) {
1024 pa_stream_disconnect(This
->stream
);
1025 while (PA_STREAM_IS_GOOD(pa_stream_get_state(This
->stream
)))
1026 pthread_cond_wait(&pulse_cond
, &pulse_lock
);
1028 pa_stream_unref(This
->stream
);
1029 This
->stream
= NULL
;
1030 list_remove(&This
->entry
);
1031 pthread_mutex_unlock(&pulse_lock
);
1033 IUnknown_Release(This
->marshal
);
1034 IMMDevice_Release(This
->parent
);
1035 HeapFree(GetProcessHeap(), 0, This
->tmp_buffer
);
1036 HeapFree(GetProcessHeap(), 0, This
->local_buffer
);
1037 HeapFree(GetProcessHeap(), 0, This
);
1042 static void dump_fmt(const WAVEFORMATEX
*fmt
)
1044 TRACE("wFormatTag: 0x%x (", fmt
->wFormatTag
);
1045 switch(fmt
->wFormatTag
) {
1046 case WAVE_FORMAT_PCM
:
1047 TRACE("WAVE_FORMAT_PCM");
1049 case WAVE_FORMAT_IEEE_FLOAT
:
1050 TRACE("WAVE_FORMAT_IEEE_FLOAT");
1052 case WAVE_FORMAT_EXTENSIBLE
:
1053 TRACE("WAVE_FORMAT_EXTENSIBLE");
1061 TRACE("nChannels: %u\n", fmt
->nChannels
);
1062 TRACE("nSamplesPerSec: %u\n", fmt
->nSamplesPerSec
);
1063 TRACE("nAvgBytesPerSec: %u\n", fmt
->nAvgBytesPerSec
);
1064 TRACE("nBlockAlign: %u\n", fmt
->nBlockAlign
);
1065 TRACE("wBitsPerSample: %u\n", fmt
->wBitsPerSample
);
1066 TRACE("cbSize: %u\n", fmt
->cbSize
);
1068 if (fmt
->wFormatTag
== WAVE_FORMAT_EXTENSIBLE
) {
1069 WAVEFORMATEXTENSIBLE
*fmtex
= (void*)fmt
;
1070 TRACE("dwChannelMask: %08x\n", fmtex
->dwChannelMask
);
1071 TRACE("Samples: %04x\n", fmtex
->Samples
.wReserved
);
1072 TRACE("SubFormat: %s\n", wine_dbgstr_guid(&fmtex
->SubFormat
));
1076 static WAVEFORMATEX
*clone_format(const WAVEFORMATEX
*fmt
)
1081 if (fmt
->wFormatTag
== WAVE_FORMAT_EXTENSIBLE
)
1082 size
= sizeof(WAVEFORMATEXTENSIBLE
);
1084 size
= sizeof(WAVEFORMATEX
);
1086 ret
= CoTaskMemAlloc(size
);
1090 memcpy(ret
, fmt
, size
);
1092 ret
->cbSize
= size
- sizeof(WAVEFORMATEX
);
1097 static DWORD
get_channel_mask(unsigned int channels
)
1103 return KSAUDIO_SPEAKER_MONO
;
1105 return KSAUDIO_SPEAKER_STEREO
;
1107 return KSAUDIO_SPEAKER_STEREO
| SPEAKER_LOW_FREQUENCY
;
1109 return KSAUDIO_SPEAKER_QUAD
; /* not _SURROUND */
1111 return KSAUDIO_SPEAKER_QUAD
| SPEAKER_LOW_FREQUENCY
;
1113 return KSAUDIO_SPEAKER_5POINT1
; /* not 5POINT1_SURROUND */
1115 return KSAUDIO_SPEAKER_5POINT1
| SPEAKER_BACK_CENTER
;
1117 return KSAUDIO_SPEAKER_7POINT1_SURROUND
; /* Vista deprecates 7POINT1 */
1119 FIXME("Unknown speaker configuration: %u\n", channels
);
1123 static void session_init_vols(AudioSession
*session
, UINT channels
)
1125 if (session
->channel_count
< channels
) {
1128 if (session
->channel_vols
)
1129 session
->channel_vols
= HeapReAlloc(GetProcessHeap(), 0,
1130 session
->channel_vols
, sizeof(float) * channels
);
1132 session
->channel_vols
= HeapAlloc(GetProcessHeap(), 0,
1133 sizeof(float) * channels
);
1134 if (!session
->channel_vols
)
1137 for(i
= session
->channel_count
; i
< channels
; ++i
)
1138 session
->channel_vols
[i
] = 1.f
;
1140 session
->channel_count
= channels
;
1144 static AudioSession
*create_session(const GUID
*guid
, IMMDevice
*device
,
1149 ret
= HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY
, sizeof(AudioSession
));
1153 memcpy(&ret
->guid
, guid
, sizeof(GUID
));
1155 ret
->device
= device
;
1157 list_init(&ret
->clients
);
1159 list_add_head(&g_sessions
, &ret
->entry
);
1161 session_init_vols(ret
, num_channels
);
1163 ret
->master_vol
= 1.f
;
1168 /* if channels == 0, then this will return or create a session with
1169 * matching dataflow and GUID. otherwise, channels must also match */
1170 static HRESULT
get_audio_session(const GUID
*sessionguid
,
1171 IMMDevice
*device
, UINT channels
, AudioSession
**out
)
1173 AudioSession
*session
;
1175 if (!sessionguid
|| IsEqualGUID(sessionguid
, &GUID_NULL
)) {
1176 *out
= create_session(&GUID_NULL
, device
, channels
);
1178 return E_OUTOFMEMORY
;
1184 LIST_FOR_EACH_ENTRY(session
, &g_sessions
, AudioSession
, entry
) {
1185 if (session
->device
== device
&&
1186 IsEqualGUID(sessionguid
, &session
->guid
)) {
1187 session_init_vols(session
, channels
);
1194 *out
= create_session(sessionguid
, device
, channels
);
1196 return E_OUTOFMEMORY
;
1202 static HRESULT
pulse_spec_from_waveformat(ACImpl
*This
, const WAVEFORMATEX
*fmt
)
1204 pa_channel_map_init(&This
->map
);
1205 This
->ss
.rate
= fmt
->nSamplesPerSec
;
1206 This
->ss
.format
= PA_SAMPLE_INVALID
;
1208 switch(fmt
->wFormatTag
) {
1209 case WAVE_FORMAT_IEEE_FLOAT
:
1210 if (!fmt
->nChannels
|| fmt
->nChannels
> 2 || fmt
->wBitsPerSample
!= 32)
1212 This
->ss
.format
= PA_SAMPLE_FLOAT32LE
;
1213 pa_channel_map_init_auto(&This
->map
, fmt
->nChannels
, PA_CHANNEL_MAP_ALSA
);
1215 case WAVE_FORMAT_PCM
:
1216 if (!fmt
->nChannels
|| fmt
->nChannels
> 2)
1218 if (fmt
->wBitsPerSample
== 8)
1219 This
->ss
.format
= PA_SAMPLE_U8
;
1220 else if (fmt
->wBitsPerSample
== 16)
1221 This
->ss
.format
= PA_SAMPLE_S16LE
;
1223 return AUDCLNT_E_UNSUPPORTED_FORMAT
;
1224 pa_channel_map_init_auto(&This
->map
, fmt
->nChannels
, PA_CHANNEL_MAP_ALSA
);
1226 case WAVE_FORMAT_EXTENSIBLE
: {
1227 WAVEFORMATEXTENSIBLE
*wfe
= (WAVEFORMATEXTENSIBLE
*)fmt
;
1228 DWORD mask
= wfe
->dwChannelMask
;
1230 if (fmt
->cbSize
!= (sizeof(*wfe
) - sizeof(*fmt
)) && fmt
->cbSize
!= sizeof(*wfe
))
1232 if (IsEqualGUID(&wfe
->SubFormat
, &KSDATAFORMAT_SUBTYPE_IEEE_FLOAT
) &&
1233 (!wfe
->Samples
.wValidBitsPerSample
|| wfe
->Samples
.wValidBitsPerSample
== 32) &&
1234 fmt
->wBitsPerSample
== 32)
1235 This
->ss
.format
= PA_SAMPLE_FLOAT32LE
;
1236 else if (IsEqualGUID(&wfe
->SubFormat
, &KSDATAFORMAT_SUBTYPE_PCM
)) {
1237 DWORD valid
= wfe
->Samples
.wValidBitsPerSample
;
1239 valid
= fmt
->wBitsPerSample
;
1240 if (!valid
|| valid
> fmt
->wBitsPerSample
)
1242 switch (fmt
->wBitsPerSample
) {
1245 This
->ss
.format
= PA_SAMPLE_U8
;
1249 This
->ss
.format
= PA_SAMPLE_S16LE
;
1253 This
->ss
.format
= PA_SAMPLE_S24LE
;
1257 This
->ss
.format
= PA_SAMPLE_S24_32LE
;
1258 else if (valid
== 32)
1259 This
->ss
.format
= PA_SAMPLE_S32LE
;
1262 return AUDCLNT_E_UNSUPPORTED_FORMAT
;
1265 This
->map
.channels
= fmt
->nChannels
;
1266 if (!mask
|| (mask
& (SPEAKER_ALL
|SPEAKER_RESERVED
)))
1267 mask
= get_channel_mask(fmt
->nChannels
);
1268 for (j
= 0; j
< sizeof(pulse_pos_from_wfx
)/sizeof(*pulse_pos_from_wfx
) && i
< fmt
->nChannels
; ++j
) {
1269 if (mask
& (1 << j
))
1270 This
->map
.map
[i
++] = pulse_pos_from_wfx
[j
];
1273 /* Special case for mono since pulse appears to map it differently */
1274 if (mask
== SPEAKER_FRONT_CENTER
)
1275 This
->map
.map
[0] = PA_CHANNEL_POSITION_MONO
;
1277 if (i
< fmt
->nChannels
|| (mask
& SPEAKER_RESERVED
)) {
1278 This
->map
.channels
= 0;
1279 ERR("Invalid channel mask: %i/%i and %x(%x)\n", i
, fmt
->nChannels
, mask
, wfe
->dwChannelMask
);
1284 case WAVE_FORMAT_ALAW
:
1285 case WAVE_FORMAT_MULAW
:
1286 if (fmt
->wBitsPerSample
!= 8) {
1287 FIXME("Unsupported bpp %u for LAW\n", fmt
->wBitsPerSample
);
1288 return AUDCLNT_E_UNSUPPORTED_FORMAT
;
1290 if (fmt
->nChannels
!= 1 && fmt
->nChannels
!= 2) {
1291 FIXME("Unsupported channels %u for LAW\n", fmt
->nChannels
);
1292 return AUDCLNT_E_UNSUPPORTED_FORMAT
;
1294 This
->ss
.format
= fmt
->wFormatTag
== WAVE_FORMAT_MULAW
? PA_SAMPLE_ULAW
: PA_SAMPLE_ALAW
;
1295 pa_channel_map_init_auto(&This
->map
, fmt
->nChannels
, PA_CHANNEL_MAP_ALSA
);
1298 WARN("Unhandled tag %x\n", fmt
->wFormatTag
);
1299 return AUDCLNT_E_UNSUPPORTED_FORMAT
;
1301 This
->ss
.channels
= This
->map
.channels
;
1302 if (!pa_channel_map_valid(&This
->map
) || This
->ss
.format
== PA_SAMPLE_INVALID
) {
1303 ERR("Invalid format! Channel spec valid: %i, format: %i\n", pa_channel_map_valid(&This
->map
), This
->ss
.format
);
1304 return AUDCLNT_E_UNSUPPORTED_FORMAT
;
1309 static HRESULT WINAPI
AudioClient_Initialize(IAudioClient
*iface
,
1310 AUDCLNT_SHAREMODE mode
, DWORD flags
, REFERENCE_TIME duration
,
1311 REFERENCE_TIME period
, const WAVEFORMATEX
*fmt
,
1312 const GUID
*sessionguid
)
1314 ACImpl
*This
= impl_from_IAudioClient(iface
);
1318 TRACE("(%p)->(%x, %x, %s, %s, %p, %s)\n", This
, mode
, flags
,
1319 wine_dbgstr_longlong(duration
), wine_dbgstr_longlong(period
), fmt
, debugstr_guid(sessionguid
));
1324 if (mode
!= AUDCLNT_SHAREMODE_SHARED
&& mode
!= AUDCLNT_SHAREMODE_EXCLUSIVE
)
1325 return AUDCLNT_E_NOT_INITIALIZED
;
1326 if (mode
== AUDCLNT_SHAREMODE_EXCLUSIVE
)
1327 return AUDCLNT_E_EXCLUSIVE_MODE_NOT_ALLOWED
;
1329 if (flags
& ~(AUDCLNT_STREAMFLAGS_CROSSPROCESS
|
1330 AUDCLNT_STREAMFLAGS_LOOPBACK
|
1331 AUDCLNT_STREAMFLAGS_EVENTCALLBACK
|
1332 AUDCLNT_STREAMFLAGS_NOPERSIST
|
1333 AUDCLNT_STREAMFLAGS_RATEADJUST
|
1334 AUDCLNT_SESSIONFLAGS_EXPIREWHENUNOWNED
|
1335 AUDCLNT_SESSIONFLAGS_DISPLAY_HIDE
|
1336 AUDCLNT_SESSIONFLAGS_DISPLAY_HIDEWHENEXPIRED
)) {
1337 TRACE("Unknown flags: %08x\n", flags
);
1338 return E_INVALIDARG
;
1341 pthread_mutex_lock(&pulse_lock
);
1343 hr
= pulse_connect();
1345 pthread_mutex_unlock(&pulse_lock
);
1350 pthread_mutex_unlock(&pulse_lock
);
1351 return AUDCLNT_E_ALREADY_INITIALIZED
;
1354 hr
= pulse_spec_from_waveformat(This
, fmt
);
1355 TRACE("Obtaining format returns %08x\n", hr
);
1361 if (mode
== AUDCLNT_SHAREMODE_SHARED
) {
1362 REFERENCE_TIME def
= pulse_def_period
[This
->dataflow
== eCapture
];
1363 REFERENCE_TIME min
= pulse_min_period
[This
->dataflow
== eCapture
];
1365 /* Switch to low latency mode if below 2 default periods,
1366 * which is 20 ms by default, this will increase the amount
1367 * of interrupts but allows very low latency. In dsound I
1368 * managed to get a total latency of ~8ms, which is well below
1371 if (duration
< 2 * def
)
1375 if (duration
< 2 * period
)
1376 duration
= 2 * period
;
1378 /* Uh oh, really low latency requested.. */
1379 if (duration
<= 2 * period
)
1382 period_bytes
= pa_frame_size(&This
->ss
) * MulDiv(period
, This
->ss
.rate
, 10000000);
1384 if (duration
< 20000000)
1385 This
->bufsize_frames
= ceil((duration
/ 10000000.) * fmt
->nSamplesPerSec
);
1387 This
->bufsize_frames
= 2 * fmt
->nSamplesPerSec
;
1388 This
->bufsize_bytes
= This
->bufsize_frames
* pa_frame_size(&This
->ss
);
1391 This
->flags
= flags
;
1392 hr
= pulse_stream_connect(This
, period_bytes
);
1393 if (SUCCEEDED(hr
)) {
1395 const pa_buffer_attr
*attr
= pa_stream_get_buffer_attr(This
->stream
);
1397 /* Update frames according to new size */
1399 if (This
->dataflow
== eRender
) {
1400 if (attr
->tlength
< This
->bufsize_bytes
) {
1401 TRACE("PulseAudio buffer too small (%u < %u), using tmp buffer\n", attr
->tlength
, This
->bufsize_bytes
);
1403 This
->local_buffer
= HeapAlloc(GetProcessHeap(), 0, This
->bufsize_bytes
);
1404 if(!This
->local_buffer
)
1408 UINT32 i
, capture_packets
;
1410 This
->capture_period
= period_bytes
= attr
->fragsize
;
1411 if ((unalign
= This
->bufsize_bytes
% period_bytes
))
1412 This
->bufsize_bytes
+= period_bytes
- unalign
;
1413 This
->bufsize_frames
= This
->bufsize_bytes
/ pa_frame_size(&This
->ss
);
1415 capture_packets
= This
->bufsize_bytes
/ This
->capture_period
;
1417 This
->local_buffer
= HeapAlloc(GetProcessHeap(), 0, This
->bufsize_bytes
+ capture_packets
* sizeof(ACPacket
));
1418 if (!This
->local_buffer
)
1421 ACPacket
*cur_packet
= (ACPacket
*)((char*)This
->local_buffer
+ This
->bufsize_bytes
);
1422 BYTE
*data
= This
->local_buffer
;
1423 silence_buffer(This
->ss
.format
, This
->local_buffer
, This
->bufsize_bytes
);
1424 list_init(&This
->packet_free_head
);
1425 list_init(&This
->packet_filled_head
);
1426 for (i
= 0; i
< capture_packets
; ++i
, ++cur_packet
) {
1427 list_add_tail(&This
->packet_free_head
, &cur_packet
->entry
);
1428 cur_packet
->data
= data
;
1429 data
+= This
->capture_period
;
1431 assert(!This
->capture_period
|| This
->bufsize_bytes
== This
->capture_period
* capture_packets
);
1432 assert(!capture_packets
|| data
- This
->bufsize_bytes
== This
->local_buffer
);
1437 hr
= get_audio_session(sessionguid
, This
->parent
, fmt
->nChannels
, &This
->session
);
1439 list_add_tail(&This
->session
->clients
, &This
->entry
);
1443 if(This
->local_buffer
)
1444 HeapFree(GetProcessHeap(), 0, This
->local_buffer
);
1445 This
->local_buffer
= NULL
;
1447 pa_stream_disconnect(This
->stream
);
1448 pa_stream_unref(This
->stream
);
1449 This
->stream
= NULL
;
1452 pthread_mutex_unlock(&pulse_lock
);
1456 static HRESULT WINAPI
AudioClient_GetBufferSize(IAudioClient
*iface
,
1459 ACImpl
*This
= impl_from_IAudioClient(iface
);
1462 TRACE("(%p)->(%p)\n", This
, out
);
1467 pthread_mutex_lock(&pulse_lock
);
1468 hr
= pulse_stream_valid(This
);
1470 *out
= This
->bufsize_frames
;
1471 pthread_mutex_unlock(&pulse_lock
);
1476 static HRESULT WINAPI
AudioClient_GetStreamLatency(IAudioClient
*iface
,
1477 REFERENCE_TIME
*latency
)
1479 ACImpl
*This
= impl_from_IAudioClient(iface
);
1480 const pa_buffer_attr
*attr
;
1484 TRACE("(%p)->(%p)\n", This
, latency
);
1489 pthread_mutex_lock(&pulse_lock
);
1490 hr
= pulse_stream_valid(This
);
1492 pthread_mutex_unlock(&pulse_lock
);
1495 attr
= pa_stream_get_buffer_attr(This
->stream
);
1496 if (This
->dataflow
== eRender
){
1497 lat
= attr
->minreq
/ pa_frame_size(&This
->ss
);
1498 lat
+= pulse_def_period
[0];
1500 lat
= attr
->fragsize
/ pa_frame_size(&This
->ss
);
1501 *latency
= 10000000;
1503 *latency
/= This
->ss
.rate
;
1504 pthread_mutex_unlock(&pulse_lock
);
1505 TRACE("Latency: %u ms\n", (DWORD
)(*latency
/ 10000));
1509 static void ACImpl_GetRenderPad(ACImpl
*This
, UINT32
*out
)
1511 *out
= This
->pad
/ pa_frame_size(&This
->ss
);
1514 static void ACImpl_GetCapturePad(ACImpl
*This
, UINT32
*out
)
1516 ACPacket
*packet
= This
->locked_ptr
;
1517 if (!packet
&& !list_empty(&This
->packet_filled_head
)) {
1518 packet
= (ACPacket
*)list_head(&This
->packet_filled_head
);
1519 This
->locked_ptr
= packet
;
1520 list_remove(&packet
->entry
);
1523 *out
= This
->pad
/ pa_frame_size(&This
->ss
);
1526 static HRESULT WINAPI
AudioClient_GetCurrentPadding(IAudioClient
*iface
,
1529 ACImpl
*This
= impl_from_IAudioClient(iface
);
1532 TRACE("(%p)->(%p)\n", This
, out
);
1537 pthread_mutex_lock(&pulse_lock
);
1538 hr
= pulse_stream_valid(This
);
1540 pthread_mutex_unlock(&pulse_lock
);
1544 if (This
->dataflow
== eRender
)
1545 ACImpl_GetRenderPad(This
, out
);
1547 ACImpl_GetCapturePad(This
, out
);
1548 pthread_mutex_unlock(&pulse_lock
);
1550 TRACE("%p Pad: %u ms (%u)\n", This
, MulDiv(*out
, 1000, This
->ss
.rate
), *out
);
1554 static HRESULT WINAPI
AudioClient_IsFormatSupported(IAudioClient
*iface
,
1555 AUDCLNT_SHAREMODE mode
, const WAVEFORMATEX
*fmt
,
1558 ACImpl
*This
= impl_from_IAudioClient(iface
);
1560 WAVEFORMATEX
*closest
= NULL
;
1563 TRACE("(%p)->(%x, %p, %p)\n", This
, mode
, fmt
, out
);
1571 if (mode
== AUDCLNT_SHAREMODE_EXCLUSIVE
) {
1574 } else if (mode
== AUDCLNT_SHAREMODE_SHARED
) {
1579 return E_INVALIDARG
;
1581 if (fmt
->nChannels
== 0)
1582 return AUDCLNT_E_UNSUPPORTED_FORMAT
;
1584 closest
= clone_format(fmt
);
1586 return E_OUTOFMEMORY
;
1590 switch (fmt
->wFormatTag
) {
1591 case WAVE_FORMAT_EXTENSIBLE
: {
1592 WAVEFORMATEXTENSIBLE
*ext
= (WAVEFORMATEXTENSIBLE
*)closest
;
1594 if ((fmt
->cbSize
!= sizeof(WAVEFORMATEXTENSIBLE
) - sizeof(WAVEFORMATEX
) &&
1595 fmt
->cbSize
!= sizeof(WAVEFORMATEXTENSIBLE
)) ||
1596 fmt
->nBlockAlign
!= fmt
->wBitsPerSample
/ 8 * fmt
->nChannels
||
1597 ext
->Samples
.wValidBitsPerSample
> fmt
->wBitsPerSample
||
1598 fmt
->nAvgBytesPerSec
!= fmt
->nBlockAlign
* fmt
->nSamplesPerSec
) {
1604 UINT32 mask
= 0, i
, channels
= 0;
1606 if (!(ext
->dwChannelMask
& (SPEAKER_ALL
| SPEAKER_RESERVED
))) {
1607 for (i
= 1; !(i
& SPEAKER_RESERVED
); i
<<= 1) {
1608 if (i
& ext
->dwChannelMask
) {
1614 if (channels
!= fmt
->nChannels
|| (ext
->dwChannelMask
& ~mask
)) {
1615 hr
= AUDCLNT_E_UNSUPPORTED_FORMAT
;
1619 hr
= AUDCLNT_E_UNSUPPORTED_FORMAT
;
1624 if (IsEqualGUID(&ext
->SubFormat
, &KSDATAFORMAT_SUBTYPE_IEEE_FLOAT
)) {
1625 if (fmt
->wBitsPerSample
!= 32) {
1630 if (ext
->Samples
.wValidBitsPerSample
!= fmt
->wBitsPerSample
) {
1632 ext
->Samples
.wValidBitsPerSample
= fmt
->wBitsPerSample
;
1634 } else if (IsEqualGUID(&ext
->SubFormat
, &KSDATAFORMAT_SUBTYPE_PCM
)) {
1635 if (!fmt
->wBitsPerSample
|| fmt
->wBitsPerSample
> 32 || fmt
->wBitsPerSample
% 8) {
1640 if (ext
->Samples
.wValidBitsPerSample
!= fmt
->wBitsPerSample
&&
1641 !(fmt
->wBitsPerSample
== 32 &&
1642 ext
->Samples
.wValidBitsPerSample
== 24)) {
1644 ext
->Samples
.wValidBitsPerSample
= fmt
->wBitsPerSample
;
1648 hr
= AUDCLNT_E_UNSUPPORTED_FORMAT
;
1655 case WAVE_FORMAT_ALAW
:
1656 case WAVE_FORMAT_MULAW
:
1657 if (fmt
->wBitsPerSample
!= 8) {
1662 case WAVE_FORMAT_IEEE_FLOAT
:
1663 if (fmt
->wFormatTag
== WAVE_FORMAT_IEEE_FLOAT
&& fmt
->wBitsPerSample
!= 32) {
1668 case WAVE_FORMAT_PCM
:
1669 if (fmt
->wFormatTag
== WAVE_FORMAT_PCM
&&
1670 (!fmt
->wBitsPerSample
|| fmt
->wBitsPerSample
> 32 || fmt
->wBitsPerSample
% 8)) {
1675 if (fmt
->nChannels
> 2) {
1676 hr
= AUDCLNT_E_UNSUPPORTED_FORMAT
;
1680 * fmt->cbSize, fmt->nBlockAlign and fmt->nAvgBytesPerSec seem to be
1681 * ignored, invalid values are happily accepted.
1685 hr
= AUDCLNT_E_UNSUPPORTED_FORMAT
;
1689 if (exclusive
&& hr
!= S_OK
) {
1690 hr
= AUDCLNT_E_UNSUPPORTED_FORMAT
;
1691 CoTaskMemFree(closest
);
1692 } else if (hr
!= S_FALSE
)
1693 CoTaskMemFree(closest
);
1697 /* Winepulse does not currently support exclusive mode, if you know of an
1698 * application that uses it, I will correct this..
1700 if (hr
== S_OK
&& exclusive
)
1701 return This
->dataflow
== eCapture
? AUDCLNT_E_UNSUPPORTED_FORMAT
: AUDCLNT_E_EXCLUSIVE_MODE_NOT_ALLOWED
;
1703 TRACE("returning: %08x %p\n", hr
, out
? *out
: NULL
);
1707 static HRESULT WINAPI
AudioClient_GetMixFormat(IAudioClient
*iface
,
1708 WAVEFORMATEX
**pwfx
)
1710 ACImpl
*This
= impl_from_IAudioClient(iface
);
1711 WAVEFORMATEXTENSIBLE
*fmt
= &pulse_fmt
[This
->dataflow
== eCapture
];
1713 TRACE("(%p)->(%p)\n", This
, pwfx
);
1718 *pwfx
= clone_format(&fmt
->Format
);
1720 return E_OUTOFMEMORY
;
1725 static HRESULT WINAPI
AudioClient_GetDevicePeriod(IAudioClient
*iface
,
1726 REFERENCE_TIME
*defperiod
, REFERENCE_TIME
*minperiod
)
1728 ACImpl
*This
= impl_from_IAudioClient(iface
);
1730 TRACE("(%p)->(%p, %p)\n", This
, defperiod
, minperiod
);
1732 if (!defperiod
&& !minperiod
)
1736 *defperiod
= pulse_def_period
[This
->dataflow
== eCapture
];
1738 *minperiod
= pulse_min_period
[This
->dataflow
== eCapture
];
1743 static HRESULT WINAPI
AudioClient_Start(IAudioClient
*iface
)
1745 ACImpl
*This
= impl_from_IAudioClient(iface
);
1750 TRACE("(%p)\n", This
);
1752 pthread_mutex_lock(&pulse_lock
);
1753 hr
= pulse_stream_valid(This
);
1755 pthread_mutex_unlock(&pulse_lock
);
1759 if ((This
->flags
& AUDCLNT_STREAMFLAGS_EVENTCALLBACK
) && !This
->event
) {
1760 pthread_mutex_unlock(&pulse_lock
);
1761 return AUDCLNT_E_EVENTHANDLE_NOT_SET
;
1764 if (This
->started
) {
1765 pthread_mutex_unlock(&pulse_lock
);
1766 return AUDCLNT_E_NOT_STOPPED
;
1769 if (pa_stream_is_corked(This
->stream
)) {
1770 o
= pa_stream_cork(This
->stream
, 0, pulse_op_cb
, &success
);
1772 while(pa_operation_get_state(o
) == PA_OPERATION_RUNNING
)
1773 pthread_cond_wait(&pulse_cond
, &pulse_lock
);
1774 pa_operation_unref(o
);
1781 if (SUCCEEDED(hr
)) {
1782 This
->started
= TRUE
;
1783 if (This
->dataflow
== eRender
&& This
->event
)
1784 pa_stream_set_latency_update_callback(This
->stream
, pulse_latency_callback
, This
);
1786 pthread_mutex_unlock(&pulse_lock
);
1790 static HRESULT WINAPI
AudioClient_Stop(IAudioClient
*iface
)
1792 ACImpl
*This
= impl_from_IAudioClient(iface
);
1797 TRACE("(%p)\n", This
);
1799 pthread_mutex_lock(&pulse_lock
);
1800 hr
= pulse_stream_valid(This
);
1802 pthread_mutex_unlock(&pulse_lock
);
1806 if (!This
->started
) {
1807 pthread_mutex_unlock(&pulse_lock
);
1811 if (This
->dataflow
== eRender
) {
1812 o
= pa_stream_cork(This
->stream
, 1, pulse_op_cb
, &success
);
1814 while(pa_operation_get_state(o
) == PA_OPERATION_RUNNING
)
1815 pthread_cond_wait(&pulse_cond
, &pulse_lock
);
1816 pa_operation_unref(o
);
1822 if (SUCCEEDED(hr
)) {
1823 This
->started
= FALSE
;
1825 pthread_mutex_unlock(&pulse_lock
);
1829 static HRESULT WINAPI
AudioClient_Reset(IAudioClient
*iface
)
1831 ACImpl
*This
= impl_from_IAudioClient(iface
);
1834 TRACE("(%p)\n", This
);
1836 pthread_mutex_lock(&pulse_lock
);
1837 hr
= pulse_stream_valid(This
);
1839 pthread_mutex_unlock(&pulse_lock
);
1843 if (This
->started
) {
1844 pthread_mutex_unlock(&pulse_lock
);
1845 return AUDCLNT_E_NOT_STOPPED
;
1849 pthread_mutex_unlock(&pulse_lock
);
1850 return AUDCLNT_E_BUFFER_OPERATION_PENDING
;
1853 if (This
->dataflow
== eRender
) {
1854 /* If there is still data in the render buffer it needs to be removed from the server */
1857 pa_operation
*o
= pa_stream_flush(This
->stream
, pulse_op_cb
, &success
);
1859 while(pa_operation_get_state(o
) == PA_OPERATION_RUNNING
)
1860 pthread_cond_wait(&pulse_cond
, &pulse_lock
);
1861 pa_operation_unref(o
);
1864 if (success
|| !This
->pad
){
1865 This
->clock_lastpos
= This
->clock_written
= This
->pad
= 0;
1866 This
->wri_offs_bytes
= This
->lcl_offs_bytes
= This
->held_bytes
= 0;
1870 This
->clock_written
+= This
->pad
;
1873 if ((p
= This
->locked_ptr
)) {
1874 This
->locked_ptr
= NULL
;
1875 list_add_tail(&This
->packet_free_head
, &p
->entry
);
1877 list_move_tail(&This
->packet_free_head
, &This
->packet_filled_head
);
1879 pthread_mutex_unlock(&pulse_lock
);
1884 static HRESULT WINAPI
AudioClient_SetEventHandle(IAudioClient
*iface
,
1887 ACImpl
*This
= impl_from_IAudioClient(iface
);
1890 TRACE("(%p)->(%p)\n", This
, event
);
1893 return E_INVALIDARG
;
1895 pthread_mutex_lock(&pulse_lock
);
1896 hr
= pulse_stream_valid(This
);
1898 pthread_mutex_unlock(&pulse_lock
);
1902 if (!(This
->flags
& AUDCLNT_STREAMFLAGS_EVENTCALLBACK
))
1903 hr
= AUDCLNT_E_EVENTHANDLE_NOT_EXPECTED
;
1904 else if (This
->event
)
1905 hr
= HRESULT_FROM_WIN32(ERROR_INVALID_NAME
);
1907 This
->event
= event
;
1908 pthread_mutex_unlock(&pulse_lock
);
1912 static HRESULT WINAPI
AudioClient_GetService(IAudioClient
*iface
, REFIID riid
,
1915 ACImpl
*This
= impl_from_IAudioClient(iface
);
1918 TRACE("(%p)->(%s, %p)\n", This
, debugstr_guid(riid
), ppv
);
1924 pthread_mutex_lock(&pulse_lock
);
1925 hr
= pulse_stream_valid(This
);
1926 pthread_mutex_unlock(&pulse_lock
);
1930 if (IsEqualIID(riid
, &IID_IAudioRenderClient
)) {
1931 if (This
->dataflow
!= eRender
)
1932 return AUDCLNT_E_WRONG_ENDPOINT_TYPE
;
1933 *ppv
= &This
->IAudioRenderClient_iface
;
1934 } else if (IsEqualIID(riid
, &IID_IAudioCaptureClient
)) {
1935 if (This
->dataflow
!= eCapture
)
1936 return AUDCLNT_E_WRONG_ENDPOINT_TYPE
;
1937 *ppv
= &This
->IAudioCaptureClient_iface
;
1938 } else if (IsEqualIID(riid
, &IID_IAudioClock
)) {
1939 *ppv
= &This
->IAudioClock_iface
;
1940 } else if (IsEqualIID(riid
, &IID_IAudioStreamVolume
)) {
1941 *ppv
= &This
->IAudioStreamVolume_iface
;
1942 } else if (IsEqualIID(riid
, &IID_IAudioSessionControl
) ||
1943 IsEqualIID(riid
, &IID_IChannelAudioVolume
) ||
1944 IsEqualIID(riid
, &IID_ISimpleAudioVolume
)) {
1945 if (!This
->session_wrapper
) {
1946 This
->session_wrapper
= AudioSessionWrapper_Create(This
);
1947 if (!This
->session_wrapper
)
1948 return E_OUTOFMEMORY
;
1950 if (IsEqualIID(riid
, &IID_IAudioSessionControl
))
1951 *ppv
= &This
->session_wrapper
->IAudioSessionControl2_iface
;
1952 else if (IsEqualIID(riid
, &IID_IChannelAudioVolume
))
1953 *ppv
= &This
->session_wrapper
->IChannelAudioVolume_iface
;
1954 else if (IsEqualIID(riid
, &IID_ISimpleAudioVolume
))
1955 *ppv
= &This
->session_wrapper
->ISimpleAudioVolume_iface
;
1959 IUnknown_AddRef((IUnknown
*)*ppv
);
1963 FIXME("stub %s\n", debugstr_guid(riid
));
1964 return E_NOINTERFACE
;
1967 static const IAudioClientVtbl AudioClient_Vtbl
=
1969 AudioClient_QueryInterface
,
1971 AudioClient_Release
,
1972 AudioClient_Initialize
,
1973 AudioClient_GetBufferSize
,
1974 AudioClient_GetStreamLatency
,
1975 AudioClient_GetCurrentPadding
,
1976 AudioClient_IsFormatSupported
,
1977 AudioClient_GetMixFormat
,
1978 AudioClient_GetDevicePeriod
,
1982 AudioClient_SetEventHandle
,
1983 AudioClient_GetService
1986 static HRESULT WINAPI
AudioRenderClient_QueryInterface(
1987 IAudioRenderClient
*iface
, REFIID riid
, void **ppv
)
1989 ACImpl
*This
= impl_from_IAudioRenderClient(iface
);
1990 TRACE("(%p)->(%s, %p)\n", iface
, debugstr_guid(riid
), ppv
);
1996 if (IsEqualIID(riid
, &IID_IUnknown
) ||
1997 IsEqualIID(riid
, &IID_IAudioRenderClient
))
2000 IUnknown_AddRef((IUnknown
*)*ppv
);
2004 if (IsEqualIID(riid
, &IID_IMarshal
))
2005 return IUnknown_QueryInterface(This
->marshal
, riid
, ppv
);
2007 WARN("Unknown interface %s\n", debugstr_guid(riid
));
2008 return E_NOINTERFACE
;
2011 static ULONG WINAPI
AudioRenderClient_AddRef(IAudioRenderClient
*iface
)
2013 ACImpl
*This
= impl_from_IAudioRenderClient(iface
);
2014 return AudioClient_AddRef(&This
->IAudioClient_iface
);
2017 static ULONG WINAPI
AudioRenderClient_Release(IAudioRenderClient
*iface
)
2019 ACImpl
*This
= impl_from_IAudioRenderClient(iface
);
2020 return AudioClient_Release(&This
->IAudioClient_iface
);
2023 static void alloc_tmp_buffer(ACImpl
*This
, UINT32 bytes
)
2025 if(This
->tmp_buffer_bytes
>= bytes
)
2028 HeapFree(GetProcessHeap(), 0, This
->tmp_buffer
);
2029 This
->tmp_buffer
= HeapAlloc(GetProcessHeap(), 0, bytes
);
2030 This
->tmp_buffer_bytes
= bytes
;
2033 static HRESULT WINAPI
AudioRenderClient_GetBuffer(IAudioRenderClient
*iface
,
2034 UINT32 frames
, BYTE
**data
)
2036 ACImpl
*This
= impl_from_IAudioRenderClient(iface
);
2037 size_t avail
, req
, bytes
= frames
* pa_frame_size(&This
->ss
);
2042 TRACE("(%p)->(%u, %p)\n", This
, frames
, data
);
2048 pthread_mutex_lock(&pulse_lock
);
2049 hr
= pulse_stream_valid(This
);
2050 if (FAILED(hr
) || This
->locked
) {
2051 pthread_mutex_unlock(&pulse_lock
);
2052 return FAILED(hr
) ? hr
: AUDCLNT_E_OUT_OF_ORDER
;
2055 pthread_mutex_unlock(&pulse_lock
);
2059 ACImpl_GetRenderPad(This
, &pad
);
2060 avail
= This
->bufsize_frames
- pad
;
2061 if (avail
< frames
|| bytes
> This
->bufsize_bytes
) {
2062 pthread_mutex_unlock(&pulse_lock
);
2063 WARN("Wanted to write %u, but only %zu available\n", frames
, avail
);
2064 return AUDCLNT_E_BUFFER_TOO_LARGE
;
2067 if(This
->local_buffer
){
2068 if(This
->wri_offs_bytes
+ bytes
> This
->bufsize_bytes
){
2069 alloc_tmp_buffer(This
, bytes
);
2070 *data
= This
->tmp_buffer
;
2071 This
->locked
= -frames
;
2073 *data
= This
->local_buffer
+ This
->wri_offs_bytes
;
2074 This
->locked
= frames
;
2078 ret
= pa_stream_begin_write(This
->stream
, &This
->locked_ptr
, &req
);
2079 if (ret
< 0 || req
< bytes
) {
2080 FIXME("%p Not using pulse locked data: %i %zu/%u %u/%u\n", This
, ret
, req
/pa_frame_size(&This
->ss
), frames
, pad
, This
->bufsize_frames
);
2082 pa_stream_cancel_write(This
->stream
);
2083 alloc_tmp_buffer(This
, bytes
);
2084 *data
= This
->tmp_buffer
;
2085 This
->locked_ptr
= NULL
;
2087 *data
= This
->locked_ptr
;
2089 This
->locked
= frames
;
2092 silence_buffer(This
->ss
.format
, *data
, bytes
);
2094 pthread_mutex_unlock(&pulse_lock
);
2099 static void pulse_wrap_buffer(ACImpl
*This
, BYTE
*buffer
, UINT32 written_bytes
)
2101 UINT32 chunk_bytes
= This
->bufsize_bytes
- This
->wri_offs_bytes
;
2103 if(written_bytes
<= chunk_bytes
){
2104 memcpy(This
->local_buffer
+ This
->wri_offs_bytes
, buffer
, written_bytes
);
2106 memcpy(This
->local_buffer
+ This
->wri_offs_bytes
, buffer
, chunk_bytes
);
2107 memcpy(This
->local_buffer
, buffer
+ chunk_bytes
,
2108 written_bytes
- chunk_bytes
);
2112 static void pulse_free_noop(void *buf
)
2116 static HRESULT WINAPI
AudioRenderClient_ReleaseBuffer(
2117 IAudioRenderClient
*iface
, UINT32 written_frames
, DWORD flags
)
2119 ACImpl
*This
= impl_from_IAudioRenderClient(iface
);
2120 UINT32 written_bytes
= written_frames
* pa_frame_size(&This
->ss
);
2122 TRACE("(%p)->(%u, %x)\n", This
, written_frames
, flags
);
2124 pthread_mutex_lock(&pulse_lock
);
2125 if (!This
->locked
|| !written_frames
) {
2126 if (This
->locked_ptr
)
2127 pa_stream_cancel_write(This
->stream
);
2129 This
->locked_ptr
= NULL
;
2130 pthread_mutex_unlock(&pulse_lock
);
2131 return written_frames
? AUDCLNT_E_OUT_OF_ORDER
: S_OK
;
2134 if (This
->locked
< written_frames
) {
2135 pthread_mutex_unlock(&pulse_lock
);
2136 return AUDCLNT_E_INVALID_SIZE
;
2139 if(This
->local_buffer
){
2142 if(This
->locked
>= 0)
2143 buffer
= This
->local_buffer
+ This
->wri_offs_bytes
;
2145 buffer
= This
->tmp_buffer
;
2147 if(flags
& AUDCLNT_BUFFERFLAGS_SILENT
)
2148 silence_buffer(This
->ss
.format
, buffer
, written_bytes
);
2150 if(This
->locked
< 0)
2151 pulse_wrap_buffer(This
, buffer
, written_bytes
);
2153 This
->wri_offs_bytes
+= written_bytes
;
2154 This
->wri_offs_bytes
%= This
->bufsize_bytes
;
2156 This
->pad
+= written_bytes
;
2157 This
->held_bytes
+= written_bytes
;
2159 if(This
->held_bytes
== This
->pad
){
2161 UINT32 to_write
= min(This
->attr
.tlength
, written_bytes
);
2163 /* nothing in PA, so send data immediately */
2165 TRACE("pre-writing %u bytes\n", to_write
);
2167 e
= pa_stream_write(This
->stream
, buffer
, to_write
, NULL
, 0, PA_SEEK_RELATIVE
);
2169 ERR("pa_stream_write failed: 0x%x\n", e
);
2171 This
->lcl_offs_bytes
+= to_write
;
2172 This
->lcl_offs_bytes
%= This
->bufsize_bytes
;
2173 This
->held_bytes
-= to_write
;
2177 if (This
->locked_ptr
) {
2178 if (flags
& AUDCLNT_BUFFERFLAGS_SILENT
)
2179 silence_buffer(This
->ss
.format
, This
->locked_ptr
, written_bytes
);
2180 pa_stream_write(This
->stream
, This
->locked_ptr
, written_bytes
, NULL
, 0, PA_SEEK_RELATIVE
);
2182 if (flags
& AUDCLNT_BUFFERFLAGS_SILENT
)
2183 silence_buffer(This
->ss
.format
, This
->tmp_buffer
, written_bytes
);
2184 pa_stream_write(This
->stream
, This
->tmp_buffer
, written_bytes
, pulse_free_noop
, 0, PA_SEEK_RELATIVE
);
2186 This
->pad
+= written_bytes
;
2189 if (!pa_stream_is_corked(This
->stream
)) {
2192 o
= pa_stream_trigger(This
->stream
, pulse_op_cb
, &success
);
2194 while(pa_operation_get_state(o
) == PA_OPERATION_RUNNING
)
2195 pthread_cond_wait(&pulse_cond
, &pulse_lock
);
2196 pa_operation_unref(o
);
2201 This
->locked_ptr
= NULL
;
2202 TRACE("Released %u, pad %zu\n", written_frames
, This
->pad
/ pa_frame_size(&This
->ss
));
2203 assert(This
->pad
<= This
->bufsize_bytes
);
2205 pthread_mutex_unlock(&pulse_lock
);
2209 static const IAudioRenderClientVtbl AudioRenderClient_Vtbl
= {
2210 AudioRenderClient_QueryInterface
,
2211 AudioRenderClient_AddRef
,
2212 AudioRenderClient_Release
,
2213 AudioRenderClient_GetBuffer
,
2214 AudioRenderClient_ReleaseBuffer
2217 static HRESULT WINAPI
AudioCaptureClient_QueryInterface(
2218 IAudioCaptureClient
*iface
, REFIID riid
, void **ppv
)
2220 ACImpl
*This
= impl_from_IAudioCaptureClient(iface
);
2221 TRACE("(%p)->(%s, %p)\n", iface
, debugstr_guid(riid
), ppv
);
2227 if (IsEqualIID(riid
, &IID_IUnknown
) ||
2228 IsEqualIID(riid
, &IID_IAudioCaptureClient
))
2231 IUnknown_AddRef((IUnknown
*)*ppv
);
2235 if (IsEqualIID(riid
, &IID_IMarshal
))
2236 return IUnknown_QueryInterface(This
->marshal
, riid
, ppv
);
2238 WARN("Unknown interface %s\n", debugstr_guid(riid
));
2239 return E_NOINTERFACE
;
2242 static ULONG WINAPI
AudioCaptureClient_AddRef(IAudioCaptureClient
*iface
)
2244 ACImpl
*This
= impl_from_IAudioCaptureClient(iface
);
2245 return IAudioClient_AddRef(&This
->IAudioClient_iface
);
2248 static ULONG WINAPI
AudioCaptureClient_Release(IAudioCaptureClient
*iface
)
2250 ACImpl
*This
= impl_from_IAudioCaptureClient(iface
);
2251 return IAudioClient_Release(&This
->IAudioClient_iface
);
2254 static HRESULT WINAPI
AudioCaptureClient_GetBuffer(IAudioCaptureClient
*iface
,
2255 BYTE
**data
, UINT32
*frames
, DWORD
*flags
, UINT64
*devpos
,
2258 ACImpl
*This
= impl_from_IAudioCaptureClient(iface
);
2262 TRACE("(%p)->(%p, %p, %p, %p, %p)\n", This
, data
, frames
, flags
,
2265 if (!data
|| !frames
|| !flags
)
2268 pthread_mutex_lock(&pulse_lock
);
2269 hr
= pulse_stream_valid(This
);
2270 if (FAILED(hr
) || This
->locked
) {
2271 pthread_mutex_unlock(&pulse_lock
);
2272 return FAILED(hr
) ? hr
: AUDCLNT_E_OUT_OF_ORDER
;
2275 ACImpl_GetCapturePad(This
, NULL
);
2276 if ((packet
= This
->locked_ptr
)) {
2277 *frames
= This
->capture_period
/ pa_frame_size(&This
->ss
);
2279 if (packet
->discont
)
2280 *flags
|= AUDCLNT_BUFFERFLAGS_DATA_DISCONTINUITY
;
2282 if (packet
->discont
)
2283 *devpos
= (This
->clock_written
+ This
->capture_period
) / pa_frame_size(&This
->ss
);
2285 *devpos
= This
->clock_written
/ pa_frame_size(&This
->ss
);
2288 *qpcpos
= packet
->qpcpos
;
2289 *data
= packet
->data
;
2293 This
->locked
= *frames
;
2294 pthread_mutex_unlock(&pulse_lock
);
2295 return *frames
? S_OK
: AUDCLNT_S_BUFFER_EMPTY
;
2298 static HRESULT WINAPI
AudioCaptureClient_ReleaseBuffer(
2299 IAudioCaptureClient
*iface
, UINT32 done
)
2301 ACImpl
*This
= impl_from_IAudioCaptureClient(iface
);
2303 TRACE("(%p)->(%u)\n", This
, done
);
2305 pthread_mutex_lock(&pulse_lock
);
2306 if (!This
->locked
&& done
) {
2307 pthread_mutex_unlock(&pulse_lock
);
2308 return AUDCLNT_E_OUT_OF_ORDER
;
2310 if (done
&& This
->locked
!= done
) {
2311 pthread_mutex_unlock(&pulse_lock
);
2312 return AUDCLNT_E_INVALID_SIZE
;
2315 ACPacket
*packet
= This
->locked_ptr
;
2316 This
->locked_ptr
= NULL
;
2317 This
->pad
-= This
->capture_period
;
2318 if (packet
->discont
)
2319 This
->clock_written
+= 2 * This
->capture_period
;
2321 This
->clock_written
+= This
->capture_period
;
2322 list_add_tail(&This
->packet_free_head
, &packet
->entry
);
2325 pthread_mutex_unlock(&pulse_lock
);
2329 static HRESULT WINAPI
AudioCaptureClient_GetNextPacketSize(
2330 IAudioCaptureClient
*iface
, UINT32
*frames
)
2332 ACImpl
*This
= impl_from_IAudioCaptureClient(iface
);
2334 TRACE("(%p)->(%p)\n", This
, frames
);
2338 pthread_mutex_lock(&pulse_lock
);
2339 ACImpl_GetCapturePad(This
, NULL
);
2340 if (This
->locked_ptr
)
2341 *frames
= This
->capture_period
/ pa_frame_size(&This
->ss
);
2344 pthread_mutex_unlock(&pulse_lock
);
2348 static const IAudioCaptureClientVtbl AudioCaptureClient_Vtbl
=
2350 AudioCaptureClient_QueryInterface
,
2351 AudioCaptureClient_AddRef
,
2352 AudioCaptureClient_Release
,
2353 AudioCaptureClient_GetBuffer
,
2354 AudioCaptureClient_ReleaseBuffer
,
2355 AudioCaptureClient_GetNextPacketSize
2358 static HRESULT WINAPI
AudioClock_QueryInterface(IAudioClock
*iface
,
2359 REFIID riid
, void **ppv
)
2361 ACImpl
*This
= impl_from_IAudioClock(iface
);
2363 TRACE("(%p)->(%s, %p)\n", iface
, debugstr_guid(riid
), ppv
);
2369 if (IsEqualIID(riid
, &IID_IUnknown
) || IsEqualIID(riid
, &IID_IAudioClock
))
2371 else if (IsEqualIID(riid
, &IID_IAudioClock2
))
2372 *ppv
= &This
->IAudioClock2_iface
;
2374 IUnknown_AddRef((IUnknown
*)*ppv
);
2378 if (IsEqualIID(riid
, &IID_IMarshal
))
2379 return IUnknown_QueryInterface(This
->marshal
, riid
, ppv
);
2381 WARN("Unknown interface %s\n", debugstr_guid(riid
));
2382 return E_NOINTERFACE
;
2385 static ULONG WINAPI
AudioClock_AddRef(IAudioClock
*iface
)
2387 ACImpl
*This
= impl_from_IAudioClock(iface
);
2388 return IAudioClient_AddRef(&This
->IAudioClient_iface
);
2391 static ULONG WINAPI
AudioClock_Release(IAudioClock
*iface
)
2393 ACImpl
*This
= impl_from_IAudioClock(iface
);
2394 return IAudioClient_Release(&This
->IAudioClient_iface
);
2397 static HRESULT WINAPI
AudioClock_GetFrequency(IAudioClock
*iface
, UINT64
*freq
)
2399 ACImpl
*This
= impl_from_IAudioClock(iface
);
2402 TRACE("(%p)->(%p)\n", This
, freq
);
2404 pthread_mutex_lock(&pulse_lock
);
2405 hr
= pulse_stream_valid(This
);
2406 if (SUCCEEDED(hr
)) {
2407 *freq
= This
->ss
.rate
;
2408 if (This
->share
== AUDCLNT_SHAREMODE_SHARED
)
2409 *freq
*= pa_frame_size(&This
->ss
);
2411 pthread_mutex_unlock(&pulse_lock
);
2415 static HRESULT WINAPI
AudioClock_GetPosition(IAudioClock
*iface
, UINT64
*pos
,
2418 ACImpl
*This
= impl_from_IAudioClock(iface
);
2421 TRACE("(%p)->(%p, %p)\n", This
, pos
, qpctime
);
2426 pthread_mutex_lock(&pulse_lock
);
2427 hr
= pulse_stream_valid(This
);
2429 pthread_mutex_unlock(&pulse_lock
);
2433 *pos
= This
->clock_written
;
2435 if (This
->share
== AUDCLNT_SHAREMODE_EXCLUSIVE
)
2436 *pos
/= pa_frame_size(&This
->ss
);
2438 /* Make time never go backwards */
2439 if (*pos
< This
->clock_lastpos
)
2440 *pos
= This
->clock_lastpos
;
2442 This
->clock_lastpos
= *pos
;
2443 pthread_mutex_unlock(&pulse_lock
);
2445 TRACE("%p Position: %u\n", This
, (unsigned)*pos
);
2448 LARGE_INTEGER stamp
, freq
;
2449 QueryPerformanceCounter(&stamp
);
2450 QueryPerformanceFrequency(&freq
);
2451 *qpctime
= (stamp
.QuadPart
* (INT64
)10000000) / freq
.QuadPart
;
2457 static HRESULT WINAPI
AudioClock_GetCharacteristics(IAudioClock
*iface
,
2460 ACImpl
*This
= impl_from_IAudioClock(iface
);
2462 TRACE("(%p)->(%p)\n", This
, chars
);
2467 *chars
= AUDIOCLOCK_CHARACTERISTIC_FIXED_FREQ
;
2472 static const IAudioClockVtbl AudioClock_Vtbl
=
2474 AudioClock_QueryInterface
,
2477 AudioClock_GetFrequency
,
2478 AudioClock_GetPosition
,
2479 AudioClock_GetCharacteristics
2482 static HRESULT WINAPI
AudioClock2_QueryInterface(IAudioClock2
*iface
,
2483 REFIID riid
, void **ppv
)
2485 ACImpl
*This
= impl_from_IAudioClock2(iface
);
2486 return IAudioClock_QueryInterface(&This
->IAudioClock_iface
, riid
, ppv
);
2489 static ULONG WINAPI
AudioClock2_AddRef(IAudioClock2
*iface
)
2491 ACImpl
*This
= impl_from_IAudioClock2(iface
);
2492 return IAudioClient_AddRef(&This
->IAudioClient_iface
);
2495 static ULONG WINAPI
AudioClock2_Release(IAudioClock2
*iface
)
2497 ACImpl
*This
= impl_from_IAudioClock2(iface
);
2498 return IAudioClient_Release(&This
->IAudioClient_iface
);
2501 static HRESULT WINAPI
AudioClock2_GetDevicePosition(IAudioClock2
*iface
,
2502 UINT64
*pos
, UINT64
*qpctime
)
2504 ACImpl
*This
= impl_from_IAudioClock2(iface
);
2505 HRESULT hr
= AudioClock_GetPosition(&This
->IAudioClock_iface
, pos
, qpctime
);
2506 if (SUCCEEDED(hr
) && This
->share
== AUDCLNT_SHAREMODE_SHARED
)
2507 *pos
/= pa_frame_size(&This
->ss
);
2511 static const IAudioClock2Vtbl AudioClock2_Vtbl
=
2513 AudioClock2_QueryInterface
,
2515 AudioClock2_Release
,
2516 AudioClock2_GetDevicePosition
2519 static HRESULT WINAPI
AudioStreamVolume_QueryInterface(
2520 IAudioStreamVolume
*iface
, REFIID riid
, void **ppv
)
2522 ACImpl
*This
= impl_from_IAudioStreamVolume(iface
);
2524 TRACE("(%p)->(%s, %p)\n", iface
, debugstr_guid(riid
), ppv
);
2530 if (IsEqualIID(riid
, &IID_IUnknown
) ||
2531 IsEqualIID(riid
, &IID_IAudioStreamVolume
))
2534 IUnknown_AddRef((IUnknown
*)*ppv
);
2538 if (IsEqualIID(riid
, &IID_IMarshal
))
2539 return IUnknown_QueryInterface(This
->marshal
, riid
, ppv
);
2541 WARN("Unknown interface %s\n", debugstr_guid(riid
));
2542 return E_NOINTERFACE
;
2545 static ULONG WINAPI
AudioStreamVolume_AddRef(IAudioStreamVolume
*iface
)
2547 ACImpl
*This
= impl_from_IAudioStreamVolume(iface
);
2548 return IAudioClient_AddRef(&This
->IAudioClient_iface
);
2551 static ULONG WINAPI
AudioStreamVolume_Release(IAudioStreamVolume
*iface
)
2553 ACImpl
*This
= impl_from_IAudioStreamVolume(iface
);
2554 return IAudioClient_Release(&This
->IAudioClient_iface
);
2557 static HRESULT WINAPI
AudioStreamVolume_GetChannelCount(
2558 IAudioStreamVolume
*iface
, UINT32
*out
)
2560 ACImpl
*This
= impl_from_IAudioStreamVolume(iface
);
2562 TRACE("(%p)->(%p)\n", This
, out
);
2567 *out
= This
->ss
.channels
;
2572 struct pulse_info_cb_data
{
2577 static HRESULT WINAPI
AudioStreamVolume_SetAllVolumes(
2578 IAudioStreamVolume
*iface
, UINT32 count
, const float *levels
)
2580 ACImpl
*This
= impl_from_IAudioStreamVolume(iface
);
2584 TRACE("(%p)->(%d, %p)\n", This
, count
, levels
);
2589 if (count
!= This
->ss
.channels
)
2590 return E_INVALIDARG
;
2592 pthread_mutex_lock(&pulse_lock
);
2593 hr
= pulse_stream_valid(This
);
2597 for (i
= 0; i
< count
; ++i
)
2598 This
->vol
[i
] = levels
[i
];
2601 pthread_mutex_unlock(&pulse_lock
);
2605 static HRESULT WINAPI
AudioStreamVolume_GetAllVolumes(
2606 IAudioStreamVolume
*iface
, UINT32 count
, float *levels
)
2608 ACImpl
*This
= impl_from_IAudioStreamVolume(iface
);
2612 TRACE("(%p)->(%d, %p)\n", This
, count
, levels
);
2617 if (count
!= This
->ss
.channels
)
2618 return E_INVALIDARG
;
2620 pthread_mutex_lock(&pulse_lock
);
2621 hr
= pulse_stream_valid(This
);
2625 for (i
= 0; i
< count
; ++i
)
2626 levels
[i
] = This
->vol
[i
];
2629 pthread_mutex_unlock(&pulse_lock
);
2633 static HRESULT WINAPI
AudioStreamVolume_SetChannelVolume(
2634 IAudioStreamVolume
*iface
, UINT32 index
, float level
)
2636 ACImpl
*This
= impl_from_IAudioStreamVolume(iface
);
2638 float volumes
[PA_CHANNELS_MAX
];
2640 TRACE("(%p)->(%d, %f)\n", This
, index
, level
);
2642 if (level
< 0.f
|| level
> 1.f
)
2643 return E_INVALIDARG
;
2645 if (index
>= This
->ss
.channels
)
2646 return E_INVALIDARG
;
2648 hr
= AudioStreamVolume_GetAllVolumes(iface
, This
->ss
.channels
, volumes
);
2649 volumes
[index
] = level
;
2651 hr
= AudioStreamVolume_SetAllVolumes(iface
, This
->ss
.channels
, volumes
);
2655 static HRESULT WINAPI
AudioStreamVolume_GetChannelVolume(
2656 IAudioStreamVolume
*iface
, UINT32 index
, float *level
)
2658 ACImpl
*This
= impl_from_IAudioStreamVolume(iface
);
2659 float volumes
[PA_CHANNELS_MAX
];
2662 TRACE("(%p)->(%d, %p)\n", This
, index
, level
);
2667 if (index
>= This
->ss
.channels
)
2668 return E_INVALIDARG
;
2670 hr
= AudioStreamVolume_GetAllVolumes(iface
, This
->ss
.channels
, volumes
);
2672 *level
= volumes
[index
];
2676 static const IAudioStreamVolumeVtbl AudioStreamVolume_Vtbl
=
2678 AudioStreamVolume_QueryInterface
,
2679 AudioStreamVolume_AddRef
,
2680 AudioStreamVolume_Release
,
2681 AudioStreamVolume_GetChannelCount
,
2682 AudioStreamVolume_SetChannelVolume
,
2683 AudioStreamVolume_GetChannelVolume
,
2684 AudioStreamVolume_SetAllVolumes
,
2685 AudioStreamVolume_GetAllVolumes
2688 static AudioSessionWrapper
*AudioSessionWrapper_Create(ACImpl
*client
)
2690 AudioSessionWrapper
*ret
;
2692 ret
= HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY
,
2693 sizeof(AudioSessionWrapper
));
2697 ret
->IAudioSessionControl2_iface
.lpVtbl
= &AudioSessionControl2_Vtbl
;
2698 ret
->ISimpleAudioVolume_iface
.lpVtbl
= &SimpleAudioVolume_Vtbl
;
2699 ret
->IChannelAudioVolume_iface
.lpVtbl
= &ChannelAudioVolume_Vtbl
;
2703 ret
->client
= client
;
2705 ret
->session
= client
->session
;
2706 AudioClient_AddRef(&client
->IAudioClient_iface
);
2712 static HRESULT WINAPI
AudioSessionControl_QueryInterface(
2713 IAudioSessionControl2
*iface
, REFIID riid
, void **ppv
)
2715 TRACE("(%p)->(%s, %p)\n", iface
, debugstr_guid(riid
), ppv
);
2721 if (IsEqualIID(riid
, &IID_IUnknown
) ||
2722 IsEqualIID(riid
, &IID_IAudioSessionControl
) ||
2723 IsEqualIID(riid
, &IID_IAudioSessionControl2
))
2726 IUnknown_AddRef((IUnknown
*)*ppv
);
2730 WARN("Unknown interface %s\n", debugstr_guid(riid
));
2731 return E_NOINTERFACE
;
2734 static ULONG WINAPI
AudioSessionControl_AddRef(IAudioSessionControl2
*iface
)
2736 AudioSessionWrapper
*This
= impl_from_IAudioSessionControl2(iface
);
2738 ref
= InterlockedIncrement(&This
->ref
);
2739 TRACE("(%p) Refcount now %u\n", This
, ref
);
2743 static ULONG WINAPI
AudioSessionControl_Release(IAudioSessionControl2
*iface
)
2745 AudioSessionWrapper
*This
= impl_from_IAudioSessionControl2(iface
);
2747 ref
= InterlockedDecrement(&This
->ref
);
2748 TRACE("(%p) Refcount now %u\n", This
, ref
);
2751 This
->client
->session_wrapper
= NULL
;
2752 AudioClient_Release(&This
->client
->IAudioClient_iface
);
2754 HeapFree(GetProcessHeap(), 0, This
);
2759 static HRESULT WINAPI
AudioSessionControl_GetState(IAudioSessionControl2
*iface
,
2760 AudioSessionState
*state
)
2762 AudioSessionWrapper
*This
= impl_from_IAudioSessionControl2(iface
);
2765 TRACE("(%p)->(%p)\n", This
, state
);
2768 return NULL_PTR_ERR
;
2770 pthread_mutex_lock(&pulse_lock
);
2771 if (list_empty(&This
->session
->clients
)) {
2772 *state
= AudioSessionStateExpired
;
2775 LIST_FOR_EACH_ENTRY(client
, &This
->session
->clients
, ACImpl
, entry
) {
2776 if (client
->started
) {
2777 *state
= AudioSessionStateActive
;
2781 *state
= AudioSessionStateInactive
;
2784 pthread_mutex_unlock(&pulse_lock
);
2788 static HRESULT WINAPI
AudioSessionControl_GetDisplayName(
2789 IAudioSessionControl2
*iface
, WCHAR
**name
)
2791 AudioSessionWrapper
*This
= impl_from_IAudioSessionControl2(iface
);
2793 FIXME("(%p)->(%p) - stub\n", This
, name
);
2798 static HRESULT WINAPI
AudioSessionControl_SetDisplayName(
2799 IAudioSessionControl2
*iface
, const WCHAR
*name
, const GUID
*session
)
2801 AudioSessionWrapper
*This
= impl_from_IAudioSessionControl2(iface
);
2803 FIXME("(%p)->(%p, %s) - stub\n", This
, name
, debugstr_guid(session
));
2808 static HRESULT WINAPI
AudioSessionControl_GetIconPath(
2809 IAudioSessionControl2
*iface
, WCHAR
**path
)
2811 AudioSessionWrapper
*This
= impl_from_IAudioSessionControl2(iface
);
2813 FIXME("(%p)->(%p) - stub\n", This
, path
);
2818 static HRESULT WINAPI
AudioSessionControl_SetIconPath(
2819 IAudioSessionControl2
*iface
, const WCHAR
*path
, const GUID
*session
)
2821 AudioSessionWrapper
*This
= impl_from_IAudioSessionControl2(iface
);
2823 FIXME("(%p)->(%p, %s) - stub\n", This
, path
, debugstr_guid(session
));
2828 static HRESULT WINAPI
AudioSessionControl_GetGroupingParam(
2829 IAudioSessionControl2
*iface
, GUID
*group
)
2831 AudioSessionWrapper
*This
= impl_from_IAudioSessionControl2(iface
);
2833 FIXME("(%p)->(%p) - stub\n", This
, group
);
2838 static HRESULT WINAPI
AudioSessionControl_SetGroupingParam(
2839 IAudioSessionControl2
*iface
, const GUID
*group
, const GUID
*session
)
2841 AudioSessionWrapper
*This
= impl_from_IAudioSessionControl2(iface
);
2843 FIXME("(%p)->(%s, %s) - stub\n", This
, debugstr_guid(group
),
2844 debugstr_guid(session
));
2849 static HRESULT WINAPI
AudioSessionControl_RegisterAudioSessionNotification(
2850 IAudioSessionControl2
*iface
, IAudioSessionEvents
*events
)
2852 AudioSessionWrapper
*This
= impl_from_IAudioSessionControl2(iface
);
2854 FIXME("(%p)->(%p) - stub\n", This
, events
);
2859 static HRESULT WINAPI
AudioSessionControl_UnregisterAudioSessionNotification(
2860 IAudioSessionControl2
*iface
, IAudioSessionEvents
*events
)
2862 AudioSessionWrapper
*This
= impl_from_IAudioSessionControl2(iface
);
2864 FIXME("(%p)->(%p) - stub\n", This
, events
);
2869 static HRESULT WINAPI
AudioSessionControl_GetSessionIdentifier(
2870 IAudioSessionControl2
*iface
, WCHAR
**id
)
2872 AudioSessionWrapper
*This
= impl_from_IAudioSessionControl2(iface
);
2874 FIXME("(%p)->(%p) - stub\n", This
, id
);
2879 static HRESULT WINAPI
AudioSessionControl_GetSessionInstanceIdentifier(
2880 IAudioSessionControl2
*iface
, WCHAR
**id
)
2882 AudioSessionWrapper
*This
= impl_from_IAudioSessionControl2(iface
);
2884 FIXME("(%p)->(%p) - stub\n", This
, id
);
2889 static HRESULT WINAPI
AudioSessionControl_GetProcessId(
2890 IAudioSessionControl2
*iface
, DWORD
*pid
)
2892 AudioSessionWrapper
*This
= impl_from_IAudioSessionControl2(iface
);
2894 TRACE("(%p)->(%p)\n", This
, pid
);
2899 *pid
= GetCurrentProcessId();
2904 static HRESULT WINAPI
AudioSessionControl_IsSystemSoundsSession(
2905 IAudioSessionControl2
*iface
)
2907 AudioSessionWrapper
*This
= impl_from_IAudioSessionControl2(iface
);
2909 TRACE("(%p)\n", This
);
2914 static HRESULT WINAPI
AudioSessionControl_SetDuckingPreference(
2915 IAudioSessionControl2
*iface
, BOOL optout
)
2917 AudioSessionWrapper
*This
= impl_from_IAudioSessionControl2(iface
);
2919 TRACE("(%p)->(%d)\n", This
, optout
);
2924 static const IAudioSessionControl2Vtbl AudioSessionControl2_Vtbl
=
2926 AudioSessionControl_QueryInterface
,
2927 AudioSessionControl_AddRef
,
2928 AudioSessionControl_Release
,
2929 AudioSessionControl_GetState
,
2930 AudioSessionControl_GetDisplayName
,
2931 AudioSessionControl_SetDisplayName
,
2932 AudioSessionControl_GetIconPath
,
2933 AudioSessionControl_SetIconPath
,
2934 AudioSessionControl_GetGroupingParam
,
2935 AudioSessionControl_SetGroupingParam
,
2936 AudioSessionControl_RegisterAudioSessionNotification
,
2937 AudioSessionControl_UnregisterAudioSessionNotification
,
2938 AudioSessionControl_GetSessionIdentifier
,
2939 AudioSessionControl_GetSessionInstanceIdentifier
,
2940 AudioSessionControl_GetProcessId
,
2941 AudioSessionControl_IsSystemSoundsSession
,
2942 AudioSessionControl_SetDuckingPreference
2945 typedef struct _SessionMgr
{
2946 IAudioSessionManager2 IAudioSessionManager2_iface
;
2953 static HRESULT WINAPI
AudioSessionManager_QueryInterface(IAudioSessionManager2
*iface
,
2954 REFIID riid
, void **ppv
)
2956 TRACE("(%p)->(%s, %p)\n", iface
, debugstr_guid(riid
), ppv
);
2962 if (IsEqualIID(riid
, &IID_IUnknown
) ||
2963 IsEqualIID(riid
, &IID_IAudioSessionManager
) ||
2964 IsEqualIID(riid
, &IID_IAudioSessionManager2
))
2967 IUnknown_AddRef((IUnknown
*)*ppv
);
2971 WARN("Unknown interface %s\n", debugstr_guid(riid
));
2972 return E_NOINTERFACE
;
2975 static inline SessionMgr
*impl_from_IAudioSessionManager2(IAudioSessionManager2
*iface
)
2977 return CONTAINING_RECORD(iface
, SessionMgr
, IAudioSessionManager2_iface
);
2980 static ULONG WINAPI
AudioSessionManager_AddRef(IAudioSessionManager2
*iface
)
2982 SessionMgr
*This
= impl_from_IAudioSessionManager2(iface
);
2984 ref
= InterlockedIncrement(&This
->ref
);
2985 TRACE("(%p) Refcount now %u\n", This
, ref
);
2989 static ULONG WINAPI
AudioSessionManager_Release(IAudioSessionManager2
*iface
)
2991 SessionMgr
*This
= impl_from_IAudioSessionManager2(iface
);
2993 ref
= InterlockedDecrement(&This
->ref
);
2994 TRACE("(%p) Refcount now %u\n", This
, ref
);
2996 HeapFree(GetProcessHeap(), 0, This
);
3000 static HRESULT WINAPI
AudioSessionManager_GetAudioSessionControl(
3001 IAudioSessionManager2
*iface
, const GUID
*session_guid
, DWORD flags
,
3002 IAudioSessionControl
**out
)
3004 SessionMgr
*This
= impl_from_IAudioSessionManager2(iface
);
3005 AudioSession
*session
;
3006 AudioSessionWrapper
*wrapper
;
3009 TRACE("(%p)->(%s, %x, %p)\n", This
, debugstr_guid(session_guid
),
3012 hr
= get_audio_session(session_guid
, This
->device
, 0, &session
);
3016 wrapper
= AudioSessionWrapper_Create(NULL
);
3018 return E_OUTOFMEMORY
;
3020 wrapper
->session
= session
;
3022 *out
= (IAudioSessionControl
*)&wrapper
->IAudioSessionControl2_iface
;
3027 static HRESULT WINAPI
AudioSessionManager_GetSimpleAudioVolume(
3028 IAudioSessionManager2
*iface
, const GUID
*session_guid
, DWORD flags
,
3029 ISimpleAudioVolume
**out
)
3031 SessionMgr
*This
= impl_from_IAudioSessionManager2(iface
);
3032 AudioSession
*session
;
3033 AudioSessionWrapper
*wrapper
;
3036 TRACE("(%p)->(%s, %x, %p)\n", This
, debugstr_guid(session_guid
),
3039 hr
= get_audio_session(session_guid
, This
->device
, 0, &session
);
3043 wrapper
= AudioSessionWrapper_Create(NULL
);
3045 return E_OUTOFMEMORY
;
3047 wrapper
->session
= session
;
3049 *out
= &wrapper
->ISimpleAudioVolume_iface
;
3054 static HRESULT WINAPI
AudioSessionManager_GetSessionEnumerator(
3055 IAudioSessionManager2
*iface
, IAudioSessionEnumerator
**out
)
3057 SessionMgr
*This
= impl_from_IAudioSessionManager2(iface
);
3058 FIXME("(%p)->(%p) - stub\n", This
, out
);
3062 static HRESULT WINAPI
AudioSessionManager_RegisterSessionNotification(
3063 IAudioSessionManager2
*iface
, IAudioSessionNotification
*notification
)
3065 SessionMgr
*This
= impl_from_IAudioSessionManager2(iface
);
3066 FIXME("(%p)->(%p) - stub\n", This
, notification
);
3070 static HRESULT WINAPI
AudioSessionManager_UnregisterSessionNotification(
3071 IAudioSessionManager2
*iface
, IAudioSessionNotification
*notification
)
3073 SessionMgr
*This
= impl_from_IAudioSessionManager2(iface
);
3074 FIXME("(%p)->(%p) - stub\n", This
, notification
);
3078 static HRESULT WINAPI
AudioSessionManager_RegisterDuckNotification(
3079 IAudioSessionManager2
*iface
, const WCHAR
*session_id
,
3080 IAudioVolumeDuckNotification
*notification
)
3082 SessionMgr
*This
= impl_from_IAudioSessionManager2(iface
);
3083 FIXME("(%p)->(%p) - stub\n", This
, notification
);
3087 static HRESULT WINAPI
AudioSessionManager_UnregisterDuckNotification(
3088 IAudioSessionManager2
*iface
,
3089 IAudioVolumeDuckNotification
*notification
)
3091 SessionMgr
*This
= impl_from_IAudioSessionManager2(iface
);
3092 FIXME("(%p)->(%p) - stub\n", This
, notification
);
3096 static const IAudioSessionManager2Vtbl AudioSessionManager2_Vtbl
=
3098 AudioSessionManager_QueryInterface
,
3099 AudioSessionManager_AddRef
,
3100 AudioSessionManager_Release
,
3101 AudioSessionManager_GetAudioSessionControl
,
3102 AudioSessionManager_GetSimpleAudioVolume
,
3103 AudioSessionManager_GetSessionEnumerator
,
3104 AudioSessionManager_RegisterSessionNotification
,
3105 AudioSessionManager_UnregisterSessionNotification
,
3106 AudioSessionManager_RegisterDuckNotification
,
3107 AudioSessionManager_UnregisterDuckNotification
3110 static HRESULT WINAPI
SimpleAudioVolume_QueryInterface(
3111 ISimpleAudioVolume
*iface
, REFIID riid
, void **ppv
)
3113 TRACE("(%p)->(%s, %p)\n", iface
, debugstr_guid(riid
), ppv
);
3119 if (IsEqualIID(riid
, &IID_IUnknown
) ||
3120 IsEqualIID(riid
, &IID_ISimpleAudioVolume
))
3123 IUnknown_AddRef((IUnknown
*)*ppv
);
3127 WARN("Unknown interface %s\n", debugstr_guid(riid
));
3128 return E_NOINTERFACE
;
3131 static ULONG WINAPI
SimpleAudioVolume_AddRef(ISimpleAudioVolume
*iface
)
3133 AudioSessionWrapper
*This
= impl_from_ISimpleAudioVolume(iface
);
3134 return AudioSessionControl_AddRef(&This
->IAudioSessionControl2_iface
);
3137 static ULONG WINAPI
SimpleAudioVolume_Release(ISimpleAudioVolume
*iface
)
3139 AudioSessionWrapper
*This
= impl_from_ISimpleAudioVolume(iface
);
3140 return AudioSessionControl_Release(&This
->IAudioSessionControl2_iface
);
3143 static HRESULT WINAPI
SimpleAudioVolume_SetMasterVolume(
3144 ISimpleAudioVolume
*iface
, float level
, const GUID
*context
)
3146 AudioSessionWrapper
*This
= impl_from_ISimpleAudioVolume(iface
);
3147 AudioSession
*session
= This
->session
;
3149 TRACE("(%p)->(%f, %s)\n", session
, level
, wine_dbgstr_guid(context
));
3151 if (level
< 0.f
|| level
> 1.f
)
3152 return E_INVALIDARG
;
3155 FIXME("Notifications not supported yet\n");
3157 TRACE("PulseAudio does not support session volume control\n");
3159 pthread_mutex_lock(&pulse_lock
);
3160 session
->master_vol
= level
;
3161 pthread_mutex_unlock(&pulse_lock
);
3166 static HRESULT WINAPI
SimpleAudioVolume_GetMasterVolume(
3167 ISimpleAudioVolume
*iface
, float *level
)
3169 AudioSessionWrapper
*This
= impl_from_ISimpleAudioVolume(iface
);
3170 AudioSession
*session
= This
->session
;
3172 TRACE("(%p)->(%p)\n", session
, level
);
3175 return NULL_PTR_ERR
;
3177 *level
= session
->master_vol
;
3182 static HRESULT WINAPI
SimpleAudioVolume_SetMute(ISimpleAudioVolume
*iface
,
3183 BOOL mute
, const GUID
*context
)
3185 AudioSessionWrapper
*This
= impl_from_ISimpleAudioVolume(iface
);
3186 AudioSession
*session
= This
->session
;
3188 TRACE("(%p)->(%u, %p)\n", session
, mute
, context
);
3191 FIXME("Notifications not supported yet\n");
3193 session
->mute
= mute
;
3198 static HRESULT WINAPI
SimpleAudioVolume_GetMute(ISimpleAudioVolume
*iface
,
3201 AudioSessionWrapper
*This
= impl_from_ISimpleAudioVolume(iface
);
3202 AudioSession
*session
= This
->session
;
3204 TRACE("(%p)->(%p)\n", session
, mute
);
3207 return NULL_PTR_ERR
;
3209 *mute
= session
->mute
;
3214 static const ISimpleAudioVolumeVtbl SimpleAudioVolume_Vtbl
=
3216 SimpleAudioVolume_QueryInterface
,
3217 SimpleAudioVolume_AddRef
,
3218 SimpleAudioVolume_Release
,
3219 SimpleAudioVolume_SetMasterVolume
,
3220 SimpleAudioVolume_GetMasterVolume
,
3221 SimpleAudioVolume_SetMute
,
3222 SimpleAudioVolume_GetMute
3225 static HRESULT WINAPI
ChannelAudioVolume_QueryInterface(
3226 IChannelAudioVolume
*iface
, REFIID riid
, void **ppv
)
3228 TRACE("(%p)->(%s, %p)\n", iface
, debugstr_guid(riid
), ppv
);
3234 if (IsEqualIID(riid
, &IID_IUnknown
) ||
3235 IsEqualIID(riid
, &IID_IChannelAudioVolume
))
3238 IUnknown_AddRef((IUnknown
*)*ppv
);
3242 WARN("Unknown interface %s\n", debugstr_guid(riid
));
3243 return E_NOINTERFACE
;
3246 static ULONG WINAPI
ChannelAudioVolume_AddRef(IChannelAudioVolume
*iface
)
3248 AudioSessionWrapper
*This
= impl_from_IChannelAudioVolume(iface
);
3249 return AudioSessionControl_AddRef(&This
->IAudioSessionControl2_iface
);
3252 static ULONG WINAPI
ChannelAudioVolume_Release(IChannelAudioVolume
*iface
)
3254 AudioSessionWrapper
*This
= impl_from_IChannelAudioVolume(iface
);
3255 return AudioSessionControl_Release(&This
->IAudioSessionControl2_iface
);
3258 static HRESULT WINAPI
ChannelAudioVolume_GetChannelCount(
3259 IChannelAudioVolume
*iface
, UINT32
*out
)
3261 AudioSessionWrapper
*This
= impl_from_IChannelAudioVolume(iface
);
3262 AudioSession
*session
= This
->session
;
3264 TRACE("(%p)->(%p)\n", session
, out
);
3267 return NULL_PTR_ERR
;
3269 *out
= session
->channel_count
;
3274 static HRESULT WINAPI
ChannelAudioVolume_SetChannelVolume(
3275 IChannelAudioVolume
*iface
, UINT32 index
, float level
,
3276 const GUID
*context
)
3278 AudioSessionWrapper
*This
= impl_from_IChannelAudioVolume(iface
);
3279 AudioSession
*session
= This
->session
;
3281 TRACE("(%p)->(%d, %f, %s)\n", session
, index
, level
,
3282 wine_dbgstr_guid(context
));
3284 if (level
< 0.f
|| level
> 1.f
)
3285 return E_INVALIDARG
;
3287 if (index
>= session
->channel_count
)
3288 return E_INVALIDARG
;
3291 FIXME("Notifications not supported yet\n");
3293 TRACE("PulseAudio does not support session volume control\n");
3295 pthread_mutex_lock(&pulse_lock
);
3296 session
->channel_vols
[index
] = level
;
3297 pthread_mutex_unlock(&pulse_lock
);
3302 static HRESULT WINAPI
ChannelAudioVolume_GetChannelVolume(
3303 IChannelAudioVolume
*iface
, UINT32 index
, float *level
)
3305 AudioSessionWrapper
*This
= impl_from_IChannelAudioVolume(iface
);
3306 AudioSession
*session
= This
->session
;
3308 TRACE("(%p)->(%d, %p)\n", session
, index
, level
);
3311 return NULL_PTR_ERR
;
3313 if (index
>= session
->channel_count
)
3314 return E_INVALIDARG
;
3316 *level
= session
->channel_vols
[index
];
3321 static HRESULT WINAPI
ChannelAudioVolume_SetAllVolumes(
3322 IChannelAudioVolume
*iface
, UINT32 count
, const float *levels
,
3323 const GUID
*context
)
3325 AudioSessionWrapper
*This
= impl_from_IChannelAudioVolume(iface
);
3326 AudioSession
*session
= This
->session
;
3329 TRACE("(%p)->(%d, %p, %s)\n", session
, count
, levels
,
3330 wine_dbgstr_guid(context
));
3333 return NULL_PTR_ERR
;
3335 if (count
!= session
->channel_count
)
3336 return E_INVALIDARG
;
3339 FIXME("Notifications not supported yet\n");
3341 TRACE("PulseAudio does not support session volume control\n");
3343 pthread_mutex_lock(&pulse_lock
);
3344 for(i
= 0; i
< count
; ++i
)
3345 session
->channel_vols
[i
] = levels
[i
];
3346 pthread_mutex_unlock(&pulse_lock
);
3350 static HRESULT WINAPI
ChannelAudioVolume_GetAllVolumes(
3351 IChannelAudioVolume
*iface
, UINT32 count
, float *levels
)
3353 AudioSessionWrapper
*This
= impl_from_IChannelAudioVolume(iface
);
3354 AudioSession
*session
= This
->session
;
3357 TRACE("(%p)->(%d, %p)\n", session
, count
, levels
);
3360 return NULL_PTR_ERR
;
3362 if (count
!= session
->channel_count
)
3363 return E_INVALIDARG
;
3365 for(i
= 0; i
< count
; ++i
)
3366 levels
[i
] = session
->channel_vols
[i
];
3371 static const IChannelAudioVolumeVtbl ChannelAudioVolume_Vtbl
=
3373 ChannelAudioVolume_QueryInterface
,
3374 ChannelAudioVolume_AddRef
,
3375 ChannelAudioVolume_Release
,
3376 ChannelAudioVolume_GetChannelCount
,
3377 ChannelAudioVolume_SetChannelVolume
,
3378 ChannelAudioVolume_GetChannelVolume
,
3379 ChannelAudioVolume_SetAllVolumes
,
3380 ChannelAudioVolume_GetAllVolumes
3383 HRESULT WINAPI
AUDDRV_GetAudioSessionManager(IMMDevice
*device
,
3384 IAudioSessionManager2
**out
)
3386 SessionMgr
*This
= HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY
, sizeof(SessionMgr
));
3389 return E_OUTOFMEMORY
;
3390 This
->IAudioSessionManager2_iface
.lpVtbl
= &AudioSessionManager2_Vtbl
;
3391 This
->device
= device
;
3393 *out
= &This
->IAudioSessionManager2_iface
;
3397 HRESULT WINAPI
AUDDRV_GetPropValue(GUID
*guid
, const PROPERTYKEY
*prop
, PROPVARIANT
*out
)
3399 TRACE("%s, (%s,%u), %p\n", wine_dbgstr_guid(guid
), wine_dbgstr_guid(&prop
->fmtid
), prop
->pid
, out
);
3401 if (IsEqualGUID(guid
, &pulse_render_guid
) && IsEqualPropertyKey(*prop
, PKEY_AudioEndpoint_PhysicalSpeakers
)) {
3403 out
->u
.ulVal
= g_phys_speakers_mask
;
3405 return out
->u
.ulVal
? S_OK
: E_FAIL
;