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
) {
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_set_port(pa_sink_new_data
*data
, const char *port
) {
106 pa_xfree(data
->active_port
);
107 data
->active_port
= pa_xstrdup(port
);
110 void pa_sink_new_data_done(pa_sink_new_data
*data
) {
113 pa_proplist_free(data
->proplist
);
118 while ((p
= pa_hashmap_steal_first(data
->ports
)))
119 pa_device_port_free(p
);
121 pa_hashmap_free(data
->ports
, NULL
, NULL
);
124 pa_xfree(data
->name
);
125 pa_xfree(data
->active_port
);
128 pa_device_port
*pa_device_port_new(const char *name
, const char *description
, size_t extra
) {
133 p
= pa_xmalloc(PA_ALIGN(sizeof(pa_device_port
)) + extra
);
134 p
->name
= pa_xstrdup(name
);
135 p
->description
= pa_xstrdup(description
);
142 void pa_device_port_free(pa_device_port
*p
) {
146 pa_xfree(p
->description
);
150 /* Called from main context */
151 static void reset_callbacks(pa_sink
*s
) {
155 s
->get_volume
= NULL
;
156 s
->set_volume
= NULL
;
159 s
->request_rewind
= NULL
;
160 s
->update_requested_latency
= NULL
;
164 /* Called from main context */
165 pa_sink
* pa_sink_new(
167 pa_sink_new_data
*data
,
168 pa_sink_flags_t flags
) {
172 char st
[PA_SAMPLE_SPEC_SNPRINT_MAX
], cm
[PA_CHANNEL_MAP_SNPRINT_MAX
];
173 pa_source_new_data source_data
;
179 pa_assert(data
->name
);
180 pa_assert_ctl_context();
182 s
= pa_msgobject_new(pa_sink
);
184 if (!(name
= pa_namereg_register(core
, data
->name
, PA_NAMEREG_SINK
, s
, data
->namereg_fail
))) {
185 pa_log_debug("Failed to register name %s.", data
->name
);
190 pa_sink_new_data_set_name(data
, name
);
192 if (pa_hook_fire(&core
->hooks
[PA_CORE_HOOK_SINK_NEW
], data
) < 0) {
194 pa_namereg_unregister(core
, name
);
198 /* FIXME, need to free s here on failure */
200 pa_return_null_if_fail(!data
->driver
|| pa_utf8_valid(data
->driver
));
201 pa_return_null_if_fail(data
->name
&& pa_utf8_valid(data
->name
) && data
->name
[0]);
203 pa_return_null_if_fail(data
->sample_spec_is_set
&& pa_sample_spec_valid(&data
->sample_spec
));
205 if (!data
->channel_map_is_set
)
206 pa_return_null_if_fail(pa_channel_map_init_auto(&data
->channel_map
, data
->sample_spec
.channels
, PA_CHANNEL_MAP_DEFAULT
));
208 pa_return_null_if_fail(pa_channel_map_valid(&data
->channel_map
));
209 pa_return_null_if_fail(data
->channel_map
.channels
== data
->sample_spec
.channels
);
211 if (!data
->volume_is_set
)
212 pa_cvolume_reset(&data
->volume
, data
->sample_spec
.channels
);
214 pa_return_null_if_fail(pa_cvolume_valid(&data
->volume
));
215 pa_return_null_if_fail(pa_cvolume_compatible(&data
->volume
, &data
->sample_spec
));
217 if (!data
->muted_is_set
)
221 pa_proplist_update(data
->proplist
, PA_UPDATE_MERGE
, data
->card
->proplist
);
223 pa_device_init_description(data
->proplist
);
224 pa_device_init_icon(data
->proplist
, TRUE
);
225 pa_device_init_intended_roles(data
->proplist
);
227 if (pa_hook_fire(&core
->hooks
[PA_CORE_HOOK_SINK_FIXATE
], data
) < 0) {
229 pa_namereg_unregister(core
, name
);
233 s
->parent
.parent
.free
= sink_free
;
234 s
->parent
.process_msg
= pa_sink_process_msg
;
237 s
->state
= PA_SINK_INIT
;
239 s
->suspend_cause
= 0;
240 s
->name
= pa_xstrdup(name
);
241 s
->proplist
= pa_proplist_copy(data
->proplist
);
242 s
->driver
= pa_xstrdup(pa_path_get_filename(data
->driver
));
243 s
->module
= data
->module
;
244 s
->card
= data
->card
;
246 s
->sample_spec
= data
->sample_spec
;
247 s
->channel_map
= data
->channel_map
;
249 s
->inputs
= pa_idxset_new(NULL
, NULL
);
252 s
->reference_volume
= s
->real_volume
= data
->volume
;
253 pa_cvolume_reset(&s
->soft_volume
, s
->sample_spec
.channels
);
254 s
->base_volume
= PA_VOLUME_NORM
;
255 s
->n_volume_steps
= PA_VOLUME_NORM
+1;
256 s
->muted
= data
->muted
;
257 s
->refresh_volume
= s
->refresh_muted
= FALSE
;
264 /* As a minor optimization we just steal the list instead of
266 s
->ports
= data
->ports
;
269 s
->active_port
= NULL
;
270 s
->save_port
= FALSE
;
272 if (data
->active_port
&& s
->ports
)
273 if ((s
->active_port
= pa_hashmap_get(s
->ports
, data
->active_port
)))
274 s
->save_port
= data
->save_port
;
276 if (!s
->active_port
&& s
->ports
) {
280 PA_HASHMAP_FOREACH(p
, s
->ports
, state
)
281 if (!s
->active_port
|| p
->priority
> s
->active_port
->priority
)
285 s
->save_volume
= data
->save_volume
;
286 s
->save_muted
= data
->save_muted
;
288 pa_silence_memchunk_get(
289 &core
->silence_cache
,
295 s
->thread_info
.rtpoll
= NULL
;
296 s
->thread_info
.inputs
= pa_hashmap_new(pa_idxset_trivial_hash_func
, pa_idxset_trivial_compare_func
);
297 s
->thread_info
.soft_volume
= s
->soft_volume
;
298 s
->thread_info
.soft_muted
= s
->muted
;
299 s
->thread_info
.state
= s
->state
;
300 s
->thread_info
.rewind_nbytes
= 0;
301 s
->thread_info
.rewind_requested
= FALSE
;
302 s
->thread_info
.max_rewind
= 0;
303 s
->thread_info
.max_request
= 0;
304 s
->thread_info
.requested_latency_valid
= FALSE
;
305 s
->thread_info
.requested_latency
= 0;
306 s
->thread_info
.min_latency
= ABSOLUTE_MIN_LATENCY
;
307 s
->thread_info
.max_latency
= ABSOLUTE_MAX_LATENCY
;
308 s
->thread_info
.fixed_latency
= flags
& PA_SINK_DYNAMIC_LATENCY
? 0 : DEFAULT_FIXED_LATENCY
;
310 /* FIXME: This should probably be moved to pa_sink_put() */
311 pa_assert_se(pa_idxset_put(core
->sinks
, s
, &s
->index
) >= 0);
314 pa_assert_se(pa_idxset_put(s
->card
->sinks
, s
, NULL
) >= 0);
316 pt
= pa_proplist_to_string_sep(s
->proplist
, "\n ");
317 pa_log_info("Created sink %u \"%s\" with sample spec %s and channel map %s\n %s",
320 pa_sample_spec_snprint(st
, sizeof(st
), &s
->sample_spec
),
321 pa_channel_map_snprint(cm
, sizeof(cm
), &s
->channel_map
),
325 pa_source_new_data_init(&source_data
);
326 pa_source_new_data_set_sample_spec(&source_data
, &s
->sample_spec
);
327 pa_source_new_data_set_channel_map(&source_data
, &s
->channel_map
);
328 source_data
.name
= pa_sprintf_malloc("%s.monitor", name
);
329 source_data
.driver
= data
->driver
;
330 source_data
.module
= data
->module
;
331 source_data
.card
= data
->card
;
333 dn
= pa_proplist_gets(s
->proplist
, PA_PROP_DEVICE_DESCRIPTION
);
334 pa_proplist_setf(source_data
.proplist
, PA_PROP_DEVICE_DESCRIPTION
, "Monitor of %s", dn
? dn
: s
->name
);
335 pa_proplist_sets(source_data
.proplist
, PA_PROP_DEVICE_CLASS
, "monitor");
337 s
->monitor_source
= pa_source_new(core
, &source_data
,
338 ((flags
& PA_SINK_LATENCY
) ? PA_SOURCE_LATENCY
: 0) |
339 ((flags
& PA_SINK_DYNAMIC_LATENCY
) ? PA_SOURCE_DYNAMIC_LATENCY
: 0));
341 pa_source_new_data_done(&source_data
);
343 if (!s
->monitor_source
) {
349 s
->monitor_source
->monitor_of
= s
;
351 pa_source_set_latency_range(s
->monitor_source
, s
->thread_info
.min_latency
, s
->thread_info
.max_latency
);
352 pa_source_set_fixed_latency(s
->monitor_source
, s
->thread_info
.fixed_latency
);
353 pa_source_set_max_rewind(s
->monitor_source
, s
->thread_info
.max_rewind
);
358 /* Called from main context */
359 static int sink_set_state(pa_sink
*s
, pa_sink_state_t state
) {
361 pa_bool_t suspend_change
;
362 pa_sink_state_t original_state
;
365 pa_assert_ctl_context();
367 if (s
->state
== state
)
370 original_state
= s
->state
;
373 (original_state
== PA_SINK_SUSPENDED
&& PA_SINK_IS_OPENED(state
)) ||
374 (PA_SINK_IS_OPENED(original_state
) && state
== PA_SINK_SUSPENDED
);
377 if ((ret
= s
->set_state(s
, state
)) < 0)
381 if ((ret
= pa_asyncmsgq_send(s
->asyncmsgq
, PA_MSGOBJECT(s
), PA_SINK_MESSAGE_SET_STATE
, PA_UINT_TO_PTR(state
), 0, NULL
)) < 0) {
384 s
->set_state(s
, original_state
);
391 if (state
!= PA_SINK_UNLINKED
) { /* if we enter UNLINKED state pa_sink_unlink() will fire the apropriate events */
392 pa_hook_fire(&s
->core
->hooks
[PA_CORE_HOOK_SINK_STATE_CHANGED
], s
);
393 pa_subscription_post(s
->core
, PA_SUBSCRIPTION_EVENT_SINK
| PA_SUBSCRIPTION_EVENT_CHANGE
, s
->index
);
396 if (suspend_change
) {
400 /* We're suspending or resuming, tell everyone about it */
402 PA_IDXSET_FOREACH(i
, s
->inputs
, idx
)
403 if (s
->state
== PA_SINK_SUSPENDED
&&
404 (i
->flags
& PA_SINK_INPUT_KILL_ON_SUSPEND
))
405 pa_sink_input_kill(i
);
407 i
->suspend(i
, state
== PA_SINK_SUSPENDED
);
409 if (s
->monitor_source
)
410 pa_source_sync_suspend(s
->monitor_source
);
416 /* Called from main context */
417 void pa_sink_put(pa_sink
* s
) {
418 pa_sink_assert_ref(s
);
419 pa_assert_ctl_context();
421 pa_assert(s
->state
== PA_SINK_INIT
);
423 /* The following fields must be initialized properly when calling _put() */
424 pa_assert(s
->asyncmsgq
);
425 pa_assert(s
->thread_info
.min_latency
<= s
->thread_info
.max_latency
);
427 /* Generally, flags should be initialized via pa_sink_new(). As a
428 * special exception we allow volume related flags to be set
429 * between _new() and _put(). */
431 if (!(s
->flags
& PA_SINK_HW_VOLUME_CTRL
))
432 s
->flags
|= PA_SINK_DECIBEL_VOLUME
;
434 if ((s
->flags
& PA_SINK_DECIBEL_VOLUME
) && s
->core
->flat_volumes
)
435 s
->flags
|= PA_SINK_FLAT_VOLUME
;
437 /* We assume that if the sink implementor changed the default
438 * volume he did so in real_volume, because that is the usual
439 * place where he is supposed to place his changes. */
440 s
->reference_volume
= s
->real_volume
;
442 s
->thread_info
.soft_volume
= s
->soft_volume
;
443 s
->thread_info
.soft_muted
= s
->muted
;
445 pa_assert((s
->flags
& PA_SINK_HW_VOLUME_CTRL
) || (s
->base_volume
== PA_VOLUME_NORM
&& s
->flags
& PA_SINK_DECIBEL_VOLUME
));
446 pa_assert(!(s
->flags
& PA_SINK_DECIBEL_VOLUME
) || s
->n_volume_steps
== PA_VOLUME_NORM
+1);
447 pa_assert(!(s
->flags
& PA_SINK_DYNAMIC_LATENCY
) == (s
->thread_info
.fixed_latency
!= 0));
448 pa_assert(!(s
->flags
& PA_SINK_LATENCY
) == !(s
->monitor_source
->flags
& PA_SOURCE_LATENCY
));
449 pa_assert(!(s
->flags
& PA_SINK_DYNAMIC_LATENCY
) == !(s
->monitor_source
->flags
& PA_SOURCE_DYNAMIC_LATENCY
));
451 pa_assert(s
->monitor_source
->thread_info
.fixed_latency
== s
->thread_info
.fixed_latency
);
452 pa_assert(s
->monitor_source
->thread_info
.min_latency
== s
->thread_info
.min_latency
);
453 pa_assert(s
->monitor_source
->thread_info
.max_latency
== s
->thread_info
.max_latency
);
455 pa_assert_se(sink_set_state(s
, PA_SINK_IDLE
) == 0);
457 pa_source_put(s
->monitor_source
);
459 pa_subscription_post(s
->core
, PA_SUBSCRIPTION_EVENT_SINK
| PA_SUBSCRIPTION_EVENT_NEW
, s
->index
);
460 pa_hook_fire(&s
->core
->hooks
[PA_CORE_HOOK_SINK_PUT
], s
);
463 /* Called from main context */
464 void pa_sink_unlink(pa_sink
* s
) {
466 pa_sink_input
*i
, *j
= NULL
;
469 pa_assert_ctl_context();
471 /* Please note that pa_sink_unlink() does more than simply
472 * reversing pa_sink_put(). It also undoes the registrations
473 * already done in pa_sink_new()! */
475 /* All operations here shall be idempotent, i.e. pa_sink_unlink()
476 * may be called multiple times on the same sink without bad
479 linked
= PA_SINK_IS_LINKED(s
->state
);
482 pa_hook_fire(&s
->core
->hooks
[PA_CORE_HOOK_SINK_UNLINK
], s
);
484 if (s
->state
!= PA_SINK_UNLINKED
)
485 pa_namereg_unregister(s
->core
, s
->name
);
486 pa_idxset_remove_by_data(s
->core
->sinks
, s
, NULL
);
489 pa_idxset_remove_by_data(s
->card
->sinks
, s
, NULL
);
491 while ((i
= pa_idxset_first(s
->inputs
, NULL
))) {
493 pa_sink_input_kill(i
);
498 sink_set_state(s
, PA_SINK_UNLINKED
);
500 s
->state
= PA_SINK_UNLINKED
;
504 if (s
->monitor_source
)
505 pa_source_unlink(s
->monitor_source
);
508 pa_subscription_post(s
->core
, PA_SUBSCRIPTION_EVENT_SINK
| PA_SUBSCRIPTION_EVENT_REMOVE
, s
->index
);
509 pa_hook_fire(&s
->core
->hooks
[PA_CORE_HOOK_SINK_UNLINK_POST
], s
);
513 /* Called from main context */
514 static void sink_free(pa_object
*o
) {
515 pa_sink
*s
= PA_SINK(o
);
519 pa_assert_ctl_context();
520 pa_assert(pa_sink_refcnt(s
) == 0);
522 if (PA_SINK_IS_LINKED(s
->state
))
525 pa_log_info("Freeing sink %u \"%s\"", s
->index
, s
->name
);
527 if (s
->monitor_source
) {
528 pa_source_unref(s
->monitor_source
);
529 s
->monitor_source
= NULL
;
532 pa_idxset_free(s
->inputs
, NULL
, NULL
);
534 while ((i
= pa_hashmap_steal_first(s
->thread_info
.inputs
)))
535 pa_sink_input_unref(i
);
537 pa_hashmap_free(s
->thread_info
.inputs
, NULL
, NULL
);
539 if (s
->silence
.memblock
)
540 pa_memblock_unref(s
->silence
.memblock
);
546 pa_proplist_free(s
->proplist
);
551 while ((p
= pa_hashmap_steal_first(s
->ports
)))
552 pa_device_port_free(p
);
554 pa_hashmap_free(s
->ports
, NULL
, NULL
);
560 /* Called from main context, and not while the IO thread is active, please */
561 void pa_sink_set_asyncmsgq(pa_sink
*s
, pa_asyncmsgq
*q
) {
562 pa_sink_assert_ref(s
);
563 pa_assert_ctl_context();
567 if (s
->monitor_source
)
568 pa_source_set_asyncmsgq(s
->monitor_source
, q
);
571 /* Called from main context, and not while the IO thread is active, please */
572 void pa_sink_update_flags(pa_sink
*s
, pa_sink_flags_t mask
, pa_sink_flags_t value
) {
573 pa_sink_assert_ref(s
);
574 pa_assert_ctl_context();
579 /* For now, allow only a minimal set of flags to be changed. */
580 pa_assert((mask
& ~(PA_SINK_DYNAMIC_LATENCY
|PA_SINK_LATENCY
)) == 0);
582 s
->flags
= (s
->flags
& ~mask
) | (value
& mask
);
584 pa_source_update_flags(s
->monitor_source
,
585 ((mask
& PA_SINK_LATENCY
) ? PA_SOURCE_LATENCY
: 0) |
586 ((mask
& PA_SINK_DYNAMIC_LATENCY
) ? PA_SOURCE_DYNAMIC_LATENCY
: 0),
587 ((value
& PA_SINK_LATENCY
) ? PA_SOURCE_LATENCY
: 0) |
588 ((value
& PA_SINK_DYNAMIC_LATENCY
) ? PA_SINK_DYNAMIC_LATENCY
: 0));
591 /* Called from IO context, or before _put() from main context */
592 void pa_sink_set_rtpoll(pa_sink
*s
, pa_rtpoll
*p
) {
593 pa_sink_assert_ref(s
);
594 pa_sink_assert_io_context(s
);
596 s
->thread_info
.rtpoll
= p
;
598 if (s
->monitor_source
)
599 pa_source_set_rtpoll(s
->monitor_source
, p
);
602 /* Called from main context */
603 int pa_sink_update_status(pa_sink
*s
) {
604 pa_sink_assert_ref(s
);
605 pa_assert_ctl_context();
606 pa_assert(PA_SINK_IS_LINKED(s
->state
));
608 if (s
->state
== PA_SINK_SUSPENDED
)
611 return sink_set_state(s
, pa_sink_used_by(s
) ? PA_SINK_RUNNING
: PA_SINK_IDLE
);
614 /* Called from main context */
615 int pa_sink_suspend(pa_sink
*s
, pa_bool_t suspend
, pa_suspend_cause_t cause
) {
616 pa_sink_assert_ref(s
);
617 pa_assert_ctl_context();
618 pa_assert(PA_SINK_IS_LINKED(s
->state
));
619 pa_assert(cause
!= 0);
622 s
->suspend_cause
|= cause
;
623 s
->monitor_source
->suspend_cause
|= cause
;
625 s
->suspend_cause
&= ~cause
;
626 s
->monitor_source
->suspend_cause
&= ~cause
;
629 if ((pa_sink_get_state(s
) == PA_SINK_SUSPENDED
) == !!s
->suspend_cause
)
632 pa_log_debug("Suspend cause of sink %s is 0x%04x, %s", s
->name
, s
->suspend_cause
, s
->suspend_cause
? "suspending" : "resuming");
634 if (s
->suspend_cause
)
635 return sink_set_state(s
, PA_SINK_SUSPENDED
);
637 return sink_set_state(s
, pa_sink_used_by(s
) ? PA_SINK_RUNNING
: PA_SINK_IDLE
);
640 /* Called from main context */
641 pa_queue
*pa_sink_move_all_start(pa_sink
*s
, pa_queue
*q
) {
642 pa_sink_input
*i
, *n
;
645 pa_sink_assert_ref(s
);
646 pa_assert_ctl_context();
647 pa_assert(PA_SINK_IS_LINKED(s
->state
));
652 for (i
= PA_SINK_INPUT(pa_idxset_first(s
->inputs
, &idx
)); i
; i
= n
) {
653 n
= PA_SINK_INPUT(pa_idxset_next(s
->inputs
, &idx
));
655 pa_sink_input_ref(i
);
657 if (pa_sink_input_start_move(i
) >= 0)
660 pa_sink_input_unref(i
);
666 /* Called from main context */
667 void pa_sink_move_all_finish(pa_sink
*s
, pa_queue
*q
, pa_bool_t save
) {
670 pa_sink_assert_ref(s
);
671 pa_assert_ctl_context();
672 pa_assert(PA_SINK_IS_LINKED(s
->state
));
675 while ((i
= PA_SINK_INPUT(pa_queue_pop(q
)))) {
676 if (pa_sink_input_finish_move(i
, s
, save
) < 0)
677 pa_sink_input_fail_move(i
);
679 pa_sink_input_unref(i
);
682 pa_queue_free(q
, NULL
, NULL
);
685 /* Called from main context */
686 void pa_sink_move_all_fail(pa_queue
*q
) {
689 pa_assert_ctl_context();
692 while ((i
= PA_SINK_INPUT(pa_queue_pop(q
)))) {
693 pa_sink_input_fail_move(i
);
694 pa_sink_input_unref(i
);
697 pa_queue_free(q
, NULL
, NULL
);
700 /* Called from IO thread context */
701 void pa_sink_process_rewind(pa_sink
*s
, size_t nbytes
) {
705 pa_sink_assert_ref(s
);
706 pa_sink_assert_io_context(s
);
707 pa_assert(PA_SINK_IS_LINKED(s
->thread_info
.state
));
709 /* If nobody requested this and this is actually no real rewind
710 * then we can short cut this. Please note that this means that
711 * not all rewind requests triggered upstream will always be
712 * translated in actual requests! */
713 if (!s
->thread_info
.rewind_requested
&& nbytes
<= 0)
716 s
->thread_info
.rewind_nbytes
= 0;
717 s
->thread_info
.rewind_requested
= FALSE
;
719 if (s
->thread_info
.state
== PA_SINK_SUSPENDED
)
723 pa_log_debug("Processing rewind...");
725 PA_HASHMAP_FOREACH(i
, s
->thread_info
.inputs
, state
) {
726 pa_sink_input_assert_ref(i
);
727 pa_sink_input_process_rewind(i
, nbytes
);
731 if (s
->monitor_source
&& PA_SOURCE_IS_LINKED(s
->monitor_source
->thread_info
.state
))
732 pa_source_process_rewind(s
->monitor_source
, nbytes
);
735 /* Called from IO thread context */
736 static unsigned fill_mix_info(pa_sink
*s
, size_t *length
, pa_mix_info
*info
, unsigned maxinfo
) {
740 size_t mixlength
= *length
;
742 pa_sink_assert_ref(s
);
743 pa_sink_assert_io_context(s
);
746 while ((i
= pa_hashmap_iterate(s
->thread_info
.inputs
, &state
, NULL
)) && maxinfo
> 0) {
747 pa_sink_input_assert_ref(i
);
749 pa_sink_input_peek(i
, *length
, &info
->chunk
, &info
->volume
);
751 if (mixlength
== 0 || info
->chunk
.length
< mixlength
)
752 mixlength
= info
->chunk
.length
;
754 if (pa_memblock_is_silence(info
->chunk
.memblock
)) {
755 pa_memblock_unref(info
->chunk
.memblock
);
759 info
->userdata
= pa_sink_input_ref(i
);
761 pa_assert(info
->chunk
.memblock
);
762 pa_assert(info
->chunk
.length
> 0);
775 /* Called from IO thread context */
776 static void inputs_drop(pa_sink
*s
, pa_mix_info
*info
, unsigned n
, pa_memchunk
*result
) {
780 unsigned n_unreffed
= 0;
782 pa_sink_assert_ref(s
);
783 pa_sink_assert_io_context(s
);
785 pa_assert(result
->memblock
);
786 pa_assert(result
->length
> 0);
788 /* We optimize for the case where the order of the inputs has not changed */
790 while ((i
= pa_hashmap_iterate(s
->thread_info
.inputs
, &state
, NULL
))) {
792 pa_mix_info
* m
= NULL
;
794 pa_sink_input_assert_ref(i
);
796 /* Let's try to find the matching entry info the pa_mix_info array */
797 for (j
= 0; j
< n
; j
++) {
799 if (info
[p
].userdata
== i
) {
810 pa_sink_input_drop(i
, result
->length
);
812 if (s
->monitor_source
&& PA_SOURCE_IS_LINKED(s
->monitor_source
->thread_info
.state
)) {
814 if (pa_hashmap_size(i
->thread_info
.direct_outputs
) > 0) {
819 if (m
&& m
->chunk
.memblock
) {
821 pa_memblock_ref(c
.memblock
);
822 pa_assert(result
->length
<= c
.length
);
823 c
.length
= result
->length
;
825 pa_memchunk_make_writable(&c
, 0);
826 pa_volume_memchunk(&c
, &s
->sample_spec
, &m
->volume
);
829 pa_memblock_ref(c
.memblock
);
830 pa_assert(result
->length
<= c
.length
);
831 c
.length
= result
->length
;
834 while ((o
= pa_hashmap_iterate(i
->thread_info
.direct_outputs
, &ostate
, NULL
))) {
835 pa_source_output_assert_ref(o
);
836 pa_assert(o
->direct_on_input
== i
);
837 pa_source_post_direct(s
->monitor_source
, o
, &c
);
840 pa_memblock_unref(c
.memblock
);
845 if (m
->chunk
.memblock
)
846 pa_memblock_unref(m
->chunk
.memblock
);
847 pa_memchunk_reset(&m
->chunk
);
849 pa_sink_input_unref(m
->userdata
);
856 /* Now drop references to entries that are included in the
857 * pa_mix_info array but don't exist anymore */
859 if (n_unreffed
< n
) {
860 for (; n
> 0; info
++, n
--) {
862 pa_sink_input_unref(info
->userdata
);
863 if (info
->chunk
.memblock
)
864 pa_memblock_unref(info
->chunk
.memblock
);
868 if (s
->monitor_source
&& PA_SOURCE_IS_LINKED(s
->monitor_source
->thread_info
.state
))
869 pa_source_post(s
->monitor_source
, result
);
872 /* Called from IO thread context */
873 void pa_sink_render(pa_sink
*s
, size_t length
, pa_memchunk
*result
) {
874 pa_mix_info info
[MAX_MIX_CHANNELS
];
876 size_t block_size_max
;
878 pa_sink_assert_ref(s
);
879 pa_sink_assert_io_context(s
);
880 pa_assert(PA_SINK_IS_LINKED(s
->thread_info
.state
));
881 pa_assert(pa_frame_aligned(length
, &s
->sample_spec
));
886 pa_assert(!s
->thread_info
.rewind_requested
);
887 pa_assert(s
->thread_info
.rewind_nbytes
== 0);
889 if (s
->thread_info
.state
== PA_SINK_SUSPENDED
) {
890 result
->memblock
= pa_memblock_ref(s
->silence
.memblock
);
891 result
->index
= s
->silence
.index
;
892 result
->length
= PA_MIN(s
->silence
.length
, length
);
897 length
= pa_frame_align(MIX_BUFFER_LENGTH
, &s
->sample_spec
);
899 block_size_max
= pa_mempool_block_size_max(s
->core
->mempool
);
900 if (length
> block_size_max
)
901 length
= pa_frame_align(block_size_max
, &s
->sample_spec
);
903 pa_assert(length
> 0);
905 n
= fill_mix_info(s
, &length
, info
, MAX_MIX_CHANNELS
);
909 *result
= s
->silence
;
910 pa_memblock_ref(result
->memblock
);
912 if (result
->length
> length
)
913 result
->length
= length
;
918 *result
= info
[0].chunk
;
919 pa_memblock_ref(result
->memblock
);
921 if (result
->length
> length
)
922 result
->length
= length
;
924 pa_sw_cvolume_multiply(&volume
, &s
->thread_info
.soft_volume
, &info
[0].volume
);
926 if (s
->thread_info
.soft_muted
|| !pa_cvolume_is_norm(&volume
)) {
927 if (s
->thread_info
.soft_muted
|| pa_cvolume_is_muted(&volume
)) {
928 pa_memblock_unref(result
->memblock
);
929 pa_silence_memchunk_get(&s
->core
->silence_cache
,
935 pa_memchunk_make_writable(result
, 0);
936 pa_volume_memchunk(result
, &s
->sample_spec
, &volume
);
941 result
->memblock
= pa_memblock_new(s
->core
->mempool
, length
);
943 ptr
= pa_memblock_acquire(result
->memblock
);
944 result
->length
= pa_mix(info
, n
,
947 &s
->thread_info
.soft_volume
,
948 s
->thread_info
.soft_muted
);
949 pa_memblock_release(result
->memblock
);
954 inputs_drop(s
, info
, n
, result
);
959 /* Called from IO thread context */
960 void pa_sink_render_into(pa_sink
*s
, pa_memchunk
*target
) {
961 pa_mix_info info
[MAX_MIX_CHANNELS
];
963 size_t length
, block_size_max
;
965 pa_sink_assert_ref(s
);
966 pa_sink_assert_io_context(s
);
967 pa_assert(PA_SINK_IS_LINKED(s
->thread_info
.state
));
969 pa_assert(target
->memblock
);
970 pa_assert(target
->length
> 0);
971 pa_assert(pa_frame_aligned(target
->length
, &s
->sample_spec
));
975 pa_assert(!s
->thread_info
.rewind_requested
);
976 pa_assert(s
->thread_info
.rewind_nbytes
== 0);
978 if (s
->thread_info
.state
== PA_SINK_SUSPENDED
) {
979 pa_silence_memchunk(target
, &s
->sample_spec
);
983 length
= target
->length
;
984 block_size_max
= pa_mempool_block_size_max(s
->core
->mempool
);
985 if (length
> block_size_max
)
986 length
= pa_frame_align(block_size_max
, &s
->sample_spec
);
988 pa_assert(length
> 0);
990 n
= fill_mix_info(s
, &length
, info
, MAX_MIX_CHANNELS
);
993 if (target
->length
> length
)
994 target
->length
= length
;
996 pa_silence_memchunk(target
, &s
->sample_spec
);
1000 if (target
->length
> length
)
1001 target
->length
= length
;
1003 pa_sw_cvolume_multiply(&volume
, &s
->thread_info
.soft_volume
, &info
[0].volume
);
1005 if (s
->thread_info
.soft_muted
|| pa_cvolume_is_muted(&volume
))
1006 pa_silence_memchunk(target
, &s
->sample_spec
);
1010 vchunk
= info
[0].chunk
;
1011 pa_memblock_ref(vchunk
.memblock
);
1013 if (vchunk
.length
> length
)
1014 vchunk
.length
= length
;
1016 if (!pa_cvolume_is_norm(&volume
)) {
1017 pa_memchunk_make_writable(&vchunk
, 0);
1018 pa_volume_memchunk(&vchunk
, &s
->sample_spec
, &volume
);
1021 pa_memchunk_memcpy(target
, &vchunk
);
1022 pa_memblock_unref(vchunk
.memblock
);
1028 ptr
= pa_memblock_acquire(target
->memblock
);
1030 target
->length
= pa_mix(info
, n
,
1031 (uint8_t*) ptr
+ target
->index
, length
,
1033 &s
->thread_info
.soft_volume
,
1034 s
->thread_info
.soft_muted
);
1036 pa_memblock_release(target
->memblock
);
1039 inputs_drop(s
, info
, n
, target
);
1044 /* Called from IO thread context */
1045 void pa_sink_render_into_full(pa_sink
*s
, pa_memchunk
*target
) {
1049 pa_sink_assert_ref(s
);
1050 pa_sink_assert_io_context(s
);
1051 pa_assert(PA_SINK_IS_LINKED(s
->thread_info
.state
));
1053 pa_assert(target
->memblock
);
1054 pa_assert(target
->length
> 0);
1055 pa_assert(pa_frame_aligned(target
->length
, &s
->sample_spec
));
1059 pa_assert(!s
->thread_info
.rewind_requested
);
1060 pa_assert(s
->thread_info
.rewind_nbytes
== 0);
1069 pa_sink_render_into(s
, &chunk
);
1078 /* Called from IO thread context */
1079 void pa_sink_render_full(pa_sink
*s
, size_t length
, pa_memchunk
*result
) {
1080 pa_mix_info info
[MAX_MIX_CHANNELS
];
1081 size_t length1st
= length
;
1084 pa_sink_assert_ref(s
);
1085 pa_sink_assert_io_context(s
);
1086 pa_assert(PA_SINK_IS_LINKED(s
->thread_info
.state
));
1087 pa_assert(length
> 0);
1088 pa_assert(pa_frame_aligned(length
, &s
->sample_spec
));
1093 pa_assert(!s
->thread_info
.rewind_requested
);
1094 pa_assert(s
->thread_info
.rewind_nbytes
== 0);
1096 pa_assert(length
> 0);
1098 n
= fill_mix_info(s
, &length1st
, info
, MAX_MIX_CHANNELS
);
1101 pa_silence_memchunk_get(&s
->core
->silence_cache
,
1106 } else if (n
== 1) {
1109 *result
= info
[0].chunk
;
1110 pa_memblock_ref(result
->memblock
);
1112 if (result
->length
> length
)
1113 result
->length
= length
;
1115 pa_sw_cvolume_multiply(&volume
, &s
->thread_info
.soft_volume
, &info
[0].volume
);
1117 if (s
->thread_info
.soft_muted
|| !pa_cvolume_is_norm(&volume
)) {
1118 if (s
->thread_info
.soft_muted
|| pa_cvolume_is_muted(&volume
)) {
1119 pa_memblock_unref(result
->memblock
);
1120 pa_silence_memchunk_get(&s
->core
->silence_cache
,
1126 pa_memchunk_make_writable(result
, length
);
1127 pa_volume_memchunk(result
, &s
->sample_spec
, &volume
);
1134 result
->memblock
= pa_memblock_new(s
->core
->mempool
, length
);
1136 ptr
= pa_memblock_acquire(result
->memblock
);
1138 result
->length
= pa_mix(info
, n
,
1139 (uint8_t*) ptr
+ result
->index
, length1st
,
1141 &s
->thread_info
.soft_volume
,
1142 s
->thread_info
.soft_muted
);
1144 pa_memblock_release(result
->memblock
);
1147 inputs_drop(s
, info
, n
, result
);
1149 if (result
->length
< length
) {
1152 pa_memchunk_make_writable(result
, length
);
1154 l
= length
- result
->length
;
1155 d
= result
->index
+ result
->length
;
1161 pa_sink_render_into(s
, &chunk
);
1166 result
->length
= length
;
1172 /* Called from main thread */
1173 pa_usec_t
pa_sink_get_latency(pa_sink
*s
) {
1176 pa_sink_assert_ref(s
);
1177 pa_assert_ctl_context();
1178 pa_assert(PA_SINK_IS_LINKED(s
->state
));
1180 /* The returned value is supposed to be in the time domain of the sound card! */
1182 if (s
->state
== PA_SINK_SUSPENDED
)
1185 if (!(s
->flags
& PA_SINK_LATENCY
))
1188 pa_assert_se(pa_asyncmsgq_send(s
->asyncmsgq
, PA_MSGOBJECT(s
), PA_SINK_MESSAGE_GET_LATENCY
, &usec
, 0, NULL
) == 0);
1193 /* Called from IO thread */
1194 pa_usec_t
pa_sink_get_latency_within_thread(pa_sink
*s
) {
1198 pa_sink_assert_ref(s
);
1199 pa_sink_assert_io_context(s
);
1200 pa_assert(PA_SINK_IS_LINKED(s
->thread_info
.state
));
1202 /* The returned value is supposed to be in the time domain of the sound card! */
1204 if (s
->thread_info
.state
== PA_SINK_SUSPENDED
)
1207 if (!(s
->flags
& PA_SINK_LATENCY
))
1210 o
= PA_MSGOBJECT(s
);
1212 /* FIXME: We probably should make this a proper vtable callback instead of going through process_msg() */
1214 if (o
->process_msg(o
, PA_SINK_MESSAGE_GET_LATENCY
, &usec
, 0, NULL
) < 0)
1220 /* Called from main context */
1221 static void compute_reference_ratios(pa_sink
*s
) {
1225 pa_sink_assert_ref(s
);
1226 pa_assert_ctl_context();
1227 pa_assert(PA_SINK_IS_LINKED(s
->state
));
1228 pa_assert(s
->flags
& PA_SINK_FLAT_VOLUME
);
1230 PA_IDXSET_FOREACH(i
, s
->inputs
, idx
) {
1232 pa_cvolume remapped
;
1235 * Calculates the reference volume from the sink's reference
1236 * volume. This basically calculates:
1238 * i->reference_ratio = i->volume / s->reference_volume
1241 remapped
= s
->reference_volume
;
1242 pa_cvolume_remap(&remapped
, &s
->channel_map
, &i
->channel_map
);
1244 i
->reference_ratio
.channels
= i
->sample_spec
.channels
;
1246 for (c
= 0; c
< i
->sample_spec
.channels
; c
++) {
1248 /* We don't update when the sink volume is 0 anyway */
1249 if (remapped
.values
[c
] <= PA_VOLUME_MUTED
)
1252 /* Don't update the reference ratio unless necessary */
1253 if (pa_sw_volume_multiply(
1254 i
->reference_ratio
.values
[c
],
1255 remapped
.values
[c
]) == i
->volume
.values
[c
])
1258 i
->reference_ratio
.values
[c
] = pa_sw_volume_divide(
1259 i
->volume
.values
[c
],
1260 remapped
.values
[c
]);
1265 /* Called from main context */
1266 static void compute_real_ratios(pa_sink
*s
) {
1270 pa_sink_assert_ref(s
);
1271 pa_assert_ctl_context();
1272 pa_assert(PA_SINK_IS_LINKED(s
->state
));
1273 pa_assert(s
->flags
& PA_SINK_FLAT_VOLUME
);
1275 PA_IDXSET_FOREACH(i
, s
->inputs
, idx
) {
1277 pa_cvolume remapped
;
1280 * This basically calculates:
1282 * i->real_ratio := i->volume / s->real_volume
1283 * i->soft_volume := i->real_ratio * i->volume_factor
1286 remapped
= s
->real_volume
;
1287 pa_cvolume_remap(&remapped
, &s
->channel_map
, &i
->channel_map
);
1289 i
->real_ratio
.channels
= i
->sample_spec
.channels
;
1290 i
->soft_volume
.channels
= i
->sample_spec
.channels
;
1292 for (c
= 0; c
< i
->sample_spec
.channels
; c
++) {
1294 if (remapped
.values
[c
] <= PA_VOLUME_MUTED
) {
1295 /* We leave i->real_ratio untouched */
1296 i
->soft_volume
.values
[c
] = PA_VOLUME_MUTED
;
1300 /* Don't lose accuracy unless necessary */
1301 if (pa_sw_volume_multiply(
1302 i
->real_ratio
.values
[c
],
1303 remapped
.values
[c
]) != i
->volume
.values
[c
])
1305 i
->real_ratio
.values
[c
] = pa_sw_volume_divide(
1306 i
->volume
.values
[c
],
1307 remapped
.values
[c
]);
1309 i
->soft_volume
.values
[c
] = pa_sw_volume_multiply(
1310 i
->real_ratio
.values
[c
],
1311 i
->volume_factor
.values
[c
]);
1314 /* We don't copy the soft_volume to the thread_info data
1315 * here. That must be done by the caller */
1319 /* Called from main thread */
1320 static void compute_real_volume(pa_sink
*s
) {
1324 pa_sink_assert_ref(s
);
1325 pa_assert_ctl_context();
1326 pa_assert(PA_SINK_IS_LINKED(s
->state
));
1327 pa_assert(s
->flags
& PA_SINK_FLAT_VOLUME
);
1329 /* This determines the maximum volume of all streams and sets
1330 * s->real_volume accordingly. */
1332 if (pa_idxset_isempty(s
->inputs
)) {
1333 /* In the special case that we have no sink input we leave the
1334 * volume unmodified. */
1335 s
->real_volume
= s
->reference_volume
;
1339 pa_cvolume_mute(&s
->real_volume
, s
->channel_map
.channels
);
1341 /* First let's determine the new maximum volume of all inputs
1342 * connected to this sink */
1343 PA_IDXSET_FOREACH(i
, s
->inputs
, idx
) {
1344 pa_cvolume remapped
;
1346 remapped
= i
->volume
;
1347 pa_cvolume_remap(&remapped
, &i
->channel_map
, &s
->channel_map
);
1348 pa_cvolume_merge(&s
->real_volume
, &s
->real_volume
, &remapped
);
1351 /* Then, let's update the real ratios/soft volumes of all inputs
1352 * connected to this sink */
1353 compute_real_ratios(s
);
1356 /* Called from main thread */
1357 static void propagate_reference_volume(pa_sink
*s
) {
1361 pa_sink_assert_ref(s
);
1362 pa_assert_ctl_context();
1363 pa_assert(PA_SINK_IS_LINKED(s
->state
));
1364 pa_assert(s
->flags
& PA_SINK_FLAT_VOLUME
);
1366 /* This is called whenever the sink volume changes that is not
1367 * caused by a sink input volume change. We need to fix up the
1368 * sink input volumes accordingly */
1370 PA_IDXSET_FOREACH(i
, s
->inputs
, idx
) {
1371 pa_cvolume old_volume
, remapped
;
1373 old_volume
= i
->volume
;
1375 /* This basically calculates:
1377 * i->volume := s->reference_volume * i->reference_ratio */
1379 remapped
= s
->reference_volume
;
1380 pa_cvolume_remap(&remapped
, &s
->channel_map
, &i
->channel_map
);
1381 pa_sw_cvolume_multiply(&i
->volume
, &remapped
, &i
->reference_ratio
);
1383 /* The reference volume changed, let's tell people so */
1384 if (!pa_cvolume_equal(&old_volume
, &i
->volume
))
1385 pa_subscription_post(i
->core
, PA_SUBSCRIPTION_EVENT_SINK_INPUT
|PA_SUBSCRIPTION_EVENT_CHANGE
, i
->index
);
1389 /* Called from main thread */
1390 void pa_sink_set_volume(
1392 const pa_cvolume
*volume
,
1396 pa_cvolume old_reference_volume
;
1397 pa_bool_t reference_changed
;
1399 pa_sink_assert_ref(s
);
1400 pa_assert_ctl_context();
1401 pa_assert(PA_SINK_IS_LINKED(s
->state
));
1402 pa_assert(!volume
|| pa_cvolume_valid(volume
));
1403 pa_assert(!volume
|| pa_cvolume_compatible(volume
, &s
->sample_spec
));
1404 pa_assert(volume
|| (s
->flags
& PA_SINK_FLAT_VOLUME
));
1406 /* If volume is NULL we synchronize the sink's real and reference
1407 * volumes with the stream volumes. If it is not NULL we update
1408 * the reference_volume with it. */
1410 old_reference_volume
= s
->reference_volume
;
1414 s
->reference_volume
= *volume
;
1416 if (s
->flags
& PA_SINK_FLAT_VOLUME
) {
1417 /* OK, propagate this volume change back to the inputs */
1418 propagate_reference_volume(s
);
1420 /* And now recalculate the real volume */
1421 compute_real_volume(s
);
1423 s
->real_volume
= s
->reference_volume
;
1426 pa_assert(s
->flags
& PA_SINK_FLAT_VOLUME
);
1428 /* Ok, let's determine the new real volume */
1429 compute_real_volume(s
);
1431 /* Let's 'push' the reference volume if necessary */
1432 pa_cvolume_merge(&s
->reference_volume
, &s
->reference_volume
, &s
->real_volume
);
1434 /* We need to fix the reference ratios of all streams now that
1435 * we changed the reference volume */
1436 compute_reference_ratios(s
);
1439 reference_changed
= !pa_cvolume_equal(&old_reference_volume
, &s
->reference_volume
);
1440 s
->save_volume
= (!reference_changed
&& s
->save_volume
) || save
;
1442 if (s
->set_volume
) {
1443 /* If we have a function set_volume(), then we do not apply a
1444 * soft volume by default. However, set_volume() is free to
1445 * apply one to s->soft_volume */
1447 pa_cvolume_reset(&s
->soft_volume
, s
->sample_spec
.channels
);
1451 /* If we have no function set_volume(), then the soft volume
1452 * becomes the virtual volume */
1453 s
->soft_volume
= s
->real_volume
;
1455 /* This tells the sink that soft and/or virtual volume changed */
1457 pa_assert_se(pa_asyncmsgq_send(s
->asyncmsgq
, PA_MSGOBJECT(s
), PA_SINK_MESSAGE_SET_VOLUME
, NULL
, 0, NULL
) == 0);
1459 if (reference_changed
)
1460 pa_subscription_post(s
->core
, PA_SUBSCRIPTION_EVENT_SINK
|PA_SUBSCRIPTION_EVENT_CHANGE
, s
->index
);
1463 /* Called from main thread. Only to be called by sink implementor */
1464 void pa_sink_set_soft_volume(pa_sink
*s
, const pa_cvolume
*volume
) {
1465 pa_sink_assert_ref(s
);
1466 pa_assert_ctl_context();
1469 pa_cvolume_reset(&s
->soft_volume
, s
->sample_spec
.channels
);
1471 s
->soft_volume
= *volume
;
1473 if (PA_SINK_IS_LINKED(s
->state
))
1474 pa_assert_se(pa_asyncmsgq_send(s
->asyncmsgq
, PA_MSGOBJECT(s
), PA_SINK_MESSAGE_SET_VOLUME
, NULL
, 0, NULL
) == 0);
1476 s
->thread_info
.soft_volume
= s
->soft_volume
;
1479 static void propagate_real_volume(pa_sink
*s
, const pa_cvolume
*old_real_volume
) {
1482 pa_cvolume old_reference_volume
;
1484 pa_sink_assert_ref(s
);
1485 pa_assert_ctl_context();
1486 pa_assert(PA_SINK_IS_LINKED(s
->state
));
1488 /* This is called when the hardware's real volume changes due to
1489 * some external event. We copy the real volume into our
1490 * reference volume and then rebuild the stream volumes based on
1491 * i->real_ratio which should stay fixed. */
1493 if (pa_cvolume_equal(old_real_volume
, &s
->real_volume
))
1496 old_reference_volume
= s
->reference_volume
;
1498 /* 1. Make the real volume the reference volume */
1499 s
->reference_volume
= s
->real_volume
;
1501 if (s
->flags
& PA_SINK_FLAT_VOLUME
) {
1503 PA_IDXSET_FOREACH(i
, s
->inputs
, idx
) {
1504 pa_cvolume old_volume
, remapped
;
1506 old_volume
= i
->volume
;
1508 /* 2. Since the sink's reference and real volumes are equal
1509 * now our ratios should be too. */
1510 i
->reference_ratio
= i
->real_ratio
;
1512 /* 3. Recalculate the new stream reference volume based on the
1513 * reference ratio and the sink's reference volume.
1515 * This basically calculates:
1517 * i->volume = s->reference_volume * i->reference_ratio
1519 * This is identical to propagate_reference_volume() */
1520 remapped
= s
->reference_volume
;
1521 pa_cvolume_remap(&remapped
, &s
->channel_map
, &i
->channel_map
);
1522 pa_sw_cvolume_multiply(&i
->volume
, &remapped
, &i
->reference_ratio
);
1524 /* Notify if something changed */
1525 if (!pa_cvolume_equal(&old_volume
, &i
->volume
))
1526 pa_subscription_post(i
->core
, PA_SUBSCRIPTION_EVENT_SINK_INPUT
|PA_SUBSCRIPTION_EVENT_CHANGE
, i
->index
);
1530 /* Something got changed in the hardware. It probably makes sense
1531 * to save changed hw settings given that hw volume changes not
1532 * triggered by PA are almost certainly done by the user. */
1533 s
->save_volume
= TRUE
;
1535 if (!pa_cvolume_equal(&old_reference_volume
, &s
->reference_volume
))
1536 pa_subscription_post(s
->core
, PA_SUBSCRIPTION_EVENT_SINK
|PA_SUBSCRIPTION_EVENT_CHANGE
, s
->index
);
1539 /* Called from main thread */
1540 const pa_cvolume
*pa_sink_get_volume(pa_sink
*s
, pa_bool_t force_refresh
) {
1541 pa_sink_assert_ref(s
);
1542 pa_assert_ctl_context();
1543 pa_assert(PA_SINK_IS_LINKED(s
->state
));
1545 if (s
->refresh_volume
|| force_refresh
) {
1546 struct pa_cvolume old_real_volume
;
1548 old_real_volume
= s
->real_volume
;
1553 pa_assert_se(pa_asyncmsgq_send(s
->asyncmsgq
, PA_MSGOBJECT(s
), PA_SINK_MESSAGE_GET_VOLUME
, NULL
, 0, NULL
) == 0);
1555 propagate_real_volume(s
, &old_real_volume
);
1558 return &s
->reference_volume
;
1561 /* Called from main thread */
1562 void pa_sink_volume_changed(pa_sink
*s
, const pa_cvolume
*new_real_volume
) {
1563 pa_cvolume old_real_volume
;
1565 pa_sink_assert_ref(s
);
1566 pa_assert_ctl_context();
1567 pa_assert(PA_SINK_IS_LINKED(s
->state
));
1569 /* The sink implementor may call this if the volume changed to make sure everyone is notified */
1571 old_real_volume
= s
->real_volume
;
1572 s
->real_volume
= *new_real_volume
;
1574 propagate_real_volume(s
, &old_real_volume
);
1577 /* Called from main thread */
1578 void pa_sink_set_mute(pa_sink
*s
, pa_bool_t mute
, pa_bool_t save
) {
1579 pa_bool_t old_muted
;
1581 pa_sink_assert_ref(s
);
1582 pa_assert_ctl_context();
1583 pa_assert(PA_SINK_IS_LINKED(s
->state
));
1585 old_muted
= s
->muted
;
1587 s
->save_muted
= (old_muted
== s
->muted
&& s
->save_muted
) || save
;
1592 pa_assert_se(pa_asyncmsgq_send(s
->asyncmsgq
, PA_MSGOBJECT(s
), PA_SINK_MESSAGE_SET_MUTE
, NULL
, 0, NULL
) == 0);
1594 if (old_muted
!= s
->muted
)
1595 pa_subscription_post(s
->core
, PA_SUBSCRIPTION_EVENT_SINK
|PA_SUBSCRIPTION_EVENT_CHANGE
, s
->index
);
1598 /* Called from main thread */
1599 pa_bool_t
pa_sink_get_mute(pa_sink
*s
, pa_bool_t force_refresh
) {
1601 pa_sink_assert_ref(s
);
1602 pa_assert_ctl_context();
1603 pa_assert(PA_SINK_IS_LINKED(s
->state
));
1605 if (s
->refresh_muted
|| force_refresh
) {
1606 pa_bool_t old_muted
= s
->muted
;
1611 pa_assert_se(pa_asyncmsgq_send(s
->asyncmsgq
, PA_MSGOBJECT(s
), PA_SINK_MESSAGE_GET_MUTE
, NULL
, 0, NULL
) == 0);
1613 if (old_muted
!= s
->muted
) {
1614 s
->save_muted
= TRUE
;
1616 pa_subscription_post(s
->core
, PA_SUBSCRIPTION_EVENT_SINK
|PA_SUBSCRIPTION_EVENT_CHANGE
, s
->index
);
1618 /* Make sure the soft mute status stays in sync */
1619 pa_assert_se(pa_asyncmsgq_send(s
->asyncmsgq
, PA_MSGOBJECT(s
), PA_SINK_MESSAGE_SET_MUTE
, NULL
, 0, NULL
) == 0);
1626 /* Called from main thread */
1627 void pa_sink_mute_changed(pa_sink
*s
, pa_bool_t new_muted
) {
1628 pa_sink_assert_ref(s
);
1629 pa_assert_ctl_context();
1630 pa_assert(PA_SINK_IS_LINKED(s
->state
));
1632 /* The sink implementor may call this if the volume changed to make sure everyone is notified */
1634 if (s
->muted
== new_muted
)
1637 s
->muted
= new_muted
;
1638 s
->save_muted
= TRUE
;
1640 pa_subscription_post(s
->core
, PA_SUBSCRIPTION_EVENT_SINK
|PA_SUBSCRIPTION_EVENT_CHANGE
, s
->index
);
1643 /* Called from main thread */
1644 pa_bool_t
pa_sink_update_proplist(pa_sink
*s
, pa_update_mode_t mode
, pa_proplist
*p
) {
1645 pa_sink_assert_ref(s
);
1646 pa_assert_ctl_context();
1649 pa_proplist_update(s
->proplist
, mode
, p
);
1651 if (PA_SINK_IS_LINKED(s
->state
)) {
1652 pa_hook_fire(&s
->core
->hooks
[PA_CORE_HOOK_SINK_PROPLIST_CHANGED
], s
);
1653 pa_subscription_post(s
->core
, PA_SUBSCRIPTION_EVENT_SINK
|PA_SUBSCRIPTION_EVENT_CHANGE
, s
->index
);
1659 /* Called from main thread */
1660 /* FIXME -- this should be dropped and be merged into pa_sink_update_proplist() */
1661 void pa_sink_set_description(pa_sink
*s
, const char *description
) {
1663 pa_sink_assert_ref(s
);
1664 pa_assert_ctl_context();
1666 if (!description
&& !pa_proplist_contains(s
->proplist
, PA_PROP_DEVICE_DESCRIPTION
))
1669 old
= pa_proplist_gets(s
->proplist
, PA_PROP_DEVICE_DESCRIPTION
);
1671 if (old
&& description
&& pa_streq(old
, description
))
1675 pa_proplist_sets(s
->proplist
, PA_PROP_DEVICE_DESCRIPTION
, description
);
1677 pa_proplist_unset(s
->proplist
, PA_PROP_DEVICE_DESCRIPTION
);
1679 if (s
->monitor_source
) {
1682 n
= pa_sprintf_malloc("Monitor Source of %s", description
? description
: s
->name
);
1683 pa_source_set_description(s
->monitor_source
, n
);
1687 if (PA_SINK_IS_LINKED(s
->state
)) {
1688 pa_subscription_post(s
->core
, PA_SUBSCRIPTION_EVENT_SINK
|PA_SUBSCRIPTION_EVENT_CHANGE
, s
->index
);
1689 pa_hook_fire(&s
->core
->hooks
[PA_CORE_HOOK_SINK_PROPLIST_CHANGED
], s
);
1693 /* Called from main thread */
1694 unsigned pa_sink_linked_by(pa_sink
*s
) {
1697 pa_sink_assert_ref(s
);
1698 pa_assert_ctl_context();
1699 pa_assert(PA_SINK_IS_LINKED(s
->state
));
1701 ret
= pa_idxset_size(s
->inputs
);
1703 /* We add in the number of streams connected to us here. Please
1704 * note the asymmmetry to pa_sink_used_by()! */
1706 if (s
->monitor_source
)
1707 ret
+= pa_source_linked_by(s
->monitor_source
);
1712 /* Called from main thread */
1713 unsigned pa_sink_used_by(pa_sink
*s
) {
1716 pa_sink_assert_ref(s
);
1717 pa_assert_ctl_context();
1718 pa_assert(PA_SINK_IS_LINKED(s
->state
));
1720 ret
= pa_idxset_size(s
->inputs
);
1721 pa_assert(ret
>= s
->n_corked
);
1723 /* Streams connected to our monitor source do not matter for
1724 * pa_sink_used_by()!.*/
1726 return ret
- s
->n_corked
;
1729 /* Called from main thread */
1730 unsigned pa_sink_check_suspend(pa_sink
*s
) {
1735 pa_sink_assert_ref(s
);
1736 pa_assert_ctl_context();
1738 if (!PA_SINK_IS_LINKED(s
->state
))
1743 PA_IDXSET_FOREACH(i
, s
->inputs
, idx
) {
1744 pa_sink_input_state_t st
;
1746 st
= pa_sink_input_get_state(i
);
1747 pa_assert(PA_SINK_INPUT_IS_LINKED(st
));
1749 if (st
== PA_SINK_INPUT_CORKED
)
1752 if (i
->flags
& PA_SINK_INPUT_DONT_INHIBIT_AUTO_SUSPEND
)
1758 if (s
->monitor_source
)
1759 ret
+= pa_source_check_suspend(s
->monitor_source
);
1764 /* Called from the IO thread */
1765 static void sync_input_volumes_within_thread(pa_sink
*s
) {
1769 pa_sink_assert_ref(s
);
1770 pa_sink_assert_io_context(s
);
1772 PA_HASHMAP_FOREACH(i
, s
->thread_info
.inputs
, state
) {
1773 if (pa_cvolume_equal(&i
->thread_info
.soft_volume
, &i
->soft_volume
))
1776 i
->thread_info
.soft_volume
= i
->soft_volume
;
1777 pa_sink_input_request_rewind(i
, 0, TRUE
, FALSE
, FALSE
);
1781 /* Called from IO thread, except when it is not */
1782 int pa_sink_process_msg(pa_msgobject
*o
, int code
, void *userdata
, int64_t offset
, pa_memchunk
*chunk
) {
1783 pa_sink
*s
= PA_SINK(o
);
1784 pa_sink_assert_ref(s
);
1786 switch ((pa_sink_message_t
) code
) {
1788 case PA_SINK_MESSAGE_ADD_INPUT
: {
1789 pa_sink_input
*i
= PA_SINK_INPUT(userdata
);
1791 /* If you change anything here, make sure to change the
1792 * sink input handling a few lines down at
1793 * PA_SINK_MESSAGE_FINISH_MOVE, too. */
1795 pa_hashmap_put(s
->thread_info
.inputs
, PA_UINT32_TO_PTR(i
->index
), pa_sink_input_ref(i
));
1797 /* Since the caller sleeps in pa_sink_input_put(), we can
1798 * safely access data outside of thread_info even though
1801 if ((i
->thread_info
.sync_prev
= i
->sync_prev
)) {
1802 pa_assert(i
->sink
== i
->thread_info
.sync_prev
->sink
);
1803 pa_assert(i
->sync_prev
->sync_next
== i
);
1804 i
->thread_info
.sync_prev
->thread_info
.sync_next
= i
;
1807 if ((i
->thread_info
.sync_next
= i
->sync_next
)) {
1808 pa_assert(i
->sink
== i
->thread_info
.sync_next
->sink
);
1809 pa_assert(i
->sync_next
->sync_prev
== i
);
1810 i
->thread_info
.sync_next
->thread_info
.sync_prev
= i
;
1813 pa_assert(!i
->thread_info
.attached
);
1814 i
->thread_info
.attached
= TRUE
;
1819 pa_sink_input_set_state_within_thread(i
, i
->state
);
1821 /* The requested latency of the sink input needs to be
1822 * fixed up and then configured on the sink */
1824 if (i
->thread_info
.requested_sink_latency
!= (pa_usec_t
) -1)
1825 pa_sink_input_set_requested_latency_within_thread(i
, i
->thread_info
.requested_sink_latency
);
1827 pa_sink_input_update_max_rewind(i
, s
->thread_info
.max_rewind
);
1828 pa_sink_input_update_max_request(i
, s
->thread_info
.max_request
);
1830 /* We don't rewind here automatically. This is left to the
1831 * sink input implementor because some sink inputs need a
1832 * slow start, i.e. need some time to buffer client
1833 * samples before beginning streaming. */
1835 /* In flat volume mode we need to update the volume as
1837 return o
->process_msg(o
, PA_SINK_MESSAGE_SET_VOLUME
, NULL
, 0, NULL
);
1840 case PA_SINK_MESSAGE_REMOVE_INPUT
: {
1841 pa_sink_input
*i
= PA_SINK_INPUT(userdata
);
1843 /* If you change anything here, make sure to change the
1844 * sink input handling a few lines down at
1845 * PA_SINK_MESSAGE_PREPAPRE_MOVE, too. */
1850 pa_sink_input_set_state_within_thread(i
, i
->state
);
1852 pa_assert(i
->thread_info
.attached
);
1853 i
->thread_info
.attached
= FALSE
;
1855 /* Since the caller sleeps in pa_sink_input_unlink(),
1856 * we can safely access data outside of thread_info even
1857 * though it is mutable */
1859 pa_assert(!i
->sync_prev
);
1860 pa_assert(!i
->sync_next
);
1862 if (i
->thread_info
.sync_prev
) {
1863 i
->thread_info
.sync_prev
->thread_info
.sync_next
= i
->thread_info
.sync_prev
->sync_next
;
1864 i
->thread_info
.sync_prev
= NULL
;
1867 if (i
->thread_info
.sync_next
) {
1868 i
->thread_info
.sync_next
->thread_info
.sync_prev
= i
->thread_info
.sync_next
->sync_prev
;
1869 i
->thread_info
.sync_next
= NULL
;
1872 if (pa_hashmap_remove(s
->thread_info
.inputs
, PA_UINT32_TO_PTR(i
->index
)))
1873 pa_sink_input_unref(i
);
1875 pa_sink_invalidate_requested_latency(s
, TRUE
);
1876 pa_sink_request_rewind(s
, (size_t) -1);
1878 /* In flat volume mode we need to update the volume as
1880 return o
->process_msg(o
, PA_SINK_MESSAGE_SET_VOLUME
, NULL
, 0, NULL
);
1883 case PA_SINK_MESSAGE_START_MOVE
: {
1884 pa_sink_input
*i
= PA_SINK_INPUT(userdata
);
1886 /* We don't support moving synchronized streams. */
1887 pa_assert(!i
->sync_prev
);
1888 pa_assert(!i
->sync_next
);
1889 pa_assert(!i
->thread_info
.sync_next
);
1890 pa_assert(!i
->thread_info
.sync_prev
);
1892 if (i
->thread_info
.state
!= PA_SINK_INPUT_CORKED
) {
1894 size_t sink_nbytes
, total_nbytes
;
1896 /* Get the latency of the sink */
1897 usec
= pa_sink_get_latency_within_thread(s
);
1898 sink_nbytes
= pa_usec_to_bytes(usec
, &s
->sample_spec
);
1899 total_nbytes
= sink_nbytes
+ pa_memblockq_get_length(i
->thread_info
.render_memblockq
);
1901 if (total_nbytes
> 0) {
1902 i
->thread_info
.rewrite_nbytes
= i
->thread_info
.resampler
? pa_resampler_request(i
->thread_info
.resampler
, total_nbytes
) : total_nbytes
;
1903 i
->thread_info
.rewrite_flush
= TRUE
;
1904 pa_sink_input_process_rewind(i
, sink_nbytes
);
1911 pa_assert(i
->thread_info
.attached
);
1912 i
->thread_info
.attached
= FALSE
;
1914 /* Let's remove the sink input ...*/
1915 if (pa_hashmap_remove(s
->thread_info
.inputs
, PA_UINT32_TO_PTR(i
->index
)))
1916 pa_sink_input_unref(i
);
1918 pa_sink_invalidate_requested_latency(s
, TRUE
);
1920 pa_log_debug("Requesting rewind due to started move");
1921 pa_sink_request_rewind(s
, (size_t) -1);
1923 /* In flat volume mode we need to update the volume as
1925 return o
->process_msg(o
, PA_SINK_MESSAGE_SET_VOLUME
, NULL
, 0, NULL
);
1928 case PA_SINK_MESSAGE_FINISH_MOVE
: {
1929 pa_sink_input
*i
= PA_SINK_INPUT(userdata
);
1931 /* We don't support moving synchronized streams. */
1932 pa_assert(!i
->sync_prev
);
1933 pa_assert(!i
->sync_next
);
1934 pa_assert(!i
->thread_info
.sync_next
);
1935 pa_assert(!i
->thread_info
.sync_prev
);
1937 pa_hashmap_put(s
->thread_info
.inputs
, PA_UINT32_TO_PTR(i
->index
), pa_sink_input_ref(i
));
1939 pa_assert(!i
->thread_info
.attached
);
1940 i
->thread_info
.attached
= TRUE
;
1945 if (i
->thread_info
.requested_sink_latency
!= (pa_usec_t
) -1)
1946 pa_sink_input_set_requested_latency_within_thread(i
, i
->thread_info
.requested_sink_latency
);
1948 pa_sink_input_update_max_rewind(i
, s
->thread_info
.max_rewind
);
1949 pa_sink_input_update_max_request(i
, s
->thread_info
.max_request
);
1951 if (i
->thread_info
.state
!= PA_SINK_INPUT_CORKED
) {
1955 /* Get the latency of the sink */
1956 usec
= pa_sink_get_latency_within_thread(s
);
1957 nbytes
= pa_usec_to_bytes(usec
, &s
->sample_spec
);
1960 pa_sink_input_drop(i
, nbytes
);
1962 pa_log_debug("Requesting rewind due to finished move");
1963 pa_sink_request_rewind(s
, nbytes
);
1966 /* In flat volume mode we need to update the volume as
1968 return o
->process_msg(o
, PA_SINK_MESSAGE_SET_VOLUME
, NULL
, 0, NULL
);
1971 case PA_SINK_MESSAGE_SET_VOLUME
:
1973 if (!pa_cvolume_equal(&s
->thread_info
.soft_volume
, &s
->soft_volume
)) {
1974 s
->thread_info
.soft_volume
= s
->soft_volume
;
1975 pa_sink_request_rewind(s
, (size_t) -1);
1978 if (!(s
->flags
& PA_SINK_FLAT_VOLUME
))
1981 /* Fall through ... */
1983 case PA_SINK_MESSAGE_SYNC_VOLUMES
:
1984 sync_input_volumes_within_thread(s
);
1987 case PA_SINK_MESSAGE_GET_VOLUME
:
1990 case PA_SINK_MESSAGE_SET_MUTE
:
1992 if (s
->thread_info
.soft_muted
!= s
->muted
) {
1993 s
->thread_info
.soft_muted
= s
->muted
;
1994 pa_sink_request_rewind(s
, (size_t) -1);
1999 case PA_SINK_MESSAGE_GET_MUTE
:
2002 case PA_SINK_MESSAGE_SET_STATE
: {
2004 pa_bool_t suspend_change
=
2005 (s
->thread_info
.state
== PA_SINK_SUSPENDED
&& PA_SINK_IS_OPENED(PA_PTR_TO_UINT(userdata
))) ||
2006 (PA_SINK_IS_OPENED(s
->thread_info
.state
) && PA_PTR_TO_UINT(userdata
) == PA_SINK_SUSPENDED
);
2008 s
->thread_info
.state
= PA_PTR_TO_UINT(userdata
);
2010 if (s
->thread_info
.state
== PA_SINK_SUSPENDED
) {
2011 s
->thread_info
.rewind_nbytes
= 0;
2012 s
->thread_info
.rewind_requested
= FALSE
;
2015 if (suspend_change
) {
2019 while ((i
= pa_hashmap_iterate(s
->thread_info
.inputs
, &state
, NULL
)))
2020 if (i
->suspend_within_thread
)
2021 i
->suspend_within_thread(i
, s
->thread_info
.state
== PA_SINK_SUSPENDED
);
2027 case PA_SINK_MESSAGE_DETACH
:
2029 /* Detach all streams */
2030 pa_sink_detach_within_thread(s
);
2033 case PA_SINK_MESSAGE_ATTACH
:
2035 /* Reattach all streams */
2036 pa_sink_attach_within_thread(s
);
2039 case PA_SINK_MESSAGE_GET_REQUESTED_LATENCY
: {
2041 pa_usec_t
*usec
= userdata
;
2042 *usec
= pa_sink_get_requested_latency_within_thread(s
);
2044 /* Yes, that's right, the IO thread will see -1 when no
2045 * explicit requested latency is configured, the main
2046 * thread will see max_latency */
2047 if (*usec
== (pa_usec_t
) -1)
2048 *usec
= s
->thread_info
.max_latency
;
2053 case PA_SINK_MESSAGE_SET_LATENCY_RANGE
: {
2054 pa_usec_t
*r
= userdata
;
2056 pa_sink_set_latency_range_within_thread(s
, r
[0], r
[1]);
2061 case PA_SINK_MESSAGE_GET_LATENCY_RANGE
: {
2062 pa_usec_t
*r
= userdata
;
2064 r
[0] = s
->thread_info
.min_latency
;
2065 r
[1] = s
->thread_info
.max_latency
;
2070 case PA_SINK_MESSAGE_GET_FIXED_LATENCY
:
2072 *((pa_usec_t
*) userdata
) = s
->thread_info
.fixed_latency
;
2075 case PA_SINK_MESSAGE_SET_FIXED_LATENCY
:
2077 pa_sink_set_fixed_latency_within_thread(s
, (pa_usec_t
) offset
);
2080 case PA_SINK_MESSAGE_GET_MAX_REWIND
:
2082 *((size_t*) userdata
) = s
->thread_info
.max_rewind
;
2085 case PA_SINK_MESSAGE_GET_MAX_REQUEST
:
2087 *((size_t*) userdata
) = s
->thread_info
.max_request
;
2090 case PA_SINK_MESSAGE_SET_MAX_REWIND
:
2092 pa_sink_set_max_rewind_within_thread(s
, (size_t) offset
);
2095 case PA_SINK_MESSAGE_SET_MAX_REQUEST
:
2097 pa_sink_set_max_request_within_thread(s
, (size_t) offset
);
2100 case PA_SINK_MESSAGE_GET_LATENCY
:
2101 case PA_SINK_MESSAGE_MAX
:
2108 /* Called from main thread */
2109 int pa_sink_suspend_all(pa_core
*c
, pa_bool_t suspend
, pa_suspend_cause_t cause
) {
2114 pa_core_assert_ref(c
);
2115 pa_assert_ctl_context();
2116 pa_assert(cause
!= 0);
2118 PA_IDXSET_FOREACH(sink
, c
->sinks
, idx
) {
2121 if ((r
= pa_sink_suspend(sink
, suspend
, cause
)) < 0)
2128 /* Called from main thread */
2129 void pa_sink_detach(pa_sink
*s
) {
2130 pa_sink_assert_ref(s
);
2131 pa_assert_ctl_context();
2132 pa_assert(PA_SINK_IS_LINKED(s
->state
));
2134 pa_assert_se(pa_asyncmsgq_send(s
->asyncmsgq
, PA_MSGOBJECT(s
), PA_SINK_MESSAGE_DETACH
, NULL
, 0, NULL
) == 0);
2137 /* Called from main thread */
2138 void pa_sink_attach(pa_sink
*s
) {
2139 pa_sink_assert_ref(s
);
2140 pa_assert_ctl_context();
2141 pa_assert(PA_SINK_IS_LINKED(s
->state
));
2143 pa_assert_se(pa_asyncmsgq_send(s
->asyncmsgq
, PA_MSGOBJECT(s
), PA_SINK_MESSAGE_ATTACH
, NULL
, 0, NULL
) == 0);
2146 /* Called from IO thread */
2147 void pa_sink_detach_within_thread(pa_sink
*s
) {
2151 pa_sink_assert_ref(s
);
2152 pa_sink_assert_io_context(s
);
2153 pa_assert(PA_SINK_IS_LINKED(s
->thread_info
.state
));
2155 PA_HASHMAP_FOREACH(i
, s
->thread_info
.inputs
, state
)
2159 if (s
->monitor_source
)
2160 pa_source_detach_within_thread(s
->monitor_source
);
2163 /* Called from IO thread */
2164 void pa_sink_attach_within_thread(pa_sink
*s
) {
2168 pa_sink_assert_ref(s
);
2169 pa_sink_assert_io_context(s
);
2170 pa_assert(PA_SINK_IS_LINKED(s
->thread_info
.state
));
2172 PA_HASHMAP_FOREACH(i
, s
->thread_info
.inputs
, state
)
2176 if (s
->monitor_source
)
2177 pa_source_attach_within_thread(s
->monitor_source
);
2180 /* Called from IO thread */
2181 void pa_sink_request_rewind(pa_sink
*s
, size_t nbytes
) {
2182 pa_sink_assert_ref(s
);
2183 pa_sink_assert_io_context(s
);
2184 pa_assert(PA_SINK_IS_LINKED(s
->thread_info
.state
));
2186 if (s
->thread_info
.state
== PA_SINK_SUSPENDED
)
2189 if (nbytes
== (size_t) -1)
2190 nbytes
= s
->thread_info
.max_rewind
;
2192 nbytes
= PA_MIN(nbytes
, s
->thread_info
.max_rewind
);
2194 if (s
->thread_info
.rewind_requested
&&
2195 nbytes
<= s
->thread_info
.rewind_nbytes
)
2198 s
->thread_info
.rewind_nbytes
= nbytes
;
2199 s
->thread_info
.rewind_requested
= TRUE
;
2201 if (s
->request_rewind
)
2202 s
->request_rewind(s
);
2205 /* Called from IO thread */
2206 pa_usec_t
pa_sink_get_requested_latency_within_thread(pa_sink
*s
) {
2207 pa_usec_t result
= (pa_usec_t
) -1;
2210 pa_usec_t monitor_latency
;
2212 pa_sink_assert_ref(s
);
2213 pa_sink_assert_io_context(s
);
2215 if (!(s
->flags
& PA_SINK_DYNAMIC_LATENCY
))
2216 return PA_CLAMP(s
->thread_info
.fixed_latency
, s
->thread_info
.min_latency
, s
->thread_info
.max_latency
);
2218 if (s
->thread_info
.requested_latency_valid
)
2219 return s
->thread_info
.requested_latency
;
2221 PA_HASHMAP_FOREACH(i
, s
->thread_info
.inputs
, state
)
2222 if (i
->thread_info
.requested_sink_latency
!= (pa_usec_t
) -1 &&
2223 (result
== (pa_usec_t
) -1 || result
> i
->thread_info
.requested_sink_latency
))
2224 result
= i
->thread_info
.requested_sink_latency
;
2226 monitor_latency
= pa_source_get_requested_latency_within_thread(s
->monitor_source
);
2228 if (monitor_latency
!= (pa_usec_t
) -1 &&
2229 (result
== (pa_usec_t
) -1 || result
> monitor_latency
))
2230 result
= monitor_latency
;
2232 if (result
!= (pa_usec_t
) -1)
2233 result
= PA_CLAMP(result
, s
->thread_info
.min_latency
, s
->thread_info
.max_latency
);
2235 if (PA_SINK_IS_LINKED(s
->thread_info
.state
)) {
2236 /* Only cache if properly initialized */
2237 s
->thread_info
.requested_latency
= result
;
2238 s
->thread_info
.requested_latency_valid
= TRUE
;
2244 /* Called from main thread */
2245 pa_usec_t
pa_sink_get_requested_latency(pa_sink
*s
) {
2248 pa_sink_assert_ref(s
);
2249 pa_assert_ctl_context();
2250 pa_assert(PA_SINK_IS_LINKED(s
->state
));
2252 if (s
->state
== PA_SINK_SUSPENDED
)
2255 pa_assert_se(pa_asyncmsgq_send(s
->asyncmsgq
, PA_MSGOBJECT(s
), PA_SINK_MESSAGE_GET_REQUESTED_LATENCY
, &usec
, 0, NULL
) == 0);
2259 /* Called from IO as well as the main thread -- the latter only before the IO thread started up */
2260 void pa_sink_set_max_rewind_within_thread(pa_sink
*s
, size_t max_rewind
) {
2264 pa_sink_assert_ref(s
);
2265 pa_sink_assert_io_context(s
);
2267 if (max_rewind
== s
->thread_info
.max_rewind
)
2270 s
->thread_info
.max_rewind
= max_rewind
;
2272 if (PA_SINK_IS_LINKED(s
->thread_info
.state
))
2273 PA_HASHMAP_FOREACH(i
, s
->thread_info
.inputs
, state
)
2274 pa_sink_input_update_max_rewind(i
, s
->thread_info
.max_rewind
);
2276 if (s
->monitor_source
)
2277 pa_source_set_max_rewind_within_thread(s
->monitor_source
, s
->thread_info
.max_rewind
);
2280 /* Called from main thread */
2281 void pa_sink_set_max_rewind(pa_sink
*s
, size_t max_rewind
) {
2282 pa_sink_assert_ref(s
);
2283 pa_assert_ctl_context();
2285 if (PA_SINK_IS_LINKED(s
->state
))
2286 pa_assert_se(pa_asyncmsgq_send(s
->asyncmsgq
, PA_MSGOBJECT(s
), PA_SINK_MESSAGE_SET_MAX_REWIND
, NULL
, max_rewind
, NULL
) == 0);
2288 pa_sink_set_max_rewind_within_thread(s
, max_rewind
);
2291 /* Called from IO as well as the main thread -- the latter only before the IO thread started up */
2292 void pa_sink_set_max_request_within_thread(pa_sink
*s
, size_t max_request
) {
2295 pa_sink_assert_ref(s
);
2296 pa_sink_assert_io_context(s
);
2298 if (max_request
== s
->thread_info
.max_request
)
2301 s
->thread_info
.max_request
= max_request
;
2303 if (PA_SINK_IS_LINKED(s
->thread_info
.state
)) {
2306 PA_HASHMAP_FOREACH(i
, s
->thread_info
.inputs
, state
)
2307 pa_sink_input_update_max_request(i
, s
->thread_info
.max_request
);
2311 /* Called from main thread */
2312 void pa_sink_set_max_request(pa_sink
*s
, size_t max_request
) {
2313 pa_sink_assert_ref(s
);
2314 pa_assert_ctl_context();
2316 if (PA_SINK_IS_LINKED(s
->state
))
2317 pa_assert_se(pa_asyncmsgq_send(s
->asyncmsgq
, PA_MSGOBJECT(s
), PA_SINK_MESSAGE_SET_MAX_REQUEST
, NULL
, max_request
, NULL
) == 0);
2319 pa_sink_set_max_request_within_thread(s
, max_request
);
2322 /* Called from IO thread */
2323 void pa_sink_invalidate_requested_latency(pa_sink
*s
, pa_bool_t dynamic
) {
2327 pa_sink_assert_ref(s
);
2328 pa_sink_assert_io_context(s
);
2330 if ((s
->flags
& PA_SINK_DYNAMIC_LATENCY
))
2331 s
->thread_info
.requested_latency_valid
= FALSE
;
2335 if (PA_SINK_IS_LINKED(s
->thread_info
.state
)) {
2337 if (s
->update_requested_latency
)
2338 s
->update_requested_latency(s
);
2340 PA_HASHMAP_FOREACH(i
, s
->thread_info
.inputs
, state
)
2341 if (i
->update_sink_requested_latency
)
2342 i
->update_sink_requested_latency(i
);
2346 /* Called from main thread */
2347 void pa_sink_set_latency_range(pa_sink
*s
, pa_usec_t min_latency
, pa_usec_t max_latency
) {
2348 pa_sink_assert_ref(s
);
2349 pa_assert_ctl_context();
2351 /* min_latency == 0: no limit
2352 * min_latency anything else: specified limit
2354 * Similar for max_latency */
2356 if (min_latency
< ABSOLUTE_MIN_LATENCY
)
2357 min_latency
= ABSOLUTE_MIN_LATENCY
;
2359 if (max_latency
<= 0 ||
2360 max_latency
> ABSOLUTE_MAX_LATENCY
)
2361 max_latency
= ABSOLUTE_MAX_LATENCY
;
2363 pa_assert(min_latency
<= max_latency
);
2365 /* Hmm, let's see if someone forgot to set PA_SINK_DYNAMIC_LATENCY here... */
2366 pa_assert((min_latency
== ABSOLUTE_MIN_LATENCY
&&
2367 max_latency
== ABSOLUTE_MAX_LATENCY
) ||
2368 (s
->flags
& PA_SINK_DYNAMIC_LATENCY
));
2370 if (PA_SINK_IS_LINKED(s
->state
)) {
2376 pa_assert_se(pa_asyncmsgq_send(s
->asyncmsgq
, PA_MSGOBJECT(s
), PA_SINK_MESSAGE_SET_LATENCY_RANGE
, r
, 0, NULL
) == 0);
2378 pa_sink_set_latency_range_within_thread(s
, min_latency
, max_latency
);
2381 /* Called from main thread */
2382 void pa_sink_get_latency_range(pa_sink
*s
, pa_usec_t
*min_latency
, pa_usec_t
*max_latency
) {
2383 pa_sink_assert_ref(s
);
2384 pa_assert_ctl_context();
2385 pa_assert(min_latency
);
2386 pa_assert(max_latency
);
2388 if (PA_SINK_IS_LINKED(s
->state
)) {
2389 pa_usec_t r
[2] = { 0, 0 };
2391 pa_assert_se(pa_asyncmsgq_send(s
->asyncmsgq
, PA_MSGOBJECT(s
), PA_SINK_MESSAGE_GET_LATENCY_RANGE
, r
, 0, NULL
) == 0);
2393 *min_latency
= r
[0];
2394 *max_latency
= r
[1];
2396 *min_latency
= s
->thread_info
.min_latency
;
2397 *max_latency
= s
->thread_info
.max_latency
;
2401 /* Called from IO thread */
2402 void pa_sink_set_latency_range_within_thread(pa_sink
*s
, pa_usec_t min_latency
, pa_usec_t max_latency
) {
2403 pa_sink_assert_ref(s
);
2404 pa_sink_assert_io_context(s
);
2406 pa_assert(min_latency
>= ABSOLUTE_MIN_LATENCY
);
2407 pa_assert(max_latency
<= ABSOLUTE_MAX_LATENCY
);
2408 pa_assert(min_latency
<= max_latency
);
2410 /* Hmm, let's see if someone forgot to set PA_SINK_DYNAMIC_LATENCY here... */
2411 pa_assert((min_latency
== ABSOLUTE_MIN_LATENCY
&&
2412 max_latency
== ABSOLUTE_MAX_LATENCY
) ||
2413 (s
->flags
& PA_SINK_DYNAMIC_LATENCY
));
2415 if (s
->thread_info
.min_latency
== min_latency
&&
2416 s
->thread_info
.max_latency
== max_latency
)
2419 s
->thread_info
.min_latency
= min_latency
;
2420 s
->thread_info
.max_latency
= max_latency
;
2422 if (PA_SINK_IS_LINKED(s
->thread_info
.state
)) {
2426 PA_HASHMAP_FOREACH(i
, s
->thread_info
.inputs
, state
)
2427 if (i
->update_sink_latency_range
)
2428 i
->update_sink_latency_range(i
);
2431 pa_sink_invalidate_requested_latency(s
, FALSE
);
2433 pa_source_set_latency_range_within_thread(s
->monitor_source
, min_latency
, max_latency
);
2436 /* Called from main thread */
2437 void pa_sink_set_fixed_latency(pa_sink
*s
, pa_usec_t latency
) {
2438 pa_sink_assert_ref(s
);
2439 pa_assert_ctl_context();
2441 if (s
->flags
& PA_SINK_DYNAMIC_LATENCY
) {
2442 pa_assert(latency
== 0);
2446 if (latency
< ABSOLUTE_MIN_LATENCY
)
2447 latency
= ABSOLUTE_MIN_LATENCY
;
2449 if (latency
> ABSOLUTE_MAX_LATENCY
)
2450 latency
= ABSOLUTE_MAX_LATENCY
;
2452 if (PA_SINK_IS_LINKED(s
->state
))
2453 pa_assert_se(pa_asyncmsgq_send(s
->asyncmsgq
, PA_MSGOBJECT(s
), PA_SINK_MESSAGE_SET_FIXED_LATENCY
, NULL
, (int64_t) latency
, NULL
) == 0);
2455 s
->thread_info
.fixed_latency
= latency
;
2457 pa_source_set_fixed_latency(s
->monitor_source
, latency
);
2460 /* Called from main thread */
2461 pa_usec_t
pa_sink_get_fixed_latency(pa_sink
*s
) {
2464 pa_sink_assert_ref(s
);
2465 pa_assert_ctl_context();
2467 if (s
->flags
& PA_SINK_DYNAMIC_LATENCY
)
2470 if (PA_SINK_IS_LINKED(s
->state
))
2471 pa_assert_se(pa_asyncmsgq_send(s
->asyncmsgq
, PA_MSGOBJECT(s
), PA_SINK_MESSAGE_GET_FIXED_LATENCY
, &latency
, 0, NULL
) == 0);
2473 latency
= s
->thread_info
.fixed_latency
;
2478 /* Called from IO thread */
2479 void pa_sink_set_fixed_latency_within_thread(pa_sink
*s
, pa_usec_t latency
) {
2480 pa_sink_assert_ref(s
);
2481 pa_sink_assert_io_context(s
);
2483 if (s
->flags
& PA_SINK_DYNAMIC_LATENCY
) {
2484 pa_assert(latency
== 0);
2488 pa_assert(latency
>= ABSOLUTE_MIN_LATENCY
);
2489 pa_assert(latency
<= ABSOLUTE_MAX_LATENCY
);
2491 if (s
->thread_info
.fixed_latency
== latency
)
2494 s
->thread_info
.fixed_latency
= latency
;
2496 if (PA_SINK_IS_LINKED(s
->thread_info
.state
)) {
2500 PA_HASHMAP_FOREACH(i
, s
->thread_info
.inputs
, state
)
2501 if (i
->update_sink_fixed_latency
)
2502 i
->update_sink_fixed_latency(i
);
2505 pa_sink_invalidate_requested_latency(s
, FALSE
);
2507 pa_source_set_fixed_latency_within_thread(s
->monitor_source
, latency
);
2510 /* Called from main context */
2511 size_t pa_sink_get_max_rewind(pa_sink
*s
) {
2513 pa_sink_assert_ref(s
);
2514 pa_assert_ctl_context();
2516 if (!PA_SINK_IS_LINKED(s
->state
))
2517 return s
->thread_info
.max_rewind
;
2519 pa_assert_se(pa_asyncmsgq_send(s
->asyncmsgq
, PA_MSGOBJECT(s
), PA_SINK_MESSAGE_GET_MAX_REWIND
, &r
, 0, NULL
) == 0);
2524 /* Called from main context */
2525 size_t pa_sink_get_max_request(pa_sink
*s
) {
2527 pa_sink_assert_ref(s
);
2528 pa_assert_ctl_context();
2530 if (!PA_SINK_IS_LINKED(s
->state
))
2531 return s
->thread_info
.max_request
;
2533 pa_assert_se(pa_asyncmsgq_send(s
->asyncmsgq
, PA_MSGOBJECT(s
), PA_SINK_MESSAGE_GET_MAX_REQUEST
, &r
, 0, NULL
) == 0);
2538 /* Called from main context */
2539 int pa_sink_set_port(pa_sink
*s
, const char *name
, pa_bool_t save
) {
2540 pa_device_port
*port
;
2542 pa_sink_assert_ref(s
);
2543 pa_assert_ctl_context();
2546 pa_log_debug("set_port() operation not implemented for sink %u \"%s\"", s
->index
, s
->name
);
2547 return -PA_ERR_NOTIMPLEMENTED
;
2551 return -PA_ERR_NOENTITY
;
2553 if (!(port
= pa_hashmap_get(s
->ports
, name
)))
2554 return -PA_ERR_NOENTITY
;
2556 if (s
->active_port
== port
) {
2557 s
->save_port
= s
->save_port
|| save
;
2561 if ((s
->set_port(s
, port
)) < 0)
2562 return -PA_ERR_NOENTITY
;
2564 pa_subscription_post(s
->core
, PA_SUBSCRIPTION_EVENT_SINK
|PA_SUBSCRIPTION_EVENT_CHANGE
, s
->index
);
2566 pa_log_info("Changed port of sink %u \"%s\" to %s", s
->index
, s
->name
, port
->name
);
2568 s
->active_port
= port
;
2569 s
->save_port
= save
;
2574 pa_bool_t
pa_device_init_icon(pa_proplist
*p
, pa_bool_t is_sink
) {
2575 const char *ff
, *c
, *t
= NULL
, *s
= "", *profile
, *bus
;
2579 if (pa_proplist_contains(p
, PA_PROP_DEVICE_ICON_NAME
))
2582 if ((ff
= pa_proplist_gets(p
, PA_PROP_DEVICE_FORM_FACTOR
))) {
2584 if (pa_streq(ff
, "microphone"))
2585 t
= "audio-input-microphone";
2586 else if (pa_streq(ff
, "webcam"))
2588 else if (pa_streq(ff
, "computer"))
2590 else if (pa_streq(ff
, "handset"))
2592 else if (pa_streq(ff
, "portable"))
2593 t
= "multimedia-player";
2594 else if (pa_streq(ff
, "tv"))
2595 t
= "video-display";
2598 * The following icons are not part of the icon naming spec,
2599 * because Rodney Dawes sucks as the maintainer of that spec.
2601 * http://lists.freedesktop.org/archives/xdg/2009-May/010397.html
2603 else if (pa_streq(ff
, "headset"))
2604 t
= "audio-headset";
2605 else if (pa_streq(ff
, "headphone"))
2606 t
= "audio-headphones";
2607 else if (pa_streq(ff
, "speaker"))
2608 t
= "audio-speakers";
2609 else if (pa_streq(ff
, "hands-free"))
2610 t
= "audio-handsfree";
2614 if ((c
= pa_proplist_gets(p
, PA_PROP_DEVICE_CLASS
)))
2615 if (pa_streq(c
, "modem"))
2622 t
= "audio-input-microphone";
2625 if ((profile
= pa_proplist_gets(p
, PA_PROP_DEVICE_PROFILE_NAME
))) {
2626 if (strstr(profile
, "analog"))
2628 else if (strstr(profile
, "iec958"))
2630 else if (strstr(profile
, "hdmi"))
2634 bus
= pa_proplist_gets(p
, PA_PROP_DEVICE_BUS
);
2636 pa_proplist_setf(p
, PA_PROP_DEVICE_ICON_NAME
, "%s%s%s%s", t
, pa_strempty(s
), bus
? "-" : "", pa_strempty(bus
));
2641 pa_bool_t
pa_device_init_description(pa_proplist
*p
) {
2642 const char *s
, *d
= NULL
, *k
;
2645 if (pa_proplist_contains(p
, PA_PROP_DEVICE_DESCRIPTION
))
2648 if ((s
= pa_proplist_gets(p
, PA_PROP_DEVICE_FORM_FACTOR
)))
2649 if (pa_streq(s
, "internal"))
2650 d
= _("Internal Audio");
2653 if ((s
= pa_proplist_gets(p
, PA_PROP_DEVICE_CLASS
)))
2654 if (pa_streq(s
, "modem"))
2658 d
= pa_proplist_gets(p
, PA_PROP_DEVICE_PRODUCT_NAME
);
2663 k
= pa_proplist_gets(p
, PA_PROP_DEVICE_PROFILE_DESCRIPTION
);
2666 pa_proplist_setf(p
, PA_PROP_DEVICE_DESCRIPTION
, _("%s %s"), d
, k
);
2668 pa_proplist_sets(p
, PA_PROP_DEVICE_DESCRIPTION
, d
);
2673 pa_bool_t
pa_device_init_intended_roles(pa_proplist
*p
) {
2677 if (pa_proplist_contains(p
, PA_PROP_DEVICE_INTENDED_ROLES
))
2680 if ((s
= pa_proplist_gets(p
, PA_PROP_DEVICE_FORM_FACTOR
)))
2681 if (pa_streq(s
, "handset") || pa_streq(s
, "hands-free")) {
2682 pa_proplist_sets(p
, PA_PROP_DEVICE_INTENDED_ROLES
, "phone");