[pulseaudio-discuss,v3,5/6] sink-input, volume: Add support for volume ramp factor

Submitted by Sangchul Lee on April 4, 2017, 2:36 p.m.

Details

Message ID 1491316616-21358-6-git-send-email-sangchul1011@gmail.com
State New
Headers show
Series "volume ramping" ( rev: 3 ) in PulseAudio

Not browsing as part of any series.

Commit Message

Sangchul Lee April 4, 2017, 2:36 p.m.
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.

functions 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@samsung.com>
---
 src/map-file               |   3 ++
 src/pulse/volume.c         |  44 +++++++++++++++++
 src/pulse/volume.h         |  13 +++++
 src/pulsecore/sink-input.c | 116 +++++++++++++++++++++++++++++++++++++++++----
 src/pulsecore/sink-input.h |   5 ++
 5 files changed, 173 insertions(+), 8 deletions(-)

Patch hide | download patch | download mbox

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 923c967..00b9253 100644
--- a/src/pulse/volume.c
+++ b/src/pulse/volume.c
@@ -1044,3 +1044,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;
+}
\ No newline at end of file
diff --git a/src/pulse/volume.h b/src/pulse/volume.h
index 672c8c4..9eea2cd 100644
--- a/src/pulse/volume.h
+++ b/src/pulse/volume.h
@@ -458,12 +458,25 @@  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, d, l) pa_cvolume_ramp_set((a), (n), (d), (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, pa_usec_t duration, 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, pa_usec_t duration, pa_volume_t vol);
 
+/** Return non-zero if the specified volume ramp is compatible with the
+ * specified sample spec. */
+int pa_cvolume_ramp_compatible(const pa_cvolume_ramp *ramp, const pa_sample_spec *ss);
+
+/** Return non-zero when the passed volume ramp structure is valid */
+int pa_cvolume_ramp_valid(const pa_cvolume_ramp *ramp);
+
+/** Multiply two per-channel ramp target volumes and return the result in *dest. */
+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 a36373a..b3ed72b 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, pa_usec_t duration) {
+    struct volume_ramp_factor_entry *entry;
+    void *state = NULL;
+
+    pa_cvolume_ramp_reset(r, channels, type, duration);
+    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);
 
@@ -501,6 +537,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);
@@ -776,6 +814,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);
 }
@@ -1379,11 +1420,72 @@  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].duration, 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].duration, 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);
+
+    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);
+}
+
+/* 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].duration);
+            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].duration, 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].duration);
+    }
+
+    volume_ramp_factor_entry_free(r);
+
+    pa_cvolume_ramp_convert(&i->ramp_factor, &i->ramp);
+
+    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,
-        const pa_cvolume_ramp *ramp,
-        bool send_msg) {
+void pa_sink_input_set_volume_ramp(pa_sink_input *i, const pa_cvolume_ramp *ramp, bool send_msg) {
     pa_sink_input_assert_ref(i);
     pa_assert_ctl_context();
     pa_assert(PA_SINK_INPUT_IS_LINKED(i->state));
@@ -1392,13 +1494,11 @@  void pa_sink_input_set_volume_ramp(
     pa_cvolume_ramp_convert(ramp, &i->ramp);
 
     pa_log_debug("setting volume ramp with target vol:%d and duration:%lu(usec)",
-		 i->ramp.ramps[0].target,
-		 i->ramp.ramps[0].duration);
+                 i->ramp.ramps[0].target, i->ramp.ramps[0].duration);
 
     /* This tells the sink that volume ramp changed */
     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);
+        pa_assert_se(pa_asyncmsgq_send(i->sink->asyncmsgq, PA_MSGOBJECT(i), PA_SINK_INPUT_MESSAGE_SET_VOLUME_RAMP, NULL, 0, NULL) == 0);
 }
 
 /* Called from main context */
diff --git a/src/pulsecore/sink-input.h b/src/pulsecore/sink-input.h
index c994ef7..309188b 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);