#include <string.h>
#include <pulse/introspect.h>
+#include <pulse/format.h>
#include <pulse/utf8.h>
#include <pulse/xmalloc.h>
#include <pulse/timeval.h>
#include <pulse/util.h>
-#include <pulse/i18n.h>
#include <pulse/rtclock.h>
#include <pulse/internal.h>
+#include <pulsecore/i18n.h>
#include <pulsecore/sink-input.h>
#include <pulsecore/namereg.h>
#include <pulsecore/core-util.h>
s->set_state = NULL;
s->get_volume = NULL;
s->set_volume = NULL;
+ s->write_volume = NULL;
s->get_mute = NULL;
s->set_mute = NULL;
s->request_rewind = NULL;
s->update_requested_latency = NULL;
s->set_port = NULL;
s->get_formats = NULL;
+ s->set_formats = NULL;
}
/* Called from main context */
PA_LLIST_HEAD_INIT(pa_sink_volume_change, s->thread_info.volume_changes);
s->thread_info.volume_changes_tail = NULL;
pa_sw_cvolume_multiply(&s->thread_info.current_hw_volume, &s->soft_volume, &s->real_volume);
- s->thread_info.volume_change_safety_margin = core->sync_volume_safety_margin_usec;
- s->thread_info.volume_change_extra_delay = core->sync_volume_extra_delay_usec;
+ s->thread_info.volume_change_safety_margin = core->deferred_volume_safety_margin_usec;
+ s->thread_info.volume_change_extra_delay = core->deferred_volume_extra_delay_usec;
/* FIXME: This should probably be moved to pa_sink_put() */
pa_assert_se(pa_idxset_put(core->sinks, s, &s->index) >= 0);
s->state = state;
- if (state != PA_SINK_UNLINKED) { /* if we enter UNLINKED state pa_sink_unlink() will fire the apropriate events */
+ if (state != PA_SINK_UNLINKED) { /* if we enter UNLINKED state pa_sink_unlink() will fire the appropriate events */
pa_hook_fire(&s->core->hooks[PA_CORE_HOOK_SINK_STATE_CHANGED], s);
pa_subscription_post(s->core, PA_SUBSCRIPTION_EVENT_SINK | PA_SUBSCRIPTION_EVENT_CHANGE, s->index);
}
flags = s->flags;
if (cb)
- s->flags |= PA_SINK_SYNC_VOLUME;
+ s->flags |= PA_SINK_DEFERRED_VOLUME;
else
- s->flags &= ~PA_SINK_SYNC_VOLUME;
+ s->flags &= ~PA_SINK_DEFERRED_VOLUME;
/* If the flags have changed after init, let any clients know via a change event */
if (s->state != PA_SINK_INIT && flags != s->flags)
* Note: All of these flags set here can change over the life time
* of the sink. */
pa_assert(!(s->flags & PA_SINK_HW_VOLUME_CTRL) || s->set_volume);
- pa_assert(!(s->flags & PA_SINK_SYNC_VOLUME) || s->write_volume);
+ pa_assert(!(s->flags & PA_SINK_DEFERRED_VOLUME) || s->write_volume);
pa_assert(!(s->flags & PA_SINK_HW_MUTE_CTRL) || s->set_mute);
/* XXX: Currently decibel volume is disabled for all sinks that use volume
if (nbytes > 0) {
pa_log_debug("Processing rewind...");
- if (s->flags & PA_SINK_SYNC_VOLUME)
+ if (s->flags & PA_SINK_DEFERRED_VOLUME)
pa_sink_volume_change_rewind(s, nbytes);
}
return FALSE;
}
+/* Called from main context */
+void pa_sink_enter_passthrough(pa_sink *s) {
+ pa_cvolume volume;
+
+ /* disable the monitor in passthrough mode */
+ if (s->monitor_source)
+ pa_source_suspend(s->monitor_source, TRUE, PA_SUSPEND_PASSTHROUGH);
+
+ /* set the volume to NORM */
+ s->saved_volume = *pa_sink_get_volume(s, TRUE);
+ s->saved_save_volume = s->save_volume;
+
+ pa_cvolume_set(&volume, s->sample_spec.channels, PA_MIN(s->base_volume, PA_VOLUME_NORM));
+ pa_sink_set_volume(s, &volume, TRUE, FALSE);
+}
+
+/* Called from main context */
+void pa_sink_leave_passthrough(pa_sink *s) {
+ /* Unsuspend monitor */
+ if (s->monitor_source)
+ pa_source_suspend(s->monitor_source, FALSE, PA_SUSPEND_PASSTHROUGH);
+
+ /* Restore sink volume to what it was before we entered passthrough mode */
+ pa_sink_set_volume(s, &s->saved_volume, TRUE, s->saved_save_volume);
+
+ pa_cvolume_init(&s->saved_volume);
+ s->saved_save_volume = FALSE;
+}
+
/* Called from main context. */
static void compute_reference_ratio(pa_sink_input *i) {
unsigned c = 0;
* due to rounding errors. If that happens, we still want to propagate
* the changed root sink volume to the sinks connected to the
* intermediate sink that didn't change its volume. This theoretical
- * possiblity is the reason why we have that !(s->flags &
+ * possibility is the reason why we have that !(s->flags &
* PA_SINK_SHARE_VOLUME_WITH_MASTER) condition. Probably nobody would
* notice even if we returned here FALSE always if
* reference_volume_changed is FALSE. */
pa_assert(volume || pa_sink_flat_volume_enabled(s));
pa_assert(!volume || volume->channels == 1 || pa_cvolume_compatible(volume, &s->sample_spec));
- /* make sure we don't change the volume when a PASSTHROUGH input is connected */
- if (pa_sink_is_passthrough(s)) {
- /* FIXME: Need to notify client that volume control is disabled */
+ /* make sure we don't change the volume when a PASSTHROUGH input is connected ...
+ * ... *except* if we're being invoked to reset the volume to ensure 0 dB gain */
+ if (pa_sink_is_passthrough(s) && (!volume || !pa_cvolume_is_norm(volume))) {
pa_log_warn("Cannot change volume, Sink is connected to PASSTHROUGH input");
return;
}
* apply one to root_sink->soft_volume */
pa_cvolume_reset(&root_sink->soft_volume, root_sink->sample_spec.channels);
- if (!(root_sink->flags & PA_SINK_SYNC_VOLUME))
+ if (!(root_sink->flags & PA_SINK_DEFERRED_VOLUME))
root_sink->set_volume(root_sink);
} else
pa_sink_assert_ref(s);
pa_assert(!(s->flags & PA_SINK_SHARE_VOLUME_WITH_MASTER));
- if (s->flags & PA_SINK_SYNC_VOLUME)
+ if (s->flags & PA_SINK_DEFERRED_VOLUME)
pa_sink_assert_io_context(s);
else
pa_assert_ctl_context();
else
s->soft_volume = *volume;
- if (PA_SINK_IS_LINKED(s->state) && !(s->flags & PA_SINK_SYNC_VOLUME))
+ if (PA_SINK_IS_LINKED(s->state) && !(s->flags & PA_SINK_DEFERRED_VOLUME))
pa_assert_se(pa_asyncmsgq_send(s->asyncmsgq, PA_MSGOBJECT(s), PA_SINK_MESSAGE_SET_VOLUME, NULL, 0, NULL) == 0);
else
s->thread_info.soft_volume = s->soft_volume;
old_real_volume = s->real_volume;
- if (!(s->flags & PA_SINK_SYNC_VOLUME) && s->get_volume)
+ if (!(s->flags & PA_SINK_DEFERRED_VOLUME) && s->get_volume)
s->get_volume(s);
pa_assert_se(pa_asyncmsgq_send(s->asyncmsgq, PA_MSGOBJECT(s), PA_SINK_MESSAGE_GET_VOLUME, NULL, 0, NULL) == 0);
s->muted = mute;
s->save_muted = (old_muted == s->muted && s->save_muted) || save;
- if (!(s->flags & PA_SINK_SYNC_VOLUME) && s->set_mute)
+ if (!(s->flags & PA_SINK_DEFERRED_VOLUME) && s->set_mute)
s->set_mute(s);
pa_assert_se(pa_asyncmsgq_send(s->asyncmsgq, PA_MSGOBJECT(s), PA_SINK_MESSAGE_SET_MUTE, NULL, 0, NULL) == 0);
if (s->refresh_muted || force_refresh) {
pa_bool_t old_muted = s->muted;
- if (!(s->flags & PA_SINK_SYNC_VOLUME) && s->get_mute)
+ if (!(s->flags & PA_SINK_DEFERRED_VOLUME) && s->get_mute)
s->get_mute(s);
pa_assert_se(pa_asyncmsgq_send(s->asyncmsgq, PA_MSGOBJECT(s), PA_SINK_MESSAGE_GET_MUTE, NULL, 0, NULL) == 0);
ret = pa_idxset_size(s->inputs);
/* We add in the number of streams connected to us here. Please
- * note the asymmmetry to pa_sink_used_by()! */
+ * note the asymmetry to pa_sink_used_by()! */
if (s->monitor_source)
ret += pa_source_linked_by(s->monitor_source);
/* We do not assert here. It is perfectly valid for a sink input to
* be in the INIT state (i.e. created, marked done but not yet put)
* and we should not care if it's unlinked as it won't contribute
- * towarards our busy status.
+ * towards our busy status.
*/
if (!PA_SINK_INPUT_IS_LINKED(st))
continue;
case PA_SINK_MESSAGE_SET_VOLUME_SYNCED:
- if (s->flags & PA_SINK_SYNC_VOLUME) {
+ if (s->flags & PA_SINK_DEFERRED_VOLUME) {
s->set_volume(s);
pa_sink_volume_change_push(s);
}
case PA_SINK_MESSAGE_GET_VOLUME:
- if ((s->flags & PA_SINK_SYNC_VOLUME) && s->get_volume) {
+ if ((s->flags & PA_SINK_DEFERRED_VOLUME) && s->get_volume) {
s->get_volume(s);
pa_sink_volume_change_flush(s);
pa_sw_cvolume_divide(&s->thread_info.current_hw_volume, &s->real_volume, &s->soft_volume);
pa_sink_request_rewind(s, (size_t) -1);
}
- if (s->flags & PA_SINK_SYNC_VOLUME && s->set_mute)
+ if (s->flags & PA_SINK_DEFERRED_VOLUME && s->set_mute)
s->set_mute(s);
return 0;
case PA_SINK_MESSAGE_GET_MUTE:
- if (s->flags & PA_SINK_SYNC_VOLUME && s->get_mute)
+ if (s->flags & PA_SINK_DEFERRED_VOLUME && s->get_mute)
s->get_mute(s);
return 0;
return 0;
}
- if (s->flags & PA_SINK_SYNC_VOLUME) {
+ if (s->flags & PA_SINK_DEFERRED_VOLUME) {
struct sink_message_set_port msg = { .port = port, .ret = 0 };
pa_assert_se(pa_asyncmsgq_send(s->asyncmsgq, PA_MSGOBJECT(s), PA_SINK_MESSAGE_SET_PORT, &msg, 0, NULL) == 0);
ret = msg.ret;
pa_bool_t ret = FALSE;
pa_assert(s);
+
+ if (!PA_SINK_IS_LINKED(s->state)) {
+ if (usec_to_next)
+ *usec_to_next = 0;
+ return ret;
+ }
+
pa_assert(s->write_volume);
while (s->thread_info.volume_changes && now >= s->thread_info.volume_changes->at) {
return ret;
}
+/* Called from the main thread */
+/* Allows an external source to set what formats a sink supports if the sink
+ * permits this. The function makes a copy of the formats on success. */
+pa_bool_t pa_sink_set_formats(pa_sink *s, pa_idxset *formats) {
+ pa_assert(s);
+ pa_assert(formats);
+
+ if (s->set_formats)
+ /* Sink supports setting formats -- let's give it a shot */
+ return s->set_formats(s, formats);
+ else
+ /* Sink doesn't support setting this -- bail out */
+ return FALSE;
+}
+
/* Called from the main thread */
/* Checks if the sink can accept this format */
pa_bool_t pa_sink_check_format(pa_sink *s, pa_format_info *f)