2 This file is part of PulseAudio.
4 Copyright 2004-2006 Lennart Poettering
5 Copyright 2006 Pierre Ossman <ossman@cendio.se> for Cendio AB
7 PulseAudio is free software; you can redistribute it and/or modify
8 it under the terms of the GNU Lesser General Public License as published
9 by the Free Software Foundation; either version 2.1 of the License,
10 or (at your option) any later version.
12 PulseAudio is distributed in the hope that it will be useful, but
13 WITHOUT ANY WARRANTY; without even the implied warranty of
14 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
15 General Public License for more details.
17 You should have received a copy of the GNU Lesser General Public License
18 along with PulseAudio; if not, write to the Free Software
19 Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
31 #include <pulse/introspect.h>
32 #include <pulse/utf8.h>
33 #include <pulse/xmalloc.h>
34 #include <pulse/timeval.h>
35 #include <pulse/util.h>
36 #include <pulse/i18n.h>
38 #include <pulsecore/sink-input.h>
39 #include <pulsecore/namereg.h>
40 #include <pulsecore/core-util.h>
41 #include <pulsecore/sample-util.h>
42 #include <pulsecore/core-subscribe.h>
43 #include <pulsecore/log.h>
44 #include <pulsecore/macro.h>
45 #include <pulsecore/play-memblockq.h>
49 #define MAX_MIX_CHANNELS 32
50 #define MIX_BUFFER_LENGTH (PA_PAGE_SIZE)
51 #define ABSOLUTE_MIN_LATENCY (500)
52 #define ABSOLUTE_MAX_LATENCY (10*PA_USEC_PER_SEC)
53 #define DEFAULT_FIXED_LATENCY (250*PA_USEC_PER_MSEC)
55 static PA_DEFINE_CHECK_TYPE(pa_sink
, pa_msgobject
);
57 static void sink_free(pa_object
*s
);
59 pa_sink_new_data
* pa_sink_new_data_init(pa_sink_new_data
*data
) {
62 memset(data
, 0, sizeof(*data
));
63 data
->proplist
= pa_proplist_new();
68 void pa_sink_new_data_set_name(pa_sink_new_data
*data
, const char *name
) {
72 data
->name
= pa_xstrdup(name
);
75 void pa_sink_new_data_set_sample_spec(pa_sink_new_data
*data
, const pa_sample_spec
*spec
) {
78 if ((data
->sample_spec_is_set
= !!spec
))
79 data
->sample_spec
= *spec
;
82 void pa_sink_new_data_set_channel_map(pa_sink_new_data
*data
, const pa_channel_map
*map
) {
85 if ((data
->channel_map_is_set
= !!map
))
86 data
->channel_map
= *map
;
89 void pa_sink_new_data_set_volume(pa_sink_new_data
*data
, const pa_cvolume
*volume
) {
92 if ((data
->volume_is_set
= !!volume
))
93 data
->volume
= *volume
;
96 void pa_sink_new_data_set_muted(pa_sink_new_data
*data
, pa_bool_t mute
) {
99 data
->muted_is_set
= TRUE
;
100 data
->muted
= !!mute
;
103 void pa_sink_new_data_done(pa_sink_new_data
*data
) {
106 pa_xfree(data
->name
);
107 pa_proplist_free(data
->proplist
);
110 /* Called from main context */
111 static void reset_callbacks(pa_sink
*s
) {
115 s
->get_volume
= NULL
;
116 s
->set_volume
= NULL
;
119 s
->request_rewind
= NULL
;
120 s
->update_requested_latency
= NULL
;
123 /* Called from main context */
124 pa_sink
* pa_sink_new(
126 pa_sink_new_data
*data
,
127 pa_sink_flags_t flags
) {
131 char st
[PA_SAMPLE_SPEC_SNPRINT_MAX
], cm
[PA_CHANNEL_MAP_SNPRINT_MAX
];
132 pa_source_new_data source_data
;
138 pa_assert(data
->name
);
140 s
= pa_msgobject_new(pa_sink
);
142 if (!(name
= pa_namereg_register(core
, data
->name
, PA_NAMEREG_SINK
, s
, data
->namereg_fail
))) {
147 pa_sink_new_data_set_name(data
, name
);
149 if (pa_hook_fire(&core
->hooks
[PA_CORE_HOOK_SINK_NEW
], data
) < 0) {
151 pa_namereg_unregister(core
, name
);
155 pa_return_null_if_fail(!data
->driver
|| pa_utf8_valid(data
->driver
));
156 pa_return_null_if_fail(data
->name
&& pa_utf8_valid(data
->name
) && data
->name
[0]);
158 pa_return_null_if_fail(data
->sample_spec_is_set
&& pa_sample_spec_valid(&data
->sample_spec
));
160 if (!data
->channel_map_is_set
)
161 pa_return_null_if_fail(pa_channel_map_init_auto(&data
->channel_map
, data
->sample_spec
.channels
, PA_CHANNEL_MAP_DEFAULT
));
163 pa_return_null_if_fail(pa_channel_map_valid(&data
->channel_map
));
164 pa_return_null_if_fail(data
->channel_map
.channels
== data
->sample_spec
.channels
);
166 if (!data
->volume_is_set
)
167 pa_cvolume_reset(&data
->volume
, data
->sample_spec
.channels
);
169 pa_return_null_if_fail(pa_cvolume_valid(&data
->volume
));
170 pa_return_null_if_fail(data
->volume
.channels
== data
->sample_spec
.channels
);
172 if (!data
->muted_is_set
)
176 pa_proplist_update(data
->proplist
, PA_UPDATE_MERGE
, data
->card
->proplist
);
178 pa_device_init_description(data
->proplist
);
179 pa_device_init_icon(data
->proplist
, TRUE
);
181 if (pa_hook_fire(&core
->hooks
[PA_CORE_HOOK_SINK_FIXATE
], data
) < 0) {
183 pa_namereg_unregister(core
, name
);
187 s
->parent
.parent
.free
= sink_free
;
188 s
->parent
.process_msg
= pa_sink_process_msg
;
191 s
->state
= PA_SINK_INIT
;
193 s
->name
= pa_xstrdup(name
);
194 s
->proplist
= pa_proplist_copy(data
->proplist
);
195 s
->driver
= pa_xstrdup(pa_path_get_filename(data
->driver
));
196 s
->module
= data
->module
;
197 s
->card
= data
->card
;
199 s
->sample_spec
= data
->sample_spec
;
200 s
->channel_map
= data
->channel_map
;
202 s
->inputs
= pa_idxset_new(NULL
, NULL
);
205 s
->reference_volume
= s
->virtual_volume
= data
->volume
;
206 pa_cvolume_reset(&s
->soft_volume
, s
->sample_spec
.channels
);
207 s
->base_volume
= PA_VOLUME_NORM
;
208 s
->n_volume_steps
= PA_VOLUME_NORM
+1;
209 s
->muted
= data
->muted
;
210 s
->refresh_volume
= s
->refresh_muted
= FALSE
;
212 s
->fixed_latency
= flags
& PA_SINK_DYNAMIC_LATENCY
? 0 : DEFAULT_FIXED_LATENCY
;
220 pa_silence_memchunk_get(
221 &core
->silence_cache
,
227 s
->thread_info
.inputs
= pa_hashmap_new(pa_idxset_trivial_hash_func
, pa_idxset_trivial_compare_func
);
228 s
->thread_info
.soft_volume
= s
->soft_volume
;
229 s
->thread_info
.soft_muted
= s
->muted
;
230 s
->thread_info
.state
= s
->state
;
231 s
->thread_info
.rewind_nbytes
= 0;
232 s
->thread_info
.rewind_requested
= FALSE
;
233 s
->thread_info
.max_rewind
= 0;
234 s
->thread_info
.max_request
= 0;
235 s
->thread_info
.requested_latency_valid
= FALSE
;
236 s
->thread_info
.requested_latency
= 0;
237 s
->thread_info
.min_latency
= ABSOLUTE_MIN_LATENCY
;
238 s
->thread_info
.max_latency
= ABSOLUTE_MAX_LATENCY
;
240 pa_assert_se(pa_idxset_put(core
->sinks
, s
, &s
->index
) >= 0);
243 pa_assert_se(pa_idxset_put(s
->card
->sinks
, s
, NULL
) >= 0);
245 pt
= pa_proplist_to_string_sep(s
->proplist
, "\n ");
246 pa_log_info("Created sink %u \"%s\" with sample spec %s and channel map %s\n %s",
249 pa_sample_spec_snprint(st
, sizeof(st
), &s
->sample_spec
),
250 pa_channel_map_snprint(cm
, sizeof(cm
), &s
->channel_map
),
254 pa_source_new_data_init(&source_data
);
255 pa_source_new_data_set_sample_spec(&source_data
, &s
->sample_spec
);
256 pa_source_new_data_set_channel_map(&source_data
, &s
->channel_map
);
257 source_data
.name
= pa_sprintf_malloc("%s.monitor", name
);
258 source_data
.driver
= data
->driver
;
259 source_data
.module
= data
->module
;
260 source_data
.card
= data
->card
;
262 dn
= pa_proplist_gets(s
->proplist
, PA_PROP_DEVICE_DESCRIPTION
);
263 pa_proplist_setf(source_data
.proplist
, PA_PROP_DEVICE_DESCRIPTION
, "Monitor of %s", dn
? dn
: s
->name
);
264 pa_proplist_sets(source_data
.proplist
, PA_PROP_DEVICE_CLASS
, "monitor");
266 s
->monitor_source
= pa_source_new(core
, &source_data
,
267 ((flags
& PA_SINK_LATENCY
) ? PA_SOURCE_LATENCY
: 0) |
268 ((flags
& PA_SINK_DYNAMIC_LATENCY
) ? PA_SOURCE_DYNAMIC_LATENCY
: 0));
270 pa_source_new_data_done(&source_data
);
272 if (!s
->monitor_source
) {
278 s
->monitor_source
->monitor_of
= s
;
280 pa_source_set_latency_range(s
->monitor_source
, s
->thread_info
.min_latency
, s
->thread_info
.max_latency
);
281 pa_source_set_max_rewind(s
->monitor_source
, s
->thread_info
.max_rewind
);
286 /* Called from main context */
287 static int sink_set_state(pa_sink
*s
, pa_sink_state_t state
) {
289 pa_bool_t suspend_change
;
290 pa_sink_state_t original_state
;
294 if (s
->state
== state
)
297 original_state
= s
->state
;
300 (original_state
== PA_SINK_SUSPENDED
&& PA_SINK_IS_OPENED(state
)) ||
301 (PA_SINK_IS_OPENED(original_state
) && state
== PA_SINK_SUSPENDED
);
304 if ((ret
= s
->set_state(s
, state
)) < 0)
308 if ((ret
= pa_asyncmsgq_send(s
->asyncmsgq
, PA_MSGOBJECT(s
), PA_SINK_MESSAGE_SET_STATE
, PA_UINT_TO_PTR(state
), 0, NULL
)) < 0) {
311 s
->set_state(s
, original_state
);
318 if (state
!= PA_SINK_UNLINKED
) { /* if we enter UNLINKED state pa_sink_unlink() will fire the apropriate events */
319 pa_hook_fire(&s
->core
->hooks
[PA_CORE_HOOK_SINK_STATE_CHANGED
], s
);
320 pa_subscription_post(s
->core
, PA_SUBSCRIPTION_EVENT_SINK
| PA_SUBSCRIPTION_EVENT_CHANGE
, s
->index
);
323 if (suspend_change
) {
327 /* We're suspending or resuming, tell everyone about it */
329 for (i
= PA_SINK_INPUT(pa_idxset_first(s
->inputs
, &idx
)); i
; i
= PA_SINK_INPUT(pa_idxset_next(s
->inputs
, &idx
)))
330 if (s
->state
== PA_SINK_SUSPENDED
&&
331 (i
->flags
& PA_SINK_INPUT_FAIL_ON_SUSPEND
))
332 pa_sink_input_kill(i
);
334 i
->suspend(i
, state
== PA_SINK_SUSPENDED
);
336 if (s
->monitor_source
)
337 pa_source_sync_suspend(s
->monitor_source
);
343 /* Called from main context */
344 void pa_sink_put(pa_sink
* s
) {
345 pa_sink_assert_ref(s
);
347 pa_assert(s
->state
== PA_SINK_INIT
);
349 /* The following fields must be initialized properly when calling _put() */
350 pa_assert(s
->asyncmsgq
);
351 pa_assert(s
->rtpoll
);
352 pa_assert(s
->thread_info
.min_latency
<= s
->thread_info
.max_latency
);
354 /* Generally, flags should be initialized via pa_sink_new(). As a
355 * special exception we allow volume related flags to be set
356 * between _new() and _put(). */
358 if (!(s
->flags
& PA_SINK_HW_VOLUME_CTRL
))
359 s
->flags
|= PA_SINK_DECIBEL_VOLUME
;
361 if ((s
->flags
& PA_SINK_DECIBEL_VOLUME
) && s
->core
->flat_volumes
)
362 s
->flags
|= PA_SINK_FLAT_VOLUME
;
364 s
->thread_info
.soft_volume
= s
->soft_volume
;
365 s
->thread_info
.soft_muted
= s
->muted
;
367 pa_assert((s
->flags
& PA_SINK_HW_VOLUME_CTRL
) || (s
->base_volume
== PA_VOLUME_NORM
&& s
->flags
& PA_SINK_DECIBEL_VOLUME
));
368 pa_assert(!(s
->flags
& PA_SINK_DECIBEL_VOLUME
) || s
->n_volume_steps
== PA_VOLUME_NORM
+1);
369 pa_assert(!(s
->flags
& PA_SINK_DYNAMIC_LATENCY
) == (s
->fixed_latency
!= 0));
370 pa_assert(!(s
->flags
& PA_SINK_LATENCY
) == !(s
->monitor_source
->flags
& PA_SOURCE_LATENCY
));
371 pa_assert(!(s
->flags
& PA_SINK_DYNAMIC_LATENCY
) == !(s
->monitor_source
->flags
& PA_SOURCE_DYNAMIC_LATENCY
));
373 pa_assert(s
->monitor_source
->fixed_latency
== s
->fixed_latency
);
374 pa_assert(s
->monitor_source
->thread_info
.min_latency
== s
->thread_info
.min_latency
);
375 pa_assert(s
->monitor_source
->thread_info
.max_latency
== s
->thread_info
.max_latency
);
377 pa_assert_se(sink_set_state(s
, PA_SINK_IDLE
) == 0);
379 pa_source_put(s
->monitor_source
);
381 pa_subscription_post(s
->core
, PA_SUBSCRIPTION_EVENT_SINK
| PA_SUBSCRIPTION_EVENT_NEW
, s
->index
);
382 pa_hook_fire(&s
->core
->hooks
[PA_CORE_HOOK_SINK_PUT
], s
);
385 /* Called from main context */
386 void pa_sink_unlink(pa_sink
* s
) {
388 pa_sink_input
*i
, *j
= NULL
;
392 /* Please note that pa_sink_unlink() does more than simply
393 * reversing pa_sink_put(). It also undoes the registrations
394 * already done in pa_sink_new()! */
396 /* All operations here shall be idempotent, i.e. pa_sink_unlink()
397 * may be called multiple times on the same sink without bad
400 linked
= PA_SINK_IS_LINKED(s
->state
);
403 pa_hook_fire(&s
->core
->hooks
[PA_CORE_HOOK_SINK_UNLINK
], s
);
405 if (s
->state
!= PA_SINK_UNLINKED
)
406 pa_namereg_unregister(s
->core
, s
->name
);
407 pa_idxset_remove_by_data(s
->core
->sinks
, s
, NULL
);
410 pa_idxset_remove_by_data(s
->card
->sinks
, s
, NULL
);
412 while ((i
= pa_idxset_first(s
->inputs
, NULL
))) {
414 pa_sink_input_kill(i
);
419 sink_set_state(s
, PA_SINK_UNLINKED
);
421 s
->state
= PA_SINK_UNLINKED
;
425 if (s
->monitor_source
)
426 pa_source_unlink(s
->monitor_source
);
429 pa_subscription_post(s
->core
, PA_SUBSCRIPTION_EVENT_SINK
| PA_SUBSCRIPTION_EVENT_REMOVE
, s
->index
);
430 pa_hook_fire(&s
->core
->hooks
[PA_CORE_HOOK_SINK_UNLINK_POST
], s
);
434 /* Called from main context */
435 static void sink_free(pa_object
*o
) {
436 pa_sink
*s
= PA_SINK(o
);
440 pa_assert(pa_sink_refcnt(s
) == 0);
442 if (PA_SINK_IS_LINKED(s
->state
))
445 pa_log_info("Freeing sink %u \"%s\"", s
->index
, s
->name
);
447 if (s
->monitor_source
) {
448 pa_source_unref(s
->monitor_source
);
449 s
->monitor_source
= NULL
;
452 pa_idxset_free(s
->inputs
, NULL
, NULL
);
454 while ((i
= pa_hashmap_steal_first(s
->thread_info
.inputs
)))
455 pa_sink_input_unref(i
);
457 pa_hashmap_free(s
->thread_info
.inputs
, NULL
, NULL
);
459 if (s
->silence
.memblock
)
460 pa_memblock_unref(s
->silence
.memblock
);
466 pa_proplist_free(s
->proplist
);
471 /* Called from main context */
472 void pa_sink_set_asyncmsgq(pa_sink
*s
, pa_asyncmsgq
*q
) {
473 pa_sink_assert_ref(s
);
477 if (s
->monitor_source
)
478 pa_source_set_asyncmsgq(s
->monitor_source
, q
);
481 /* Called from main context */
482 void pa_sink_set_rtpoll(pa_sink
*s
, pa_rtpoll
*p
) {
483 pa_sink_assert_ref(s
);
486 if (s
->monitor_source
)
487 pa_source_set_rtpoll(s
->monitor_source
, p
);
490 /* Called from main context */
491 int pa_sink_update_status(pa_sink
*s
) {
492 pa_sink_assert_ref(s
);
493 pa_assert(PA_SINK_IS_LINKED(s
->state
));
495 if (s
->state
== PA_SINK_SUSPENDED
)
498 return sink_set_state(s
, pa_sink_used_by(s
) ? PA_SINK_RUNNING
: PA_SINK_IDLE
);
501 /* Called from main context */
502 int pa_sink_suspend(pa_sink
*s
, pa_bool_t suspend
) {
503 pa_sink_assert_ref(s
);
504 pa_assert(PA_SINK_IS_LINKED(s
->state
));
507 return sink_set_state(s
, PA_SINK_SUSPENDED
);
509 return sink_set_state(s
, pa_sink_used_by(s
) ? PA_SINK_RUNNING
: PA_SINK_IDLE
);
512 /* Called from main context */
513 pa_queue
*pa_sink_move_all_start(pa_sink
*s
) {
515 pa_sink_input
*i
, *n
;
518 pa_sink_assert_ref(s
);
519 pa_assert(PA_SINK_IS_LINKED(s
->state
));
523 for (i
= PA_SINK_INPUT(pa_idxset_first(s
->inputs
, &idx
)); i
; i
= n
) {
524 n
= PA_SINK_INPUT(pa_idxset_next(s
->inputs
, &idx
));
526 pa_sink_input_ref(i
);
528 if (pa_sink_input_start_move(i
) >= 0)
531 pa_sink_input_unref(i
);
537 /* Called from main context */
538 void pa_sink_move_all_finish(pa_sink
*s
, pa_queue
*q
, pa_bool_t save
) {
541 pa_sink_assert_ref(s
);
542 pa_assert(PA_SINK_IS_LINKED(s
->state
));
545 while ((i
= PA_SINK_INPUT(pa_queue_pop(q
)))) {
546 if (pa_sink_input_finish_move(i
, s
, save
) < 0)
547 pa_sink_input_kill(i
);
549 pa_sink_input_unref(i
);
552 pa_queue_free(q
, NULL
, NULL
);
555 /* Called from main context */
556 void pa_sink_move_all_fail(pa_queue
*q
) {
560 while ((i
= PA_SINK_INPUT(pa_queue_pop(q
)))) {
561 if (pa_hook_fire(&i
->core
->hooks
[PA_CORE_HOOK_SINK_INPUT_MOVE_FAIL
], i
) == PA_HOOK_OK
) {
562 pa_sink_input_kill(i
);
563 pa_sink_input_unref(i
);
567 pa_queue_free(q
, NULL
, NULL
);
570 /* Called from IO thread context */
571 void pa_sink_process_rewind(pa_sink
*s
, size_t nbytes
) {
574 pa_sink_assert_ref(s
);
575 pa_assert(PA_SINK_IS_LINKED(s
->thread_info
.state
));
577 /* If nobody requested this and this is actually no real rewind
578 * then we can short cut this */
579 if (!s
->thread_info
.rewind_requested
&& nbytes
<= 0)
582 s
->thread_info
.rewind_nbytes
= 0;
583 s
->thread_info
.rewind_requested
= FALSE
;
585 if (s
->thread_info
.state
== PA_SINK_SUSPENDED
)
589 pa_log_debug("Processing rewind...");
591 while ((i
= pa_hashmap_iterate(s
->thread_info
.inputs
, &state
, NULL
))) {
592 pa_sink_input_assert_ref(i
);
593 pa_sink_input_process_rewind(i
, nbytes
);
597 if (s
->monitor_source
&& PA_SOURCE_IS_LINKED(s
->monitor_source
->thread_info
.state
))
598 pa_source_process_rewind(s
->monitor_source
, nbytes
);
601 /* Called from IO thread context */
602 static unsigned fill_mix_info(pa_sink
*s
, size_t *length
, pa_mix_info
*info
, unsigned maxinfo
) {
606 size_t mixlength
= *length
;
608 pa_sink_assert_ref(s
);
611 while ((i
= pa_hashmap_iterate(s
->thread_info
.inputs
, &state
, NULL
)) && maxinfo
> 0) {
612 pa_sink_input_assert_ref(i
);
614 pa_sink_input_peek(i
, *length
, &info
->chunk
, &info
->volume
);
616 if (mixlength
== 0 || info
->chunk
.length
< mixlength
)
617 mixlength
= info
->chunk
.length
;
619 if (pa_memblock_is_silence(info
->chunk
.memblock
)) {
620 pa_memblock_unref(info
->chunk
.memblock
);
624 info
->userdata
= pa_sink_input_ref(i
);
626 pa_assert(info
->chunk
.memblock
);
627 pa_assert(info
->chunk
.length
> 0);
640 /* Called from IO thread context */
641 static void inputs_drop(pa_sink
*s
, pa_mix_info
*info
, unsigned n
, pa_memchunk
*result
) {
645 unsigned n_unreffed
= 0;
647 pa_sink_assert_ref(s
);
649 pa_assert(result
->memblock
);
650 pa_assert(result
->length
> 0);
652 /* We optimize for the case where the order of the inputs has not changed */
654 while ((i
= pa_hashmap_iterate(s
->thread_info
.inputs
, &state
, NULL
))) {
656 pa_mix_info
* m
= NULL
;
658 pa_sink_input_assert_ref(i
);
660 /* Let's try to find the matching entry info the pa_mix_info array */
661 for (j
= 0; j
< n
; j
++) {
663 if (info
[p
].userdata
== i
) {
674 pa_sink_input_drop(i
, result
->length
);
676 if (s
->monitor_source
&& PA_SOURCE_IS_LINKED(s
->monitor_source
->thread_info
.state
)) {
678 if (pa_hashmap_size(i
->thread_info
.direct_outputs
) > 0) {
683 if (m
&& m
->chunk
.memblock
) {
685 pa_memblock_ref(c
.memblock
);
686 pa_assert(result
->length
<= c
.length
);
687 c
.length
= result
->length
;
689 pa_memchunk_make_writable(&c
, 0);
690 pa_volume_memchunk(&c
, &s
->sample_spec
, &m
->volume
);
693 pa_memblock_ref(c
.memblock
);
694 pa_assert(result
->length
<= c
.length
);
695 c
.length
= result
->length
;
698 while ((o
= pa_hashmap_iterate(i
->thread_info
.direct_outputs
, &ostate
, NULL
))) {
699 pa_source_output_assert_ref(o
);
700 pa_assert(o
->direct_on_input
== i
);
701 pa_source_post_direct(s
->monitor_source
, o
, &c
);
704 pa_memblock_unref(c
.memblock
);
709 if (m
->chunk
.memblock
)
710 pa_memblock_unref(m
->chunk
.memblock
);
711 pa_memchunk_reset(&m
->chunk
);
713 pa_sink_input_unref(m
->userdata
);
720 /* Now drop references to entries that are included in the
721 * pa_mix_info array but don't exist anymore */
723 if (n_unreffed
< n
) {
724 for (; n
> 0; info
++, n
--) {
726 pa_sink_input_unref(info
->userdata
);
727 if (info
->chunk
.memblock
)
728 pa_memblock_unref(info
->chunk
.memblock
);
732 if (s
->monitor_source
&& PA_SOURCE_IS_LINKED(s
->monitor_source
->thread_info
.state
))
733 pa_source_post(s
->monitor_source
, result
);
736 /* Called from IO thread context */
737 void pa_sink_render(pa_sink
*s
, size_t length
, pa_memchunk
*result
) {
738 pa_mix_info info
[MAX_MIX_CHANNELS
];
740 size_t block_size_max
;
742 pa_sink_assert_ref(s
);
743 pa_assert(PA_SINK_IS_LINKED(s
->thread_info
.state
));
744 pa_assert(pa_frame_aligned(length
, &s
->sample_spec
));
749 pa_assert(!s
->thread_info
.rewind_requested
);
750 pa_assert(s
->thread_info
.rewind_nbytes
== 0);
752 if (s
->thread_info
.state
== PA_SINK_SUSPENDED
) {
753 result
->memblock
= pa_memblock_ref(s
->silence
.memblock
);
754 result
->index
= s
->silence
.index
;
755 result
->length
= PA_MIN(s
->silence
.length
, length
);
760 length
= pa_frame_align(MIX_BUFFER_LENGTH
, &s
->sample_spec
);
762 block_size_max
= pa_mempool_block_size_max(s
->core
->mempool
);
763 if (length
> block_size_max
)
764 length
= pa_frame_align(block_size_max
, &s
->sample_spec
);
766 pa_assert(length
> 0);
768 n
= fill_mix_info(s
, &length
, info
, MAX_MIX_CHANNELS
);
772 *result
= s
->silence
;
773 pa_memblock_ref(result
->memblock
);
775 if (result
->length
> length
)
776 result
->length
= length
;
781 *result
= info
[0].chunk
;
782 pa_memblock_ref(result
->memblock
);
784 if (result
->length
> length
)
785 result
->length
= length
;
787 pa_sw_cvolume_multiply(&volume
, &s
->thread_info
.soft_volume
, &info
[0].volume
);
789 if (s
->thread_info
.soft_muted
|| !pa_cvolume_is_norm(&volume
)) {
790 pa_memchunk_make_writable(result
, 0);
791 if (s
->thread_info
.soft_muted
|| pa_cvolume_is_muted(&volume
))
792 pa_silence_memchunk(result
, &s
->sample_spec
);
794 pa_volume_memchunk(result
, &s
->sample_spec
, &volume
);
798 result
->memblock
= pa_memblock_new(s
->core
->mempool
, length
);
800 ptr
= pa_memblock_acquire(result
->memblock
);
801 result
->length
= pa_mix(info
, n
,
804 &s
->thread_info
.soft_volume
,
805 s
->thread_info
.soft_muted
);
806 pa_memblock_release(result
->memblock
);
811 inputs_drop(s
, info
, n
, result
);
816 /* Called from IO thread context */
817 void pa_sink_render_into(pa_sink
*s
, pa_memchunk
*target
) {
818 pa_mix_info info
[MAX_MIX_CHANNELS
];
820 size_t length
, block_size_max
;
822 pa_sink_assert_ref(s
);
823 pa_assert(PA_SINK_IS_LINKED(s
->thread_info
.state
));
825 pa_assert(target
->memblock
);
826 pa_assert(target
->length
> 0);
827 pa_assert(pa_frame_aligned(target
->length
, &s
->sample_spec
));
831 pa_assert(!s
->thread_info
.rewind_requested
);
832 pa_assert(s
->thread_info
.rewind_nbytes
== 0);
834 if (s
->thread_info
.state
== PA_SINK_SUSPENDED
) {
835 pa_silence_memchunk(target
, &s
->sample_spec
);
839 length
= target
->length
;
840 block_size_max
= pa_mempool_block_size_max(s
->core
->mempool
);
841 if (length
> block_size_max
)
842 length
= pa_frame_align(block_size_max
, &s
->sample_spec
);
844 pa_assert(length
> 0);
846 n
= fill_mix_info(s
, &length
, info
, MAX_MIX_CHANNELS
);
849 if (target
->length
> length
)
850 target
->length
= length
;
852 pa_silence_memchunk(target
, &s
->sample_spec
);
856 if (target
->length
> length
)
857 target
->length
= length
;
859 pa_sw_cvolume_multiply(&volume
, &s
->thread_info
.soft_volume
, &info
[0].volume
);
861 if (s
->thread_info
.soft_muted
|| pa_cvolume_is_muted(&volume
))
862 pa_silence_memchunk(target
, &s
->sample_spec
);
866 vchunk
= info
[0].chunk
;
867 pa_memblock_ref(vchunk
.memblock
);
869 if (vchunk
.length
> length
)
870 vchunk
.length
= length
;
872 if (!pa_cvolume_is_norm(&volume
)) {
873 pa_memchunk_make_writable(&vchunk
, 0);
874 pa_volume_memchunk(&vchunk
, &s
->sample_spec
, &volume
);
877 pa_memchunk_memcpy(target
, &vchunk
);
878 pa_memblock_unref(vchunk
.memblock
);
884 ptr
= pa_memblock_acquire(target
->memblock
);
886 target
->length
= pa_mix(info
, n
,
887 (uint8_t*) ptr
+ target
->index
, length
,
889 &s
->thread_info
.soft_volume
,
890 s
->thread_info
.soft_muted
);
892 pa_memblock_release(target
->memblock
);
895 inputs_drop(s
, info
, n
, target
);
900 /* Called from IO thread context */
901 void pa_sink_render_into_full(pa_sink
*s
, pa_memchunk
*target
) {
905 pa_sink_assert_ref(s
);
906 pa_assert(PA_SINK_IS_LINKED(s
->thread_info
.state
));
908 pa_assert(target
->memblock
);
909 pa_assert(target
->length
> 0);
910 pa_assert(pa_frame_aligned(target
->length
, &s
->sample_spec
));
914 pa_assert(!s
->thread_info
.rewind_requested
);
915 pa_assert(s
->thread_info
.rewind_nbytes
== 0);
924 pa_sink_render_into(s
, &chunk
);
933 /* Called from IO thread context */
934 void pa_sink_render_full(pa_sink
*s
, size_t length
, pa_memchunk
*result
) {
935 pa_mix_info info
[MAX_MIX_CHANNELS
];
936 size_t length1st
= length
;
939 pa_sink_assert_ref(s
);
940 pa_assert(PA_SINK_IS_LINKED(s
->thread_info
.state
));
941 pa_assert(length
> 0);
942 pa_assert(pa_frame_aligned(length
, &s
->sample_spec
));
947 pa_assert(!s
->thread_info
.rewind_requested
);
948 pa_assert(s
->thread_info
.rewind_nbytes
== 0);
950 pa_assert(length
> 0);
952 n
= fill_mix_info(s
, &length1st
, info
, MAX_MIX_CHANNELS
);
955 pa_silence_memchunk_get(&s
->core
->silence_cache
,
963 *result
= info
[0].chunk
;
964 pa_memblock_ref(result
->memblock
);
966 if (result
->length
> length
)
967 result
->length
= length
;
969 pa_sw_cvolume_multiply(&volume
, &s
->thread_info
.soft_volume
, &info
[0].volume
);
971 if (s
->thread_info
.soft_muted
|| !pa_cvolume_is_norm(&volume
)) {
972 pa_memchunk_make_writable(result
, length
);
973 if (s
->thread_info
.soft_muted
|| pa_cvolume_is_muted(&volume
))
974 pa_silence_memchunk(result
, &s
->sample_spec
);
976 pa_volume_memchunk(result
, &s
->sample_spec
, &volume
);
982 result
->memblock
= pa_memblock_new(s
->core
->mempool
, length
);
984 ptr
= pa_memblock_acquire(result
->memblock
);
986 result
->length
= pa_mix(info
, n
,
987 (uint8_t*) ptr
+ result
->index
, length1st
,
989 &s
->thread_info
.soft_volume
,
990 s
->thread_info
.soft_muted
);
992 pa_memblock_release(result
->memblock
);
995 inputs_drop(s
, info
, n
, result
);
997 if (result
->length
< length
) {
1000 pa_memchunk_make_writable(result
, length
);
1001 result
->length
= length
;
1003 l
= length
- result
->length
;
1004 d
= result
->index
+ result
->length
;
1008 chunk
.length
-= d
- result
->index
;
1010 pa_sink_render_into(s
, &chunk
);
1015 result
->length
= length
;
1021 /* Called from main thread */
1022 pa_usec_t
pa_sink_get_latency(pa_sink
*s
) {
1025 pa_sink_assert_ref(s
);
1026 pa_assert(PA_SINK_IS_LINKED(s
->state
));
1028 /* The returned value is supposed to be in the time domain of the sound card! */
1030 if (s
->state
== PA_SINK_SUSPENDED
)
1033 if (!(s
->flags
& PA_SINK_LATENCY
))
1036 pa_assert_se(pa_asyncmsgq_send(s
->asyncmsgq
, PA_MSGOBJECT(s
), PA_SINK_MESSAGE_GET_LATENCY
, &usec
, 0, NULL
) == 0);
1041 /* Called from IO thread */
1042 pa_usec_t
pa_sink_get_latency_within_thread(pa_sink
*s
) {
1046 pa_sink_assert_ref(s
);
1047 pa_assert(PA_SINK_IS_LINKED(s
->thread_info
.state
));
1049 /* The returned value is supposed to be in the time domain of the sound card! */
1051 if (s
->thread_info
.state
== PA_SINK_SUSPENDED
)
1054 if (!(s
->flags
& PA_SINK_LATENCY
))
1057 o
= PA_MSGOBJECT(s
);
1059 /* We probably should make this a proper vtable callback instead of going through process_msg() */
1061 if (o
->process_msg(o
, PA_SINK_MESSAGE_GET_LATENCY
, &usec
, 0, NULL
) < 0)
1067 static void compute_new_soft_volume(pa_sink_input
*i
, const pa_cvolume
*new_volume
) {
1070 pa_sink_input_assert_ref(i
);
1071 pa_assert(new_volume
->channels
== i
->sample_spec
.channels
);
1074 * This basically calculates:
1076 * i->relative_volume := i->virtual_volume / new_volume
1077 * i->soft_volume := i->relative_volume * i->volume_factor
1080 /* The new sink volume passed in here must already be remapped to
1081 * the sink input's channel map! */
1083 i
->soft_volume
.channels
= i
->sample_spec
.channels
;
1085 for (c
= 0; c
< i
->sample_spec
.channels
; c
++)
1087 if (new_volume
->values
[c
] <= PA_VOLUME_MUTED
)
1088 /* We leave i->relative_volume untouched */
1089 i
->soft_volume
.values
[c
] = PA_VOLUME_MUTED
;
1091 i
->relative_volume
[c
] =
1092 pa_sw_volume_to_linear(i
->virtual_volume
.values
[c
]) /
1093 pa_sw_volume_to_linear(new_volume
->values
[c
]);
1095 i
->soft_volume
.values
[c
] = pa_sw_volume_from_linear(
1096 i
->relative_volume
[c
] *
1097 pa_sw_volume_to_linear(i
->volume_factor
.values
[c
]));
1100 /* Hooks have the ability to play games with i->soft_volume */
1101 pa_hook_fire(&i
->core
->hooks
[PA_CORE_HOOK_SINK_INPUT_SET_VOLUME
], i
);
1103 /* We don't copy the soft_volume to the thread_info data
1104 * here. That must be done by the caller */
1107 /* Called from main thread */
1108 void pa_sink_update_flat_volume(pa_sink
*s
, pa_cvolume
*new_volume
) {
1112 pa_sink_assert_ref(s
);
1113 pa_assert(new_volume
);
1114 pa_assert(PA_SINK_IS_LINKED(s
->state
));
1115 pa_assert(s
->flags
& PA_SINK_FLAT_VOLUME
);
1117 /* This is called whenever a sink input volume changes or a sink
1118 * input is added/removed and we might need to fix up the sink
1119 * volume accordingly. Please note that we don't actually update
1120 * the sinks volume here, we only return how it needs to be
1121 * updated. The caller should then call pa_sink_set_volume().*/
1123 if (pa_idxset_isempty(s
->inputs
)) {
1124 /* In the special case that we have no sink input we leave the
1125 * volume unmodified. */
1126 *new_volume
= s
->reference_volume
;
1130 pa_cvolume_mute(new_volume
, s
->channel_map
.channels
);
1132 /* First let's determine the new maximum volume of all inputs
1133 * connected to this sink */
1134 for (i
= PA_SINK_INPUT(pa_idxset_first(s
->inputs
, &idx
)); i
; i
= PA_SINK_INPUT(pa_idxset_next(s
->inputs
, &idx
))) {
1136 pa_cvolume remapped_volume
;
1138 remapped_volume
= i
->virtual_volume
;
1139 pa_cvolume_remap(&remapped_volume
, &i
->channel_map
, &s
->channel_map
);
1141 for (c
= 0; c
< new_volume
->channels
; c
++)
1142 if (remapped_volume
.values
[c
] > new_volume
->values
[c
])
1143 new_volume
->values
[c
] = remapped_volume
.values
[c
];
1146 /* Then, let's update the soft volumes of all inputs connected
1148 for (i
= PA_SINK_INPUT(pa_idxset_first(s
->inputs
, &idx
)); i
; i
= PA_SINK_INPUT(pa_idxset_next(s
->inputs
, &idx
))) {
1149 pa_cvolume remapped_new_volume
;
1151 remapped_new_volume
= *new_volume
;
1152 pa_cvolume_remap(&remapped_new_volume
, &s
->channel_map
, &i
->channel_map
);
1153 compute_new_soft_volume(i
, &remapped_new_volume
);
1155 /* We don't copy soft_volume to the thread_info data here
1156 * (i.e. issue PA_SINK_INPUT_MESSAGE_SET_VOLUME) because we
1157 * want the update to be atomically with the sink volume
1158 * update, hence we do it within the pa_sink_set_volume() call
1163 /* Called from main thread */
1164 void pa_sink_propagate_flat_volume(pa_sink
*s
) {
1168 pa_sink_assert_ref(s
);
1169 pa_assert(PA_SINK_IS_LINKED(s
->state
));
1170 pa_assert(s
->flags
& PA_SINK_FLAT_VOLUME
);
1172 /* This is called whenever the sink volume changes that is not
1173 * caused by a sink input volume change. We need to fix up the
1174 * sink input volumes accordingly */
1176 for (i
= PA_SINK_INPUT(pa_idxset_first(s
->inputs
, &idx
)); i
; i
= PA_SINK_INPUT(pa_idxset_next(s
->inputs
, &idx
))) {
1177 pa_cvolume sink_volume
, new_virtual_volume
;
1180 /* This basically calculates i->virtual_volume := i->relative_volume * s->virtual_volume */
1182 sink_volume
= s
->virtual_volume
;
1183 pa_cvolume_remap(&sink_volume
, &s
->channel_map
, &i
->channel_map
);
1185 for (c
= 0; c
< i
->sample_spec
.channels
; c
++)
1186 new_virtual_volume
.values
[c
] = pa_sw_volume_from_linear(
1187 i
->relative_volume
[c
] *
1188 pa_sw_volume_to_linear(sink_volume
.values
[c
]));
1190 new_virtual_volume
.channels
= i
->sample_spec
.channels
;
1192 if (!pa_cvolume_equal(&new_virtual_volume
, &i
->virtual_volume
)) {
1193 i
->virtual_volume
= new_virtual_volume
;
1195 /* Hmm, the soft volume might no longer actually match
1196 * what has been chosen as new virtual volume here,
1197 * especially when the old volume was
1198 * PA_VOLUME_MUTED. Hence let's recalculate the soft
1200 compute_new_soft_volume(i
, &sink_volume
);
1202 /* The virtual volume changed, let's tell people so */
1203 pa_subscription_post(i
->core
, PA_SUBSCRIPTION_EVENT_SINK_INPUT
|PA_SUBSCRIPTION_EVENT_CHANGE
, i
->index
);
1207 /* If the soft_volume of any of the sink inputs got changed, let's
1208 * make sure the thread copies are synced up. */
1209 pa_assert_se(pa_asyncmsgq_send(s
->asyncmsgq
, PA_MSGOBJECT(s
), PA_SINK_MESSAGE_SYNC_VOLUMES
, NULL
, 0, NULL
) == 0);
1212 /* Called from main thread */
1213 void pa_sink_set_volume(pa_sink
*s
, const pa_cvolume
*volume
, pa_bool_t propagate
, pa_bool_t sendmsg
, pa_bool_t become_reference
) {
1214 pa_bool_t virtual_volume_changed
;
1216 pa_sink_assert_ref(s
);
1217 pa_assert(PA_SINK_IS_LINKED(s
->state
));
1219 pa_assert(pa_cvolume_valid(volume
));
1220 pa_assert(pa_cvolume_compatible(volume
, &s
->sample_spec
));
1222 virtual_volume_changed
= !pa_cvolume_equal(volume
, &s
->virtual_volume
);
1223 s
->virtual_volume
= *volume
;
1225 if (become_reference
)
1226 s
->reference_volume
= s
->virtual_volume
;
1228 /* Propagate this volume change back to the inputs */
1229 if (virtual_volume_changed
)
1230 if (propagate
&& (s
->flags
& PA_SINK_FLAT_VOLUME
))
1231 pa_sink_propagate_flat_volume(s
);
1233 if (s
->set_volume
) {
1234 /* If we have a function set_volume(), then we do not apply a
1235 * soft volume by default. However, set_volume() is free to
1236 * apply one to s->soft_volume */
1238 pa_cvolume_reset(&s
->soft_volume
, s
->sample_spec
.channels
);
1242 /* If we have no function set_volume(), then the soft volume
1243 * becomes the virtual volume */
1244 s
->soft_volume
= s
->virtual_volume
;
1246 /* This tells the sink that soft and/or virtual volume changed */
1248 pa_assert_se(pa_asyncmsgq_send(s
->asyncmsgq
, PA_MSGOBJECT(s
), PA_SINK_MESSAGE_SET_VOLUME
, NULL
, 0, NULL
) == 0);
1250 if (virtual_volume_changed
)
1251 pa_subscription_post(s
->core
, PA_SUBSCRIPTION_EVENT_SINK
|PA_SUBSCRIPTION_EVENT_CHANGE
, s
->index
);
1254 /* Called from main thread. Only to be called by sink implementor */
1255 void pa_sink_set_soft_volume(pa_sink
*s
, const pa_cvolume
*volume
) {
1256 pa_sink_assert_ref(s
);
1259 s
->soft_volume
= *volume
;
1261 if (PA_SINK_IS_LINKED(s
->state
))
1262 pa_assert_se(pa_asyncmsgq_send(s
->asyncmsgq
, PA_MSGOBJECT(s
), PA_SINK_MESSAGE_SET_VOLUME
, NULL
, 0, NULL
) == 0);
1264 s
->thread_info
.soft_volume
= *volume
;
1267 /* Called from main thread */
1268 const pa_cvolume
*pa_sink_get_volume(pa_sink
*s
, pa_bool_t force_refresh
, pa_bool_t reference
) {
1269 pa_sink_assert_ref(s
);
1271 if (s
->refresh_volume
|| force_refresh
) {
1272 struct pa_cvolume old_virtual_volume
= s
->virtual_volume
;
1277 pa_assert_se(pa_asyncmsgq_send(s
->asyncmsgq
, PA_MSGOBJECT(s
), PA_SINK_MESSAGE_GET_VOLUME
, NULL
, 0, NULL
) == 0);
1279 if (!pa_cvolume_equal(&old_virtual_volume
, &s
->virtual_volume
)) {
1281 s
->reference_volume
= s
->virtual_volume
;
1283 if (s
->flags
& PA_SINK_FLAT_VOLUME
)
1284 pa_sink_propagate_flat_volume(s
);
1286 pa_subscription_post(s
->core
, PA_SUBSCRIPTION_EVENT_SINK
|PA_SUBSCRIPTION_EVENT_CHANGE
, s
->index
);
1290 return reference
? &s
->reference_volume
: &s
->virtual_volume
;
1293 /* Called from main thread */
1294 void pa_sink_volume_changed(pa_sink
*s
, const pa_cvolume
*new_volume
) {
1295 pa_sink_assert_ref(s
);
1297 /* The sink implementor may call this if the volume changed to make sure everyone is notified */
1299 if (pa_cvolume_equal(&s
->virtual_volume
, new_volume
))
1302 s
->reference_volume
= s
->virtual_volume
= *new_volume
;
1304 if (s
->flags
& PA_SINK_FLAT_VOLUME
)
1305 pa_sink_propagate_flat_volume(s
);
1307 pa_subscription_post(s
->core
, PA_SUBSCRIPTION_EVENT_SINK
|PA_SUBSCRIPTION_EVENT_CHANGE
, s
->index
);
1310 /* Called from main thread */
1311 void pa_sink_set_mute(pa_sink
*s
, pa_bool_t mute
) {
1312 pa_bool_t old_muted
;
1314 pa_sink_assert_ref(s
);
1315 pa_assert(PA_SINK_IS_LINKED(s
->state
));
1317 old_muted
= s
->muted
;
1323 pa_assert_se(pa_asyncmsgq_send(s
->asyncmsgq
, PA_MSGOBJECT(s
), PA_SINK_MESSAGE_SET_MUTE
, NULL
, 0, NULL
) == 0);
1325 if (old_muted
!= s
->muted
)
1326 pa_subscription_post(s
->core
, PA_SUBSCRIPTION_EVENT_SINK
|PA_SUBSCRIPTION_EVENT_CHANGE
, s
->index
);
1329 /* Called from main thread */
1330 pa_bool_t
pa_sink_get_mute(pa_sink
*s
, pa_bool_t force_refresh
) {
1332 pa_sink_assert_ref(s
);
1334 if (s
->refresh_muted
|| force_refresh
) {
1335 pa_bool_t old_muted
= s
->muted
;
1340 pa_assert_se(pa_asyncmsgq_send(s
->asyncmsgq
, PA_MSGOBJECT(s
), PA_SINK_MESSAGE_GET_MUTE
, NULL
, 0, NULL
) == 0);
1342 if (old_muted
!= s
->muted
)
1343 pa_subscription_post(s
->core
, PA_SUBSCRIPTION_EVENT_SINK
|PA_SUBSCRIPTION_EVENT_CHANGE
, s
->index
);
1349 /* Called from main thread */
1350 void pa_sink_mute_changed(pa_sink
*s
, pa_bool_t new_muted
) {
1351 pa_sink_assert_ref(s
);
1353 /* The sink implementor may call this if the volume changed to make sure everyone is notified */
1355 if (s
->muted
== new_muted
)
1358 s
->muted
= new_muted
;
1359 pa_subscription_post(s
->core
, PA_SUBSCRIPTION_EVENT_SINK
|PA_SUBSCRIPTION_EVENT_CHANGE
, s
->index
);
1362 /* Called from main thread */
1363 pa_bool_t
pa_sink_update_proplist(pa_sink
*s
, pa_update_mode_t mode
, pa_proplist
*p
) {
1364 pa_sink_assert_ref(s
);
1367 pa_proplist_update(s
->proplist
, mode
, p
);
1369 if (PA_SINK_IS_LINKED(s
->state
)) {
1370 pa_hook_fire(&s
->core
->hooks
[PA_CORE_HOOK_SINK_PROPLIST_CHANGED
], s
);
1371 pa_subscription_post(s
->core
, PA_SUBSCRIPTION_EVENT_SINK
|PA_SUBSCRIPTION_EVENT_CHANGE
, s
->index
);
1377 /* Called from main thread */
1378 void pa_sink_set_description(pa_sink
*s
, const char *description
) {
1380 pa_sink_assert_ref(s
);
1382 if (!description
&& !pa_proplist_contains(s
->proplist
, PA_PROP_DEVICE_DESCRIPTION
))
1385 old
= pa_proplist_gets(s
->proplist
, PA_PROP_DEVICE_DESCRIPTION
);
1387 if (old
&& description
&& !strcmp(old
, description
))
1391 pa_proplist_sets(s
->proplist
, PA_PROP_DEVICE_DESCRIPTION
, description
);
1393 pa_proplist_unset(s
->proplist
, PA_PROP_DEVICE_DESCRIPTION
);
1395 if (s
->monitor_source
) {
1398 n
= pa_sprintf_malloc("Monitor Source of %s", description
? description
: s
->name
);
1399 pa_source_set_description(s
->monitor_source
, n
);
1403 if (PA_SINK_IS_LINKED(s
->state
)) {
1404 pa_subscription_post(s
->core
, PA_SUBSCRIPTION_EVENT_SINK
|PA_SUBSCRIPTION_EVENT_CHANGE
, s
->index
);
1405 pa_hook_fire(&s
->core
->hooks
[PA_CORE_HOOK_SINK_PROPLIST_CHANGED
], s
);
1409 /* Called from main thread */
1410 unsigned pa_sink_linked_by(pa_sink
*s
) {
1413 pa_sink_assert_ref(s
);
1414 pa_assert(PA_SINK_IS_LINKED(s
->state
));
1416 ret
= pa_idxset_size(s
->inputs
);
1418 /* We add in the number of streams connected to us here. Please
1419 * note the asymmmetry to pa_sink_used_by()! */
1421 if (s
->monitor_source
)
1422 ret
+= pa_source_linked_by(s
->monitor_source
);
1427 /* Called from main thread */
1428 unsigned pa_sink_used_by(pa_sink
*s
) {
1431 pa_sink_assert_ref(s
);
1432 pa_assert(PA_SINK_IS_LINKED(s
->state
));
1434 ret
= pa_idxset_size(s
->inputs
);
1435 pa_assert(ret
>= s
->n_corked
);
1437 /* Streams connected to our monitor source do not matter for
1438 * pa_sink_used_by()!.*/
1440 return ret
- s
->n_corked
;
1443 /* Called from main thread */
1444 unsigned pa_sink_check_suspend(pa_sink
*s
) {
1449 pa_sink_assert_ref(s
);
1451 if (!PA_SINK_IS_LINKED(s
->state
))
1456 for (i
= PA_SINK_INPUT(pa_idxset_first(s
->inputs
, &idx
)); i
; i
= PA_SINK_INPUT(pa_idxset_next(s
->inputs
, &idx
))) {
1457 pa_sink_input_state_t st
;
1459 st
= pa_sink_input_get_state(i
);
1460 pa_assert(PA_SINK_INPUT_IS_LINKED(st
));
1462 if (st
== PA_SINK_INPUT_CORKED
)
1465 if (i
->flags
& PA_SINK_INPUT_DONT_INHIBIT_AUTO_SUSPEND
)
1471 if (s
->monitor_source
)
1472 ret
+= pa_source_check_suspend(s
->monitor_source
);
1477 /* Called from the IO thread */
1478 static void sync_input_volumes_within_thread(pa_sink
*s
) {
1482 pa_sink_assert_ref(s
);
1484 while ((i
= PA_SINK_INPUT(pa_hashmap_iterate(s
->thread_info
.inputs
, &state
, NULL
)))) {
1485 if (pa_cvolume_equal(&i
->thread_info
.soft_volume
, &i
->soft_volume
))
1488 i
->thread_info
.soft_volume
= i
->soft_volume
;
1489 pa_sink_input_request_rewind(i
, 0, TRUE
, FALSE
, FALSE
);
1493 /* Called from IO thread, except when it is not */
1494 int pa_sink_process_msg(pa_msgobject
*o
, int code
, void *userdata
, int64_t offset
, pa_memchunk
*chunk
) {
1495 pa_sink
*s
= PA_SINK(o
);
1496 pa_sink_assert_ref(s
);
1498 switch ((pa_sink_message_t
) code
) {
1500 case PA_SINK_MESSAGE_ADD_INPUT
: {
1501 pa_sink_input
*i
= PA_SINK_INPUT(userdata
);
1503 /* If you change anything here, make sure to change the
1504 * sink input handling a few lines down at
1505 * PA_SINK_MESSAGE_FINISH_MOVE, too. */
1507 pa_hashmap_put(s
->thread_info
.inputs
, PA_UINT32_TO_PTR(i
->index
), pa_sink_input_ref(i
));
1509 /* Since the caller sleeps in pa_sink_input_put(), we can
1510 * safely access data outside of thread_info even though
1513 if ((i
->thread_info
.sync_prev
= i
->sync_prev
)) {
1514 pa_assert(i
->sink
== i
->thread_info
.sync_prev
->sink
);
1515 pa_assert(i
->sync_prev
->sync_next
== i
);
1516 i
->thread_info
.sync_prev
->thread_info
.sync_next
= i
;
1519 if ((i
->thread_info
.sync_next
= i
->sync_next
)) {
1520 pa_assert(i
->sink
== i
->thread_info
.sync_next
->sink
);
1521 pa_assert(i
->sync_next
->sync_prev
== i
);
1522 i
->thread_info
.sync_next
->thread_info
.sync_prev
= i
;
1525 pa_assert(!i
->thread_info
.attached
);
1526 i
->thread_info
.attached
= TRUE
;
1531 pa_sink_input_set_state_within_thread(i
, i
->state
);
1533 /* The requested latency of the sink input needs to be
1534 * fixed up and then configured on the sink */
1536 if (i
->thread_info
.requested_sink_latency
!= (pa_usec_t
) -1)
1537 pa_sink_input_set_requested_latency_within_thread(i
, i
->thread_info
.requested_sink_latency
);
1539 pa_sink_input_update_max_rewind(i
, s
->thread_info
.max_rewind
);
1540 pa_sink_input_update_max_request(i
, s
->thread_info
.max_request
);
1542 /* We don't rewind here automatically. This is left to the
1543 * sink input implementor because some sink inputs need a
1544 * slow start, i.e. need some time to buffer client
1545 * samples before beginning streaming. */
1547 /* In flat volume mode we need to update the volume as
1549 return o
->process_msg(o
, PA_SINK_MESSAGE_SET_VOLUME
, NULL
, 0, NULL
);
1552 case PA_SINK_MESSAGE_REMOVE_INPUT
: {
1553 pa_sink_input
*i
= PA_SINK_INPUT(userdata
);
1555 /* If you change anything here, make sure to change the
1556 * sink input handling a few lines down at
1557 * PA_SINK_MESSAGE_PREPAPRE_MOVE, too. */
1562 pa_sink_input_set_state_within_thread(i
, i
->state
);
1564 pa_assert(i
->thread_info
.attached
);
1565 i
->thread_info
.attached
= FALSE
;
1567 /* Since the caller sleeps in pa_sink_input_unlink(),
1568 * we can safely access data outside of thread_info even
1569 * though it is mutable */
1571 pa_assert(!i
->sync_prev
);
1572 pa_assert(!i
->sync_next
);
1574 if (i
->thread_info
.sync_prev
) {
1575 i
->thread_info
.sync_prev
->thread_info
.sync_next
= i
->thread_info
.sync_prev
->sync_next
;
1576 i
->thread_info
.sync_prev
= NULL
;
1579 if (i
->thread_info
.sync_next
) {
1580 i
->thread_info
.sync_next
->thread_info
.sync_prev
= i
->thread_info
.sync_next
->sync_prev
;
1581 i
->thread_info
.sync_next
= NULL
;
1584 if (pa_hashmap_remove(s
->thread_info
.inputs
, PA_UINT32_TO_PTR(i
->index
)))
1585 pa_sink_input_unref(i
);
1587 pa_sink_invalidate_requested_latency(s
);
1588 pa_sink_request_rewind(s
, (size_t) -1);
1590 /* In flat volume mode we need to update the volume as
1592 return o
->process_msg(o
, PA_SINK_MESSAGE_SET_VOLUME
, NULL
, 0, NULL
);
1595 case PA_SINK_MESSAGE_START_MOVE
: {
1596 pa_sink_input
*i
= PA_SINK_INPUT(userdata
);
1598 /* We don't support moving synchronized streams. */
1599 pa_assert(!i
->sync_prev
);
1600 pa_assert(!i
->sync_next
);
1601 pa_assert(!i
->thread_info
.sync_next
);
1602 pa_assert(!i
->thread_info
.sync_prev
);
1604 if (i
->thread_info
.state
!= PA_SINK_INPUT_CORKED
) {
1606 size_t sink_nbytes
, total_nbytes
;
1608 /* Get the latency of the sink */
1609 if (!(s
->flags
& PA_SINK_LATENCY
) ||
1610 PA_MSGOBJECT(s
)->process_msg(PA_MSGOBJECT(s
), PA_SINK_MESSAGE_GET_LATENCY
, &usec
, 0, NULL
) < 0)
1613 sink_nbytes
= pa_usec_to_bytes(usec
, &s
->sample_spec
);
1614 total_nbytes
= sink_nbytes
+ pa_memblockq_get_length(i
->thread_info
.render_memblockq
);
1616 if (total_nbytes
> 0) {
1617 i
->thread_info
.rewrite_nbytes
= i
->thread_info
.resampler
? pa_resampler_request(i
->thread_info
.resampler
, total_nbytes
) : total_nbytes
;
1618 i
->thread_info
.rewrite_flush
= TRUE
;
1619 pa_sink_input_process_rewind(i
, sink_nbytes
);
1626 pa_assert(i
->thread_info
.attached
);
1627 i
->thread_info
.attached
= FALSE
;
1629 /* Let's remove the sink input ...*/
1630 if (pa_hashmap_remove(s
->thread_info
.inputs
, PA_UINT32_TO_PTR(i
->index
)))
1631 pa_sink_input_unref(i
);
1633 pa_sink_invalidate_requested_latency(s
);
1635 pa_log_debug("Requesting rewind due to started move");
1636 pa_sink_request_rewind(s
, (size_t) -1);
1638 /* In flat volume mode we need to update the volume as
1640 return o
->process_msg(o
, PA_SINK_MESSAGE_SET_VOLUME
, NULL
, 0, NULL
);
1643 case PA_SINK_MESSAGE_FINISH_MOVE
: {
1644 pa_sink_input
*i
= PA_SINK_INPUT(userdata
);
1646 /* We don't support moving synchronized streams. */
1647 pa_assert(!i
->sync_prev
);
1648 pa_assert(!i
->sync_next
);
1649 pa_assert(!i
->thread_info
.sync_next
);
1650 pa_assert(!i
->thread_info
.sync_prev
);
1652 pa_hashmap_put(s
->thread_info
.inputs
, PA_UINT32_TO_PTR(i
->index
), pa_sink_input_ref(i
));
1654 pa_assert(!i
->thread_info
.attached
);
1655 i
->thread_info
.attached
= TRUE
;
1660 if (i
->thread_info
.requested_sink_latency
!= (pa_usec_t
) -1)
1661 pa_sink_input_set_requested_latency_within_thread(i
, i
->thread_info
.requested_sink_latency
);
1663 pa_sink_input_update_max_rewind(i
, s
->thread_info
.max_rewind
);
1664 pa_sink_input_update_max_request(i
, s
->thread_info
.max_request
);
1666 if (i
->thread_info
.state
!= PA_SINK_INPUT_CORKED
) {
1670 /* Get the latency of the sink */
1671 if (!(s
->flags
& PA_SINK_LATENCY
) ||
1672 PA_MSGOBJECT(s
)->process_msg(PA_MSGOBJECT(s
), PA_SINK_MESSAGE_GET_LATENCY
, &usec
, 0, NULL
) < 0)
1675 nbytes
= pa_usec_to_bytes(usec
, &s
->sample_spec
);
1678 pa_sink_input_drop(i
, nbytes
);
1680 pa_log_debug("Requesting rewind due to finished move");
1681 pa_sink_request_rewind(s
, nbytes
);
1684 /* In flat volume mode we need to update the volume as
1686 return o
->process_msg(o
, PA_SINK_MESSAGE_SET_VOLUME
, NULL
, 0, NULL
);
1689 case PA_SINK_MESSAGE_SET_VOLUME
:
1691 if (!pa_cvolume_equal(&s
->thread_info
.soft_volume
, &s
->soft_volume
)) {
1692 s
->thread_info
.soft_volume
= s
->soft_volume
;
1693 pa_sink_request_rewind(s
, (size_t) -1);
1696 if (!(s
->flags
& PA_SINK_FLAT_VOLUME
))
1699 /* Fall through ... */
1701 case PA_SINK_MESSAGE_SYNC_VOLUMES
:
1702 sync_input_volumes_within_thread(s
);
1705 case PA_SINK_MESSAGE_GET_VOLUME
:
1708 case PA_SINK_MESSAGE_SET_MUTE
:
1710 if (s
->thread_info
.soft_muted
!= s
->muted
) {
1711 s
->thread_info
.soft_muted
= s
->muted
;
1712 pa_sink_request_rewind(s
, (size_t) -1);
1717 case PA_SINK_MESSAGE_GET_MUTE
:
1720 case PA_SINK_MESSAGE_SET_STATE
: {
1722 pa_bool_t suspend_change
=
1723 (s
->thread_info
.state
== PA_SINK_SUSPENDED
&& PA_SINK_IS_OPENED(PA_PTR_TO_UINT(userdata
))) ||
1724 (PA_SINK_IS_OPENED(s
->thread_info
.state
) && PA_PTR_TO_UINT(userdata
) == PA_SINK_SUSPENDED
);
1726 s
->thread_info
.state
= PA_PTR_TO_UINT(userdata
);
1728 if (s
->thread_info
.state
== PA_SINK_SUSPENDED
) {
1729 s
->thread_info
.rewind_nbytes
= 0;
1730 s
->thread_info
.rewind_requested
= FALSE
;
1733 if (suspend_change
) {
1737 while ((i
= pa_hashmap_iterate(s
->thread_info
.inputs
, &state
, NULL
)))
1738 if (i
->suspend_within_thread
)
1739 i
->suspend_within_thread(i
, s
->thread_info
.state
== PA_SINK_SUSPENDED
);
1745 case PA_SINK_MESSAGE_DETACH
:
1747 /* Detach all streams */
1748 pa_sink_detach_within_thread(s
);
1751 case PA_SINK_MESSAGE_ATTACH
:
1753 /* Reattach all streams */
1754 pa_sink_attach_within_thread(s
);
1757 case PA_SINK_MESSAGE_GET_REQUESTED_LATENCY
: {
1759 pa_usec_t
*usec
= userdata
;
1760 *usec
= pa_sink_get_requested_latency_within_thread(s
);
1762 if (*usec
== (pa_usec_t
) -1)
1763 *usec
= s
->thread_info
.max_latency
;
1768 case PA_SINK_MESSAGE_SET_LATENCY_RANGE
: {
1769 pa_usec_t
*r
= userdata
;
1771 pa_sink_set_latency_range_within_thread(s
, r
[0], r
[1]);
1776 case PA_SINK_MESSAGE_GET_LATENCY_RANGE
: {
1777 pa_usec_t
*r
= userdata
;
1779 r
[0] = s
->thread_info
.min_latency
;
1780 r
[1] = s
->thread_info
.max_latency
;
1785 case PA_SINK_MESSAGE_GET_MAX_REWIND
:
1787 *((size_t*) userdata
) = s
->thread_info
.max_rewind
;
1790 case PA_SINK_MESSAGE_GET_MAX_REQUEST
:
1792 *((size_t*) userdata
) = s
->thread_info
.max_request
;
1795 case PA_SINK_MESSAGE_SET_MAX_REWIND
:
1797 pa_sink_set_max_rewind_within_thread(s
, (size_t) offset
);
1800 case PA_SINK_MESSAGE_SET_MAX_REQUEST
:
1802 pa_sink_set_max_request_within_thread(s
, (size_t) offset
);
1805 case PA_SINK_MESSAGE_GET_LATENCY
:
1806 case PA_SINK_MESSAGE_MAX
:
1813 /* Called from main thread */
1814 int pa_sink_suspend_all(pa_core
*c
, pa_bool_t suspend
) {
1819 pa_core_assert_ref(c
);
1821 for (sink
= PA_SINK(pa_idxset_first(c
->sinks
, &idx
)); sink
; sink
= PA_SINK(pa_idxset_next(c
->sinks
, &idx
))) {
1824 if ((r
= pa_sink_suspend(sink
, suspend
)) < 0)
1831 /* Called from main thread */
1832 void pa_sink_detach(pa_sink
*s
) {
1833 pa_sink_assert_ref(s
);
1834 pa_assert(PA_SINK_IS_LINKED(s
->state
));
1836 pa_assert_se(pa_asyncmsgq_send(s
->asyncmsgq
, PA_MSGOBJECT(s
), PA_SINK_MESSAGE_DETACH
, NULL
, 0, NULL
) == 0);
1839 /* Called from main thread */
1840 void pa_sink_attach(pa_sink
*s
) {
1841 pa_sink_assert_ref(s
);
1842 pa_assert(PA_SINK_IS_LINKED(s
->state
));
1844 pa_assert_se(pa_asyncmsgq_send(s
->asyncmsgq
, PA_MSGOBJECT(s
), PA_SINK_MESSAGE_ATTACH
, NULL
, 0, NULL
) == 0);
1847 /* Called from IO thread */
1848 void pa_sink_detach_within_thread(pa_sink
*s
) {
1852 pa_sink_assert_ref(s
);
1853 pa_assert(PA_SINK_IS_LINKED(s
->thread_info
.state
));
1855 while ((i
= pa_hashmap_iterate(s
->thread_info
.inputs
, &state
, NULL
)))
1859 if (s
->monitor_source
)
1860 pa_source_detach_within_thread(s
->monitor_source
);
1863 /* Called from IO thread */
1864 void pa_sink_attach_within_thread(pa_sink
*s
) {
1868 pa_sink_assert_ref(s
);
1869 pa_assert(PA_SINK_IS_LINKED(s
->thread_info
.state
));
1871 while ((i
= pa_hashmap_iterate(s
->thread_info
.inputs
, &state
, NULL
)))
1875 if (s
->monitor_source
)
1876 pa_source_attach_within_thread(s
->monitor_source
);
1879 /* Called from IO thread */
1880 void pa_sink_request_rewind(pa_sink
*s
, size_t nbytes
) {
1881 pa_sink_assert_ref(s
);
1882 pa_assert(PA_SINK_IS_LINKED(s
->thread_info
.state
));
1884 if (s
->thread_info
.state
== PA_SINK_SUSPENDED
)
1887 if (nbytes
== (size_t) -1)
1888 nbytes
= s
->thread_info
.max_rewind
;
1890 nbytes
= PA_MIN(nbytes
, s
->thread_info
.max_rewind
);
1892 if (s
->thread_info
.rewind_requested
&&
1893 nbytes
<= s
->thread_info
.rewind_nbytes
)
1896 s
->thread_info
.rewind_nbytes
= nbytes
;
1897 s
->thread_info
.rewind_requested
= TRUE
;
1899 if (s
->request_rewind
)
1900 s
->request_rewind(s
);
1903 /* Called from IO thread */
1904 pa_usec_t
pa_sink_get_requested_latency_within_thread(pa_sink
*s
) {
1905 pa_usec_t result
= (pa_usec_t
) -1;
1908 pa_usec_t monitor_latency
;
1910 pa_sink_assert_ref(s
);
1912 if (!(s
->flags
& PA_SINK_DYNAMIC_LATENCY
))
1913 return PA_CLAMP(s
->fixed_latency
, s
->thread_info
.min_latency
, s
->thread_info
.max_latency
);
1915 if (s
->thread_info
.requested_latency_valid
)
1916 return s
->thread_info
.requested_latency
;
1918 while ((i
= pa_hashmap_iterate(s
->thread_info
.inputs
, &state
, NULL
)))
1920 if (i
->thread_info
.requested_sink_latency
!= (pa_usec_t
) -1 &&
1921 (result
== (pa_usec_t
) -1 || result
> i
->thread_info
.requested_sink_latency
))
1922 result
= i
->thread_info
.requested_sink_latency
;
1924 monitor_latency
= pa_source_get_requested_latency_within_thread(s
->monitor_source
);
1926 if (monitor_latency
!= (pa_usec_t
) -1 &&
1927 (result
== (pa_usec_t
) -1 || result
> monitor_latency
))
1928 result
= monitor_latency
;
1930 if (result
!= (pa_usec_t
) -1)
1931 result
= PA_CLAMP(result
, s
->thread_info
.min_latency
, s
->thread_info
.max_latency
);
1933 if (PA_SINK_IS_LINKED(s
->thread_info
.state
)) {
1934 /* Only cache if properly initialized */
1935 s
->thread_info
.requested_latency
= result
;
1936 s
->thread_info
.requested_latency_valid
= TRUE
;
1942 /* Called from main thread */
1943 pa_usec_t
pa_sink_get_requested_latency(pa_sink
*s
) {
1946 pa_sink_assert_ref(s
);
1947 pa_assert(PA_SINK_IS_LINKED(s
->state
));
1949 if (s
->state
== PA_SINK_SUSPENDED
)
1952 pa_assert_se(pa_asyncmsgq_send(s
->asyncmsgq
, PA_MSGOBJECT(s
), PA_SINK_MESSAGE_GET_REQUESTED_LATENCY
, &usec
, 0, NULL
) == 0);
1956 /* Called from IO as well as the main thread -- the latter only before the IO thread started up */
1957 void pa_sink_set_max_rewind_within_thread(pa_sink
*s
, size_t max_rewind
) {
1961 pa_sink_assert_ref(s
);
1963 if (max_rewind
== s
->thread_info
.max_rewind
)
1966 s
->thread_info
.max_rewind
= max_rewind
;
1968 if (PA_SINK_IS_LINKED(s
->thread_info
.state
)) {
1969 while ((i
= pa_hashmap_iterate(s
->thread_info
.inputs
, &state
, NULL
)))
1970 pa_sink_input_update_max_rewind(i
, s
->thread_info
.max_rewind
);
1973 if (s
->monitor_source
)
1974 pa_source_set_max_rewind_within_thread(s
->monitor_source
, s
->thread_info
.max_rewind
);
1977 /* Called from main thread */
1978 void pa_sink_set_max_rewind(pa_sink
*s
, size_t max_rewind
) {
1979 pa_sink_assert_ref(s
);
1981 if (PA_SINK_IS_LINKED(s
->state
))
1982 pa_assert_se(pa_asyncmsgq_send(s
->asyncmsgq
, PA_MSGOBJECT(s
), PA_SINK_MESSAGE_SET_MAX_REWIND
, NULL
, max_rewind
, NULL
) == 0);
1984 pa_sink_set_max_rewind_within_thread(s
, max_rewind
);
1987 /* Called from IO as well as the main thread -- the latter only before the IO thread started up */
1988 void pa_sink_set_max_request_within_thread(pa_sink
*s
, size_t max_request
) {
1991 pa_sink_assert_ref(s
);
1993 if (max_request
== s
->thread_info
.max_request
)
1996 s
->thread_info
.max_request
= max_request
;
1998 if (PA_SINK_IS_LINKED(s
->thread_info
.state
)) {
2001 while ((i
= pa_hashmap_iterate(s
->thread_info
.inputs
, &state
, NULL
)))
2002 pa_sink_input_update_max_request(i
, s
->thread_info
.max_request
);
2006 /* Called from main thread */
2007 void pa_sink_set_max_request(pa_sink
*s
, size_t max_request
) {
2008 pa_sink_assert_ref(s
);
2010 if (PA_SINK_IS_LINKED(s
->state
))
2011 pa_assert_se(pa_asyncmsgq_send(s
->asyncmsgq
, PA_MSGOBJECT(s
), PA_SINK_MESSAGE_SET_MAX_REQUEST
, NULL
, max_request
, NULL
) == 0);
2013 pa_sink_set_max_request_within_thread(s
, max_request
);
2016 /* Called from IO thread */
2017 void pa_sink_invalidate_requested_latency(pa_sink
*s
) {
2021 pa_sink_assert_ref(s
);
2023 if (!(s
->flags
& PA_SINK_DYNAMIC_LATENCY
))
2026 s
->thread_info
.requested_latency_valid
= FALSE
;
2028 if (PA_SINK_IS_LINKED(s
->thread_info
.state
)) {
2030 if (s
->update_requested_latency
)
2031 s
->update_requested_latency(s
);
2033 while ((i
= pa_hashmap_iterate(s
->thread_info
.inputs
, &state
, NULL
)))
2034 if (i
->update_sink_requested_latency
)
2035 i
->update_sink_requested_latency(i
);
2039 /* Called from main thread */
2040 void pa_sink_set_latency_range(pa_sink
*s
, pa_usec_t min_latency
, pa_usec_t max_latency
) {
2041 pa_sink_assert_ref(s
);
2043 /* min_latency == 0: no limit
2044 * min_latency anything else: specified limit
2046 * Similar for max_latency */
2048 if (min_latency
< ABSOLUTE_MIN_LATENCY
)
2049 min_latency
= ABSOLUTE_MIN_LATENCY
;
2051 if (max_latency
<= 0 ||
2052 max_latency
> ABSOLUTE_MAX_LATENCY
)
2053 max_latency
= ABSOLUTE_MAX_LATENCY
;
2055 pa_assert(min_latency
<= max_latency
);
2057 /* Hmm, let's see if someone forgot to set PA_SINK_DYNAMIC_LATENCY here... */
2058 pa_assert((min_latency
== ABSOLUTE_MIN_LATENCY
&&
2059 max_latency
== ABSOLUTE_MAX_LATENCY
) ||
2060 (s
->flags
& PA_SINK_DYNAMIC_LATENCY
));
2062 if (PA_SINK_IS_LINKED(s
->state
)) {
2068 pa_assert_se(pa_asyncmsgq_send(s
->asyncmsgq
, PA_MSGOBJECT(s
), PA_SINK_MESSAGE_SET_LATENCY_RANGE
, r
, 0, NULL
) == 0);
2070 pa_sink_set_latency_range_within_thread(s
, min_latency
, max_latency
);
2073 /* Called from main thread */
2074 void pa_sink_get_latency_range(pa_sink
*s
, pa_usec_t
*min_latency
, pa_usec_t
*max_latency
) {
2075 pa_sink_assert_ref(s
);
2076 pa_assert(min_latency
);
2077 pa_assert(max_latency
);
2079 if (PA_SINK_IS_LINKED(s
->state
)) {
2080 pa_usec_t r
[2] = { 0, 0 };
2082 pa_assert_se(pa_asyncmsgq_send(s
->asyncmsgq
, PA_MSGOBJECT(s
), PA_SINK_MESSAGE_GET_LATENCY_RANGE
, r
, 0, NULL
) == 0);
2084 *min_latency
= r
[0];
2085 *max_latency
= r
[1];
2087 *min_latency
= s
->thread_info
.min_latency
;
2088 *max_latency
= s
->thread_info
.max_latency
;
2092 /* Called from IO thread */
2093 void pa_sink_set_latency_range_within_thread(pa_sink
*s
, pa_usec_t min_latency
, pa_usec_t max_latency
) {
2096 pa_sink_assert_ref(s
);
2098 pa_assert(min_latency
>= ABSOLUTE_MIN_LATENCY
);
2099 pa_assert(max_latency
<= ABSOLUTE_MAX_LATENCY
);
2100 pa_assert(min_latency
<= max_latency
);
2102 /* Hmm, let's see if someone forgot to set PA_SINK_DYNAMIC_LATENCY here... */
2103 pa_assert((min_latency
== ABSOLUTE_MIN_LATENCY
&&
2104 max_latency
== ABSOLUTE_MAX_LATENCY
) ||
2105 (s
->flags
& PA_SINK_DYNAMIC_LATENCY
));
2107 s
->thread_info
.min_latency
= min_latency
;
2108 s
->thread_info
.max_latency
= max_latency
;
2110 if (PA_SINK_IS_LINKED(s
->thread_info
.state
)) {
2113 while ((i
= pa_hashmap_iterate(s
->thread_info
.inputs
, &state
, NULL
)))
2114 if (i
->update_sink_latency_range
)
2115 i
->update_sink_latency_range(i
);
2118 pa_sink_invalidate_requested_latency(s
);
2120 pa_source_set_latency_range_within_thread(s
->monitor_source
, min_latency
, max_latency
);
2123 /* Called from main thread, before the sink is put */
2124 void pa_sink_set_fixed_latency(pa_sink
*s
, pa_usec_t latency
) {
2125 pa_sink_assert_ref(s
);
2127 pa_assert(pa_sink_get_state(s
) == PA_SINK_INIT
);
2129 if (latency
< ABSOLUTE_MIN_LATENCY
)
2130 latency
= ABSOLUTE_MIN_LATENCY
;
2132 if (latency
> ABSOLUTE_MAX_LATENCY
)
2133 latency
= ABSOLUTE_MAX_LATENCY
;
2135 s
->fixed_latency
= latency
;
2136 pa_source_set_fixed_latency(s
->monitor_source
, latency
);
2139 /* Called from main context */
2140 size_t pa_sink_get_max_rewind(pa_sink
*s
) {
2142 pa_sink_assert_ref(s
);
2144 if (!PA_SINK_IS_LINKED(s
->state
))
2145 return s
->thread_info
.max_rewind
;
2147 pa_assert_se(pa_asyncmsgq_send(s
->asyncmsgq
, PA_MSGOBJECT(s
), PA_SINK_MESSAGE_GET_MAX_REWIND
, &r
, 0, NULL
) == 0);
2152 /* Called from main context */
2153 size_t pa_sink_get_max_request(pa_sink
*s
) {
2155 pa_sink_assert_ref(s
);
2157 if (!PA_SINK_IS_LINKED(s
->state
))
2158 return s
->thread_info
.max_request
;
2160 pa_assert_se(pa_asyncmsgq_send(s
->asyncmsgq
, PA_MSGOBJECT(s
), PA_SINK_MESSAGE_GET_MAX_REQUEST
, &r
, 0, NULL
) == 0);
2165 /* Called from main context */
2166 pa_bool_t
pa_device_init_icon(pa_proplist
*p
, pa_bool_t is_sink
) {
2167 const char *ff
, *c
, *t
= NULL
, *s
= "", *profile
, *bus
;
2171 if (pa_proplist_contains(p
, PA_PROP_DEVICE_ICON_NAME
))
2174 if ((ff
= pa_proplist_gets(p
, PA_PROP_DEVICE_FORM_FACTOR
))) {
2176 if (pa_streq(ff
, "microphone"))
2177 t
= "audio-input-microphone";
2178 else if (pa_streq(ff
, "webcam"))
2180 else if (pa_streq(ff
, "computer"))
2182 else if (pa_streq(ff
, "handset"))
2184 else if (pa_streq(ff
, "portable"))
2185 t
= "multimedia-player";
2186 else if (pa_streq(ff
, "tv"))
2187 t
= "video-display";
2191 if ((c
= pa_proplist_gets(p
, PA_PROP_DEVICE_CLASS
)))
2192 if (pa_streq(c
, "modem"))
2199 t
= "audio-input-microphone";
2202 if ((profile
= pa_proplist_gets(p
, PA_PROP_DEVICE_PROFILE_NAME
))) {
2203 if (strstr(profile
, "analog"))
2205 else if (strstr(profile
, "iec958"))
2207 else if (strstr(profile
, "hdmi"))
2211 bus
= pa_proplist_gets(p
, PA_PROP_DEVICE_BUS
);
2213 pa_proplist_setf(p
, PA_PROP_DEVICE_ICON_NAME
, "%s%s%s%s", t
, pa_strempty(s
), bus
? "-" : "", pa_strempty(bus
));
2218 pa_bool_t
pa_device_init_description(pa_proplist
*p
) {
2222 if (pa_proplist_contains(p
, PA_PROP_DEVICE_DESCRIPTION
))
2225 if ((s
= pa_proplist_gets(p
, PA_PROP_DEVICE_FORM_FACTOR
)))
2226 if (pa_streq(s
, "internal")) {
2227 pa_proplist_sets(p
, PA_PROP_DEVICE_DESCRIPTION
, _("Internal Audio"));
2231 if ((s
= pa_proplist_gets(p
, PA_PROP_DEVICE_CLASS
)))
2232 if (pa_streq(s
, "modem")) {
2233 pa_proplist_sets(p
, PA_PROP_DEVICE_DESCRIPTION
, _("Modem"));
2237 if ((s
= pa_proplist_gets(p
, PA_PROP_DEVICE_PRODUCT_NAME
))) {
2238 pa_proplist_sets(p
, PA_PROP_DEVICE_DESCRIPTION
, s
);