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 if (!(flags
& PA_SINK_HW_VOLUME_CTRL
))
188 flags
|= PA_SINK_DECIBEL_VOLUME
;
190 if ((flags
& PA_SINK_DECIBEL_VOLUME
) && core
->flat_volumes
)
191 flags
|= PA_SINK_FLAT_VOLUME
;
193 s
->parent
.parent
.free
= sink_free
;
194 s
->parent
.process_msg
= pa_sink_process_msg
;
197 s
->state
= PA_SINK_INIT
;
199 s
->name
= pa_xstrdup(name
);
200 s
->proplist
= pa_proplist_copy(data
->proplist
);
201 s
->driver
= pa_xstrdup(pa_path_get_filename(data
->driver
));
202 s
->module
= data
->module
;
203 s
->card
= data
->card
;
205 s
->sample_spec
= data
->sample_spec
;
206 s
->channel_map
= data
->channel_map
;
208 s
->inputs
= pa_idxset_new(NULL
, NULL
);
211 s
->reference_volume
= s
->virtual_volume
= data
->volume
;
212 pa_cvolume_reset(&s
->soft_volume
, s
->sample_spec
.channels
);
213 s
->base_volume
= PA_VOLUME_NORM
;
214 s
->n_volume_steps
= PA_VOLUME_NORM
+1;
215 s
->muted
= data
->muted
;
216 s
->refresh_volume
= s
->refresh_muted
= FALSE
;
218 s
->fixed_latency
= flags
& PA_SINK_DYNAMIC_LATENCY
? 0 : DEFAULT_FIXED_LATENCY
;
226 pa_silence_memchunk_get(
227 &core
->silence_cache
,
233 s
->thread_info
.inputs
= pa_hashmap_new(pa_idxset_trivial_hash_func
, pa_idxset_trivial_compare_func
);
234 s
->thread_info
.soft_volume
= s
->soft_volume
;
235 s
->thread_info
.soft_muted
= s
->muted
;
236 s
->thread_info
.state
= s
->state
;
237 s
->thread_info
.rewind_nbytes
= 0;
238 s
->thread_info
.rewind_requested
= FALSE
;
239 s
->thread_info
.max_rewind
= 0;
240 s
->thread_info
.max_request
= 0;
241 s
->thread_info
.requested_latency_valid
= FALSE
;
242 s
->thread_info
.requested_latency
= 0;
243 s
->thread_info
.min_latency
= ABSOLUTE_MIN_LATENCY
;
244 s
->thread_info
.max_latency
= ABSOLUTE_MAX_LATENCY
;
246 pa_assert_se(pa_idxset_put(core
->sinks
, s
, &s
->index
) >= 0);
249 pa_assert_se(pa_idxset_put(s
->card
->sinks
, s
, NULL
) >= 0);
251 pt
= pa_proplist_to_string_sep(s
->proplist
, "\n ");
252 pa_log_info("Created sink %u \"%s\" with sample spec %s and channel map %s\n %s",
255 pa_sample_spec_snprint(st
, sizeof(st
), &s
->sample_spec
),
256 pa_channel_map_snprint(cm
, sizeof(cm
), &s
->channel_map
),
260 pa_source_new_data_init(&source_data
);
261 pa_source_new_data_set_sample_spec(&source_data
, &s
->sample_spec
);
262 pa_source_new_data_set_channel_map(&source_data
, &s
->channel_map
);
263 source_data
.name
= pa_sprintf_malloc("%s.monitor", name
);
264 source_data
.driver
= data
->driver
;
265 source_data
.module
= data
->module
;
266 source_data
.card
= data
->card
;
268 dn
= pa_proplist_gets(s
->proplist
, PA_PROP_DEVICE_DESCRIPTION
);
269 pa_proplist_setf(source_data
.proplist
, PA_PROP_DEVICE_DESCRIPTION
, "Monitor of %s", dn
? dn
: s
->name
);
270 pa_proplist_sets(source_data
.proplist
, PA_PROP_DEVICE_CLASS
, "monitor");
272 s
->monitor_source
= pa_source_new(core
, &source_data
,
273 ((flags
& PA_SINK_LATENCY
) ? PA_SOURCE_LATENCY
: 0) |
274 ((flags
& PA_SINK_DYNAMIC_LATENCY
) ? PA_SOURCE_DYNAMIC_LATENCY
: 0));
276 pa_source_new_data_done(&source_data
);
278 if (!s
->monitor_source
) {
284 s
->monitor_source
->monitor_of
= s
;
286 pa_source_set_latency_range(s
->monitor_source
, s
->thread_info
.min_latency
, s
->thread_info
.max_latency
);
287 pa_source_set_max_rewind(s
->monitor_source
, s
->thread_info
.max_rewind
);
292 /* Called from main context */
293 static int sink_set_state(pa_sink
*s
, pa_sink_state_t state
) {
295 pa_bool_t suspend_change
;
296 pa_sink_state_t original_state
;
300 if (s
->state
== state
)
303 original_state
= s
->state
;
306 (original_state
== PA_SINK_SUSPENDED
&& PA_SINK_IS_OPENED(state
)) ||
307 (PA_SINK_IS_OPENED(original_state
) && state
== PA_SINK_SUSPENDED
);
310 if ((ret
= s
->set_state(s
, state
)) < 0)
314 if ((ret
= pa_asyncmsgq_send(s
->asyncmsgq
, PA_MSGOBJECT(s
), PA_SINK_MESSAGE_SET_STATE
, PA_UINT_TO_PTR(state
), 0, NULL
)) < 0) {
317 s
->set_state(s
, original_state
);
324 if (state
!= PA_SINK_UNLINKED
) { /* if we enter UNLINKED state pa_sink_unlink() will fire the apropriate events */
325 pa_hook_fire(&s
->core
->hooks
[PA_CORE_HOOK_SINK_STATE_CHANGED
], s
);
326 pa_subscription_post(s
->core
, PA_SUBSCRIPTION_EVENT_SINK
| PA_SUBSCRIPTION_EVENT_CHANGE
, s
->index
);
329 if (suspend_change
) {
333 /* We're suspending or resuming, tell everyone about it */
335 for (i
= PA_SINK_INPUT(pa_idxset_first(s
->inputs
, &idx
)); i
; i
= PA_SINK_INPUT(pa_idxset_next(s
->inputs
, &idx
)))
336 if (s
->state
== PA_SINK_SUSPENDED
&&
337 (i
->flags
& PA_SINK_INPUT_FAIL_ON_SUSPEND
))
338 pa_sink_input_kill(i
);
340 i
->suspend(i
, state
== PA_SINK_SUSPENDED
);
342 if (s
->monitor_source
)
343 pa_source_sync_suspend(s
->monitor_source
);
349 /* Called from main context */
350 void pa_sink_put(pa_sink
* s
) {
351 pa_sink_assert_ref(s
);
353 pa_assert(s
->state
== PA_SINK_INIT
);
355 /* The following fields must be initialized properly when calling _put() */
356 pa_assert(s
->asyncmsgq
);
357 pa_assert(s
->rtpoll
);
358 pa_assert(s
->thread_info
.min_latency
<= s
->thread_info
.max_latency
);
360 s
->thread_info
.soft_volume
= s
->soft_volume
;
361 s
->thread_info
.soft_muted
= s
->muted
;
363 pa_assert((s
->flags
& PA_SINK_HW_VOLUME_CTRL
) || (s
->base_volume
== PA_VOLUME_NORM
&& s
->flags
& PA_SINK_DECIBEL_VOLUME
));
364 pa_assert(!(s
->flags
& PA_SINK_DECIBEL_VOLUME
) || s
->n_volume_steps
== PA_VOLUME_NORM
+1);
365 pa_assert(!(s
->flags
& PA_SINK_DYNAMIC_LATENCY
) == (s
->fixed_latency
!= 0));
366 pa_assert(!(s
->flags
& PA_SINK_LATENCY
) == !(s
->monitor_source
->flags
& PA_SOURCE_LATENCY
));
367 pa_assert(!(s
->flags
& PA_SINK_DYNAMIC_LATENCY
) == !(s
->monitor_source
->flags
& PA_SOURCE_DYNAMIC_LATENCY
));
369 pa_assert(s
->monitor_source
->fixed_latency
== s
->fixed_latency
);
370 pa_assert(s
->monitor_source
->thread_info
.min_latency
== s
->thread_info
.min_latency
);
371 pa_assert(s
->monitor_source
->thread_info
.max_latency
== s
->thread_info
.max_latency
);
373 pa_assert_se(sink_set_state(s
, PA_SINK_IDLE
) == 0);
375 pa_source_put(s
->monitor_source
);
377 pa_subscription_post(s
->core
, PA_SUBSCRIPTION_EVENT_SINK
| PA_SUBSCRIPTION_EVENT_NEW
, s
->index
);
378 pa_hook_fire(&s
->core
->hooks
[PA_CORE_HOOK_SINK_PUT
], s
);
381 /* Called from main context */
382 void pa_sink_unlink(pa_sink
* s
) {
384 pa_sink_input
*i
, *j
= NULL
;
388 /* Please note that pa_sink_unlink() does more than simply
389 * reversing pa_sink_put(). It also undoes the registrations
390 * already done in pa_sink_new()! */
392 /* All operations here shall be idempotent, i.e. pa_sink_unlink()
393 * may be called multiple times on the same sink without bad
396 linked
= PA_SINK_IS_LINKED(s
->state
);
399 pa_hook_fire(&s
->core
->hooks
[PA_CORE_HOOK_SINK_UNLINK
], s
);
401 if (s
->state
!= PA_SINK_UNLINKED
)
402 pa_namereg_unregister(s
->core
, s
->name
);
403 pa_idxset_remove_by_data(s
->core
->sinks
, s
, NULL
);
406 pa_idxset_remove_by_data(s
->card
->sinks
, s
, NULL
);
408 while ((i
= pa_idxset_first(s
->inputs
, NULL
))) {
410 pa_sink_input_kill(i
);
415 sink_set_state(s
, PA_SINK_UNLINKED
);
417 s
->state
= PA_SINK_UNLINKED
;
421 if (s
->monitor_source
)
422 pa_source_unlink(s
->monitor_source
);
425 pa_subscription_post(s
->core
, PA_SUBSCRIPTION_EVENT_SINK
| PA_SUBSCRIPTION_EVENT_REMOVE
, s
->index
);
426 pa_hook_fire(&s
->core
->hooks
[PA_CORE_HOOK_SINK_UNLINK_POST
], s
);
430 /* Called from main context */
431 static void sink_free(pa_object
*o
) {
432 pa_sink
*s
= PA_SINK(o
);
436 pa_assert(pa_sink_refcnt(s
) == 0);
438 if (PA_SINK_IS_LINKED(s
->state
))
441 pa_log_info("Freeing sink %u \"%s\"", s
->index
, s
->name
);
443 if (s
->monitor_source
) {
444 pa_source_unref(s
->monitor_source
);
445 s
->monitor_source
= NULL
;
448 pa_idxset_free(s
->inputs
, NULL
, NULL
);
450 while ((i
= pa_hashmap_steal_first(s
->thread_info
.inputs
)))
451 pa_sink_input_unref(i
);
453 pa_hashmap_free(s
->thread_info
.inputs
, NULL
, NULL
);
455 if (s
->silence
.memblock
)
456 pa_memblock_unref(s
->silence
.memblock
);
462 pa_proplist_free(s
->proplist
);
467 /* Called from main context */
468 void pa_sink_set_asyncmsgq(pa_sink
*s
, pa_asyncmsgq
*q
) {
469 pa_sink_assert_ref(s
);
473 if (s
->monitor_source
)
474 pa_source_set_asyncmsgq(s
->monitor_source
, q
);
477 /* Called from main context */
478 void pa_sink_set_rtpoll(pa_sink
*s
, pa_rtpoll
*p
) {
479 pa_sink_assert_ref(s
);
482 if (s
->monitor_source
)
483 pa_source_set_rtpoll(s
->monitor_source
, p
);
486 /* Called from main context */
487 int pa_sink_update_status(pa_sink
*s
) {
488 pa_sink_assert_ref(s
);
489 pa_assert(PA_SINK_IS_LINKED(s
->state
));
491 if (s
->state
== PA_SINK_SUSPENDED
)
494 return sink_set_state(s
, pa_sink_used_by(s
) ? PA_SINK_RUNNING
: PA_SINK_IDLE
);
497 /* Called from main context */
498 int pa_sink_suspend(pa_sink
*s
, pa_bool_t suspend
) {
499 pa_sink_assert_ref(s
);
500 pa_assert(PA_SINK_IS_LINKED(s
->state
));
503 return sink_set_state(s
, PA_SINK_SUSPENDED
);
505 return sink_set_state(s
, pa_sink_used_by(s
) ? PA_SINK_RUNNING
: PA_SINK_IDLE
);
508 /* Called from main context */
509 pa_queue
*pa_sink_move_all_start(pa_sink
*s
) {
511 pa_sink_input
*i
, *n
;
514 pa_sink_assert_ref(s
);
515 pa_assert(PA_SINK_IS_LINKED(s
->state
));
519 for (i
= PA_SINK_INPUT(pa_idxset_first(s
->inputs
, &idx
)); i
; i
= n
) {
520 n
= PA_SINK_INPUT(pa_idxset_next(s
->inputs
, &idx
));
522 pa_sink_input_ref(i
);
524 if (pa_sink_input_start_move(i
) >= 0)
527 pa_sink_input_unref(i
);
533 /* Called from main context */
534 void pa_sink_move_all_finish(pa_sink
*s
, pa_queue
*q
, pa_bool_t save
) {
537 pa_sink_assert_ref(s
);
538 pa_assert(PA_SINK_IS_LINKED(s
->state
));
541 while ((i
= PA_SINK_INPUT(pa_queue_pop(q
)))) {
542 if (pa_sink_input_finish_move(i
, s
, save
) < 0)
543 pa_sink_input_kill(i
);
545 pa_sink_input_unref(i
);
548 pa_queue_free(q
, NULL
, NULL
);
551 /* Called from main context */
552 void pa_sink_move_all_fail(pa_queue
*q
) {
556 while ((i
= PA_SINK_INPUT(pa_queue_pop(q
)))) {
557 if (pa_hook_fire(&i
->core
->hooks
[PA_CORE_HOOK_SINK_INPUT_MOVE_FAIL
], i
) == PA_HOOK_OK
) {
558 pa_sink_input_kill(i
);
559 pa_sink_input_unref(i
);
563 pa_queue_free(q
, NULL
, NULL
);
566 /* Called from IO thread context */
567 void pa_sink_process_rewind(pa_sink
*s
, size_t nbytes
) {
570 pa_sink_assert_ref(s
);
571 pa_assert(PA_SINK_IS_LINKED(s
->thread_info
.state
));
573 /* If nobody requested this and this is actually no real rewind
574 * then we can short cut this */
575 if (!s
->thread_info
.rewind_requested
&& nbytes
<= 0)
578 s
->thread_info
.rewind_nbytes
= 0;
579 s
->thread_info
.rewind_requested
= FALSE
;
581 if (s
->thread_info
.state
== PA_SINK_SUSPENDED
)
585 pa_log_debug("Processing rewind...");
587 while ((i
= pa_hashmap_iterate(s
->thread_info
.inputs
, &state
, NULL
))) {
588 pa_sink_input_assert_ref(i
);
589 pa_sink_input_process_rewind(i
, nbytes
);
593 if (s
->monitor_source
&& PA_SOURCE_IS_LINKED(s
->monitor_source
->thread_info
.state
))
594 pa_source_process_rewind(s
->monitor_source
, nbytes
);
597 /* Called from IO thread context */
598 static unsigned fill_mix_info(pa_sink
*s
, size_t *length
, pa_mix_info
*info
, unsigned maxinfo
) {
602 size_t mixlength
= *length
;
604 pa_sink_assert_ref(s
);
607 while ((i
= pa_hashmap_iterate(s
->thread_info
.inputs
, &state
, NULL
)) && maxinfo
> 0) {
608 pa_sink_input_assert_ref(i
);
610 pa_sink_input_peek(i
, *length
, &info
->chunk
, &info
->volume
);
612 if (mixlength
== 0 || info
->chunk
.length
< mixlength
)
613 mixlength
= info
->chunk
.length
;
615 if (pa_memblock_is_silence(info
->chunk
.memblock
)) {
616 pa_memblock_unref(info
->chunk
.memblock
);
620 info
->userdata
= pa_sink_input_ref(i
);
622 pa_assert(info
->chunk
.memblock
);
623 pa_assert(info
->chunk
.length
> 0);
636 /* Called from IO thread context */
637 static void inputs_drop(pa_sink
*s
, pa_mix_info
*info
, unsigned n
, pa_memchunk
*result
) {
641 unsigned n_unreffed
= 0;
643 pa_sink_assert_ref(s
);
645 pa_assert(result
->memblock
);
646 pa_assert(result
->length
> 0);
648 /* We optimize for the case where the order of the inputs has not changed */
650 while ((i
= pa_hashmap_iterate(s
->thread_info
.inputs
, &state
, NULL
))) {
652 pa_mix_info
* m
= NULL
;
654 pa_sink_input_assert_ref(i
);
656 /* Let's try to find the matching entry info the pa_mix_info array */
657 for (j
= 0; j
< n
; j
++) {
659 if (info
[p
].userdata
== i
) {
670 pa_sink_input_drop(i
, result
->length
);
672 if (s
->monitor_source
&& PA_SOURCE_IS_LINKED(s
->monitor_source
->thread_info
.state
)) {
674 if (pa_hashmap_size(i
->thread_info
.direct_outputs
) > 0) {
679 if (m
&& m
->chunk
.memblock
) {
681 pa_memblock_ref(c
.memblock
);
682 pa_assert(result
->length
<= c
.length
);
683 c
.length
= result
->length
;
685 pa_memchunk_make_writable(&c
, 0);
686 pa_volume_memchunk(&c
, &s
->sample_spec
, &m
->volume
);
689 pa_memblock_ref(c
.memblock
);
690 pa_assert(result
->length
<= c
.length
);
691 c
.length
= result
->length
;
694 while ((o
= pa_hashmap_iterate(i
->thread_info
.direct_outputs
, &ostate
, NULL
))) {
695 pa_source_output_assert_ref(o
);
696 pa_assert(o
->direct_on_input
== i
);
697 pa_source_post_direct(s
->monitor_source
, o
, &c
);
700 pa_memblock_unref(c
.memblock
);
705 if (m
->chunk
.memblock
)
706 pa_memblock_unref(m
->chunk
.memblock
);
707 pa_memchunk_reset(&m
->chunk
);
709 pa_sink_input_unref(m
->userdata
);
716 /* Now drop references to entries that are included in the
717 * pa_mix_info array but don't exist anymore */
719 if (n_unreffed
< n
) {
720 for (; n
> 0; info
++, n
--) {
722 pa_sink_input_unref(info
->userdata
);
723 if (info
->chunk
.memblock
)
724 pa_memblock_unref(info
->chunk
.memblock
);
728 if (s
->monitor_source
&& PA_SOURCE_IS_LINKED(s
->monitor_source
->thread_info
.state
))
729 pa_source_post(s
->monitor_source
, result
);
732 /* Called from IO thread context */
733 void pa_sink_render(pa_sink
*s
, size_t length
, pa_memchunk
*result
) {
734 pa_mix_info info
[MAX_MIX_CHANNELS
];
736 size_t block_size_max
;
738 pa_sink_assert_ref(s
);
739 pa_assert(PA_SINK_IS_LINKED(s
->thread_info
.state
));
740 pa_assert(pa_frame_aligned(length
, &s
->sample_spec
));
745 pa_assert(!s
->thread_info
.rewind_requested
);
746 pa_assert(s
->thread_info
.rewind_nbytes
== 0);
748 if (s
->thread_info
.state
== PA_SINK_SUSPENDED
) {
749 result
->memblock
= pa_memblock_ref(s
->silence
.memblock
);
750 result
->index
= s
->silence
.index
;
751 result
->length
= PA_MIN(s
->silence
.length
, length
);
756 length
= pa_frame_align(MIX_BUFFER_LENGTH
, &s
->sample_spec
);
758 block_size_max
= pa_mempool_block_size_max(s
->core
->mempool
);
759 if (length
> block_size_max
)
760 length
= pa_frame_align(block_size_max
, &s
->sample_spec
);
762 pa_assert(length
> 0);
764 n
= fill_mix_info(s
, &length
, info
, MAX_MIX_CHANNELS
);
768 *result
= s
->silence
;
769 pa_memblock_ref(result
->memblock
);
771 if (result
->length
> length
)
772 result
->length
= length
;
777 *result
= info
[0].chunk
;
778 pa_memblock_ref(result
->memblock
);
780 if (result
->length
> length
)
781 result
->length
= length
;
783 pa_sw_cvolume_multiply(&volume
, &s
->thread_info
.soft_volume
, &info
[0].volume
);
785 if (s
->thread_info
.soft_muted
|| !pa_cvolume_is_norm(&volume
)) {
786 pa_memchunk_make_writable(result
, 0);
787 if (s
->thread_info
.soft_muted
|| pa_cvolume_is_muted(&volume
))
788 pa_silence_memchunk(result
, &s
->sample_spec
);
790 pa_volume_memchunk(result
, &s
->sample_spec
, &volume
);
794 result
->memblock
= pa_memblock_new(s
->core
->mempool
, length
);
796 ptr
= pa_memblock_acquire(result
->memblock
);
797 result
->length
= pa_mix(info
, n
,
800 &s
->thread_info
.soft_volume
,
801 s
->thread_info
.soft_muted
);
802 pa_memblock_release(result
->memblock
);
807 inputs_drop(s
, info
, n
, result
);
812 /* Called from IO thread context */
813 void pa_sink_render_into(pa_sink
*s
, pa_memchunk
*target
) {
814 pa_mix_info info
[MAX_MIX_CHANNELS
];
816 size_t length
, block_size_max
;
818 pa_sink_assert_ref(s
);
819 pa_assert(PA_SINK_IS_LINKED(s
->thread_info
.state
));
821 pa_assert(target
->memblock
);
822 pa_assert(target
->length
> 0);
823 pa_assert(pa_frame_aligned(target
->length
, &s
->sample_spec
));
827 pa_assert(!s
->thread_info
.rewind_requested
);
828 pa_assert(s
->thread_info
.rewind_nbytes
== 0);
830 if (s
->thread_info
.state
== PA_SINK_SUSPENDED
) {
831 pa_silence_memchunk(target
, &s
->sample_spec
);
835 length
= target
->length
;
836 block_size_max
= pa_mempool_block_size_max(s
->core
->mempool
);
837 if (length
> block_size_max
)
838 length
= pa_frame_align(block_size_max
, &s
->sample_spec
);
840 pa_assert(length
> 0);
842 n
= fill_mix_info(s
, &length
, info
, MAX_MIX_CHANNELS
);
845 if (target
->length
> length
)
846 target
->length
= length
;
848 pa_silence_memchunk(target
, &s
->sample_spec
);
852 if (target
->length
> length
)
853 target
->length
= length
;
855 pa_sw_cvolume_multiply(&volume
, &s
->thread_info
.soft_volume
, &info
[0].volume
);
857 if (s
->thread_info
.soft_muted
|| pa_cvolume_is_muted(&volume
))
858 pa_silence_memchunk(target
, &s
->sample_spec
);
862 vchunk
= info
[0].chunk
;
863 pa_memblock_ref(vchunk
.memblock
);
865 if (vchunk
.length
> length
)
866 vchunk
.length
= length
;
868 if (!pa_cvolume_is_norm(&volume
)) {
869 pa_memchunk_make_writable(&vchunk
, 0);
870 pa_volume_memchunk(&vchunk
, &s
->sample_spec
, &volume
);
873 pa_memchunk_memcpy(target
, &vchunk
);
874 pa_memblock_unref(vchunk
.memblock
);
880 ptr
= pa_memblock_acquire(target
->memblock
);
882 target
->length
= pa_mix(info
, n
,
883 (uint8_t*) ptr
+ target
->index
, length
,
885 &s
->thread_info
.soft_volume
,
886 s
->thread_info
.soft_muted
);
888 pa_memblock_release(target
->memblock
);
891 inputs_drop(s
, info
, n
, target
);
896 /* Called from IO thread context */
897 void pa_sink_render_into_full(pa_sink
*s
, pa_memchunk
*target
) {
901 pa_sink_assert_ref(s
);
902 pa_assert(PA_SINK_IS_LINKED(s
->thread_info
.state
));
904 pa_assert(target
->memblock
);
905 pa_assert(target
->length
> 0);
906 pa_assert(pa_frame_aligned(target
->length
, &s
->sample_spec
));
910 pa_assert(!s
->thread_info
.rewind_requested
);
911 pa_assert(s
->thread_info
.rewind_nbytes
== 0);
920 pa_sink_render_into(s
, &chunk
);
929 /* Called from IO thread context */
930 void pa_sink_render_full(pa_sink
*s
, size_t length
, pa_memchunk
*result
) {
931 pa_sink_assert_ref(s
);
932 pa_assert(PA_SINK_IS_LINKED(s
->thread_info
.state
));
933 pa_assert(length
> 0);
934 pa_assert(pa_frame_aligned(length
, &s
->sample_spec
));
937 pa_assert(!s
->thread_info
.rewind_requested
);
938 pa_assert(s
->thread_info
.rewind_nbytes
== 0);
940 /*** This needs optimization ***/
943 result
->length
= length
;
944 result
->memblock
= pa_memblock_new(s
->core
->mempool
, length
);
946 pa_sink_render_into_full(s
, result
);
949 /* Called from main thread */
950 pa_usec_t
pa_sink_get_latency(pa_sink
*s
) {
953 pa_sink_assert_ref(s
);
954 pa_assert(PA_SINK_IS_LINKED(s
->state
));
956 /* The returned value is supposed to be in the time domain of the sound card! */
958 if (s
->state
== PA_SINK_SUSPENDED
)
961 if (!(s
->flags
& PA_SINK_LATENCY
))
964 pa_assert_se(pa_asyncmsgq_send(s
->asyncmsgq
, PA_MSGOBJECT(s
), PA_SINK_MESSAGE_GET_LATENCY
, &usec
, 0, NULL
) == 0);
969 /* Called from IO thread */
970 pa_usec_t
pa_sink_get_latency_within_thread(pa_sink
*s
) {
974 pa_sink_assert_ref(s
);
975 pa_assert(PA_SINK_IS_LINKED(s
->thread_info
.state
));
977 /* The returned value is supposed to be in the time domain of the sound card! */
979 if (s
->thread_info
.state
== PA_SINK_SUSPENDED
)
982 if (!(s
->flags
& PA_SINK_LATENCY
))
987 /* We probably should make this a proper vtable callback instead of going through process_msg() */
989 if (o
->process_msg(o
, PA_SINK_MESSAGE_GET_LATENCY
, &usec
, 0, NULL
) < 0)
995 static void compute_new_soft_volume(pa_sink_input
*i
, const pa_cvolume
*new_volume
) {
998 pa_sink_input_assert_ref(i
);
999 pa_assert(new_volume
->channels
== i
->sample_spec
.channels
);
1002 * This basically calculates:
1004 * i->relative_volume := i->virtual_volume / new_volume
1005 * i->soft_volume := i->relative_volume * i->volume_factor
1008 /* The new sink volume passed in here must already be remapped to
1009 * the sink input's channel map! */
1011 i
->soft_volume
.channels
= i
->sample_spec
.channels
;
1013 for (c
= 0; c
< i
->sample_spec
.channels
; c
++)
1015 if (new_volume
->values
[c
] <= PA_VOLUME_MUTED
)
1016 /* We leave i->relative_volume untouched */
1017 i
->soft_volume
.values
[c
] = PA_VOLUME_MUTED
;
1019 i
->relative_volume
[c
] =
1020 pa_sw_volume_to_linear(i
->virtual_volume
.values
[c
]) /
1021 pa_sw_volume_to_linear(new_volume
->values
[c
]);
1023 i
->soft_volume
.values
[c
] = pa_sw_volume_from_linear(
1024 i
->relative_volume
[c
] *
1025 pa_sw_volume_to_linear(i
->volume_factor
.values
[c
]));
1028 /* Hooks have the ability to play games with i->soft_volume */
1029 pa_hook_fire(&i
->core
->hooks
[PA_CORE_HOOK_SINK_INPUT_SET_VOLUME
], i
);
1031 /* We don't copy the soft_volume to the thread_info data
1032 * here. That must be done by the caller */
1035 /* Called from main thread */
1036 void pa_sink_update_flat_volume(pa_sink
*s
, pa_cvolume
*new_volume
) {
1040 pa_sink_assert_ref(s
);
1041 pa_assert(new_volume
);
1042 pa_assert(PA_SINK_IS_LINKED(s
->state
));
1043 pa_assert(s
->flags
& PA_SINK_FLAT_VOLUME
);
1045 /* This is called whenever a sink input volume changes or a sink
1046 * input is added/removed and we might need to fix up the sink
1047 * volume accordingly. Please note that we don't actually update
1048 * the sinks volume here, we only return how it needs to be
1049 * updated. The caller should then call pa_sink_set_volume().*/
1051 if (pa_idxset_isempty(s
->inputs
)) {
1052 /* In the special case that we have no sink input we leave the
1053 * volume unmodified. */
1054 *new_volume
= s
->reference_volume
;
1058 pa_cvolume_mute(new_volume
, s
->channel_map
.channels
);
1060 /* First let's determine the new maximum volume of all inputs
1061 * connected to this sink */
1062 for (i
= PA_SINK_INPUT(pa_idxset_first(s
->inputs
, &idx
)); i
; i
= PA_SINK_INPUT(pa_idxset_next(s
->inputs
, &idx
))) {
1064 pa_cvolume remapped_volume
;
1066 remapped_volume
= i
->virtual_volume
;
1067 pa_cvolume_remap(&remapped_volume
, &i
->channel_map
, &s
->channel_map
);
1069 for (c
= 0; c
< new_volume
->channels
; c
++)
1070 if (remapped_volume
.values
[c
] > new_volume
->values
[c
])
1071 new_volume
->values
[c
] = remapped_volume
.values
[c
];
1074 /* Then, let's update the soft volumes of all inputs connected
1076 for (i
= PA_SINK_INPUT(pa_idxset_first(s
->inputs
, &idx
)); i
; i
= PA_SINK_INPUT(pa_idxset_next(s
->inputs
, &idx
))) {
1077 pa_cvolume remapped_new_volume
;
1079 remapped_new_volume
= *new_volume
;
1080 pa_cvolume_remap(&remapped_new_volume
, &s
->channel_map
, &i
->channel_map
);
1081 compute_new_soft_volume(i
, &remapped_new_volume
);
1083 /* We don't copy soft_volume to the thread_info data here
1084 * (i.e. issue PA_SINK_INPUT_MESSAGE_SET_VOLUME) because we
1085 * want the update to be atomically with the sink volume
1086 * update, hence we do it within the pa_sink_set_volume() call
1091 /* Called from main thread */
1092 void pa_sink_propagate_flat_volume(pa_sink
*s
) {
1096 pa_sink_assert_ref(s
);
1097 pa_assert(PA_SINK_IS_LINKED(s
->state
));
1098 pa_assert(s
->flags
& PA_SINK_FLAT_VOLUME
);
1100 /* This is called whenever the sink volume changes that is not
1101 * caused by a sink input volume change. We need to fix up the
1102 * sink input volumes accordingly */
1104 for (i
= PA_SINK_INPUT(pa_idxset_first(s
->inputs
, &idx
)); i
; i
= PA_SINK_INPUT(pa_idxset_next(s
->inputs
, &idx
))) {
1105 pa_cvolume sink_volume
, new_virtual_volume
;
1108 /* This basically calculates i->virtual_volume := i->relative_volume * s->virtual_volume */
1110 sink_volume
= s
->virtual_volume
;
1111 pa_cvolume_remap(&sink_volume
, &s
->channel_map
, &i
->channel_map
);
1113 for (c
= 0; c
< i
->sample_spec
.channels
; c
++)
1114 new_virtual_volume
.values
[c
] = pa_sw_volume_from_linear(
1115 i
->relative_volume
[c
] *
1116 pa_sw_volume_to_linear(sink_volume
.values
[c
]));
1118 new_virtual_volume
.channels
= i
->sample_spec
.channels
;
1120 if (!pa_cvolume_equal(&new_virtual_volume
, &i
->virtual_volume
)) {
1121 i
->virtual_volume
= new_virtual_volume
;
1123 /* Hmm, the soft volume might no longer actually match
1124 * what has been chosen as new virtual volume here,
1125 * especially when the old volume was
1126 * PA_VOLUME_MUTED. Hence let's recalculate the soft
1128 compute_new_soft_volume(i
, &sink_volume
);
1130 /* The virtual volume changed, let's tell people so */
1131 pa_subscription_post(i
->core
, PA_SUBSCRIPTION_EVENT_SINK_INPUT
|PA_SUBSCRIPTION_EVENT_CHANGE
, i
->index
);
1135 /* If the soft_volume of any of the sink inputs got changed, let's
1136 * make sure the thread copies are synced up. */
1137 pa_assert_se(pa_asyncmsgq_send(s
->asyncmsgq
, PA_MSGOBJECT(s
), PA_SINK_MESSAGE_SYNC_VOLUMES
, NULL
, 0, NULL
) == 0);
1140 /* Called from main thread */
1141 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
) {
1142 pa_bool_t virtual_volume_changed
;
1144 pa_sink_assert_ref(s
);
1145 pa_assert(PA_SINK_IS_LINKED(s
->state
));
1147 pa_assert(pa_cvolume_valid(volume
));
1148 pa_assert(pa_cvolume_compatible(volume
, &s
->sample_spec
));
1150 virtual_volume_changed
= !pa_cvolume_equal(volume
, &s
->virtual_volume
);
1151 s
->virtual_volume
= *volume
;
1153 if (become_reference
)
1154 s
->reference_volume
= s
->virtual_volume
;
1156 /* Propagate this volume change back to the inputs */
1157 if (virtual_volume_changed
)
1158 if (propagate
&& (s
->flags
& PA_SINK_FLAT_VOLUME
))
1159 pa_sink_propagate_flat_volume(s
);
1161 if (s
->set_volume
) {
1162 /* If we have a function set_volume(), then we do not apply a
1163 * soft volume by default. However, set_volume() is free to
1164 * apply one to s->soft_volume */
1166 pa_cvolume_reset(&s
->soft_volume
, s
->sample_spec
.channels
);
1170 /* If we have no function set_volume(), then the soft volume
1171 * becomes the virtual volume */
1172 s
->soft_volume
= s
->virtual_volume
;
1174 /* This tells the sink that soft and/or virtual volume changed */
1176 pa_assert_se(pa_asyncmsgq_send(s
->asyncmsgq
, PA_MSGOBJECT(s
), PA_SINK_MESSAGE_SET_VOLUME
, NULL
, 0, NULL
) == 0);
1178 if (virtual_volume_changed
)
1179 pa_subscription_post(s
->core
, PA_SUBSCRIPTION_EVENT_SINK
|PA_SUBSCRIPTION_EVENT_CHANGE
, s
->index
);
1182 /* Called from main thread. Only to be called by sink implementor */
1183 void pa_sink_set_soft_volume(pa_sink
*s
, const pa_cvolume
*volume
) {
1184 pa_sink_assert_ref(s
);
1187 s
->soft_volume
= *volume
;
1189 if (PA_SINK_IS_LINKED(s
->state
))
1190 pa_assert_se(pa_asyncmsgq_send(s
->asyncmsgq
, PA_MSGOBJECT(s
), PA_SINK_MESSAGE_SET_VOLUME
, NULL
, 0, NULL
) == 0);
1192 s
->thread_info
.soft_volume
= *volume
;
1195 /* Called from main thread */
1196 const pa_cvolume
*pa_sink_get_volume(pa_sink
*s
, pa_bool_t force_refresh
, pa_bool_t reference
) {
1197 pa_sink_assert_ref(s
);
1199 if (s
->refresh_volume
|| force_refresh
) {
1200 struct pa_cvolume old_virtual_volume
= s
->virtual_volume
;
1205 pa_assert_se(pa_asyncmsgq_send(s
->asyncmsgq
, PA_MSGOBJECT(s
), PA_SINK_MESSAGE_GET_VOLUME
, NULL
, 0, NULL
) == 0);
1207 if (!pa_cvolume_equal(&old_virtual_volume
, &s
->virtual_volume
)) {
1209 s
->reference_volume
= s
->virtual_volume
;
1211 if (s
->flags
& PA_SINK_FLAT_VOLUME
)
1212 pa_sink_propagate_flat_volume(s
);
1214 pa_subscription_post(s
->core
, PA_SUBSCRIPTION_EVENT_SINK
|PA_SUBSCRIPTION_EVENT_CHANGE
, s
->index
);
1218 return reference
? &s
->reference_volume
: &s
->virtual_volume
;
1221 /* Called from main thread */
1222 void pa_sink_volume_changed(pa_sink
*s
, const pa_cvolume
*new_volume
) {
1223 pa_sink_assert_ref(s
);
1225 /* The sink implementor may call this if the volume changed to make sure everyone is notified */
1227 if (pa_cvolume_equal(&s
->virtual_volume
, new_volume
))
1230 s
->reference_volume
= s
->virtual_volume
= *new_volume
;
1232 if (s
->flags
& PA_SINK_FLAT_VOLUME
)
1233 pa_sink_propagate_flat_volume(s
);
1235 pa_subscription_post(s
->core
, PA_SUBSCRIPTION_EVENT_SINK
|PA_SUBSCRIPTION_EVENT_CHANGE
, s
->index
);
1238 /* Called from main thread */
1239 void pa_sink_set_mute(pa_sink
*s
, pa_bool_t mute
) {
1240 pa_bool_t old_muted
;
1242 pa_sink_assert_ref(s
);
1243 pa_assert(PA_SINK_IS_LINKED(s
->state
));
1245 old_muted
= s
->muted
;
1251 pa_assert_se(pa_asyncmsgq_send(s
->asyncmsgq
, PA_MSGOBJECT(s
), PA_SINK_MESSAGE_SET_MUTE
, NULL
, 0, NULL
) == 0);
1253 if (old_muted
!= s
->muted
)
1254 pa_subscription_post(s
->core
, PA_SUBSCRIPTION_EVENT_SINK
|PA_SUBSCRIPTION_EVENT_CHANGE
, s
->index
);
1257 /* Called from main thread */
1258 pa_bool_t
pa_sink_get_mute(pa_sink
*s
, pa_bool_t force_refresh
) {
1260 pa_sink_assert_ref(s
);
1262 if (s
->refresh_muted
|| force_refresh
) {
1263 pa_bool_t old_muted
= s
->muted
;
1268 pa_assert_se(pa_asyncmsgq_send(s
->asyncmsgq
, PA_MSGOBJECT(s
), PA_SINK_MESSAGE_GET_MUTE
, NULL
, 0, NULL
) == 0);
1270 if (old_muted
!= s
->muted
)
1271 pa_subscription_post(s
->core
, PA_SUBSCRIPTION_EVENT_SINK
|PA_SUBSCRIPTION_EVENT_CHANGE
, s
->index
);
1277 /* Called from main thread */
1278 void pa_sink_mute_changed(pa_sink
*s
, pa_bool_t new_muted
) {
1279 pa_sink_assert_ref(s
);
1281 /* The sink implementor may call this if the volume changed to make sure everyone is notified */
1283 if (s
->muted
== new_muted
)
1286 s
->muted
= new_muted
;
1287 pa_subscription_post(s
->core
, PA_SUBSCRIPTION_EVENT_SINK
|PA_SUBSCRIPTION_EVENT_CHANGE
, s
->index
);
1290 /* Called from main thread */
1291 pa_bool_t
pa_sink_update_proplist(pa_sink
*s
, pa_update_mode_t mode
, pa_proplist
*p
) {
1292 pa_sink_assert_ref(s
);
1295 pa_proplist_update(s
->proplist
, mode
, p
);
1297 if (PA_SINK_IS_LINKED(s
->state
)) {
1298 pa_hook_fire(&s
->core
->hooks
[PA_CORE_HOOK_SINK_PROPLIST_CHANGED
], s
);
1299 pa_subscription_post(s
->core
, PA_SUBSCRIPTION_EVENT_SINK
|PA_SUBSCRIPTION_EVENT_CHANGE
, s
->index
);
1305 /* Called from main thread */
1306 void pa_sink_set_description(pa_sink
*s
, const char *description
) {
1308 pa_sink_assert_ref(s
);
1310 if (!description
&& !pa_proplist_contains(s
->proplist
, PA_PROP_DEVICE_DESCRIPTION
))
1313 old
= pa_proplist_gets(s
->proplist
, PA_PROP_DEVICE_DESCRIPTION
);
1315 if (old
&& description
&& !strcmp(old
, description
))
1319 pa_proplist_sets(s
->proplist
, PA_PROP_DEVICE_DESCRIPTION
, description
);
1321 pa_proplist_unset(s
->proplist
, PA_PROP_DEVICE_DESCRIPTION
);
1323 if (s
->monitor_source
) {
1326 n
= pa_sprintf_malloc("Monitor Source of %s", description
? description
: s
->name
);
1327 pa_source_set_description(s
->monitor_source
, n
);
1331 if (PA_SINK_IS_LINKED(s
->state
)) {
1332 pa_subscription_post(s
->core
, PA_SUBSCRIPTION_EVENT_SINK
|PA_SUBSCRIPTION_EVENT_CHANGE
, s
->index
);
1333 pa_hook_fire(&s
->core
->hooks
[PA_CORE_HOOK_SINK_PROPLIST_CHANGED
], s
);
1337 /* Called from main thread */
1338 unsigned pa_sink_linked_by(pa_sink
*s
) {
1341 pa_sink_assert_ref(s
);
1342 pa_assert(PA_SINK_IS_LINKED(s
->state
));
1344 ret
= pa_idxset_size(s
->inputs
);
1346 /* We add in the number of streams connected to us here. Please
1347 * note the asymmmetry to pa_sink_used_by()! */
1349 if (s
->monitor_source
)
1350 ret
+= pa_source_linked_by(s
->monitor_source
);
1355 /* Called from main thread */
1356 unsigned pa_sink_used_by(pa_sink
*s
) {
1359 pa_sink_assert_ref(s
);
1360 pa_assert(PA_SINK_IS_LINKED(s
->state
));
1362 ret
= pa_idxset_size(s
->inputs
);
1363 pa_assert(ret
>= s
->n_corked
);
1365 /* Streams connected to our monitor source do not matter for
1366 * pa_sink_used_by()!.*/
1368 return ret
- s
->n_corked
;
1371 /* Called from main thread */
1372 unsigned pa_sink_check_suspend(pa_sink
*s
) {
1377 pa_sink_assert_ref(s
);
1379 if (!PA_SINK_IS_LINKED(s
->state
))
1384 for (i
= PA_SINK_INPUT(pa_idxset_first(s
->inputs
, &idx
)); i
; i
= PA_SINK_INPUT(pa_idxset_next(s
->inputs
, &idx
))) {
1385 pa_sink_input_state_t st
;
1387 st
= pa_sink_input_get_state(i
);
1388 pa_assert(PA_SINK_INPUT_IS_LINKED(st
));
1390 if (st
== PA_SINK_INPUT_CORKED
)
1393 if (i
->flags
& PA_SINK_INPUT_DONT_INHIBIT_AUTO_SUSPEND
)
1399 if (s
->monitor_source
)
1400 ret
+= pa_source_check_suspend(s
->monitor_source
);
1405 /* Called from the IO thread */
1406 static void sync_input_volumes_within_thread(pa_sink
*s
) {
1410 pa_sink_assert_ref(s
);
1412 while ((i
= PA_SINK_INPUT(pa_hashmap_iterate(s
->thread_info
.inputs
, &state
, NULL
)))) {
1413 if (pa_cvolume_equal(&i
->thread_info
.soft_volume
, &i
->soft_volume
))
1416 i
->thread_info
.soft_volume
= i
->soft_volume
;
1417 pa_sink_input_request_rewind(i
, 0, TRUE
, FALSE
, FALSE
);
1421 /* Called from IO thread, except when it is not */
1422 int pa_sink_process_msg(pa_msgobject
*o
, int code
, void *userdata
, int64_t offset
, pa_memchunk
*chunk
) {
1423 pa_sink
*s
= PA_SINK(o
);
1424 pa_sink_assert_ref(s
);
1426 switch ((pa_sink_message_t
) code
) {
1428 case PA_SINK_MESSAGE_ADD_INPUT
: {
1429 pa_sink_input
*i
= PA_SINK_INPUT(userdata
);
1431 /* If you change anything here, make sure to change the
1432 * sink input handling a few lines down at
1433 * PA_SINK_MESSAGE_FINISH_MOVE, too. */
1435 pa_hashmap_put(s
->thread_info
.inputs
, PA_UINT32_TO_PTR(i
->index
), pa_sink_input_ref(i
));
1437 /* Since the caller sleeps in pa_sink_input_put(), we can
1438 * safely access data outside of thread_info even though
1441 if ((i
->thread_info
.sync_prev
= i
->sync_prev
)) {
1442 pa_assert(i
->sink
== i
->thread_info
.sync_prev
->sink
);
1443 pa_assert(i
->sync_prev
->sync_next
== i
);
1444 i
->thread_info
.sync_prev
->thread_info
.sync_next
= i
;
1447 if ((i
->thread_info
.sync_next
= i
->sync_next
)) {
1448 pa_assert(i
->sink
== i
->thread_info
.sync_next
->sink
);
1449 pa_assert(i
->sync_next
->sync_prev
== i
);
1450 i
->thread_info
.sync_next
->thread_info
.sync_prev
= i
;
1453 pa_assert(!i
->thread_info
.attached
);
1454 i
->thread_info
.attached
= TRUE
;
1459 pa_sink_input_set_state_within_thread(i
, i
->state
);
1461 /* The requested latency of the sink input needs to be
1462 * fixed up and then configured on the sink */
1464 if (i
->thread_info
.requested_sink_latency
!= (pa_usec_t
) -1)
1465 pa_sink_input_set_requested_latency_within_thread(i
, i
->thread_info
.requested_sink_latency
);
1467 pa_sink_input_update_max_rewind(i
, s
->thread_info
.max_rewind
);
1468 pa_sink_input_update_max_request(i
, s
->thread_info
.max_request
);
1470 /* We don't rewind here automatically. This is left to the
1471 * sink input implementor because some sink inputs need a
1472 * slow start, i.e. need some time to buffer client
1473 * samples before beginning streaming. */
1475 /* In flat volume mode we need to update the volume as
1477 return o
->process_msg(o
, PA_SINK_MESSAGE_SET_VOLUME
, NULL
, 0, NULL
);
1480 case PA_SINK_MESSAGE_REMOVE_INPUT
: {
1481 pa_sink_input
*i
= PA_SINK_INPUT(userdata
);
1483 /* If you change anything here, make sure to change the
1484 * sink input handling a few lines down at
1485 * PA_SINK_MESSAGE_PREPAPRE_MOVE, too. */
1490 pa_sink_input_set_state_within_thread(i
, i
->state
);
1492 pa_assert(i
->thread_info
.attached
);
1493 i
->thread_info
.attached
= FALSE
;
1495 /* Since the caller sleeps in pa_sink_input_unlink(),
1496 * we can safely access data outside of thread_info even
1497 * though it is mutable */
1499 pa_assert(!i
->sync_prev
);
1500 pa_assert(!i
->sync_next
);
1502 if (i
->thread_info
.sync_prev
) {
1503 i
->thread_info
.sync_prev
->thread_info
.sync_next
= i
->thread_info
.sync_prev
->sync_next
;
1504 i
->thread_info
.sync_prev
= NULL
;
1507 if (i
->thread_info
.sync_next
) {
1508 i
->thread_info
.sync_next
->thread_info
.sync_prev
= i
->thread_info
.sync_next
->sync_prev
;
1509 i
->thread_info
.sync_next
= NULL
;
1512 if (pa_hashmap_remove(s
->thread_info
.inputs
, PA_UINT32_TO_PTR(i
->index
)))
1513 pa_sink_input_unref(i
);
1515 pa_sink_invalidate_requested_latency(s
);
1516 pa_sink_request_rewind(s
, (size_t) -1);
1518 /* In flat volume mode we need to update the volume as
1520 return o
->process_msg(o
, PA_SINK_MESSAGE_SET_VOLUME
, NULL
, 0, NULL
);
1523 case PA_SINK_MESSAGE_START_MOVE
: {
1524 pa_sink_input
*i
= PA_SINK_INPUT(userdata
);
1526 /* We don't support moving synchronized streams. */
1527 pa_assert(!i
->sync_prev
);
1528 pa_assert(!i
->sync_next
);
1529 pa_assert(!i
->thread_info
.sync_next
);
1530 pa_assert(!i
->thread_info
.sync_prev
);
1532 if (i
->thread_info
.state
!= PA_SINK_INPUT_CORKED
) {
1534 size_t sink_nbytes
, total_nbytes
;
1536 /* Get the latency of the sink */
1537 if (!(s
->flags
& PA_SINK_LATENCY
) ||
1538 PA_MSGOBJECT(s
)->process_msg(PA_MSGOBJECT(s
), PA_SINK_MESSAGE_GET_LATENCY
, &usec
, 0, NULL
) < 0)
1541 sink_nbytes
= pa_usec_to_bytes(usec
, &s
->sample_spec
);
1542 total_nbytes
= sink_nbytes
+ pa_memblockq_get_length(i
->thread_info
.render_memblockq
);
1544 if (total_nbytes
> 0) {
1545 i
->thread_info
.rewrite_nbytes
= i
->thread_info
.resampler
? pa_resampler_request(i
->thread_info
.resampler
, total_nbytes
) : total_nbytes
;
1546 i
->thread_info
.rewrite_flush
= TRUE
;
1547 pa_sink_input_process_rewind(i
, sink_nbytes
);
1554 pa_assert(i
->thread_info
.attached
);
1555 i
->thread_info
.attached
= FALSE
;
1557 /* Let's remove the sink input ...*/
1558 if (pa_hashmap_remove(s
->thread_info
.inputs
, PA_UINT32_TO_PTR(i
->index
)))
1559 pa_sink_input_unref(i
);
1561 pa_sink_invalidate_requested_latency(s
);
1563 pa_log_debug("Requesting rewind due to started move");
1564 pa_sink_request_rewind(s
, (size_t) -1);
1566 /* In flat volume mode we need to update the volume as
1568 return o
->process_msg(o
, PA_SINK_MESSAGE_SET_VOLUME
, NULL
, 0, NULL
);
1571 case PA_SINK_MESSAGE_FINISH_MOVE
: {
1572 pa_sink_input
*i
= PA_SINK_INPUT(userdata
);
1574 /* We don't support moving synchronized streams. */
1575 pa_assert(!i
->sync_prev
);
1576 pa_assert(!i
->sync_next
);
1577 pa_assert(!i
->thread_info
.sync_next
);
1578 pa_assert(!i
->thread_info
.sync_prev
);
1580 pa_hashmap_put(s
->thread_info
.inputs
, PA_UINT32_TO_PTR(i
->index
), pa_sink_input_ref(i
));
1582 pa_assert(!i
->thread_info
.attached
);
1583 i
->thread_info
.attached
= TRUE
;
1588 if (i
->thread_info
.requested_sink_latency
!= (pa_usec_t
) -1)
1589 pa_sink_input_set_requested_latency_within_thread(i
, i
->thread_info
.requested_sink_latency
);
1591 pa_sink_input_update_max_rewind(i
, s
->thread_info
.max_rewind
);
1592 pa_sink_input_update_max_request(i
, s
->thread_info
.max_request
);
1594 if (i
->thread_info
.state
!= PA_SINK_INPUT_CORKED
) {
1598 /* Get the latency of the sink */
1599 if (!(s
->flags
& PA_SINK_LATENCY
) ||
1600 PA_MSGOBJECT(s
)->process_msg(PA_MSGOBJECT(s
), PA_SINK_MESSAGE_GET_LATENCY
, &usec
, 0, NULL
) < 0)
1603 nbytes
= pa_usec_to_bytes(usec
, &s
->sample_spec
);
1606 pa_sink_input_drop(i
, nbytes
);
1608 pa_log_debug("Requesting rewind due to finished move");
1609 pa_sink_request_rewind(s
, nbytes
);
1612 /* In flat volume mode we need to update the volume as
1614 return o
->process_msg(o
, PA_SINK_MESSAGE_SET_VOLUME
, NULL
, 0, NULL
);
1617 case PA_SINK_MESSAGE_SET_VOLUME
:
1619 if (!pa_cvolume_equal(&s
->thread_info
.soft_volume
, &s
->soft_volume
)) {
1620 s
->thread_info
.soft_volume
= s
->soft_volume
;
1621 pa_sink_request_rewind(s
, (size_t) -1);
1624 if (!(s
->flags
& PA_SINK_FLAT_VOLUME
))
1627 /* Fall through ... */
1629 case PA_SINK_MESSAGE_SYNC_VOLUMES
:
1630 sync_input_volumes_within_thread(s
);
1633 case PA_SINK_MESSAGE_GET_VOLUME
:
1636 case PA_SINK_MESSAGE_SET_MUTE
:
1638 if (s
->thread_info
.soft_muted
!= s
->muted
) {
1639 s
->thread_info
.soft_muted
= s
->muted
;
1640 pa_sink_request_rewind(s
, (size_t) -1);
1645 case PA_SINK_MESSAGE_GET_MUTE
:
1648 case PA_SINK_MESSAGE_SET_STATE
: {
1650 pa_bool_t suspend_change
=
1651 (s
->thread_info
.state
== PA_SINK_SUSPENDED
&& PA_SINK_IS_OPENED(PA_PTR_TO_UINT(userdata
))) ||
1652 (PA_SINK_IS_OPENED(s
->thread_info
.state
) && PA_PTR_TO_UINT(userdata
) == PA_SINK_SUSPENDED
);
1654 s
->thread_info
.state
= PA_PTR_TO_UINT(userdata
);
1656 if (s
->thread_info
.state
== PA_SINK_SUSPENDED
) {
1657 s
->thread_info
.rewind_nbytes
= 0;
1658 s
->thread_info
.rewind_requested
= FALSE
;
1661 if (suspend_change
) {
1665 while ((i
= pa_hashmap_iterate(s
->thread_info
.inputs
, &state
, NULL
)))
1666 if (i
->suspend_within_thread
)
1667 i
->suspend_within_thread(i
, s
->thread_info
.state
== PA_SINK_SUSPENDED
);
1673 case PA_SINK_MESSAGE_DETACH
:
1675 /* Detach all streams */
1676 pa_sink_detach_within_thread(s
);
1679 case PA_SINK_MESSAGE_ATTACH
:
1681 /* Reattach all streams */
1682 pa_sink_attach_within_thread(s
);
1685 case PA_SINK_MESSAGE_GET_REQUESTED_LATENCY
: {
1687 pa_usec_t
*usec
= userdata
;
1688 *usec
= pa_sink_get_requested_latency_within_thread(s
);
1690 if (*usec
== (pa_usec_t
) -1)
1691 *usec
= s
->thread_info
.max_latency
;
1696 case PA_SINK_MESSAGE_SET_LATENCY_RANGE
: {
1697 pa_usec_t
*r
= userdata
;
1699 pa_sink_set_latency_range_within_thread(s
, r
[0], r
[1]);
1704 case PA_SINK_MESSAGE_GET_LATENCY_RANGE
: {
1705 pa_usec_t
*r
= userdata
;
1707 r
[0] = s
->thread_info
.min_latency
;
1708 r
[1] = s
->thread_info
.max_latency
;
1713 case PA_SINK_MESSAGE_GET_MAX_REWIND
:
1715 *((size_t*) userdata
) = s
->thread_info
.max_rewind
;
1718 case PA_SINK_MESSAGE_GET_MAX_REQUEST
:
1720 *((size_t*) userdata
) = s
->thread_info
.max_request
;
1723 case PA_SINK_MESSAGE_SET_MAX_REWIND
:
1725 pa_sink_set_max_rewind_within_thread(s
, (size_t) offset
);
1728 case PA_SINK_MESSAGE_SET_MAX_REQUEST
:
1730 pa_sink_set_max_request_within_thread(s
, (size_t) offset
);
1733 case PA_SINK_MESSAGE_GET_LATENCY
:
1734 case PA_SINK_MESSAGE_MAX
:
1741 /* Called from main thread */
1742 int pa_sink_suspend_all(pa_core
*c
, pa_bool_t suspend
) {
1747 pa_core_assert_ref(c
);
1749 for (sink
= PA_SINK(pa_idxset_first(c
->sinks
, &idx
)); sink
; sink
= PA_SINK(pa_idxset_next(c
->sinks
, &idx
))) {
1752 if ((r
= pa_sink_suspend(sink
, suspend
)) < 0)
1759 /* Called from main thread */
1760 void pa_sink_detach(pa_sink
*s
) {
1761 pa_sink_assert_ref(s
);
1762 pa_assert(PA_SINK_IS_LINKED(s
->state
));
1764 pa_assert_se(pa_asyncmsgq_send(s
->asyncmsgq
, PA_MSGOBJECT(s
), PA_SINK_MESSAGE_DETACH
, NULL
, 0, NULL
) == 0);
1767 /* Called from main thread */
1768 void pa_sink_attach(pa_sink
*s
) {
1769 pa_sink_assert_ref(s
);
1770 pa_assert(PA_SINK_IS_LINKED(s
->state
));
1772 pa_assert_se(pa_asyncmsgq_send(s
->asyncmsgq
, PA_MSGOBJECT(s
), PA_SINK_MESSAGE_ATTACH
, NULL
, 0, NULL
) == 0);
1775 /* Called from IO thread */
1776 void pa_sink_detach_within_thread(pa_sink
*s
) {
1780 pa_sink_assert_ref(s
);
1781 pa_assert(PA_SINK_IS_LINKED(s
->thread_info
.state
));
1783 while ((i
= pa_hashmap_iterate(s
->thread_info
.inputs
, &state
, NULL
)))
1787 if (s
->monitor_source
)
1788 pa_source_detach_within_thread(s
->monitor_source
);
1791 /* Called from IO thread */
1792 void pa_sink_attach_within_thread(pa_sink
*s
) {
1796 pa_sink_assert_ref(s
);
1797 pa_assert(PA_SINK_IS_LINKED(s
->thread_info
.state
));
1799 while ((i
= pa_hashmap_iterate(s
->thread_info
.inputs
, &state
, NULL
)))
1803 if (s
->monitor_source
)
1804 pa_source_attach_within_thread(s
->monitor_source
);
1807 /* Called from IO thread */
1808 void pa_sink_request_rewind(pa_sink
*s
, size_t nbytes
) {
1809 pa_sink_assert_ref(s
);
1810 pa_assert(PA_SINK_IS_LINKED(s
->thread_info
.state
));
1812 if (s
->thread_info
.state
== PA_SINK_SUSPENDED
)
1815 if (nbytes
== (size_t) -1)
1816 nbytes
= s
->thread_info
.max_rewind
;
1818 nbytes
= PA_MIN(nbytes
, s
->thread_info
.max_rewind
);
1820 if (s
->thread_info
.rewind_requested
&&
1821 nbytes
<= s
->thread_info
.rewind_nbytes
)
1824 s
->thread_info
.rewind_nbytes
= nbytes
;
1825 s
->thread_info
.rewind_requested
= TRUE
;
1827 if (s
->request_rewind
)
1828 s
->request_rewind(s
);
1831 /* Called from IO thread */
1832 pa_usec_t
pa_sink_get_requested_latency_within_thread(pa_sink
*s
) {
1833 pa_usec_t result
= (pa_usec_t
) -1;
1836 pa_usec_t monitor_latency
;
1838 pa_sink_assert_ref(s
);
1840 if (!(s
->flags
& PA_SINK_DYNAMIC_LATENCY
))
1841 return PA_CLAMP(s
->fixed_latency
, s
->thread_info
.min_latency
, s
->thread_info
.max_latency
);
1843 if (s
->thread_info
.requested_latency_valid
)
1844 return s
->thread_info
.requested_latency
;
1846 while ((i
= pa_hashmap_iterate(s
->thread_info
.inputs
, &state
, NULL
)))
1848 if (i
->thread_info
.requested_sink_latency
!= (pa_usec_t
) -1 &&
1849 (result
== (pa_usec_t
) -1 || result
> i
->thread_info
.requested_sink_latency
))
1850 result
= i
->thread_info
.requested_sink_latency
;
1852 monitor_latency
= pa_source_get_requested_latency_within_thread(s
->monitor_source
);
1854 if (monitor_latency
!= (pa_usec_t
) -1 &&
1855 (result
== (pa_usec_t
) -1 || result
> monitor_latency
))
1856 result
= monitor_latency
;
1858 if (result
!= (pa_usec_t
) -1)
1859 result
= PA_CLAMP(result
, s
->thread_info
.min_latency
, s
->thread_info
.max_latency
);
1861 if (PA_SINK_IS_LINKED(s
->thread_info
.state
)) {
1862 /* Only cache if properly initialized */
1863 s
->thread_info
.requested_latency
= result
;
1864 s
->thread_info
.requested_latency_valid
= TRUE
;
1870 /* Called from main thread */
1871 pa_usec_t
pa_sink_get_requested_latency(pa_sink
*s
) {
1874 pa_sink_assert_ref(s
);
1875 pa_assert(PA_SINK_IS_LINKED(s
->state
));
1877 if (s
->state
== PA_SINK_SUSPENDED
)
1880 pa_assert_se(pa_asyncmsgq_send(s
->asyncmsgq
, PA_MSGOBJECT(s
), PA_SINK_MESSAGE_GET_REQUESTED_LATENCY
, &usec
, 0, NULL
) == 0);
1884 /* Called from IO as well as the main thread -- the latter only before the IO thread started up */
1885 void pa_sink_set_max_rewind_within_thread(pa_sink
*s
, size_t max_rewind
) {
1889 pa_sink_assert_ref(s
);
1891 if (max_rewind
== s
->thread_info
.max_rewind
)
1894 s
->thread_info
.max_rewind
= max_rewind
;
1896 if (PA_SINK_IS_LINKED(s
->thread_info
.state
)) {
1897 while ((i
= pa_hashmap_iterate(s
->thread_info
.inputs
, &state
, NULL
)))
1898 pa_sink_input_update_max_rewind(i
, s
->thread_info
.max_rewind
);
1901 if (s
->monitor_source
)
1902 pa_source_set_max_rewind_within_thread(s
->monitor_source
, s
->thread_info
.max_rewind
);
1905 /* Called from main thread */
1906 void pa_sink_set_max_rewind(pa_sink
*s
, size_t max_rewind
) {
1907 pa_sink_assert_ref(s
);
1909 if (PA_SINK_IS_LINKED(s
->state
))
1910 pa_assert_se(pa_asyncmsgq_send(s
->asyncmsgq
, PA_MSGOBJECT(s
), PA_SINK_MESSAGE_SET_MAX_REWIND
, NULL
, max_rewind
, NULL
) == 0);
1912 pa_sink_set_max_rewind_within_thread(s
, max_rewind
);
1915 /* Called from IO as well as the main thread -- the latter only before the IO thread started up */
1916 void pa_sink_set_max_request_within_thread(pa_sink
*s
, size_t max_request
) {
1919 pa_sink_assert_ref(s
);
1921 if (max_request
== s
->thread_info
.max_request
)
1924 s
->thread_info
.max_request
= max_request
;
1926 if (PA_SINK_IS_LINKED(s
->thread_info
.state
)) {
1929 while ((i
= pa_hashmap_iterate(s
->thread_info
.inputs
, &state
, NULL
)))
1930 pa_sink_input_update_max_request(i
, s
->thread_info
.max_request
);
1934 /* Called from main thread */
1935 void pa_sink_set_max_request(pa_sink
*s
, size_t max_request
) {
1936 pa_sink_assert_ref(s
);
1938 if (PA_SINK_IS_LINKED(s
->state
))
1939 pa_assert_se(pa_asyncmsgq_send(s
->asyncmsgq
, PA_MSGOBJECT(s
), PA_SINK_MESSAGE_SET_MAX_REQUEST
, NULL
, max_request
, NULL
) == 0);
1941 pa_sink_set_max_request_within_thread(s
, max_request
);
1944 /* Called from IO thread */
1945 void pa_sink_invalidate_requested_latency(pa_sink
*s
) {
1949 pa_sink_assert_ref(s
);
1951 if (!(s
->flags
& PA_SINK_DYNAMIC_LATENCY
))
1954 s
->thread_info
.requested_latency_valid
= FALSE
;
1956 if (PA_SINK_IS_LINKED(s
->thread_info
.state
)) {
1958 if (s
->update_requested_latency
)
1959 s
->update_requested_latency(s
);
1961 while ((i
= pa_hashmap_iterate(s
->thread_info
.inputs
, &state
, NULL
)))
1962 if (i
->update_sink_requested_latency
)
1963 i
->update_sink_requested_latency(i
);
1967 /* Called from main thread */
1968 void pa_sink_set_latency_range(pa_sink
*s
, pa_usec_t min_latency
, pa_usec_t max_latency
) {
1969 pa_sink_assert_ref(s
);
1971 /* min_latency == 0: no limit
1972 * min_latency anything else: specified limit
1974 * Similar for max_latency */
1976 if (min_latency
< ABSOLUTE_MIN_LATENCY
)
1977 min_latency
= ABSOLUTE_MIN_LATENCY
;
1979 if (max_latency
<= 0 ||
1980 max_latency
> ABSOLUTE_MAX_LATENCY
)
1981 max_latency
= ABSOLUTE_MAX_LATENCY
;
1983 pa_assert(min_latency
<= max_latency
);
1985 /* Hmm, let's see if someone forgot to set PA_SINK_DYNAMIC_LATENCY here... */
1986 pa_assert((min_latency
== ABSOLUTE_MIN_LATENCY
&&
1987 max_latency
== ABSOLUTE_MAX_LATENCY
) ||
1988 (s
->flags
& PA_SINK_DYNAMIC_LATENCY
));
1990 if (PA_SINK_IS_LINKED(s
->state
)) {
1996 pa_assert_se(pa_asyncmsgq_send(s
->asyncmsgq
, PA_MSGOBJECT(s
), PA_SINK_MESSAGE_SET_LATENCY_RANGE
, r
, 0, NULL
) == 0);
1998 pa_sink_set_latency_range_within_thread(s
, min_latency
, max_latency
);
2001 /* Called from main thread */
2002 void pa_sink_get_latency_range(pa_sink
*s
, pa_usec_t
*min_latency
, pa_usec_t
*max_latency
) {
2003 pa_sink_assert_ref(s
);
2004 pa_assert(min_latency
);
2005 pa_assert(max_latency
);
2007 if (PA_SINK_IS_LINKED(s
->state
)) {
2008 pa_usec_t r
[2] = { 0, 0 };
2010 pa_assert_se(pa_asyncmsgq_send(s
->asyncmsgq
, PA_MSGOBJECT(s
), PA_SINK_MESSAGE_GET_LATENCY_RANGE
, r
, 0, NULL
) == 0);
2012 *min_latency
= r
[0];
2013 *max_latency
= r
[1];
2015 *min_latency
= s
->thread_info
.min_latency
;
2016 *max_latency
= s
->thread_info
.max_latency
;
2020 /* Called from IO thread */
2021 void pa_sink_set_latency_range_within_thread(pa_sink
*s
, pa_usec_t min_latency
, pa_usec_t max_latency
) {
2024 pa_sink_assert_ref(s
);
2026 pa_assert(min_latency
>= ABSOLUTE_MIN_LATENCY
);
2027 pa_assert(max_latency
<= ABSOLUTE_MAX_LATENCY
);
2028 pa_assert(min_latency
<= max_latency
);
2030 /* Hmm, let's see if someone forgot to set PA_SINK_DYNAMIC_LATENCY here... */
2031 pa_assert((min_latency
== ABSOLUTE_MIN_LATENCY
&&
2032 max_latency
== ABSOLUTE_MAX_LATENCY
) ||
2033 (s
->flags
& PA_SINK_DYNAMIC_LATENCY
));
2035 s
->thread_info
.min_latency
= min_latency
;
2036 s
->thread_info
.max_latency
= max_latency
;
2038 if (PA_SINK_IS_LINKED(s
->thread_info
.state
)) {
2041 while ((i
= pa_hashmap_iterate(s
->thread_info
.inputs
, &state
, NULL
)))
2042 if (i
->update_sink_latency_range
)
2043 i
->update_sink_latency_range(i
);
2046 pa_sink_invalidate_requested_latency(s
);
2048 pa_source_set_latency_range_within_thread(s
->monitor_source
, min_latency
, max_latency
);
2051 /* Called from main thread, before the sink is put */
2052 void pa_sink_set_fixed_latency(pa_sink
*s
, pa_usec_t latency
) {
2053 pa_sink_assert_ref(s
);
2055 pa_assert(pa_sink_get_state(s
) == PA_SINK_INIT
);
2057 if (latency
< ABSOLUTE_MIN_LATENCY
)
2058 latency
= ABSOLUTE_MIN_LATENCY
;
2060 if (latency
> ABSOLUTE_MAX_LATENCY
)
2061 latency
= ABSOLUTE_MAX_LATENCY
;
2063 s
->fixed_latency
= latency
;
2064 pa_source_set_fixed_latency(s
->monitor_source
, latency
);
2067 /* Called from main context */
2068 size_t pa_sink_get_max_rewind(pa_sink
*s
) {
2070 pa_sink_assert_ref(s
);
2072 if (!PA_SINK_IS_LINKED(s
->state
))
2073 return s
->thread_info
.max_rewind
;
2075 pa_assert_se(pa_asyncmsgq_send(s
->asyncmsgq
, PA_MSGOBJECT(s
), PA_SINK_MESSAGE_GET_MAX_REWIND
, &r
, 0, NULL
) == 0);
2080 /* Called from main context */
2081 size_t pa_sink_get_max_request(pa_sink
*s
) {
2083 pa_sink_assert_ref(s
);
2085 if (!PA_SINK_IS_LINKED(s
->state
))
2086 return s
->thread_info
.max_request
;
2088 pa_assert_se(pa_asyncmsgq_send(s
->asyncmsgq
, PA_MSGOBJECT(s
), PA_SINK_MESSAGE_GET_MAX_REQUEST
, &r
, 0, NULL
) == 0);
2093 /* Called from main context */
2094 pa_bool_t
pa_device_init_icon(pa_proplist
*p
, pa_bool_t is_sink
) {
2095 const char *ff
, *c
, *t
= NULL
, *s
= "", *profile
, *bus
;
2099 if (pa_proplist_contains(p
, PA_PROP_DEVICE_ICON_NAME
))
2102 if ((ff
= pa_proplist_gets(p
, PA_PROP_DEVICE_FORM_FACTOR
))) {
2104 if (pa_streq(ff
, "microphone"))
2105 t
= "audio-input-microphone";
2106 else if (pa_streq(ff
, "webcam"))
2108 else if (pa_streq(ff
, "computer"))
2110 else if (pa_streq(ff
, "handset"))
2112 else if (pa_streq(ff
, "portable"))
2113 t
= "multimedia-player";
2114 else if (pa_streq(ff
, "tv"))
2115 t
= "video-display";
2119 if ((c
= pa_proplist_gets(p
, PA_PROP_DEVICE_CLASS
)))
2120 if (pa_streq(c
, "modem"))
2127 t
= "audio-input-microphone";
2130 if ((profile
= pa_proplist_gets(p
, PA_PROP_DEVICE_PROFILE_NAME
))) {
2131 if (strstr(profile
, "analog"))
2133 else if (strstr(profile
, "iec958"))
2135 else if (strstr(profile
, "hdmi"))
2139 bus
= pa_proplist_gets(p
, PA_PROP_DEVICE_BUS
);
2141 pa_proplist_setf(p
, PA_PROP_DEVICE_ICON_NAME
, "%s%s%s%s", t
, pa_strempty(s
), bus
? "-" : "", pa_strempty(bus
));
2146 pa_bool_t
pa_device_init_description(pa_proplist
*p
) {
2150 if (pa_proplist_contains(p
, PA_PROP_DEVICE_DESCRIPTION
))
2153 if ((s
= pa_proplist_gets(p
, PA_PROP_DEVICE_FORM_FACTOR
)))
2154 if (pa_streq(s
, "internal")) {
2155 pa_proplist_sets(p
, PA_PROP_DEVICE_DESCRIPTION
, _("Internal Audio"));
2159 if ((s
= pa_proplist_gets(p
, PA_PROP_DEVICE_CLASS
)))
2160 if (pa_streq(s
, "modem")) {
2161 pa_proplist_sets(p
, PA_PROP_DEVICE_DESCRIPTION
, _("Modem"));
2165 if ((s
= pa_proplist_gets(p
, PA_PROP_DEVICE_PRODUCT_NAME
))) {
2166 pa_proplist_sets(p
, PA_PROP_DEVICE_DESCRIPTION
, s
);