[pulseaudio-discuss] [PATCH v2 4/5] sink-input, volume: Add support for volume ramp factor

Sangchul Lee sangchul1011 at gmail.com
Sat Aug 27 12:33:19 UTC 2016


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 <sc11.lee at samsung.com>
---
 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 3b54119..2240e0e 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);
@@ -762,6 +800,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);
 }
@@ -1365,6 +1406,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 0efc7e0..1e07f63 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;
@@ -377,6 +380,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);
 
-- 
2.7.4



More information about the pulseaudio-discuss mailing list