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);
void *state;
pa_device_port *p;
- PA_HASHMAP_FOREACH(p, s->ports, state)
+ PA_HASHMAP_FOREACH(p, s->ports, state) {
+ if (p->available == PA_AVAILABLE_NO)
+ continue;
+
if (!s->active_port || p->priority > s->active_port->priority)
s->active_port = p;
+ }
+ if (!s->active_port) {
+ PA_HASHMAP_FOREACH(p, s->ports, state)
+ if (!s->active_port || p->priority > s->active_port->priority)
+ s->active_port = p;
+ }
}
if (s->active_port)
&s->sample_spec,
0);
+ pa_cvolume_ramp_int_init(&s->ramp, PA_VOLUME_NORM, data->sample_spec.channels);
+
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;
s->thread_info.volume_change_extra_delay = core->deferred_volume_extra_delay_usec;
s->thread_info.latency_offset = s->latency_offset;
+ s->thread_info.ramp = s->ramp;
+
/* FIXME: This should probably be moved to pa_sink_put() */
pa_assert_se(pa_idxset_put(core->sinks, s, &s->index) >= 0);
}
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);
}
} else if (n == 1) {
pa_cvolume volume;
+ pa_cvolume target;
*result = info[0].chunk;
pa_memblock_ref(result->memblock);
result,
&s->sample_spec,
result->length);
- } else if (!pa_cvolume_is_norm(&volume)) {
+ } else if (!pa_cvolume_is_norm(&volume) || pa_cvolume_ramp_target_active(&s->thread_info.ramp) || pa_cvolume_ramp_active(&s->thread_info.ramp)) {
pa_memchunk_make_writable(result, 0);
- pa_volume_memchunk(result, &s->sample_spec, &volume);
+ if (pa_cvolume_ramp_active(&s->thread_info.ramp)) {
+ if (!pa_cvolume_is_norm(&volume))
+ pa_volume_memchunk(result, &s->sample_spec, &volume);
+ pa_volume_ramp_memchunk(result, &s->sample_spec, &(s->thread_info.ramp));
+ }
+ else {
+ if (pa_cvolume_ramp_target_active(&s->thread_info.ramp)) {
+ pa_cvolume_ramp_get_targets(&s->thread_info.ramp, &target);
+ pa_sw_cvolume_multiply(&volume, &volume, &target);
+ }
+ pa_volume_memchunk(result, &s->sample_spec, &volume);
+ }
}
} else {
void *ptr;
+ pa_cvolume target_vol;
+
result->memblock = pa_memblock_new(s->core->mempool, length);
ptr = pa_memblock_acquire(result->memblock);
&s->sample_spec,
&s->thread_info.soft_volume,
s->thread_info.soft_muted);
+
+ if (pa_cvolume_ramp_target_active(&s->thread_info.ramp) || pa_cvolume_ramp_active(&s->thread_info.ramp)) {
+ if (pa_cvolume_ramp_active(&s->thread_info.ramp))
+ pa_volume_ramp_memchunk(result, &s->sample_spec, &(s->thread_info.ramp));
+ else {
+ pa_cvolume_ramp_get_targets(&s->thread_info.ramp, &target_vol);
+ pa_volume_memchunk(result, &s->sample_spec, &target_vol);
+ }
+ }
+
pa_memblock_release(result->memblock);
result->index = 0;
pa_silence_memchunk(target, &s->sample_spec);
else {
pa_memchunk vchunk;
+ pa_cvolume target_vol;
vchunk = info[0].chunk;
pa_memblock_ref(vchunk.memblock);
if (vchunk.length > length)
vchunk.length = length;
- if (!pa_cvolume_is_norm(&volume)) {
+ if (!pa_cvolume_is_norm(&volume) || pa_cvolume_ramp_target_active(&s->thread_info.ramp) || pa_cvolume_ramp_active(&s->thread_info.ramp)) {
pa_memchunk_make_writable(&vchunk, 0);
- pa_volume_memchunk(&vchunk, &s->sample_spec, &volume);
+ if (pa_cvolume_ramp_active(&s->thread_info.ramp)) {
+ if (!pa_cvolume_is_norm(&volume))
+ pa_volume_memchunk(&vchunk, &s->sample_spec, &volume);
+ pa_volume_ramp_memchunk(&vchunk, &s->sample_spec, &(s->thread_info.ramp));
+ }
+ else {
+ if (pa_cvolume_ramp_target_active(&s->thread_info.ramp)) {
+ pa_cvolume_ramp_get_targets(&s->thread_info.ramp, &target_vol);
+ pa_sw_cvolume_multiply(&volume, &volume, &target_vol);
+ }
+ pa_volume_memchunk(&vchunk, &s->sample_spec, &volume);
+ }
}
pa_memchunk_memcpy(target, &vchunk);
} else {
void *ptr;
+ pa_cvolume target_vol;
ptr = pa_memblock_acquire(target->memblock);
&s->thread_info.soft_volume,
s->thread_info.soft_muted);
+ if (pa_cvolume_ramp_target_active(&s->thread_info.ramp) || pa_cvolume_ramp_active(&s->thread_info.ramp)) {
+ if (pa_cvolume_ramp_active(&s->thread_info.ramp))
+ pa_volume_ramp_memchunk(target, &s->sample_spec, &(s->thread_info.ramp));
+ else {
+ pa_cvolume_ramp_get_targets(&s->thread_info.ramp, &target_vol);
+ pa_volume_memchunk(target, &s->sample_spec, &target_vol);
+ }
+ }
+
pa_memblock_release(target->memblock);
}
}
/* 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;
bool use_alternate = false;
if (rate == s->sample_spec.rate)
- return true;
+ 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 == 0) || (default_rate % 11025 == 0));
}
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_assert_se(pa_asyncmsgq_send(root_sink->asyncmsgq, PA_MSGOBJECT(root_sink), PA_SINK_MESSAGE_SET_SHARED_VOLUME, NULL, 0, NULL) == 0);
}
+/* Called from main thread */
+void pa_sink_set_volume_ramp(
+ pa_sink *s,
+ const pa_cvolume_ramp *ramp,
+ bool send_msg,
+ bool save) {
+
+ pa_sink_assert_ref(s);
+ pa_assert_ctl_context();
+ pa_assert(PA_SINK_IS_LINKED(s->state));
+ pa_assert(ramp);
+
+ /* 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)) {
+ pa_log_warn("Cannot do volume ramp, Sink is connected to PASSTHROUGH input");
+ return;
+ }
+
+ pa_cvolume_ramp_convert(ramp, &s->ramp, s->sample_spec.rate);
+
+ /* This tells the sink that volume ramp changed */
+ if (send_msg)
+ pa_assert_se(pa_asyncmsgq_send(s->asyncmsgq, PA_MSGOBJECT(s), PA_SINK_MESSAGE_SET_VOLUME_RAMP, NULL, 0, NULL) == 0);
+}
+
/* Called from the io thread if sync volume is used, otherwise from the main thread.
* Only to be called by sink implementor */
void pa_sink_set_soft_volume(pa_sink *s, const pa_cvolume *volume) {
s->thread_info.soft_volume = s->soft_volume;
pa_sink_request_rewind(s, (size_t) -1);
}
-
/* Fall through ... */
case PA_SINK_MESSAGE_SYNC_VOLUMES:
sync_input_volumes_within_thread(s);
return 0;
+ case PA_SINK_MESSAGE_SET_VOLUME_RAMP:
+ /* if we have ongoing ramp where we take current start values */
+ pa_cvolume_ramp_start_from(&s->thread_info.ramp, &s->ramp);
+ s->thread_info.ramp = s->ramp;
+ pa_sink_request_rewind(s, (size_t) -1);
+ return 0;
+
case PA_SINK_MESSAGE_GET_VOLUME:
if ((s->flags & PA_SINK_DEFERRED_VOLUME) && s->get_volume) {
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;