pa_zero(*data);
data->proplist = pa_proplist_new();
- data->ports = pa_hashmap_new(pa_idxset_string_hash_func, pa_idxset_string_compare_func);
+ data->ports = pa_hashmap_new_full(pa_idxset_string_hash_func, pa_idxset_string_compare_func, NULL, (pa_free_cb_t) pa_device_port_unref);
return data;
}
pa_proplist_free(data->proplist);
if (data->ports)
- pa_hashmap_free(data->ports, (pa_free_cb_t) pa_device_port_unref);
+ pa_hashmap_free(data->ports);
pa_xfree(data->name);
pa_xfree(data->active_port);
if (data->card)
pa_proplist_update(data->proplist, PA_UPDATE_MERGE, data->card->proplist);
- pa_device_init_description(data->proplist);
+ pa_device_init_description(data->proplist, data->card);
pa_device_init_icon(data->proplist, true);
pa_device_init_intended_roles(data->proplist);
+ if (!data->active_port) {
+ pa_device_port *p = pa_device_port_find_best(data->ports);
+ if (p)
+ pa_sink_new_data_set_port(data, p->name);
+ }
+
if (pa_hook_fire(&core->hooks[PA_CORE_HOOK_SINK_FIXATE], data) < 0) {
pa_xfree(s);
pa_namereg_unregister(core, name);
if ((s->active_port = pa_hashmap_get(s->ports, data->active_port)))
s->save_port = data->save_port;
- if (!s->active_port) {
- void *state;
- pa_device_port *p;
-
- PA_HASHMAP_FOREACH(p, s->ports, state)
- if (!s->active_port || p->priority > s->active_port->priority)
- s->active_port = p;
- }
+ /* Hopefully the active port has already been assigned in the previous call
+ to pa_device_port_find_best, but better safe than sorry */
+ if (!s->active_port)
+ s->active_port = pa_device_port_find_best(s->ports);
if (s->active_port)
s->latency_offset = s->active_port->latency_offset;
0);
s->thread_info.rtpoll = NULL;
- s->thread_info.inputs = pa_hashmap_new(pa_idxset_trivial_hash_func, pa_idxset_trivial_compare_func);
+ s->thread_info.inputs = pa_hashmap_new_full(pa_idxset_trivial_hash_func, pa_idxset_trivial_compare_func, NULL,
+ (pa_free_cb_t) pa_sink_input_unref);
s->thread_info.soft_volume = s->soft_volume;
s->thread_info.soft_muted = s->muted;
s->thread_info.state = s->state;
}
pa_idxset_free(s->inputs, NULL);
- pa_hashmap_free(s->thread_info.inputs, (pa_free_cb_t) pa_sink_input_unref);
+ pa_hashmap_free(s->thread_info.inputs);
if (s->silence.memblock)
pa_memblock_unref(s->silence.memblock);
pa_proplist_free(s->proplist);
if (s->ports)
- pa_hashmap_free(s->ports, (pa_free_cb_t) pa_device_port_unref);
+ pa_hashmap_free(s->ports);
pa_xfree(s);
}
}
/* Called from main thread */
-bool pa_sink_update_rate(pa_sink *s, uint32_t rate, bool passthrough) {
- bool ret = false;
+int pa_sink_update_rate(pa_sink *s, uint32_t rate, bool passthrough) {
+ int ret = -1;
uint32_t desired_rate = rate;
uint32_t default_rate = s->default_sample_rate;
uint32_t alternate_rate = s->alternate_sample_rate;
pa_sink_input *i;
bool use_alternate = false;
+ if (rate == s->sample_spec.rate)
+ return 0;
+
if (!s->update_rate)
- return false;
+ return -1;
if (PA_UNLIKELY(default_rate == alternate_rate && !passthrough)) {
pa_log_debug("Default and alternate sample rates are the same.");
- return false;
+ return -1;
}
if (PA_SINK_IS_RUNNING(s->state)) {
pa_log_info("Cannot update rate, SINK_IS_RUNNING, will keep using %u Hz",
s->sample_spec.rate);
- return false;
+ return -1;
}
if (s->monitor_source) {
if (PA_SOURCE_IS_RUNNING(s->monitor_source->state) == true) {
pa_log_info("Cannot update rate, monitor source is RUNNING");
- return false;
+ return -1;
}
}
- if (PA_UNLIKELY (desired_rate < 8000 ||
- desired_rate > PA_RATE_MAX))
- return false;
+ if (PA_UNLIKELY(!pa_sample_rate_valid(desired_rate)))
+ return -1;
if (!passthrough) {
- pa_assert(default_rate % 4000 || default_rate % 11025);
- pa_assert(alternate_rate % 4000 || alternate_rate % 11025);
+ pa_assert((default_rate % 4000 == 0) || (default_rate % 11025 == 0));
+ pa_assert((alternate_rate % 4000 == 0) || (alternate_rate % 11025 == 0));
- if (default_rate % 4000) {
- /* default is a 11025 multiple */
+ if (default_rate % 11025 == 0) {
if ((alternate_rate % 4000 == 0) && (desired_rate % 4000 == 0))
use_alternate=true;
} else {
}
if (desired_rate == s->sample_spec.rate)
- return false;
+ return -1;
if (!passthrough && pa_sink_used_by(s) > 0)
- return false;
+ return -1;
pa_log_debug("Suspending sink %s due to changing the sample rate.", s->name);
pa_sink_suspend(s, true, PA_SUSPEND_INTERNAL);
- if (s->update_rate(s, desired_rate) == true) {
+ if (s->update_rate(s, desired_rate) >= 0) {
/* update monitor source as well */
if (s->monitor_source && !passthrough)
pa_source_update_rate(s->monitor_source, desired_rate, false);
pa_sink_input_update_rate(i);
}
- ret = true;
+ ret = 0;
}
pa_sink_suspend(s, false, PA_SUSPEND_INTERNAL);
PA_IDXSET_FOREACH(i, s->inputs, idx) {
if (i->origin_sink && (i->origin_sink->flags & PA_SINK_SHARE_VOLUME_WITH_MASTER)) {
if (pa_sink_flat_volume_enabled(s)) {
- pa_cvolume old_volume = i->volume;
+ pa_cvolume new_input_volume;
/* Follow the root sink's real volume. */
- i->volume = *new_volume;
- pa_cvolume_remap(&i->volume, channel_map, &i->channel_map);
+ new_input_volume = *new_volume;
+ pa_cvolume_remap(&new_input_volume, channel_map, &i->channel_map);
+ pa_sink_input_set_volume_direct(i, &new_input_volume);
compute_reference_ratio(i);
-
- /* The volume changed, let's tell people so */
- if (!pa_cvolume_equal(&old_volume, &i->volume)) {
- if (i->volume_changed)
- i->volume_changed(i);
-
- pa_subscription_post(i->core, PA_SUBSCRIPTION_EVENT_SINK_INPUT|PA_SUBSCRIPTION_EVENT_CHANGE, i->index);
- }
}
update_real_volume(i->origin_sink, new_volume, channel_map);
* sink input volumes accordingly */
PA_IDXSET_FOREACH(i, s->inputs, idx) {
- pa_cvolume old_volume;
+ pa_cvolume new_volume;
if (i->origin_sink && (i->origin_sink->flags & PA_SINK_SHARE_VOLUME_WITH_MASTER)) {
propagate_reference_volume(i->origin_sink);
continue;
}
- old_volume = i->volume;
-
/* This basically calculates:
*
* i->volume := s->reference_volume * i->reference_ratio */
- i->volume = s->reference_volume;
- pa_cvolume_remap(&i->volume, &s->channel_map, &i->channel_map);
- pa_sw_cvolume_multiply(&i->volume, &i->volume, &i->reference_ratio);
-
- /* The volume changed, let's tell people so */
- if (!pa_cvolume_equal(&old_volume, &i->volume)) {
-
- if (i->volume_changed)
- i->volume_changed(i);
-
- pa_subscription_post(i->core, PA_SUBSCRIPTION_EVENT_SINK_INPUT|PA_SUBSCRIPTION_EVENT_CHANGE, i->index);
- }
+ new_volume = s->reference_volume;
+ pa_cvolume_remap(&new_volume, &s->channel_map, &i->channel_map);
+ pa_sw_cvolume_multiply(&new_volume, &new_volume, &i->reference_ratio);
+ pa_sink_input_set_volume_direct(i, &new_volume);
}
}
pa_cvolume_remap(&volume, channel_map, &s->channel_map);
reference_volume_changed = !pa_cvolume_equal(&volume, &s->reference_volume);
- s->reference_volume = volume;
+ pa_sink_set_reference_volume_direct(s, &volume);
s->save_volume = (!reference_volume_changed && s->save_volume) || save;
- if (reference_volume_changed)
- pa_subscription_post(s->core, PA_SUBSCRIPTION_EVENT_SINK|PA_SUBSCRIPTION_EVENT_CHANGE, s->index);
- else if (!(s->flags & PA_SINK_SHARE_VOLUME_WITH_MASTER))
+ if (!reference_volume_changed && !(s->flags & PA_SINK_SHARE_VOLUME_WITH_MASTER))
/* If the root sink's volume doesn't change, then there can't be any
* changes in the other sinks in the sink tree either.
*
/* Let's 'push' the reference volume if necessary */
pa_cvolume_merge(&new_reference_volume, &s->reference_volume, &root_sink->real_volume);
- /* If the sink and it's root don't have the same number of channels, we need to remap */
+ /* If the sink and its root don't have the same number of channels, we need to remap */
if (s != root_sink && !pa_channel_map_equal(&s->channel_map, &root_sink->channel_map))
pa_cvolume_remap(&new_reference_volume, &s->channel_map, &root_sink->channel_map);
update_reference_volume(root_sink, &new_reference_volume, &root_sink->channel_map, save);
if (pa_sink_flat_volume_enabled(s)) {
PA_IDXSET_FOREACH(i, s->inputs, idx) {
- pa_cvolume old_volume = i->volume;
+ pa_cvolume new_volume;
/* 2. Since the sink's reference and real volumes are equal
* now our ratios should be too. */
* i->volume = s->reference_volume * i->reference_ratio
*
* This is identical to propagate_reference_volume() */
- i->volume = s->reference_volume;
- pa_cvolume_remap(&i->volume, &s->channel_map, &i->channel_map);
- pa_sw_cvolume_multiply(&i->volume, &i->volume, &i->reference_ratio);
-
- /* Notify if something changed */
- if (!pa_cvolume_equal(&old_volume, &i->volume)) {
-
- if (i->volume_changed)
- i->volume_changed(i);
-
- pa_subscription_post(i->core, PA_SUBSCRIPTION_EVENT_SINK_INPUT|PA_SUBSCRIPTION_EVENT_CHANGE, i->index);
- }
+ new_volume = s->reference_volume;
+ pa_cvolume_remap(&new_volume, &s->channel_map, &i->channel_map);
+ pa_sw_cvolume_multiply(&new_volume, &new_volume, &i->reference_ratio);
+ pa_sink_input_set_volume_direct(i, &new_volume);
if (i->origin_sink && (i->origin_sink->flags & PA_SINK_SHARE_VOLUME_WITH_MASTER))
propagate_real_volume(i->origin_sink, old_real_volume);
pa_assert(PA_SINK_IS_LINKED(s->state));
old_muted = s->muted;
+
+ if (mute == old_muted) {
+ s->save_muted |= save;
+ return;
+ }
+
s->muted = mute;
- s->save_muted = (old_muted == s->muted && s->save_muted) || save;
+ s->save_muted = save;
if (!(s->flags & PA_SINK_DEFERRED_VOLUME) && s->set_mute)
s->set_mute(s);
+ pa_log_debug("The mute of sink %s changed from %s to %s.", s->name, pa_yes_no(old_muted), pa_yes_no(mute));
pa_assert_se(pa_asyncmsgq_send(s->asyncmsgq, PA_MSGOBJECT(s), PA_SINK_MESSAGE_SET_MUTE, NULL, 0, NULL) == 0);
-
- if (old_muted != s->muted)
- pa_subscription_post(s->core, PA_SUBSCRIPTION_EVENT_SINK|PA_SUBSCRIPTION_EVENT_CHANGE, s->index);
+ pa_subscription_post(s->core, PA_SUBSCRIPTION_EVENT_SINK|PA_SUBSCRIPTION_EVENT_CHANGE, s->index);
}
/* Called from main thread */
i->thread_info.sync_next = NULL;
}
- if (pa_hashmap_remove(s->thread_info.inputs, PA_UINT32_TO_PTR(i->index)))
- pa_sink_input_unref(i);
-
+ pa_hashmap_remove_and_free(s->thread_info.inputs, PA_UINT32_TO_PTR(i->index));
pa_sink_invalidate_requested_latency(s, true);
pa_sink_request_rewind(s, (size_t) -1);
i->thread_info.attached = false;
/* Let's remove the sink input ...*/
- if (pa_hashmap_remove(s->thread_info.inputs, PA_UINT32_TO_PTR(i->index)))
- pa_sink_input_unref(i);
+ pa_hashmap_remove_and_free(s->thread_info.inputs, PA_UINT32_TO_PTR(i->index));
pa_sink_invalidate_requested_latency(s, true);
return 0;
}
- case PA_SINK_MESSAGE_DETACH:
-
- /* Detach all streams */
- pa_sink_detach_within_thread(s);
- return 0;
-
- case PA_SINK_MESSAGE_ATTACH:
-
- /* Reattach all streams */
- pa_sink_attach_within_thread(s);
- return 0;
-
case PA_SINK_MESSAGE_GET_REQUESTED_LATENCY: {
pa_usec_t *usec = userdata;
return ret;
}
-/* Called from main thread */
-void pa_sink_detach(pa_sink *s) {
- pa_sink_assert_ref(s);
- pa_assert_ctl_context();
- pa_assert(PA_SINK_IS_LINKED(s->state));
-
- pa_assert_se(pa_asyncmsgq_send(s->asyncmsgq, PA_MSGOBJECT(s), PA_SINK_MESSAGE_DETACH, NULL, 0, NULL) == 0);
-}
-
-/* Called from main thread */
-void pa_sink_attach(pa_sink *s) {
- pa_sink_assert_ref(s);
- pa_assert_ctl_context();
- pa_assert(PA_SINK_IS_LINKED(s->state));
-
- pa_assert_se(pa_asyncmsgq_send(s->asyncmsgq, PA_MSGOBJECT(s), PA_SINK_MESSAGE_ATTACH, NULL, 0, NULL) == 0);
-}
-
/* Called from IO thread */
void pa_sink_detach_within_thread(pa_sink *s) {
pa_sink_input *i;
return true;
}
-bool pa_device_init_description(pa_proplist *p) {
+bool pa_device_init_description(pa_proplist *p, pa_card *card) {
const char *s, *d = NULL, *k;
pa_assert(p);
if (pa_proplist_contains(p, PA_PROP_DEVICE_DESCRIPTION))
return true;
- if ((s = pa_proplist_gets(p, PA_PROP_DEVICE_FORM_FACTOR)))
- if (pa_streq(s, "internal"))
- d = _("Built-in Audio");
+ if (card)
+ if ((s = pa_proplist_gets(card->proplist, PA_PROP_DEVICE_DESCRIPTION)))
+ d = s;
+
+ if (!d)
+ if ((s = pa_proplist_gets(p, PA_PROP_DEVICE_FORM_FACTOR)))
+ if (pa_streq(s, "internal"))
+ d = _("Built-in Audio");
if (!d)
if ((s = pa_proplist_gets(p, PA_PROP_DEVICE_CLASS)))
return out_formats;
}
+
+/* Called from the main thread. */
+void pa_sink_set_reference_volume_direct(pa_sink *s, const pa_cvolume *volume) {
+ pa_cvolume old_volume;
+ char old_volume_str[PA_CVOLUME_SNPRINT_VERBOSE_MAX];
+ char new_volume_str[PA_CVOLUME_SNPRINT_VERBOSE_MAX];
+
+ pa_assert(s);
+ pa_assert(volume);
+
+ old_volume = s->reference_volume;
+
+ if (pa_cvolume_equal(volume, &old_volume))
+ return;
+
+ s->reference_volume = *volume;
+ pa_log_debug("The reference volume of sink %s changed from %s to %s.", s->name,
+ pa_cvolume_snprint_verbose(old_volume_str, sizeof(old_volume_str), &old_volume, &s->channel_map,
+ s->flags & PA_SINK_DECIBEL_VOLUME),
+ pa_cvolume_snprint_verbose(new_volume_str, sizeof(new_volume_str), volume, &s->channel_map,
+ s->flags & PA_SINK_DECIBEL_VOLUME));
+
+ pa_subscription_post(s->core, PA_SUBSCRIPTION_EVENT_SINK|PA_SUBSCRIPTION_EVENT_CHANGE, s->index);
+}