From a98e78ccc4f12d6efad2832a09202651e2a8b6cd Mon Sep 17 00:00:00 2001 From: Sangchul Lee Date: Sat, 27 Aug 2016 21:33:19 +0900 Subject: [PATCH 5/6] sink-input, volume: Add support for volume ramp factor Previously, using pa_sink_input_set_volume_ramp() is hard to manage if there are several callers. These new volume ramp factor APIs make it easy for caller to use and to set more than one volume ramp factor. New volume ramp factor will be applied by the multiplication of the other ramp factors that have been already set. APIs are added as below. - pa_sink_input_add_volume_ramp_factor() - pa_sink_input_remove_volume_ramp_factor() - pa_cvolume_ramp_compatible() - pa_sw_cvolume_ramp_multiply() - pa_cvolume_ramp_valid() Signed-off-by: Sangchul Lee --- src/map-file | 3 ++ src/pulse/volume.c | 44 ++++++++++++++++++ src/pulse/volume.h | 9 ++++ src/pulsecore/sink-input.c | 108 +++++++++++++++++++++++++++++++++++++++++++++ src/pulsecore/sink-input.h | 5 +++ 5 files changed, 169 insertions(+) diff --git a/src/map-file b/src/map-file index ef9b57d..7577c14 100644 --- a/src/map-file +++ b/src/map-file @@ -142,6 +142,8 @@ pa_cvolume_ramp_equal; pa_cvolume_ramp_init; pa_cvolume_ramp_set; pa_cvolume_ramp_channel_ramp_set; +pa_cvolume_ramp_compatible; +pa_cvolume_ramp_valid; pa_cvolume_remap; pa_cvolume_scale; pa_cvolume_scale_mask; @@ -344,6 +346,7 @@ pa_sw_cvolume_divide_scalar; pa_sw_cvolume_multiply; pa_sw_cvolume_multiply_scalar; pa_sw_cvolume_snprint_dB; +pa_sw_cvolume_ramp_multiply; pa_sw_volume_divide; pa_sw_volume_from_dB; pa_sw_volume_from_linear; diff --git a/src/pulse/volume.c b/src/pulse/volume.c index 85072c1..8d99150 100644 --- a/src/pulse/volume.c +++ b/src/pulse/volume.c @@ -1049,3 +1049,47 @@ pa_cvolume_ramp* pa_cvolume_ramp_channel_ramp_set(pa_cvolume_ramp *ramp, unsigne return ramp; } + +int pa_cvolume_ramp_compatible(const pa_cvolume_ramp *ramp, const pa_sample_spec *ss) { + + pa_assert(ramp); + pa_assert(ss); + + pa_return_val_if_fail(pa_cvolume_ramp_valid(ramp), 0); + pa_return_val_if_fail(pa_sample_spec_valid(ss), 0); + + return ramp->channels == ss->channels; +} + +pa_cvolume_ramp *pa_sw_cvolume_ramp_multiply(pa_cvolume_ramp *dest, const pa_cvolume_ramp *a, const pa_cvolume_ramp *b) { + unsigned i; + + pa_assert(dest); + pa_assert(a); + pa_assert(b); + + pa_return_val_if_fail(pa_cvolume_ramp_valid(a), NULL); + pa_return_val_if_fail(pa_cvolume_ramp_valid(b), NULL); + + for (i = 0; i < a->channels && i < b->channels; i++) + dest->ramps[i].target = pa_sw_volume_multiply(a->ramps[i].target, b->ramps[i].target); + + dest->channels = (uint8_t) i; + + return dest; +} + +int pa_cvolume_ramp_valid(const pa_cvolume_ramp *ramp) { + unsigned c; + + pa_assert(ramp); + + if (!pa_channels_valid(ramp->channels)) + return 0; + + for (c = 0; c < ramp->channels; c++) + if (!PA_VOLUME_IS_VALID(ramp->ramps[c].target)) + return 0; + + return 1; +} diff --git a/src/pulse/volume.h b/src/pulse/volume.h index 2ae3451..65fcb08 100644 --- a/src/pulse/volume.h +++ b/src/pulse/volume.h @@ -458,12 +458,21 @@ int pa_cvolume_ramp_equal(const pa_cvolume_ramp *a, const pa_cvolume_ramp *b); /** Init volume ramp struct */ pa_cvolume_ramp* pa_cvolume_ramp_init(pa_cvolume_ramp *ramp); +/** Set the volume ramp of the first n channels to PA_VOLUME_NORM */ +#define pa_cvolume_ramp_reset(a, n, t, l) pa_cvolume_ramp_set((a), (n), (t), (l), PA_VOLUME_NORM) + /** Set first n channels of ramp struct to certain value */ pa_cvolume_ramp* pa_cvolume_ramp_set(pa_cvolume_ramp *ramp, unsigned channel, pa_volume_ramp_type_t type, long time, pa_volume_t vol); /** Set individual channel in the channel struct */ pa_cvolume_ramp* pa_cvolume_ramp_channel_ramp_set(pa_cvolume_ramp *ramp, unsigned channel, pa_volume_ramp_type_t type, long time, pa_volume_t vol); +int pa_cvolume_ramp_compatible(const pa_cvolume_ramp *ramp, const pa_sample_spec *ss); + +int pa_cvolume_ramp_valid(const pa_cvolume_ramp *ramp); + +pa_cvolume_ramp *pa_sw_cvolume_ramp_multiply(pa_cvolume_ramp *dest, const pa_cvolume_ramp *a, const pa_cvolume_ramp *b); + PA_C_DECL_END #endif diff --git a/src/pulsecore/sink-input.c b/src/pulsecore/sink-input.c index e1968e0..6f89aa1 100644 --- a/src/pulsecore/sink-input.c +++ b/src/pulsecore/sink-input.c @@ -53,6 +53,11 @@ struct volume_factor_entry { pa_cvolume volume; }; +struct volume_ramp_factor_entry { + char *key; + pa_cvolume_ramp ramp; +}; + static struct volume_factor_entry *volume_factor_entry_new(const char *key, const pa_cvolume *volume) { struct volume_factor_entry *entry; @@ -83,6 +88,37 @@ static void volume_factor_from_hashmap(pa_cvolume *v, pa_hashmap *items, uint8_t pa_sw_cvolume_multiply(v, v, &entry->volume); } +static struct volume_ramp_factor_entry *volume_ramp_factor_entry_new(const char *key, const pa_cvolume_ramp *ramp) { + struct volume_ramp_factor_entry *entry; + + pa_assert(key); + pa_assert(ramp); + + entry = pa_xnew(struct volume_ramp_factor_entry, 1); + entry->key = pa_xstrdup(key); + + entry->ramp = *ramp; + + return entry; +} + +static void volume_ramp_factor_entry_free(struct volume_ramp_factor_entry *ramp_entry) { + pa_assert(ramp_entry); + + pa_xfree(ramp_entry->key); + pa_xfree(ramp_entry); +} + +static void volume_ramp_factor_from_hashmap(pa_cvolume_ramp *r, pa_hashmap *items, uint8_t channels, pa_volume_ramp_type_t type, long length) { + struct volume_ramp_factor_entry *entry; + void *state = NULL; + + pa_cvolume_ramp_reset(r, channels, type, length); + PA_HASHMAP_FOREACH(entry, items, state) + pa_sw_cvolume_ramp_multiply(r, r, &entry->ramp); + +} + static void sink_input_free(pa_object *o); static void set_real_ratio(pa_sink_input *i, const pa_cvolume *v); @@ -500,6 +536,8 @@ int pa_sink_input_new( i->volume_factor_sink_items = data->volume_factor_sink_items; data->volume_factor_sink_items = NULL; volume_factor_from_hashmap(&i->volume_factor_sink, i->volume_factor_sink_items, i->sink->sample_spec.channels); + i->ramp_factor_items = pa_hashmap_new_full(pa_idxset_string_hash_func, pa_idxset_string_compare_func, NULL, + (pa_free_cb_t) volume_ramp_factor_entry_free); i->real_ratio = i->reference_ratio = data->volume; pa_cvolume_reset(&i->soft_volume, i->sample_spec.channels); @@ -764,6 +802,9 @@ static void sink_input_free(pa_object *o) { if (i->volume_factor_sink_items) pa_hashmap_free(i->volume_factor_sink_items); + if (i->ramp_factor_items) + pa_hashmap_free(i->ramp_factor_items); + pa_xfree(i->driver); pa_xfree(i); } @@ -1367,6 +1408,73 @@ int pa_sink_input_remove_volume_factor(pa_sink_input *i, const char *key) { return 0; } +void pa_sink_input_add_volume_ramp_factor(pa_sink_input *i, const char *key, const pa_cvolume_ramp *ramp_factor, bool send_msg) { + struct volume_ramp_factor_entry *r; + + pa_sink_input_assert_ref(i); + pa_assert_ctl_context(); + pa_assert(PA_SINK_INPUT_IS_LINKED(i->state)); + pa_assert(ramp_factor); + pa_assert(key); + pa_assert(pa_cvolume_ramp_valid(ramp_factor)); + pa_assert(ramp_factor->channels == 1 || pa_cvolume_ramp_compatible(ramp_factor, &i->sample_spec)); + + r = volume_ramp_factor_entry_new(key, ramp_factor); + if (!pa_cvolume_ramp_compatible(ramp_factor, &i->sample_spec)) + pa_cvolume_ramp_set(&r->ramp, i->sample_spec.channels, ramp_factor->ramps[0].type, ramp_factor->ramps[0].length, ramp_factor->ramps[0].target); + + pa_assert_se(pa_hashmap_put(i->ramp_factor_items, r->key, r) >= 0); + if (pa_hashmap_size(i->ramp_factor_items) == 1) + pa_cvolume_ramp_set(&i->ramp_factor, i->sample_spec.channels, r->ramp.ramps[0].type, r->ramp.ramps[0].length, r->ramp.ramps[0].target); + else + pa_sw_cvolume_ramp_multiply(&i->ramp_factor, &i->ramp_factor, &r->ramp); + + pa_cvolume_ramp_convert(&i->ramp_factor, &i->ramp, i->sample_spec.rate); + + if (send_msg) + pa_assert_se(pa_asyncmsgq_send(i->sink->asyncmsgq, PA_MSGOBJECT(i), PA_SINK_INPUT_MESSAGE_SET_VOLUME_RAMP, NULL, 0, NULL) == 0); + + return 0; +} + +/* Returns 0 if an entry was removed and -1 if no entry for the given key was + * found. */ +int pa_sink_input_remove_volume_ramp_factor(pa_sink_input *i, const char *key, bool send_msg) { + struct volume_ramp_factor_entry *r; + + pa_sink_input_assert_ref(i); + pa_assert(key); + pa_assert_ctl_context(); + pa_assert(PA_SINK_INPUT_IS_LINKED(i->state)); + + r = pa_hashmap_remove(i->ramp_factor_items, key); + if (!r) + return -1; + + switch (pa_hashmap_size(i->ramp_factor_items)) { + case 0: + pa_cvolume_ramp_reset(&i->ramp_factor, i->sample_spec.channels, r->ramp.ramps[0].type, r->ramp.ramps[0].length); + break; + case 1: { + struct volume_ramp_factor_entry *rf; + rf = pa_hashmap_first(i->ramp_factor_items); + pa_cvolume_ramp_set(&i->ramp_factor, i->sample_spec.channels, r->ramp.ramps[0].type, r->ramp.ramps[0].length, rf->ramp.ramps[0].target); + break; + } + default: + volume_ramp_factor_from_hashmap(&i->ramp_factor, i->ramp_factor_items, i->ramp_factor.channels, i->ramp_factor.ramps[0].type, i->ramp_factor.ramps[0].length); + } + + volume_ramp_factor_entry_free(r); + + pa_cvolume_ramp_convert(&i->ramp_factor, &i->ramp, i->sample_spec.rate); + + if (send_msg) + pa_assert_se(pa_asyncmsgq_send(i->sink->asyncmsgq, PA_MSGOBJECT(i), PA_SINK_INPUT_MESSAGE_SET_VOLUME_RAMP, NULL, 0, NULL) == 0); + + return 0; +} + /* Called from main thread */ void pa_sink_input_set_volume_ramp( pa_sink_input *i, diff --git a/src/pulsecore/sink-input.h b/src/pulsecore/sink-input.h index 92f61c3..5430d53 100644 --- a/src/pulsecore/sink-input.h +++ b/src/pulsecore/sink-input.h @@ -113,6 +113,9 @@ struct pa_sink_input { pa_cvolume volume_factor_sink; /* A second volume factor in format of the sink this stream is connected to. */ pa_hashmap *volume_factor_sink_items; + pa_cvolume_ramp ramp_factor; + pa_hashmap *ramp_factor_items; + bool volume_writable:1; bool muted:1; @@ -379,6 +382,8 @@ void pa_sink_input_add_volume_factor(pa_sink_input *i, const char *key, const pa int pa_sink_input_remove_volume_factor(pa_sink_input *i, const char *key); pa_cvolume *pa_sink_input_get_volume(pa_sink_input *i, pa_cvolume *volume, bool absolute); void pa_sink_input_set_volume_ramp(pa_sink_input *i, const pa_cvolume_ramp *ramp, bool send_msg); +void pa_sink_input_add_volume_ramp_factor(pa_sink_input *i, const char *key, const pa_cvolume_ramp *ramp_factor, bool send_msg); +int pa_sink_input_remove_volume_ramp_factor(pa_sink_input *i, const char *key, bool send_msg); void pa_sink_input_set_mute(pa_sink_input *i, bool mute, bool save); -- 1.9.1