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
->suspend_cause
= 0;
194 s
->name
= pa_xstrdup(name
);
195 s
->proplist
= pa_proplist_copy(data
->proplist
);
196 s
->driver
= pa_xstrdup(pa_path_get_filename(data
->driver
));
197 s
->module
= data
->module
;
198 s
->card
= data
->card
;
200 s
->sample_spec
= data
->sample_spec
;
201 s
->channel_map
= data
->channel_map
;
203 s
->inputs
= pa_idxset_new(NULL
, NULL
);
206 s
->reference_volume
= s
->virtual_volume
= data
->volume
;
207 pa_cvolume_reset(&s
->soft_volume
, s
->sample_spec
.channels
);
208 s
->base_volume
= PA_VOLUME_NORM
;
209 s
->n_volume_steps
= PA_VOLUME_NORM
+1;
210 s
->muted
= data
->muted
;
211 s
->refresh_volume
= s
->refresh_muted
= FALSE
;
213 s
->fixed_latency
= flags
& PA_SINK_DYNAMIC_LATENCY
? 0 : DEFAULT_FIXED_LATENCY
;
221 pa_silence_memchunk_get(
222 &core
->silence_cache
,
228 s
->thread_info
.inputs
= pa_hashmap_new(pa_idxset_trivial_hash_func
, pa_idxset_trivial_compare_func
);
229 s
->thread_info
.soft_volume
= s
->soft_volume
;
230 s
->thread_info
.soft_muted
= s
->muted
;
231 s
->thread_info
.state
= s
->state
;
232 s
->thread_info
.rewind_nbytes
= 0;
233 s
->thread_info
.rewind_requested
= FALSE
;
234 s
->thread_info
.max_rewind
= 0;
235 s
->thread_info
.max_request
= 0;
236 s
->thread_info
.requested_latency_valid
= FALSE
;
237 s
->thread_info
.requested_latency
= 0;
238 s
->thread_info
.min_latency
= ABSOLUTE_MIN_LATENCY
;
239 s
->thread_info
.max_latency
= ABSOLUTE_MAX_LATENCY
;
241 pa_assert_se(pa_idxset_put(core
->sinks
, s
, &s
->index
) >= 0);
244 pa_assert_se(pa_idxset_put(s
->card
->sinks
, s
, NULL
) >= 0);
246 pt
= pa_proplist_to_string_sep(s
->proplist
, "\n ");
247 pa_log_info("Created sink %u \"%s\" with sample spec %s and channel map %s\n %s",
250 pa_sample_spec_snprint(st
, sizeof(st
), &s
->sample_spec
),
251 pa_channel_map_snprint(cm
, sizeof(cm
), &s
->channel_map
),
255 pa_source_new_data_init(&source_data
);
256 pa_source_new_data_set_sample_spec(&source_data
, &s
->sample_spec
);
257 pa_source_new_data_set_channel_map(&source_data
, &s
->channel_map
);
258 source_data
.name
= pa_sprintf_malloc("%s.monitor", name
);
259 source_data
.driver
= data
->driver
;
260 source_data
.module
= data
->module
;
261 source_data
.card
= data
->card
;
263 dn
= pa_proplist_gets(s
->proplist
, PA_PROP_DEVICE_DESCRIPTION
);
264 pa_proplist_setf(source_data
.proplist
, PA_PROP_DEVICE_DESCRIPTION
, "Monitor of %s", dn
? dn
: s
->name
);
265 pa_proplist_sets(source_data
.proplist
, PA_PROP_DEVICE_CLASS
, "monitor");
267 s
->monitor_source
= pa_source_new(core
, &source_data
,
268 ((flags
& PA_SINK_LATENCY
) ? PA_SOURCE_LATENCY
: 0) |
269 ((flags
& PA_SINK_DYNAMIC_LATENCY
) ? PA_SOURCE_DYNAMIC_LATENCY
: 0));
271 pa_source_new_data_done(&source_data
);
273 if (!s
->monitor_source
) {
279 s
->monitor_source
->monitor_of
= s
;
281 pa_source_set_latency_range(s
->monitor_source
, s
->thread_info
.min_latency
, s
->thread_info
.max_latency
);
282 pa_source_set_max_rewind(s
->monitor_source
, s
->thread_info
.max_rewind
);
287 /* Called from main context */
288 static int sink_set_state(pa_sink
*s
, pa_sink_state_t state
) {
290 pa_bool_t suspend_change
;
291 pa_sink_state_t original_state
;
295 if (s
->state
== state
)
298 original_state
= s
->state
;
301 (original_state
== PA_SINK_SUSPENDED
&& PA_SINK_IS_OPENED(state
)) ||
302 (PA_SINK_IS_OPENED(original_state
) && state
== PA_SINK_SUSPENDED
);
305 if ((ret
= s
->set_state(s
, state
)) < 0)
309 if ((ret
= pa_asyncmsgq_send(s
->asyncmsgq
, PA_MSGOBJECT(s
), PA_SINK_MESSAGE_SET_STATE
, PA_UINT_TO_PTR(state
), 0, NULL
)) < 0) {
312 s
->set_state(s
, original_state
);
319 if (state
!= PA_SINK_UNLINKED
) { /* if we enter UNLINKED state pa_sink_unlink() will fire the apropriate events */
320 pa_hook_fire(&s
->core
->hooks
[PA_CORE_HOOK_SINK_STATE_CHANGED
], s
);
321 pa_subscription_post(s
->core
, PA_SUBSCRIPTION_EVENT_SINK
| PA_SUBSCRIPTION_EVENT_CHANGE
, s
->index
);
324 if (suspend_change
) {
328 /* We're suspending or resuming, tell everyone about it */
330 for (i
= PA_SINK_INPUT(pa_idxset_first(s
->inputs
, &idx
)); i
; i
= PA_SINK_INPUT(pa_idxset_next(s
->inputs
, &idx
)))
331 if (s
->state
== PA_SINK_SUSPENDED
&&
332 (i
->flags
& PA_SINK_INPUT_FAIL_ON_SUSPEND
))
333 pa_sink_input_kill(i
);
335 i
->suspend(i
, state
== PA_SINK_SUSPENDED
);
337 if (s
->monitor_source
)
338 pa_source_sync_suspend(s
->monitor_source
);
344 /* Called from main context */
345 void pa_sink_put(pa_sink
* s
) {
346 pa_sink_assert_ref(s
);
348 pa_assert(s
->state
== PA_SINK_INIT
);
350 /* The following fields must be initialized properly when calling _put() */
351 pa_assert(s
->asyncmsgq
);
352 pa_assert(s
->rtpoll
);
353 pa_assert(s
->thread_info
.min_latency
<= s
->thread_info
.max_latency
);
355 /* Generally, flags should be initialized via pa_sink_new(). As a
356 * special exception we allow volume related flags to be set
357 * between _new() and _put(). */
359 if (!(s
->flags
& PA_SINK_HW_VOLUME_CTRL
))
360 s
->flags
|= PA_SINK_DECIBEL_VOLUME
;
362 if ((s
->flags
& PA_SINK_DECIBEL_VOLUME
) && s
->core
->flat_volumes
)
363 s
->flags
|= PA_SINK_FLAT_VOLUME
;
365 s
->thread_info
.soft_volume
= s
->soft_volume
;
366 s
->thread_info
.soft_muted
= s
->muted
;
368 pa_assert((s
->flags
& PA_SINK_HW_VOLUME_CTRL
) || (s
->base_volume
== PA_VOLUME_NORM
&& s
->flags
& PA_SINK_DECIBEL_VOLUME
));
369 pa_assert(!(s
->flags
& PA_SINK_DECIBEL_VOLUME
) || s
->n_volume_steps
== PA_VOLUME_NORM
+1);
370 pa_assert(!(s
->flags
& PA_SINK_DYNAMIC_LATENCY
) == (s
->fixed_latency
!= 0));
371 pa_assert(!(s
->flags
& PA_SINK_LATENCY
) == !(s
->monitor_source
->flags
& PA_SOURCE_LATENCY
));
372 pa_assert(!(s
->flags
& PA_SINK_DYNAMIC_LATENCY
) == !(s
->monitor_source
->flags
& PA_SOURCE_DYNAMIC_LATENCY
));
374 pa_assert(s
->monitor_source
->fixed_latency
== s
->fixed_latency
);
375 pa_assert(s
->monitor_source
->thread_info
.min_latency
== s
->thread_info
.min_latency
);
376 pa_assert(s
->monitor_source
->thread_info
.max_latency
== s
->thread_info
.max_latency
);
378 pa_assert_se(sink_set_state(s
, PA_SINK_IDLE
) == 0);
380 pa_source_put(s
->monitor_source
);
382 pa_subscription_post(s
->core
, PA_SUBSCRIPTION_EVENT_SINK
| PA_SUBSCRIPTION_EVENT_NEW
, s
->index
);
383 pa_hook_fire(&s
->core
->hooks
[PA_CORE_HOOK_SINK_PUT
], s
);
386 /* Called from main context */
387 void pa_sink_unlink(pa_sink
* s
) {
389 pa_sink_input
*i
, *j
= NULL
;
393 /* Please note that pa_sink_unlink() does more than simply
394 * reversing pa_sink_put(). It also undoes the registrations
395 * already done in pa_sink_new()! */
397 /* All operations here shall be idempotent, i.e. pa_sink_unlink()
398 * may be called multiple times on the same sink without bad
401 linked
= PA_SINK_IS_LINKED(s
->state
);
404 pa_hook_fire(&s
->core
->hooks
[PA_CORE_HOOK_SINK_UNLINK
], s
);
406 if (s
->state
!= PA_SINK_UNLINKED
)
407 pa_namereg_unregister(s
->core
, s
->name
);
408 pa_idxset_remove_by_data(s
->core
->sinks
, s
, NULL
);
411 pa_idxset_remove_by_data(s
->card
->sinks
, s
, NULL
);
413 while ((i
= pa_idxset_first(s
->inputs
, NULL
))) {
415 pa_sink_input_kill(i
);
420 sink_set_state(s
, PA_SINK_UNLINKED
);
422 s
->state
= PA_SINK_UNLINKED
;
426 if (s
->monitor_source
)
427 pa_source_unlink(s
->monitor_source
);
430 pa_subscription_post(s
->core
, PA_SUBSCRIPTION_EVENT_SINK
| PA_SUBSCRIPTION_EVENT_REMOVE
, s
->index
);
431 pa_hook_fire(&s
->core
->hooks
[PA_CORE_HOOK_SINK_UNLINK_POST
], s
);
435 /* Called from main context */
436 static void sink_free(pa_object
*o
) {
437 pa_sink
*s
= PA_SINK(o
);
441 pa_assert(pa_sink_refcnt(s
) == 0);
443 if (PA_SINK_IS_LINKED(s
->state
))
446 pa_log_info("Freeing sink %u \"%s\"", s
->index
, s
->name
);
448 if (s
->monitor_source
) {
449 pa_source_unref(s
->monitor_source
);
450 s
->monitor_source
= NULL
;
453 pa_idxset_free(s
->inputs
, NULL
, NULL
);
455 while ((i
= pa_hashmap_steal_first(s
->thread_info
.inputs
)))
456 pa_sink_input_unref(i
);
458 pa_hashmap_free(s
->thread_info
.inputs
, NULL
, NULL
);
460 if (s
->silence
.memblock
)
461 pa_memblock_unref(s
->silence
.memblock
);
467 pa_proplist_free(s
->proplist
);
472 /* Called from main context */
473 void pa_sink_set_asyncmsgq(pa_sink
*s
, pa_asyncmsgq
*q
) {
474 pa_sink_assert_ref(s
);
478 if (s
->monitor_source
)
479 pa_source_set_asyncmsgq(s
->monitor_source
, q
);
482 /* Called from main context */
483 void pa_sink_set_rtpoll(pa_sink
*s
, pa_rtpoll
*p
) {
484 pa_sink_assert_ref(s
);
487 if (s
->monitor_source
)
488 pa_source_set_rtpoll(s
->monitor_source
, p
);
491 /* Called from main context */
492 int pa_sink_update_status(pa_sink
*s
) {
493 pa_sink_assert_ref(s
);
494 pa_assert(PA_SINK_IS_LINKED(s
->state
));
496 if (s
->state
== PA_SINK_SUSPENDED
)
499 return sink_set_state(s
, pa_sink_used_by(s
) ? PA_SINK_RUNNING
: PA_SINK_IDLE
);
502 /* Called from main context */
503 int pa_sink_suspend(pa_sink
*s
, pa_bool_t suspend
, pa_suspend_cause_t cause
) {
504 pa_sink_assert_ref(s
);
505 pa_assert(PA_SINK_IS_LINKED(s
->state
));
506 pa_assert(cause
!= 0);
509 s
->suspend_cause
|= cause
;
510 s
->monitor_source
->suspend_cause
|= cause
;
512 s
->suspend_cause
&= ~cause
;
513 s
->monitor_source
->suspend_cause
&= ~cause
;
516 if ((pa_sink_get_state(s
) == PA_SINK_SUSPENDED
) == !!s
->suspend_cause
)
519 pa_log_debug("Suspend cause of sink %s is 0x%04x, %s", s
->name
, s
->suspend_cause
, s
->suspend_cause
? "suspending" : "resuming");
521 if (s
->suspend_cause
)
522 return sink_set_state(s
, PA_SINK_SUSPENDED
);
524 return sink_set_state(s
, pa_sink_used_by(s
) ? PA_SINK_RUNNING
: PA_SINK_IDLE
);
527 /* Called from main context */
528 pa_queue
*pa_sink_move_all_start(pa_sink
*s
) {
530 pa_sink_input
*i
, *n
;
533 pa_sink_assert_ref(s
);
534 pa_assert(PA_SINK_IS_LINKED(s
->state
));
538 for (i
= PA_SINK_INPUT(pa_idxset_first(s
->inputs
, &idx
)); i
; i
= n
) {
539 n
= PA_SINK_INPUT(pa_idxset_next(s
->inputs
, &idx
));
541 pa_sink_input_ref(i
);
543 if (pa_sink_input_start_move(i
) >= 0)
546 pa_sink_input_unref(i
);
552 /* Called from main context */
553 void pa_sink_move_all_finish(pa_sink
*s
, pa_queue
*q
, pa_bool_t save
) {
556 pa_sink_assert_ref(s
);
557 pa_assert(PA_SINK_IS_LINKED(s
->state
));
560 while ((i
= PA_SINK_INPUT(pa_queue_pop(q
)))) {
561 if (pa_sink_input_finish_move(i
, s
, save
) < 0)
562 pa_sink_input_kill(i
);
564 pa_sink_input_unref(i
);
567 pa_queue_free(q
, NULL
, NULL
);
570 /* Called from main context */
571 void pa_sink_move_all_fail(pa_queue
*q
) {
575 while ((i
= PA_SINK_INPUT(pa_queue_pop(q
)))) {
576 if (pa_hook_fire(&i
->core
->hooks
[PA_CORE_HOOK_SINK_INPUT_MOVE_FAIL
], i
) == PA_HOOK_OK
) {
577 pa_sink_input_kill(i
);
578 pa_sink_input_unref(i
);
582 pa_queue_free(q
, NULL
, NULL
);
585 /* Called from IO thread context */
586 void pa_sink_process_rewind(pa_sink
*s
, size_t nbytes
) {
589 pa_sink_assert_ref(s
);
590 pa_assert(PA_SINK_IS_LINKED(s
->thread_info
.state
));
592 /* If nobody requested this and this is actually no real rewind
593 * then we can short cut this */
594 if (!s
->thread_info
.rewind_requested
&& nbytes
<= 0)
597 s
->thread_info
.rewind_nbytes
= 0;
598 s
->thread_info
.rewind_requested
= FALSE
;
600 if (s
->thread_info
.state
== PA_SINK_SUSPENDED
)
604 pa_log_debug("Processing rewind...");
606 while ((i
= pa_hashmap_iterate(s
->thread_info
.inputs
, &state
, NULL
))) {
607 pa_sink_input_assert_ref(i
);
608 pa_sink_input_process_rewind(i
, nbytes
);
612 if (s
->monitor_source
&& PA_SOURCE_IS_LINKED(s
->monitor_source
->thread_info
.state
))
613 pa_source_process_rewind(s
->monitor_source
, nbytes
);
616 /* Called from IO thread context */
617 static unsigned fill_mix_info(pa_sink
*s
, size_t *length
, pa_mix_info
*info
, unsigned maxinfo
) {
621 size_t mixlength
= *length
;
623 pa_sink_assert_ref(s
);
626 while ((i
= pa_hashmap_iterate(s
->thread_info
.inputs
, &state
, NULL
)) && maxinfo
> 0) {
627 pa_sink_input_assert_ref(i
);
629 pa_sink_input_peek(i
, *length
, &info
->chunk
, &info
->volume
);
631 if (mixlength
== 0 || info
->chunk
.length
< mixlength
)
632 mixlength
= info
->chunk
.length
;
634 if (pa_memblock_is_silence(info
->chunk
.memblock
)) {
635 pa_memblock_unref(info
->chunk
.memblock
);
639 info
->userdata
= pa_sink_input_ref(i
);
641 pa_assert(info
->chunk
.memblock
);
642 pa_assert(info
->chunk
.length
> 0);
655 /* Called from IO thread context */
656 static void inputs_drop(pa_sink
*s
, pa_mix_info
*info
, unsigned n
, pa_memchunk
*result
) {
660 unsigned n_unreffed
= 0;
662 pa_sink_assert_ref(s
);
664 pa_assert(result
->memblock
);
665 pa_assert(result
->length
> 0);
667 /* We optimize for the case where the order of the inputs has not changed */
669 while ((i
= pa_hashmap_iterate(s
->thread_info
.inputs
, &state
, NULL
))) {
671 pa_mix_info
* m
= NULL
;
673 pa_sink_input_assert_ref(i
);
675 /* Let's try to find the matching entry info the pa_mix_info array */
676 for (j
= 0; j
< n
; j
++) {
678 if (info
[p
].userdata
== i
) {
689 pa_sink_input_drop(i
, result
->length
);
691 if (s
->monitor_source
&& PA_SOURCE_IS_LINKED(s
->monitor_source
->thread_info
.state
)) {
693 if (pa_hashmap_size(i
->thread_info
.direct_outputs
) > 0) {
698 if (m
&& m
->chunk
.memblock
) {
700 pa_memblock_ref(c
.memblock
);
701 pa_assert(result
->length
<= c
.length
);
702 c
.length
= result
->length
;
704 pa_memchunk_make_writable(&c
, 0);
705 pa_volume_memchunk(&c
, &s
->sample_spec
, &m
->volume
);
708 pa_memblock_ref(c
.memblock
);
709 pa_assert(result
->length
<= c
.length
);
710 c
.length
= result
->length
;
713 while ((o
= pa_hashmap_iterate(i
->thread_info
.direct_outputs
, &ostate
, NULL
))) {
714 pa_source_output_assert_ref(o
);
715 pa_assert(o
->direct_on_input
== i
);
716 pa_source_post_direct(s
->monitor_source
, o
, &c
);
719 pa_memblock_unref(c
.memblock
);
724 if (m
->chunk
.memblock
)
725 pa_memblock_unref(m
->chunk
.memblock
);
726 pa_memchunk_reset(&m
->chunk
);
728 pa_sink_input_unref(m
->userdata
);
735 /* Now drop references to entries that are included in the
736 * pa_mix_info array but don't exist anymore */
738 if (n_unreffed
< n
) {
739 for (; n
> 0; info
++, n
--) {
741 pa_sink_input_unref(info
->userdata
);
742 if (info
->chunk
.memblock
)
743 pa_memblock_unref(info
->chunk
.memblock
);
747 if (s
->monitor_source
&& PA_SOURCE_IS_LINKED(s
->monitor_source
->thread_info
.state
))
748 pa_source_post(s
->monitor_source
, result
);
751 /* Called from IO thread context */
752 void pa_sink_render(pa_sink
*s
, size_t length
, pa_memchunk
*result
) {
753 pa_mix_info info
[MAX_MIX_CHANNELS
];
755 size_t block_size_max
;
757 pa_sink_assert_ref(s
);
758 pa_assert(PA_SINK_IS_LINKED(s
->thread_info
.state
));
759 pa_assert(pa_frame_aligned(length
, &s
->sample_spec
));
764 pa_assert(!s
->thread_info
.rewind_requested
);
765 pa_assert(s
->thread_info
.rewind_nbytes
== 0);
767 if (s
->thread_info
.state
== PA_SINK_SUSPENDED
) {
768 result
->memblock
= pa_memblock_ref(s
->silence
.memblock
);
769 result
->index
= s
->silence
.index
;
770 result
->length
= PA_MIN(s
->silence
.length
, length
);
775 length
= pa_frame_align(MIX_BUFFER_LENGTH
, &s
->sample_spec
);
777 block_size_max
= pa_mempool_block_size_max(s
->core
->mempool
);
778 if (length
> block_size_max
)
779 length
= pa_frame_align(block_size_max
, &s
->sample_spec
);
781 pa_assert(length
> 0);
783 n
= fill_mix_info(s
, &length
, info
, MAX_MIX_CHANNELS
);
787 *result
= s
->silence
;
788 pa_memblock_ref(result
->memblock
);
790 if (result
->length
> length
)
791 result
->length
= length
;
796 *result
= info
[0].chunk
;
797 pa_memblock_ref(result
->memblock
);
799 if (result
->length
> length
)
800 result
->length
= length
;
802 pa_sw_cvolume_multiply(&volume
, &s
->thread_info
.soft_volume
, &info
[0].volume
);
804 if (s
->thread_info
.soft_muted
|| !pa_cvolume_is_norm(&volume
)) {
805 if (s
->thread_info
.soft_muted
|| pa_cvolume_is_muted(&volume
)) {
806 pa_memblock_unref(result
->memblock
);
807 pa_silence_memchunk_get(&s
->core
->silence_cache
,
813 pa_memchunk_make_writable(result
, 0);
814 pa_volume_memchunk(result
, &s
->sample_spec
, &volume
);
819 result
->memblock
= pa_memblock_new(s
->core
->mempool
, length
);
821 ptr
= pa_memblock_acquire(result
->memblock
);
822 result
->length
= pa_mix(info
, n
,
825 &s
->thread_info
.soft_volume
,
826 s
->thread_info
.soft_muted
);
827 pa_memblock_release(result
->memblock
);
832 inputs_drop(s
, info
, n
, result
);
837 /* Called from IO thread context */
838 void pa_sink_render_into(pa_sink
*s
, pa_memchunk
*target
) {
839 pa_mix_info info
[MAX_MIX_CHANNELS
];
841 size_t length
, block_size_max
;
843 pa_sink_assert_ref(s
);
844 pa_assert(PA_SINK_IS_LINKED(s
->thread_info
.state
));
846 pa_assert(target
->memblock
);
847 pa_assert(target
->length
> 0);
848 pa_assert(pa_frame_aligned(target
->length
, &s
->sample_spec
));
852 pa_assert(!s
->thread_info
.rewind_requested
);
853 pa_assert(s
->thread_info
.rewind_nbytes
== 0);
855 if (s
->thread_info
.state
== PA_SINK_SUSPENDED
) {
856 pa_silence_memchunk(target
, &s
->sample_spec
);
860 length
= target
->length
;
861 block_size_max
= pa_mempool_block_size_max(s
->core
->mempool
);
862 if (length
> block_size_max
)
863 length
= pa_frame_align(block_size_max
, &s
->sample_spec
);
865 pa_assert(length
> 0);
867 n
= fill_mix_info(s
, &length
, info
, MAX_MIX_CHANNELS
);
870 if (target
->length
> length
)
871 target
->length
= length
;
873 pa_silence_memchunk(target
, &s
->sample_spec
);
877 if (target
->length
> length
)
878 target
->length
= length
;
880 pa_sw_cvolume_multiply(&volume
, &s
->thread_info
.soft_volume
, &info
[0].volume
);
882 if (s
->thread_info
.soft_muted
|| pa_cvolume_is_muted(&volume
))
883 pa_silence_memchunk(target
, &s
->sample_spec
);
887 vchunk
= info
[0].chunk
;
888 pa_memblock_ref(vchunk
.memblock
);
890 if (vchunk
.length
> length
)
891 vchunk
.length
= length
;
893 if (!pa_cvolume_is_norm(&volume
)) {
894 pa_memchunk_make_writable(&vchunk
, 0);
895 pa_volume_memchunk(&vchunk
, &s
->sample_spec
, &volume
);
898 pa_memchunk_memcpy(target
, &vchunk
);
899 pa_memblock_unref(vchunk
.memblock
);
905 ptr
= pa_memblock_acquire(target
->memblock
);
907 target
->length
= pa_mix(info
, n
,
908 (uint8_t*) ptr
+ target
->index
, length
,
910 &s
->thread_info
.soft_volume
,
911 s
->thread_info
.soft_muted
);
913 pa_memblock_release(target
->memblock
);
916 inputs_drop(s
, info
, n
, target
);
921 /* Called from IO thread context */
922 void pa_sink_render_into_full(pa_sink
*s
, pa_memchunk
*target
) {
926 pa_sink_assert_ref(s
);
927 pa_assert(PA_SINK_IS_LINKED(s
->thread_info
.state
));
929 pa_assert(target
->memblock
);
930 pa_assert(target
->length
> 0);
931 pa_assert(pa_frame_aligned(target
->length
, &s
->sample_spec
));
935 pa_assert(!s
->thread_info
.rewind_requested
);
936 pa_assert(s
->thread_info
.rewind_nbytes
== 0);
945 pa_sink_render_into(s
, &chunk
);
954 /* Called from IO thread context */
955 void pa_sink_render_full(pa_sink
*s
, size_t length
, pa_memchunk
*result
) {
956 pa_mix_info info
[MAX_MIX_CHANNELS
];
957 size_t length1st
= length
;
960 pa_sink_assert_ref(s
);
961 pa_assert(PA_SINK_IS_LINKED(s
->thread_info
.state
));
962 pa_assert(length
> 0);
963 pa_assert(pa_frame_aligned(length
, &s
->sample_spec
));
968 pa_assert(!s
->thread_info
.rewind_requested
);
969 pa_assert(s
->thread_info
.rewind_nbytes
== 0);
971 pa_assert(length
> 0);
973 n
= fill_mix_info(s
, &length1st
, info
, MAX_MIX_CHANNELS
);
976 pa_silence_memchunk_get(&s
->core
->silence_cache
,
984 *result
= info
[0].chunk
;
985 pa_memblock_ref(result
->memblock
);
987 if (result
->length
> length
)
988 result
->length
= length
;
990 pa_sw_cvolume_multiply(&volume
, &s
->thread_info
.soft_volume
, &info
[0].volume
);
992 if (s
->thread_info
.soft_muted
|| !pa_cvolume_is_norm(&volume
)) {
993 if (s
->thread_info
.soft_muted
|| pa_cvolume_is_muted(&volume
)) {
994 pa_memblock_unref(result
->memblock
);
995 pa_silence_memchunk_get(&s
->core
->silence_cache
,
1001 pa_memchunk_make_writable(result
, length
);
1002 pa_volume_memchunk(result
, &s
->sample_spec
, &volume
);
1009 result
->memblock
= pa_memblock_new(s
->core
->mempool
, length
);
1011 ptr
= pa_memblock_acquire(result
->memblock
);
1013 result
->length
= pa_mix(info
, n
,
1014 (uint8_t*) ptr
+ result
->index
, length1st
,
1016 &s
->thread_info
.soft_volume
,
1017 s
->thread_info
.soft_muted
);
1019 pa_memblock_release(result
->memblock
);
1022 inputs_drop(s
, info
, n
, result
);
1024 if (result
->length
< length
) {
1027 pa_memchunk_make_writable(result
, length
);
1029 l
= length
- result
->length
;
1030 d
= result
->index
+ result
->length
;
1036 pa_sink_render_into(s
, &chunk
);
1041 result
->length
= length
;
1047 /* Called from main thread */
1048 pa_usec_t
pa_sink_get_latency(pa_sink
*s
) {
1051 pa_sink_assert_ref(s
);
1052 pa_assert(PA_SINK_IS_LINKED(s
->state
));
1054 /* The returned value is supposed to be in the time domain of the sound card! */
1056 if (s
->state
== PA_SINK_SUSPENDED
)
1059 if (!(s
->flags
& PA_SINK_LATENCY
))
1062 pa_assert_se(pa_asyncmsgq_send(s
->asyncmsgq
, PA_MSGOBJECT(s
), PA_SINK_MESSAGE_GET_LATENCY
, &usec
, 0, NULL
) == 0);
1067 /* Called from IO thread */
1068 pa_usec_t
pa_sink_get_latency_within_thread(pa_sink
*s
) {
1072 pa_sink_assert_ref(s
);
1073 pa_assert(PA_SINK_IS_LINKED(s
->thread_info
.state
));
1075 /* The returned value is supposed to be in the time domain of the sound card! */
1077 if (s
->thread_info
.state
== PA_SINK_SUSPENDED
)
1080 if (!(s
->flags
& PA_SINK_LATENCY
))
1083 o
= PA_MSGOBJECT(s
);
1085 /* We probably should make this a proper vtable callback instead of going through process_msg() */
1087 if (o
->process_msg(o
, PA_SINK_MESSAGE_GET_LATENCY
, &usec
, 0, NULL
) < 0)
1093 static void compute_new_soft_volume(pa_sink_input
*i
, const pa_cvolume
*new_volume
) {
1096 pa_sink_input_assert_ref(i
);
1097 pa_assert(new_volume
->channels
== i
->sample_spec
.channels
);
1100 * This basically calculates:
1102 * i->relative_volume := i->virtual_volume / new_volume
1103 * i->soft_volume := i->relative_volume * i->volume_factor
1106 /* The new sink volume passed in here must already be remapped to
1107 * the sink input's channel map! */
1109 i
->soft_volume
.channels
= i
->sample_spec
.channels
;
1111 for (c
= 0; c
< i
->sample_spec
.channels
; c
++)
1113 if (new_volume
->values
[c
] <= PA_VOLUME_MUTED
)
1114 /* We leave i->relative_volume untouched */
1115 i
->soft_volume
.values
[c
] = PA_VOLUME_MUTED
;
1117 i
->relative_volume
[c
] =
1118 pa_sw_volume_to_linear(i
->virtual_volume
.values
[c
]) /
1119 pa_sw_volume_to_linear(new_volume
->values
[c
]);
1121 i
->soft_volume
.values
[c
] = pa_sw_volume_from_linear(
1122 i
->relative_volume
[c
] *
1123 pa_sw_volume_to_linear(i
->volume_factor
.values
[c
]));
1126 /* Hooks have the ability to play games with i->soft_volume */
1127 pa_hook_fire(&i
->core
->hooks
[PA_CORE_HOOK_SINK_INPUT_SET_VOLUME
], i
);
1129 /* We don't copy the soft_volume to the thread_info data
1130 * here. That must be done by the caller */
1133 /* Called from main thread */
1134 void pa_sink_update_flat_volume(pa_sink
*s
, pa_cvolume
*new_volume
) {
1138 pa_sink_assert_ref(s
);
1139 pa_assert(new_volume
);
1140 pa_assert(PA_SINK_IS_LINKED(s
->state
));
1141 pa_assert(s
->flags
& PA_SINK_FLAT_VOLUME
);
1143 /* This is called whenever a sink input volume changes or a sink
1144 * input is added/removed and we might need to fix up the sink
1145 * volume accordingly. Please note that we don't actually update
1146 * the sinks volume here, we only return how it needs to be
1147 * updated. The caller should then call pa_sink_set_volume().*/
1149 if (pa_idxset_isempty(s
->inputs
)) {
1150 /* In the special case that we have no sink input we leave the
1151 * volume unmodified. */
1152 *new_volume
= s
->reference_volume
;
1156 pa_cvolume_mute(new_volume
, s
->channel_map
.channels
);
1158 /* First let's determine the new maximum volume of all inputs
1159 * connected to this sink */
1160 for (i
= PA_SINK_INPUT(pa_idxset_first(s
->inputs
, &idx
)); i
; i
= PA_SINK_INPUT(pa_idxset_next(s
->inputs
, &idx
))) {
1162 pa_cvolume remapped_volume
;
1164 remapped_volume
= i
->virtual_volume
;
1165 pa_cvolume_remap(&remapped_volume
, &i
->channel_map
, &s
->channel_map
);
1167 for (c
= 0; c
< new_volume
->channels
; c
++)
1168 if (remapped_volume
.values
[c
] > new_volume
->values
[c
])
1169 new_volume
->values
[c
] = remapped_volume
.values
[c
];
1172 /* Then, let's update the soft volumes of all inputs connected
1174 for (i
= PA_SINK_INPUT(pa_idxset_first(s
->inputs
, &idx
)); i
; i
= PA_SINK_INPUT(pa_idxset_next(s
->inputs
, &idx
))) {
1175 pa_cvolume remapped_new_volume
;
1177 remapped_new_volume
= *new_volume
;
1178 pa_cvolume_remap(&remapped_new_volume
, &s
->channel_map
, &i
->channel_map
);
1179 compute_new_soft_volume(i
, &remapped_new_volume
);
1181 /* We don't copy soft_volume to the thread_info data here
1182 * (i.e. issue PA_SINK_INPUT_MESSAGE_SET_VOLUME) because we
1183 * want the update to be atomically with the sink volume
1184 * update, hence we do it within the pa_sink_set_volume() call
1189 /* Called from main thread */
1190 void pa_sink_propagate_flat_volume(pa_sink
*s
) {
1194 pa_sink_assert_ref(s
);
1195 pa_assert(PA_SINK_IS_LINKED(s
->state
));
1196 pa_assert(s
->flags
& PA_SINK_FLAT_VOLUME
);
1198 /* This is called whenever the sink volume changes that is not
1199 * caused by a sink input volume change. We need to fix up the
1200 * sink input volumes accordingly */
1202 for (i
= PA_SINK_INPUT(pa_idxset_first(s
->inputs
, &idx
)); i
; i
= PA_SINK_INPUT(pa_idxset_next(s
->inputs
, &idx
))) {
1203 pa_cvolume sink_volume
, new_virtual_volume
;
1206 /* This basically calculates i->virtual_volume := i->relative_volume * s->virtual_volume */
1208 sink_volume
= s
->virtual_volume
;
1209 pa_cvolume_remap(&sink_volume
, &s
->channel_map
, &i
->channel_map
);
1211 for (c
= 0; c
< i
->sample_spec
.channels
; c
++)
1212 new_virtual_volume
.values
[c
] = pa_sw_volume_from_linear(
1213 i
->relative_volume
[c
] *
1214 pa_sw_volume_to_linear(sink_volume
.values
[c
]));
1216 new_virtual_volume
.channels
= i
->sample_spec
.channels
;
1218 if (!pa_cvolume_equal(&new_virtual_volume
, &i
->virtual_volume
)) {
1219 i
->virtual_volume
= new_virtual_volume
;
1221 /* Hmm, the soft volume might no longer actually match
1222 * what has been chosen as new virtual volume here,
1223 * especially when the old volume was
1224 * PA_VOLUME_MUTED. Hence let's recalculate the soft
1226 compute_new_soft_volume(i
, &sink_volume
);
1228 /* The virtual volume changed, let's tell people so */
1229 pa_subscription_post(i
->core
, PA_SUBSCRIPTION_EVENT_SINK_INPUT
|PA_SUBSCRIPTION_EVENT_CHANGE
, i
->index
);
1233 /* If the soft_volume of any of the sink inputs got changed, let's
1234 * make sure the thread copies are synced up. */
1235 pa_assert_se(pa_asyncmsgq_send(s
->asyncmsgq
, PA_MSGOBJECT(s
), PA_SINK_MESSAGE_SYNC_VOLUMES
, NULL
, 0, NULL
) == 0);
1238 /* Called from main thread */
1239 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
) {
1240 pa_bool_t virtual_volume_changed
;
1242 pa_sink_assert_ref(s
);
1243 pa_assert(PA_SINK_IS_LINKED(s
->state
));
1245 pa_assert(pa_cvolume_valid(volume
));
1246 pa_assert(pa_cvolume_compatible(volume
, &s
->sample_spec
));
1248 virtual_volume_changed
= !pa_cvolume_equal(volume
, &s
->virtual_volume
);
1249 s
->virtual_volume
= *volume
;
1251 if (become_reference
)
1252 s
->reference_volume
= s
->virtual_volume
;
1254 /* Propagate this volume change back to the inputs */
1255 if (virtual_volume_changed
)
1256 if (propagate
&& (s
->flags
& PA_SINK_FLAT_VOLUME
))
1257 pa_sink_propagate_flat_volume(s
);
1259 if (s
->set_volume
) {
1260 /* If we have a function set_volume(), then we do not apply a
1261 * soft volume by default. However, set_volume() is free to
1262 * apply one to s->soft_volume */
1264 pa_cvolume_reset(&s
->soft_volume
, s
->sample_spec
.channels
);
1268 /* If we have no function set_volume(), then the soft volume
1269 * becomes the virtual volume */
1270 s
->soft_volume
= s
->virtual_volume
;
1272 /* This tells the sink that soft and/or virtual volume changed */
1274 pa_assert_se(pa_asyncmsgq_send(s
->asyncmsgq
, PA_MSGOBJECT(s
), PA_SINK_MESSAGE_SET_VOLUME
, NULL
, 0, NULL
) == 0);
1276 if (virtual_volume_changed
)
1277 pa_subscription_post(s
->core
, PA_SUBSCRIPTION_EVENT_SINK
|PA_SUBSCRIPTION_EVENT_CHANGE
, s
->index
);
1280 /* Called from main thread. Only to be called by sink implementor */
1281 void pa_sink_set_soft_volume(pa_sink
*s
, const pa_cvolume
*volume
) {
1282 pa_sink_assert_ref(s
);
1285 s
->soft_volume
= *volume
;
1287 if (PA_SINK_IS_LINKED(s
->state
))
1288 pa_assert_se(pa_asyncmsgq_send(s
->asyncmsgq
, PA_MSGOBJECT(s
), PA_SINK_MESSAGE_SET_VOLUME
, NULL
, 0, NULL
) == 0);
1290 s
->thread_info
.soft_volume
= *volume
;
1293 /* Called from main thread */
1294 const pa_cvolume
*pa_sink_get_volume(pa_sink
*s
, pa_bool_t force_refresh
, pa_bool_t reference
) {
1295 pa_sink_assert_ref(s
);
1297 if (s
->refresh_volume
|| force_refresh
) {
1298 struct pa_cvolume old_virtual_volume
= s
->virtual_volume
;
1303 pa_assert_se(pa_asyncmsgq_send(s
->asyncmsgq
, PA_MSGOBJECT(s
), PA_SINK_MESSAGE_GET_VOLUME
, NULL
, 0, NULL
) == 0);
1305 if (!pa_cvolume_equal(&old_virtual_volume
, &s
->virtual_volume
)) {
1307 s
->reference_volume
= s
->virtual_volume
;
1309 if (s
->flags
& PA_SINK_FLAT_VOLUME
)
1310 pa_sink_propagate_flat_volume(s
);
1312 pa_subscription_post(s
->core
, PA_SUBSCRIPTION_EVENT_SINK
|PA_SUBSCRIPTION_EVENT_CHANGE
, s
->index
);
1316 return reference
? &s
->reference_volume
: &s
->virtual_volume
;
1319 /* Called from main thread */
1320 void pa_sink_volume_changed(pa_sink
*s
, const pa_cvolume
*new_volume
) {
1321 pa_sink_assert_ref(s
);
1323 /* The sink implementor may call this if the volume changed to make sure everyone is notified */
1325 if (pa_cvolume_equal(&s
->virtual_volume
, new_volume
))
1328 s
->reference_volume
= s
->virtual_volume
= *new_volume
;
1330 if (s
->flags
& PA_SINK_FLAT_VOLUME
)
1331 pa_sink_propagate_flat_volume(s
);
1333 pa_subscription_post(s
->core
, PA_SUBSCRIPTION_EVENT_SINK
|PA_SUBSCRIPTION_EVENT_CHANGE
, s
->index
);
1336 /* Called from main thread */
1337 void pa_sink_set_mute(pa_sink
*s
, pa_bool_t mute
) {
1338 pa_bool_t old_muted
;
1340 pa_sink_assert_ref(s
);
1341 pa_assert(PA_SINK_IS_LINKED(s
->state
));
1343 old_muted
= s
->muted
;
1349 pa_assert_se(pa_asyncmsgq_send(s
->asyncmsgq
, PA_MSGOBJECT(s
), PA_SINK_MESSAGE_SET_MUTE
, NULL
, 0, NULL
) == 0);
1351 if (old_muted
!= s
->muted
)
1352 pa_subscription_post(s
->core
, PA_SUBSCRIPTION_EVENT_SINK
|PA_SUBSCRIPTION_EVENT_CHANGE
, s
->index
);
1355 /* Called from main thread */
1356 pa_bool_t
pa_sink_get_mute(pa_sink
*s
, pa_bool_t force_refresh
) {
1358 pa_sink_assert_ref(s
);
1360 if (s
->refresh_muted
|| force_refresh
) {
1361 pa_bool_t old_muted
= s
->muted
;
1366 pa_assert_se(pa_asyncmsgq_send(s
->asyncmsgq
, PA_MSGOBJECT(s
), PA_SINK_MESSAGE_GET_MUTE
, NULL
, 0, NULL
) == 0);
1368 if (old_muted
!= s
->muted
) {
1369 pa_subscription_post(s
->core
, PA_SUBSCRIPTION_EVENT_SINK
|PA_SUBSCRIPTION_EVENT_CHANGE
, s
->index
);
1371 /* Make sure the soft mute status stays in sync */
1372 pa_assert_se(pa_asyncmsgq_send(s
->asyncmsgq
, PA_MSGOBJECT(s
), PA_SINK_MESSAGE_SET_MUTE
, NULL
, 0, NULL
) == 0);
1379 /* Called from main thread */
1380 void pa_sink_mute_changed(pa_sink
*s
, pa_bool_t new_muted
) {
1381 pa_sink_assert_ref(s
);
1383 /* The sink implementor may call this if the volume changed to make sure everyone is notified */
1385 if (s
->muted
== new_muted
)
1388 s
->muted
= new_muted
;
1389 pa_subscription_post(s
->core
, PA_SUBSCRIPTION_EVENT_SINK
|PA_SUBSCRIPTION_EVENT_CHANGE
, s
->index
);
1392 /* Called from main thread */
1393 pa_bool_t
pa_sink_update_proplist(pa_sink
*s
, pa_update_mode_t mode
, pa_proplist
*p
) {
1394 pa_sink_assert_ref(s
);
1397 pa_proplist_update(s
->proplist
, mode
, p
);
1399 if (PA_SINK_IS_LINKED(s
->state
)) {
1400 pa_hook_fire(&s
->core
->hooks
[PA_CORE_HOOK_SINK_PROPLIST_CHANGED
], s
);
1401 pa_subscription_post(s
->core
, PA_SUBSCRIPTION_EVENT_SINK
|PA_SUBSCRIPTION_EVENT_CHANGE
, s
->index
);
1407 /* Called from main thread */
1408 void pa_sink_set_description(pa_sink
*s
, const char *description
) {
1410 pa_sink_assert_ref(s
);
1412 if (!description
&& !pa_proplist_contains(s
->proplist
, PA_PROP_DEVICE_DESCRIPTION
))
1415 old
= pa_proplist_gets(s
->proplist
, PA_PROP_DEVICE_DESCRIPTION
);
1417 if (old
&& description
&& !strcmp(old
, description
))
1421 pa_proplist_sets(s
->proplist
, PA_PROP_DEVICE_DESCRIPTION
, description
);
1423 pa_proplist_unset(s
->proplist
, PA_PROP_DEVICE_DESCRIPTION
);
1425 if (s
->monitor_source
) {
1428 n
= pa_sprintf_malloc("Monitor Source of %s", description
? description
: s
->name
);
1429 pa_source_set_description(s
->monitor_source
, n
);
1433 if (PA_SINK_IS_LINKED(s
->state
)) {
1434 pa_subscription_post(s
->core
, PA_SUBSCRIPTION_EVENT_SINK
|PA_SUBSCRIPTION_EVENT_CHANGE
, s
->index
);
1435 pa_hook_fire(&s
->core
->hooks
[PA_CORE_HOOK_SINK_PROPLIST_CHANGED
], s
);
1439 /* Called from main thread */
1440 unsigned pa_sink_linked_by(pa_sink
*s
) {
1443 pa_sink_assert_ref(s
);
1444 pa_assert(PA_SINK_IS_LINKED(s
->state
));
1446 ret
= pa_idxset_size(s
->inputs
);
1448 /* We add in the number of streams connected to us here. Please
1449 * note the asymmmetry to pa_sink_used_by()! */
1451 if (s
->monitor_source
)
1452 ret
+= pa_source_linked_by(s
->monitor_source
);
1457 /* Called from main thread */
1458 unsigned pa_sink_used_by(pa_sink
*s
) {
1461 pa_sink_assert_ref(s
);
1462 pa_assert(PA_SINK_IS_LINKED(s
->state
));
1464 ret
= pa_idxset_size(s
->inputs
);
1465 pa_assert(ret
>= s
->n_corked
);
1467 /* Streams connected to our monitor source do not matter for
1468 * pa_sink_used_by()!.*/
1470 return ret
- s
->n_corked
;
1473 /* Called from main thread */
1474 unsigned pa_sink_check_suspend(pa_sink
*s
) {
1479 pa_sink_assert_ref(s
);
1481 if (!PA_SINK_IS_LINKED(s
->state
))
1486 for (i
= PA_SINK_INPUT(pa_idxset_first(s
->inputs
, &idx
)); i
; i
= PA_SINK_INPUT(pa_idxset_next(s
->inputs
, &idx
))) {
1487 pa_sink_input_state_t st
;
1489 st
= pa_sink_input_get_state(i
);
1490 pa_assert(PA_SINK_INPUT_IS_LINKED(st
));
1492 if (st
== PA_SINK_INPUT_CORKED
)
1495 if (i
->flags
& PA_SINK_INPUT_DONT_INHIBIT_AUTO_SUSPEND
)
1501 if (s
->monitor_source
)
1502 ret
+= pa_source_check_suspend(s
->monitor_source
);
1507 /* Called from the IO thread */
1508 static void sync_input_volumes_within_thread(pa_sink
*s
) {
1512 pa_sink_assert_ref(s
);
1514 while ((i
= PA_SINK_INPUT(pa_hashmap_iterate(s
->thread_info
.inputs
, &state
, NULL
)))) {
1515 if (pa_cvolume_equal(&i
->thread_info
.soft_volume
, &i
->soft_volume
))
1518 i
->thread_info
.soft_volume
= i
->soft_volume
;
1519 pa_sink_input_request_rewind(i
, 0, TRUE
, FALSE
, FALSE
);
1523 /* Called from IO thread, except when it is not */
1524 int pa_sink_process_msg(pa_msgobject
*o
, int code
, void *userdata
, int64_t offset
, pa_memchunk
*chunk
) {
1525 pa_sink
*s
= PA_SINK(o
);
1526 pa_sink_assert_ref(s
);
1528 switch ((pa_sink_message_t
) code
) {
1530 case PA_SINK_MESSAGE_ADD_INPUT
: {
1531 pa_sink_input
*i
= PA_SINK_INPUT(userdata
);
1533 /* If you change anything here, make sure to change the
1534 * sink input handling a few lines down at
1535 * PA_SINK_MESSAGE_FINISH_MOVE, too. */
1537 pa_hashmap_put(s
->thread_info
.inputs
, PA_UINT32_TO_PTR(i
->index
), pa_sink_input_ref(i
));
1539 /* Since the caller sleeps in pa_sink_input_put(), we can
1540 * safely access data outside of thread_info even though
1543 if ((i
->thread_info
.sync_prev
= i
->sync_prev
)) {
1544 pa_assert(i
->sink
== i
->thread_info
.sync_prev
->sink
);
1545 pa_assert(i
->sync_prev
->sync_next
== i
);
1546 i
->thread_info
.sync_prev
->thread_info
.sync_next
= i
;
1549 if ((i
->thread_info
.sync_next
= i
->sync_next
)) {
1550 pa_assert(i
->sink
== i
->thread_info
.sync_next
->sink
);
1551 pa_assert(i
->sync_next
->sync_prev
== i
);
1552 i
->thread_info
.sync_next
->thread_info
.sync_prev
= i
;
1555 pa_assert(!i
->thread_info
.attached
);
1556 i
->thread_info
.attached
= TRUE
;
1561 pa_sink_input_set_state_within_thread(i
, i
->state
);
1563 /* The requested latency of the sink input needs to be
1564 * fixed up and then configured on the sink */
1566 if (i
->thread_info
.requested_sink_latency
!= (pa_usec_t
) -1)
1567 pa_sink_input_set_requested_latency_within_thread(i
, i
->thread_info
.requested_sink_latency
);
1569 pa_sink_input_update_max_rewind(i
, s
->thread_info
.max_rewind
);
1570 pa_sink_input_update_max_request(i
, s
->thread_info
.max_request
);
1572 /* We don't rewind here automatically. This is left to the
1573 * sink input implementor because some sink inputs need a
1574 * slow start, i.e. need some time to buffer client
1575 * samples before beginning streaming. */
1577 /* In flat volume mode we need to update the volume as
1579 return o
->process_msg(o
, PA_SINK_MESSAGE_SET_VOLUME
, NULL
, 0, NULL
);
1582 case PA_SINK_MESSAGE_REMOVE_INPUT
: {
1583 pa_sink_input
*i
= PA_SINK_INPUT(userdata
);
1585 /* If you change anything here, make sure to change the
1586 * sink input handling a few lines down at
1587 * PA_SINK_MESSAGE_PREPAPRE_MOVE, too. */
1592 pa_sink_input_set_state_within_thread(i
, i
->state
);
1594 pa_assert(i
->thread_info
.attached
);
1595 i
->thread_info
.attached
= FALSE
;
1597 /* Since the caller sleeps in pa_sink_input_unlink(),
1598 * we can safely access data outside of thread_info even
1599 * though it is mutable */
1601 pa_assert(!i
->sync_prev
);
1602 pa_assert(!i
->sync_next
);
1604 if (i
->thread_info
.sync_prev
) {
1605 i
->thread_info
.sync_prev
->thread_info
.sync_next
= i
->thread_info
.sync_prev
->sync_next
;
1606 i
->thread_info
.sync_prev
= NULL
;
1609 if (i
->thread_info
.sync_next
) {
1610 i
->thread_info
.sync_next
->thread_info
.sync_prev
= i
->thread_info
.sync_next
->sync_prev
;
1611 i
->thread_info
.sync_next
= NULL
;
1614 if (pa_hashmap_remove(s
->thread_info
.inputs
, PA_UINT32_TO_PTR(i
->index
)))
1615 pa_sink_input_unref(i
);
1617 pa_sink_invalidate_requested_latency(s
);
1618 pa_sink_request_rewind(s
, (size_t) -1);
1620 /* In flat volume mode we need to update the volume as
1622 return o
->process_msg(o
, PA_SINK_MESSAGE_SET_VOLUME
, NULL
, 0, NULL
);
1625 case PA_SINK_MESSAGE_START_MOVE
: {
1626 pa_sink_input
*i
= PA_SINK_INPUT(userdata
);
1628 /* We don't support moving synchronized streams. */
1629 pa_assert(!i
->sync_prev
);
1630 pa_assert(!i
->sync_next
);
1631 pa_assert(!i
->thread_info
.sync_next
);
1632 pa_assert(!i
->thread_info
.sync_prev
);
1634 if (i
->thread_info
.state
!= PA_SINK_INPUT_CORKED
) {
1636 size_t sink_nbytes
, total_nbytes
;
1638 /* Get the latency of the sink */
1639 if (!(s
->flags
& PA_SINK_LATENCY
) ||
1640 PA_MSGOBJECT(s
)->process_msg(PA_MSGOBJECT(s
), PA_SINK_MESSAGE_GET_LATENCY
, &usec
, 0, NULL
) < 0)
1643 sink_nbytes
= pa_usec_to_bytes(usec
, &s
->sample_spec
);
1644 total_nbytes
= sink_nbytes
+ pa_memblockq_get_length(i
->thread_info
.render_memblockq
);
1646 if (total_nbytes
> 0) {
1647 i
->thread_info
.rewrite_nbytes
= i
->thread_info
.resampler
? pa_resampler_request(i
->thread_info
.resampler
, total_nbytes
) : total_nbytes
;
1648 i
->thread_info
.rewrite_flush
= TRUE
;
1649 pa_sink_input_process_rewind(i
, sink_nbytes
);
1656 pa_assert(i
->thread_info
.attached
);
1657 i
->thread_info
.attached
= FALSE
;
1659 /* Let's remove the sink input ...*/
1660 if (pa_hashmap_remove(s
->thread_info
.inputs
, PA_UINT32_TO_PTR(i
->index
)))
1661 pa_sink_input_unref(i
);
1663 pa_sink_invalidate_requested_latency(s
);
1665 pa_log_debug("Requesting rewind due to started move");
1666 pa_sink_request_rewind(s
, (size_t) -1);
1668 /* In flat volume mode we need to update the volume as
1670 return o
->process_msg(o
, PA_SINK_MESSAGE_SET_VOLUME
, NULL
, 0, NULL
);
1673 case PA_SINK_MESSAGE_FINISH_MOVE
: {
1674 pa_sink_input
*i
= PA_SINK_INPUT(userdata
);
1676 /* We don't support moving synchronized streams. */
1677 pa_assert(!i
->sync_prev
);
1678 pa_assert(!i
->sync_next
);
1679 pa_assert(!i
->thread_info
.sync_next
);
1680 pa_assert(!i
->thread_info
.sync_prev
);
1682 pa_hashmap_put(s
->thread_info
.inputs
, PA_UINT32_TO_PTR(i
->index
), pa_sink_input_ref(i
));
1684 pa_assert(!i
->thread_info
.attached
);
1685 i
->thread_info
.attached
= TRUE
;
1690 if (i
->thread_info
.requested_sink_latency
!= (pa_usec_t
) -1)
1691 pa_sink_input_set_requested_latency_within_thread(i
, i
->thread_info
.requested_sink_latency
);
1693 pa_sink_input_update_max_rewind(i
, s
->thread_info
.max_rewind
);
1694 pa_sink_input_update_max_request(i
, s
->thread_info
.max_request
);
1696 if (i
->thread_info
.state
!= PA_SINK_INPUT_CORKED
) {
1700 /* Get the latency of the sink */
1701 if (!(s
->flags
& PA_SINK_LATENCY
) ||
1702 PA_MSGOBJECT(s
)->process_msg(PA_MSGOBJECT(s
), PA_SINK_MESSAGE_GET_LATENCY
, &usec
, 0, NULL
) < 0)
1705 nbytes
= pa_usec_to_bytes(usec
, &s
->sample_spec
);
1708 pa_sink_input_drop(i
, nbytes
);
1710 pa_log_debug("Requesting rewind due to finished move");
1711 pa_sink_request_rewind(s
, nbytes
);
1714 /* In flat volume mode we need to update the volume as
1716 return o
->process_msg(o
, PA_SINK_MESSAGE_SET_VOLUME
, NULL
, 0, NULL
);
1719 case PA_SINK_MESSAGE_SET_VOLUME
:
1721 if (!pa_cvolume_equal(&s
->thread_info
.soft_volume
, &s
->soft_volume
)) {
1722 s
->thread_info
.soft_volume
= s
->soft_volume
;
1723 pa_sink_request_rewind(s
, (size_t) -1);
1726 if (!(s
->flags
& PA_SINK_FLAT_VOLUME
))
1729 /* Fall through ... */
1731 case PA_SINK_MESSAGE_SYNC_VOLUMES
:
1732 sync_input_volumes_within_thread(s
);
1735 case PA_SINK_MESSAGE_GET_VOLUME
:
1738 case PA_SINK_MESSAGE_SET_MUTE
:
1740 if (s
->thread_info
.soft_muted
!= s
->muted
) {
1741 s
->thread_info
.soft_muted
= s
->muted
;
1742 pa_sink_request_rewind(s
, (size_t) -1);
1747 case PA_SINK_MESSAGE_GET_MUTE
:
1750 case PA_SINK_MESSAGE_SET_STATE
: {
1752 pa_bool_t suspend_change
=
1753 (s
->thread_info
.state
== PA_SINK_SUSPENDED
&& PA_SINK_IS_OPENED(PA_PTR_TO_UINT(userdata
))) ||
1754 (PA_SINK_IS_OPENED(s
->thread_info
.state
) && PA_PTR_TO_UINT(userdata
) == PA_SINK_SUSPENDED
);
1756 s
->thread_info
.state
= PA_PTR_TO_UINT(userdata
);
1758 if (s
->thread_info
.state
== PA_SINK_SUSPENDED
) {
1759 s
->thread_info
.rewind_nbytes
= 0;
1760 s
->thread_info
.rewind_requested
= FALSE
;
1763 if (suspend_change
) {
1767 while ((i
= pa_hashmap_iterate(s
->thread_info
.inputs
, &state
, NULL
)))
1768 if (i
->suspend_within_thread
)
1769 i
->suspend_within_thread(i
, s
->thread_info
.state
== PA_SINK_SUSPENDED
);
1775 case PA_SINK_MESSAGE_DETACH
:
1777 /* Detach all streams */
1778 pa_sink_detach_within_thread(s
);
1781 case PA_SINK_MESSAGE_ATTACH
:
1783 /* Reattach all streams */
1784 pa_sink_attach_within_thread(s
);
1787 case PA_SINK_MESSAGE_GET_REQUESTED_LATENCY
: {
1789 pa_usec_t
*usec
= userdata
;
1790 *usec
= pa_sink_get_requested_latency_within_thread(s
);
1792 if (*usec
== (pa_usec_t
) -1)
1793 *usec
= s
->thread_info
.max_latency
;
1798 case PA_SINK_MESSAGE_SET_LATENCY_RANGE
: {
1799 pa_usec_t
*r
= userdata
;
1801 pa_sink_set_latency_range_within_thread(s
, r
[0], r
[1]);
1806 case PA_SINK_MESSAGE_GET_LATENCY_RANGE
: {
1807 pa_usec_t
*r
= userdata
;
1809 r
[0] = s
->thread_info
.min_latency
;
1810 r
[1] = s
->thread_info
.max_latency
;
1815 case PA_SINK_MESSAGE_GET_MAX_REWIND
:
1817 *((size_t*) userdata
) = s
->thread_info
.max_rewind
;
1820 case PA_SINK_MESSAGE_GET_MAX_REQUEST
:
1822 *((size_t*) userdata
) = s
->thread_info
.max_request
;
1825 case PA_SINK_MESSAGE_SET_MAX_REWIND
:
1827 pa_sink_set_max_rewind_within_thread(s
, (size_t) offset
);
1830 case PA_SINK_MESSAGE_SET_MAX_REQUEST
:
1832 pa_sink_set_max_request_within_thread(s
, (size_t) offset
);
1835 case PA_SINK_MESSAGE_GET_LATENCY
:
1836 case PA_SINK_MESSAGE_MAX
:
1843 /* Called from main thread */
1844 int pa_sink_suspend_all(pa_core
*c
, pa_bool_t suspend
, pa_suspend_cause_t cause
) {
1849 pa_core_assert_ref(c
);
1850 pa_assert(cause
!= 0);
1852 for (sink
= PA_SINK(pa_idxset_first(c
->sinks
, &idx
)); sink
; sink
= PA_SINK(pa_idxset_next(c
->sinks
, &idx
))) {
1855 if ((r
= pa_sink_suspend(sink
, suspend
, cause
)) < 0)
1862 /* Called from main thread */
1863 void pa_sink_detach(pa_sink
*s
) {
1864 pa_sink_assert_ref(s
);
1865 pa_assert(PA_SINK_IS_LINKED(s
->state
));
1867 pa_assert_se(pa_asyncmsgq_send(s
->asyncmsgq
, PA_MSGOBJECT(s
), PA_SINK_MESSAGE_DETACH
, NULL
, 0, NULL
) == 0);
1870 /* Called from main thread */
1871 void pa_sink_attach(pa_sink
*s
) {
1872 pa_sink_assert_ref(s
);
1873 pa_assert(PA_SINK_IS_LINKED(s
->state
));
1875 pa_assert_se(pa_asyncmsgq_send(s
->asyncmsgq
, PA_MSGOBJECT(s
), PA_SINK_MESSAGE_ATTACH
, NULL
, 0, NULL
) == 0);
1878 /* Called from IO thread */
1879 void pa_sink_detach_within_thread(pa_sink
*s
) {
1883 pa_sink_assert_ref(s
);
1884 pa_assert(PA_SINK_IS_LINKED(s
->thread_info
.state
));
1886 while ((i
= pa_hashmap_iterate(s
->thread_info
.inputs
, &state
, NULL
)))
1890 if (s
->monitor_source
)
1891 pa_source_detach_within_thread(s
->monitor_source
);
1894 /* Called from IO thread */
1895 void pa_sink_attach_within_thread(pa_sink
*s
) {
1899 pa_sink_assert_ref(s
);
1900 pa_assert(PA_SINK_IS_LINKED(s
->thread_info
.state
));
1902 while ((i
= pa_hashmap_iterate(s
->thread_info
.inputs
, &state
, NULL
)))
1906 if (s
->monitor_source
)
1907 pa_source_attach_within_thread(s
->monitor_source
);
1910 /* Called from IO thread */
1911 void pa_sink_request_rewind(pa_sink
*s
, size_t nbytes
) {
1912 pa_sink_assert_ref(s
);
1913 pa_assert(PA_SINK_IS_LINKED(s
->thread_info
.state
));
1915 if (s
->thread_info
.state
== PA_SINK_SUSPENDED
)
1918 if (nbytes
== (size_t) -1)
1919 nbytes
= s
->thread_info
.max_rewind
;
1921 nbytes
= PA_MIN(nbytes
, s
->thread_info
.max_rewind
);
1923 if (s
->thread_info
.rewind_requested
&&
1924 nbytes
<= s
->thread_info
.rewind_nbytes
)
1927 s
->thread_info
.rewind_nbytes
= nbytes
;
1928 s
->thread_info
.rewind_requested
= TRUE
;
1930 if (s
->request_rewind
)
1931 s
->request_rewind(s
);
1934 /* Called from IO thread */
1935 pa_usec_t
pa_sink_get_requested_latency_within_thread(pa_sink
*s
) {
1936 pa_usec_t result
= (pa_usec_t
) -1;
1939 pa_usec_t monitor_latency
;
1941 pa_sink_assert_ref(s
);
1943 if (!(s
->flags
& PA_SINK_DYNAMIC_LATENCY
))
1944 return PA_CLAMP(s
->fixed_latency
, s
->thread_info
.min_latency
, s
->thread_info
.max_latency
);
1946 if (s
->thread_info
.requested_latency_valid
)
1947 return s
->thread_info
.requested_latency
;
1949 while ((i
= pa_hashmap_iterate(s
->thread_info
.inputs
, &state
, NULL
)))
1951 if (i
->thread_info
.requested_sink_latency
!= (pa_usec_t
) -1 &&
1952 (result
== (pa_usec_t
) -1 || result
> i
->thread_info
.requested_sink_latency
))
1953 result
= i
->thread_info
.requested_sink_latency
;
1955 monitor_latency
= pa_source_get_requested_latency_within_thread(s
->monitor_source
);
1957 if (monitor_latency
!= (pa_usec_t
) -1 &&
1958 (result
== (pa_usec_t
) -1 || result
> monitor_latency
))
1959 result
= monitor_latency
;
1961 if (result
!= (pa_usec_t
) -1)
1962 result
= PA_CLAMP(result
, s
->thread_info
.min_latency
, s
->thread_info
.max_latency
);
1964 if (PA_SINK_IS_LINKED(s
->thread_info
.state
)) {
1965 /* Only cache if properly initialized */
1966 s
->thread_info
.requested_latency
= result
;
1967 s
->thread_info
.requested_latency_valid
= TRUE
;
1973 /* Called from main thread */
1974 pa_usec_t
pa_sink_get_requested_latency(pa_sink
*s
) {
1977 pa_sink_assert_ref(s
);
1978 pa_assert(PA_SINK_IS_LINKED(s
->state
));
1980 if (s
->state
== PA_SINK_SUSPENDED
)
1983 pa_assert_se(pa_asyncmsgq_send(s
->asyncmsgq
, PA_MSGOBJECT(s
), PA_SINK_MESSAGE_GET_REQUESTED_LATENCY
, &usec
, 0, NULL
) == 0);
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_rewind_within_thread(pa_sink
*s
, size_t max_rewind
) {
1992 pa_sink_assert_ref(s
);
1994 if (max_rewind
== s
->thread_info
.max_rewind
)
1997 s
->thread_info
.max_rewind
= max_rewind
;
1999 if (PA_SINK_IS_LINKED(s
->thread_info
.state
)) {
2000 while ((i
= pa_hashmap_iterate(s
->thread_info
.inputs
, &state
, NULL
)))
2001 pa_sink_input_update_max_rewind(i
, s
->thread_info
.max_rewind
);
2004 if (s
->monitor_source
)
2005 pa_source_set_max_rewind_within_thread(s
->monitor_source
, s
->thread_info
.max_rewind
);
2008 /* Called from main thread */
2009 void pa_sink_set_max_rewind(pa_sink
*s
, size_t max_rewind
) {
2010 pa_sink_assert_ref(s
);
2012 if (PA_SINK_IS_LINKED(s
->state
))
2013 pa_assert_se(pa_asyncmsgq_send(s
->asyncmsgq
, PA_MSGOBJECT(s
), PA_SINK_MESSAGE_SET_MAX_REWIND
, NULL
, max_rewind
, NULL
) == 0);
2015 pa_sink_set_max_rewind_within_thread(s
, max_rewind
);
2018 /* Called from IO as well as the main thread -- the latter only before the IO thread started up */
2019 void pa_sink_set_max_request_within_thread(pa_sink
*s
, size_t max_request
) {
2022 pa_sink_assert_ref(s
);
2024 if (max_request
== s
->thread_info
.max_request
)
2027 s
->thread_info
.max_request
= max_request
;
2029 if (PA_SINK_IS_LINKED(s
->thread_info
.state
)) {
2032 while ((i
= pa_hashmap_iterate(s
->thread_info
.inputs
, &state
, NULL
)))
2033 pa_sink_input_update_max_request(i
, s
->thread_info
.max_request
);
2037 /* Called from main thread */
2038 void pa_sink_set_max_request(pa_sink
*s
, size_t max_request
) {
2039 pa_sink_assert_ref(s
);
2041 if (PA_SINK_IS_LINKED(s
->state
))
2042 pa_assert_se(pa_asyncmsgq_send(s
->asyncmsgq
, PA_MSGOBJECT(s
), PA_SINK_MESSAGE_SET_MAX_REQUEST
, NULL
, max_request
, NULL
) == 0);
2044 pa_sink_set_max_request_within_thread(s
, max_request
);
2047 /* Called from IO thread */
2048 void pa_sink_invalidate_requested_latency(pa_sink
*s
) {
2052 pa_sink_assert_ref(s
);
2054 if (!(s
->flags
& PA_SINK_DYNAMIC_LATENCY
))
2057 s
->thread_info
.requested_latency_valid
= FALSE
;
2059 if (PA_SINK_IS_LINKED(s
->thread_info
.state
)) {
2061 if (s
->update_requested_latency
)
2062 s
->update_requested_latency(s
);
2064 while ((i
= pa_hashmap_iterate(s
->thread_info
.inputs
, &state
, NULL
)))
2065 if (i
->update_sink_requested_latency
)
2066 i
->update_sink_requested_latency(i
);
2070 /* Called from main thread */
2071 void pa_sink_set_latency_range(pa_sink
*s
, pa_usec_t min_latency
, pa_usec_t max_latency
) {
2072 pa_sink_assert_ref(s
);
2074 /* min_latency == 0: no limit
2075 * min_latency anything else: specified limit
2077 * Similar for max_latency */
2079 if (min_latency
< ABSOLUTE_MIN_LATENCY
)
2080 min_latency
= ABSOLUTE_MIN_LATENCY
;
2082 if (max_latency
<= 0 ||
2083 max_latency
> ABSOLUTE_MAX_LATENCY
)
2084 max_latency
= ABSOLUTE_MAX_LATENCY
;
2086 pa_assert(min_latency
<= max_latency
);
2088 /* Hmm, let's see if someone forgot to set PA_SINK_DYNAMIC_LATENCY here... */
2089 pa_assert((min_latency
== ABSOLUTE_MIN_LATENCY
&&
2090 max_latency
== ABSOLUTE_MAX_LATENCY
) ||
2091 (s
->flags
& PA_SINK_DYNAMIC_LATENCY
));
2093 if (PA_SINK_IS_LINKED(s
->state
)) {
2099 pa_assert_se(pa_asyncmsgq_send(s
->asyncmsgq
, PA_MSGOBJECT(s
), PA_SINK_MESSAGE_SET_LATENCY_RANGE
, r
, 0, NULL
) == 0);
2101 pa_sink_set_latency_range_within_thread(s
, min_latency
, max_latency
);
2104 /* Called from main thread */
2105 void pa_sink_get_latency_range(pa_sink
*s
, pa_usec_t
*min_latency
, pa_usec_t
*max_latency
) {
2106 pa_sink_assert_ref(s
);
2107 pa_assert(min_latency
);
2108 pa_assert(max_latency
);
2110 if (PA_SINK_IS_LINKED(s
->state
)) {
2111 pa_usec_t r
[2] = { 0, 0 };
2113 pa_assert_se(pa_asyncmsgq_send(s
->asyncmsgq
, PA_MSGOBJECT(s
), PA_SINK_MESSAGE_GET_LATENCY_RANGE
, r
, 0, NULL
) == 0);
2115 *min_latency
= r
[0];
2116 *max_latency
= r
[1];
2118 *min_latency
= s
->thread_info
.min_latency
;
2119 *max_latency
= s
->thread_info
.max_latency
;
2123 /* Called from IO thread */
2124 void pa_sink_set_latency_range_within_thread(pa_sink
*s
, pa_usec_t min_latency
, pa_usec_t max_latency
) {
2127 pa_sink_assert_ref(s
);
2129 pa_assert(min_latency
>= ABSOLUTE_MIN_LATENCY
);
2130 pa_assert(max_latency
<= ABSOLUTE_MAX_LATENCY
);
2131 pa_assert(min_latency
<= max_latency
);
2133 /* Hmm, let's see if someone forgot to set PA_SINK_DYNAMIC_LATENCY here... */
2134 pa_assert((min_latency
== ABSOLUTE_MIN_LATENCY
&&
2135 max_latency
== ABSOLUTE_MAX_LATENCY
) ||
2136 (s
->flags
& PA_SINK_DYNAMIC_LATENCY
));
2138 s
->thread_info
.min_latency
= min_latency
;
2139 s
->thread_info
.max_latency
= max_latency
;
2141 if (PA_SINK_IS_LINKED(s
->thread_info
.state
)) {
2144 while ((i
= pa_hashmap_iterate(s
->thread_info
.inputs
, &state
, NULL
)))
2145 if (i
->update_sink_latency_range
)
2146 i
->update_sink_latency_range(i
);
2149 pa_sink_invalidate_requested_latency(s
);
2151 pa_source_set_latency_range_within_thread(s
->monitor_source
, min_latency
, max_latency
);
2154 /* Called from main thread, before the sink is put */
2155 void pa_sink_set_fixed_latency(pa_sink
*s
, pa_usec_t latency
) {
2156 pa_sink_assert_ref(s
);
2158 pa_assert(pa_sink_get_state(s
) == PA_SINK_INIT
);
2160 if (latency
< ABSOLUTE_MIN_LATENCY
)
2161 latency
= ABSOLUTE_MIN_LATENCY
;
2163 if (latency
> ABSOLUTE_MAX_LATENCY
)
2164 latency
= ABSOLUTE_MAX_LATENCY
;
2166 s
->fixed_latency
= latency
;
2167 pa_source_set_fixed_latency(s
->monitor_source
, latency
);
2170 /* Called from main context */
2171 size_t pa_sink_get_max_rewind(pa_sink
*s
) {
2173 pa_sink_assert_ref(s
);
2175 if (!PA_SINK_IS_LINKED(s
->state
))
2176 return s
->thread_info
.max_rewind
;
2178 pa_assert_se(pa_asyncmsgq_send(s
->asyncmsgq
, PA_MSGOBJECT(s
), PA_SINK_MESSAGE_GET_MAX_REWIND
, &r
, 0, NULL
) == 0);
2183 /* Called from main context */
2184 size_t pa_sink_get_max_request(pa_sink
*s
) {
2186 pa_sink_assert_ref(s
);
2188 if (!PA_SINK_IS_LINKED(s
->state
))
2189 return s
->thread_info
.max_request
;
2191 pa_assert_se(pa_asyncmsgq_send(s
->asyncmsgq
, PA_MSGOBJECT(s
), PA_SINK_MESSAGE_GET_MAX_REQUEST
, &r
, 0, NULL
) == 0);
2196 /* Called from main context */
2197 pa_bool_t
pa_device_init_icon(pa_proplist
*p
, pa_bool_t is_sink
) {
2198 const char *ff
, *c
, *t
= NULL
, *s
= "", *profile
, *bus
;
2202 if (pa_proplist_contains(p
, PA_PROP_DEVICE_ICON_NAME
))
2205 if ((ff
= pa_proplist_gets(p
, PA_PROP_DEVICE_FORM_FACTOR
))) {
2207 if (pa_streq(ff
, "microphone"))
2208 t
= "audio-input-microphone";
2209 else if (pa_streq(ff
, "webcam"))
2211 else if (pa_streq(ff
, "computer"))
2213 else if (pa_streq(ff
, "handset"))
2215 else if (pa_streq(ff
, "portable"))
2216 t
= "multimedia-player";
2217 else if (pa_streq(ff
, "tv"))
2218 t
= "video-display";
2221 * The following icons are not part of the icon naming spec,
2222 * because Rodney Dawes sucks as the maintainer of that spec.
2224 * http://lists.freedesktop.org/archives/xdg/2009-May/010397.html
2226 else if (pa_streq(ff
, "headset"))
2227 t
= "audio-headset";
2228 else if (pa_streq(ff
, "headphone"))
2229 t
= "audio-headphones";
2230 else if (pa_streq(ff
, "speaker"))
2231 t
= "audio-speakers";
2232 else if (pa_streq(ff
, "hands-free"))
2233 t
= "audio-handsfree";
2237 if ((c
= pa_proplist_gets(p
, PA_PROP_DEVICE_CLASS
)))
2238 if (pa_streq(c
, "modem"))
2245 t
= "audio-input-microphone";
2248 if ((profile
= pa_proplist_gets(p
, PA_PROP_DEVICE_PROFILE_NAME
))) {
2249 if (strstr(profile
, "analog"))
2251 else if (strstr(profile
, "iec958"))
2253 else if (strstr(profile
, "hdmi"))
2257 bus
= pa_proplist_gets(p
, PA_PROP_DEVICE_BUS
);
2259 pa_proplist_setf(p
, PA_PROP_DEVICE_ICON_NAME
, "%s%s%s%s", t
, pa_strempty(s
), bus
? "-" : "", pa_strempty(bus
));
2264 pa_bool_t
pa_device_init_description(pa_proplist
*p
) {
2268 if (pa_proplist_contains(p
, PA_PROP_DEVICE_DESCRIPTION
))
2271 if ((s
= pa_proplist_gets(p
, PA_PROP_DEVICE_FORM_FACTOR
)))
2272 if (pa_streq(s
, "internal")) {
2273 pa_proplist_sets(p
, PA_PROP_DEVICE_DESCRIPTION
, _("Internal Audio"));
2277 if ((s
= pa_proplist_gets(p
, PA_PROP_DEVICE_CLASS
)))
2278 if (pa_streq(s
, "modem")) {
2279 pa_proplist_sets(p
, PA_PROP_DEVICE_DESCRIPTION
, _("Modem"));
2283 if ((s
= pa_proplist_gets(p
, PA_PROP_DEVICE_PRODUCT_NAME
))) {
2284 pa_proplist_sets(p
, PA_PROP_DEVICE_DESCRIPTION
, s
);