[pulseaudio-discuss] [PATCH v2 1/3] sink_input: new volume_factor system

Flavio Ceolin flavio.ceolin at profusion.mobi
Thu Nov 29 05:04:11 PST 2012


Implement setting of more than one volume factor.  The
real value of the volume_factor will be the multiplication of these
values.
---
 src/modules/module-position-event-sounds.c |   4 +-
 src/pulsecore/sink-input.c                 | 166 ++++++++++++++++++++++++-----
 src/pulsecore/sink-input.h                 |  19 ++--
 3 files changed, 154 insertions(+), 35 deletions(-)

diff --git a/src/modules/module-position-event-sounds.c b/src/modules/module-position-event-sounds.c
index ce37588..c298e74 100644
--- a/src/modules/module-position-event-sounds.c
+++ b/src/modules/module-position-event-sounds.c
@@ -52,6 +52,7 @@ static const char* const valid_modargs[] = {
 
 struct userdata {
     pa_hook_slot *sink_input_fixate_hook_slot;
+    const char *name;
 };
 
 static int parse_pos(const char *pos, double *f) {
@@ -132,7 +133,7 @@ static pa_hook_result_t sink_input_fixate_hook_callback(pa_core *core, pa_sink_i
     }
 
     pa_log_debug("Final volume factor %s.", pa_cvolume_snprint(t, sizeof(t), &v));
-    pa_sink_input_new_data_apply_volume_factor_sink(data, &v);
+    pa_sink_input_new_data_add_volume_factor_sink(data, u->name, &v);
 
     return PA_HOOK_OK;
 }
@@ -152,6 +153,7 @@ int pa__init(pa_module*m) {
     u->sink_input_fixate_hook_slot = pa_hook_connect(&m->core->hooks[PA_CORE_HOOK_SINK_INPUT_FIXATE], PA_HOOK_EARLY, (pa_hook_cb_t) sink_input_fixate_hook_callback, u);
 
     pa_modargs_free(ma);
+    u->name = m->name;
 
     return 0;
 
diff --git a/src/pulsecore/sink-input.c b/src/pulsecore/sink-input.c
index aaabf5c..11f60d5 100644
--- a/src/pulsecore/sink-input.c
+++ b/src/pulsecore/sink-input.c
@@ -48,6 +48,48 @@
 
 PA_DEFINE_PUBLIC_CLASS(pa_sink_input, pa_msgobject);
 
+struct volume_factor_entry {
+    char *key;
+    pa_cvolume volume;
+};
+
+static struct volume_factor_entry *volume_factor_entry_new(const char *key, const pa_cvolume *volume) {
+    struct volume_factor_entry *entry;
+
+    pa_assert(key);
+    pa_assert(volume);
+
+    entry = pa_xnew(struct volume_factor_entry, 1);
+    entry->key = pa_xstrdup(key);
+
+    entry->volume = *volume;
+
+    return entry;
+}
+
+static void volume_factor_entry_free(struct volume_factor_entry *volume_entry) {
+    pa_assert(volume_entry);
+
+    pa_xfree(volume_entry->key);
+    pa_xfree(volume_entry);
+}
+
+static void volume_factor_entry_free2(struct volume_factor_entry *volume_entry, void *userdarta) {
+    volume_factor_entry_free(volume_entry);
+}
+
+static pa_cvolume volume_factor_from_hashmap_get(pa_hashmap *items, uint8_t channels) {
+    pa_cvolume v;
+    struct volume_factor_entry *entry;
+    void *state = NULL;
+
+    pa_cvolume_reset(&v, channels);
+    PA_HASHMAP_FOREACH(entry, items, state)
+        pa_sw_cvolume_multiply(&v, &v, &entry->volume);
+
+    return v;
+}
+
 static void sink_input_free(pa_object *o);
 static void set_real_ratio(pa_sink_input *i, const pa_cvolume *v);
 
@@ -74,6 +116,9 @@ pa_sink_input_new_data* pa_sink_input_new_data_init(pa_sink_input_new_data *data
     data->proplist = pa_proplist_new();
     data->volume_writable = TRUE;
 
+    data->volume_factor_items = pa_hashmap_new(pa_idxset_string_hash_func, pa_idxset_string_compare_func);
+    data->volume_factor_sink_items = pa_hashmap_new(pa_idxset_string_hash_func, pa_idxset_string_compare_func);
+
     return data;
 }
 
@@ -111,28 +156,26 @@ void pa_sink_input_new_data_set_volume(pa_sink_input_new_data *data, const pa_cv
         data->volume = *volume;
 }
 
-void pa_sink_input_new_data_apply_volume_factor(pa_sink_input_new_data *data, const pa_cvolume *volume_factor) {
+void pa_sink_input_new_data_add_volume_factor(pa_sink_input_new_data *data, const char *key, const pa_cvolume *volume_factor) {
+    struct volume_factor_entry *v;
+
     pa_assert(data);
+    pa_assert(key);
     pa_assert(volume_factor);
 
-    if (data->volume_factor_is_set)
-        pa_sw_cvolume_multiply(&data->volume_factor, &data->volume_factor, volume_factor);
-    else {
-        data->volume_factor_is_set = TRUE;
-        data->volume_factor = *volume_factor;
-    }
+    v = volume_factor_entry_new(key, volume_factor);
+    pa_assert_se(pa_hashmap_put(data->volume_factor_items, v->key, v) >= 0);
 }
 
-void pa_sink_input_new_data_apply_volume_factor_sink(pa_sink_input_new_data *data, const pa_cvolume *volume_factor) {
+void pa_sink_input_new_data_add_volume_factor_sink(pa_sink_input_new_data *data, const char *key, const pa_cvolume *volume_factor) {
+    struct volume_factor_entry *v;
+
     pa_assert(data);
+    pa_assert(key);
     pa_assert(volume_factor);
 
-    if (data->volume_factor_sink_is_set)
-        pa_sw_cvolume_multiply(&data->volume_factor_sink, &data->volume_factor_sink, volume_factor);
-    else {
-        data->volume_factor_sink_is_set = TRUE;
-        data->volume_factor_sink = *volume_factor;
-    }
+    v = volume_factor_entry_new(key, volume_factor);
+    pa_assert_se(pa_hashmap_put(data->volume_factor_sink_items, v->key, v) >= 0);
 }
 
 void pa_sink_input_new_data_set_muted(pa_sink_input_new_data *data, pa_bool_t mute) {
@@ -204,6 +247,12 @@ void pa_sink_input_new_data_done(pa_sink_input_new_data *data) {
     if (data->format)
         pa_format_info_free(data->format);
 
+    if (data->volume_factor_items)
+        pa_hashmap_free(data->volume_factor_items, (pa_free2_cb_t) volume_factor_entry_free2, NULL);
+
+    if (data->volume_factor_sink_items)
+        pa_hashmap_free(data->volume_factor_sink_items, (pa_free2_cb_t) volume_factor_entry_free2, NULL);
+
     pa_proplist_free(data->proplist);
 }
 
@@ -335,16 +384,6 @@ int pa_sink_input_new(
 
     pa_return_val_if_fail(pa_cvolume_compatible(&data->volume, &data->sample_spec), -PA_ERR_INVALID);
 
-    if (!data->volume_factor_is_set)
-        pa_cvolume_reset(&data->volume_factor, data->sample_spec.channels);
-
-    pa_return_val_if_fail(pa_cvolume_compatible(&data->volume_factor, &data->sample_spec), -PA_ERR_INVALID);
-
-    if (!data->volume_factor_sink_is_set)
-        pa_cvolume_reset(&data->volume_factor_sink, data->sink->sample_spec.channels);
-
-    pa_return_val_if_fail(pa_cvolume_compatible(&data->volume_factor_sink, &data->sink->sample_spec), -PA_ERR_INVALID);
-
     if (!data->muted_is_set)
         data->muted = FALSE;
 
@@ -455,8 +494,14 @@ int pa_sink_input_new(
     } else
         i->volume = data->volume;
 
-    i->volume_factor = data->volume_factor;
-    i->volume_factor_sink = data->volume_factor_sink;
+    i->volume_factor_items = data->volume_factor_items;
+    data->volume_factor_items = NULL;
+    i->volume_factor = volume_factor_from_hashmap_get(i->volume_factor_items, i->sample_spec.channels);
+
+    i->volume_factor_sink_items = data->volume_factor_sink_items;
+    data->volume_factor_sink_items = NULL;
+    i->volume_factor_sink = volume_factor_from_hashmap_get(i->volume_factor_sink_items, i->sample_spec.channels);
+
     i->real_ratio = i->reference_ratio = data->volume;
     pa_cvolume_reset(&i->soft_volume, i->sample_spec.channels);
     pa_cvolume_reset(&i->real_ratio, i->sample_spec.channels);
@@ -705,6 +750,11 @@ static void sink_input_free(pa_object *o) {
     if (i->thread_info.direct_outputs)
         pa_hashmap_free(i->thread_info.direct_outputs, NULL, NULL);
 
+    if (i->volume_factor_items)
+        pa_hashmap_free(i->volume_factor_items, (pa_free2_cb_t) volume_factor_entry_free2, NULL);
+    if (i->volume_factor_sink_items)
+        pa_hashmap_free(i->volume_factor_sink_items, (pa_free2_cb_t) volume_factor_entry_free2, NULL);
+
     pa_xfree(i->driver);
     pa_xfree(i);
 }
@@ -1214,6 +1264,61 @@ void pa_sink_input_set_volume(pa_sink_input *i, const pa_cvolume *volume, pa_boo
     pa_subscription_post(i->core, PA_SUBSCRIPTION_EVENT_SINK_INPUT|PA_SUBSCRIPTION_EVENT_CHANGE, i->index);
 }
 
+void pa_sink_input_add_volume_factor(pa_sink_input *i, const char *key, const pa_cvolume *volume_factor) {
+    struct volume_factor_entry *v;
+
+    pa_sink_input_assert_ref(i);
+    pa_assert_ctl_context();
+    pa_assert(PA_SINK_INPUT_IS_LINKED(i->state));
+    pa_assert(volume_factor);
+    pa_assert(key);
+    pa_assert(pa_cvolume_valid(volume_factor));
+    pa_assert(volume_factor->channels == 1 || pa_cvolume_compatible(volume_factor, &i->sample_spec));
+
+    v = volume_factor_entry_new(key, volume_factor);
+    if (!pa_cvolume_compatible(volume_factor, &i->sample_spec))
+        pa_cvolume_set(&v->volume, i->sample_spec.channels, volume_factor->values[0]);
+
+    pa_assert_se(pa_hashmap_put(i->volume_factor_items, key, v) >= 0);
+    if (pa_hashmap_size(i->volume_factor_items) == 1)
+        i->volume_factor = v->volume;
+    else
+        pa_sw_cvolume_multiply(&i->volume_factor, &i->volume_factor, &v->volume);
+
+    pa_sw_cvolume_multiply(&i->soft_volume, &i->real_ratio, &i->volume_factor);
+
+    /* Copy the new soft_volume to the thread_info struct */
+    pa_assert_se(pa_asyncmsgq_send(i->sink->asyncmsgq, PA_MSGOBJECT(i), PA_SINK_INPUT_MESSAGE_SET_SOFT_VOLUME, NULL, 0, NULL) == 0);
+}
+
+void pa_sink_input_remove_volume_factor(pa_sink_input *i, const char *key) {
+    struct volume_factor_entry *v;
+
+    pa_sink_input_assert_ref(i);
+    pa_assert(key);
+    pa_assert_ctl_context();
+    pa_assert(PA_SINK_INPUT_IS_LINKED(i->state));
+    pa_assert(v = pa_hashmap_remove(i->volume_factor_items, key));
+
+    volume_factor_entry_free(v);
+    switch (pa_hashmap_size(i->volume_factor_items)) {
+        case 0:
+            pa_cvolume_reset(&i->volume_factor, i->sample_spec.channels);
+            break;
+        case 1:
+            v = (struct volume_factor_entry *)pa_hashmap_first(i->volume_factor_items);
+            i->volume_factor = v->volume;
+            break;
+        default:
+            i->volume_factor = volume_factor_from_hashmap_get(i->volume_factor_items, i->volume_factor.channels);
+    }
+
+    pa_sw_cvolume_multiply(&i->soft_volume, &i->real_ratio, &i->volume_factor);
+
+    /* Copy the new soft_volume to the thread_info struct */
+    pa_assert_se(pa_asyncmsgq_send(i->sink->asyncmsgq, PA_MSGOBJECT(i), PA_SINK_INPUT_MESSAGE_SET_SOFT_VOLUME, NULL, 0, NULL) == 0);
+}
+
 /* Called from main context */
 static void set_real_ratio(pa_sink_input *i, const pa_cvolume *v) {
     pa_sink_input_assert_ref(i);
@@ -1442,6 +1547,8 @@ pa_bool_t pa_sink_input_may_move_to(pa_sink_input *i, pa_sink *dest) {
 /* Called from main context */
 int pa_sink_input_start_move(pa_sink_input *i) {
     pa_source_output *o, *p = NULL;
+    struct volume_factor_entry *v;
+    void *state = NULL;
     int r;
 
     pa_sink_input_assert_ref(i);
@@ -1479,7 +1586,12 @@ int pa_sink_input_start_move(pa_sink_input *i) {
     pa_assert_se(pa_asyncmsgq_send(i->sink->asyncmsgq, PA_MSGOBJECT(i->sink), PA_SINK_MESSAGE_START_MOVE, i, 0, NULL) == 0);
 
     pa_sink_update_status(i->sink);
-    pa_cvolume_remap(&i->volume_factor_sink, &i->sink->channel_map, &i->channel_map);
+    PA_HASHMAP_FOREACH(v, i->volume_factor_items, state) {
+        pa_cvolume_remap(&v->volume, &i->sink->channel_map, &i->channel_map);
+    }
+
+    pa_cvolume_remap(&i->volume_factor, &i->sink->channel_map, &i->channel_map);
+
     i->sink = NULL;
 
     pa_sink_input_unref(i);
diff --git a/src/pulsecore/sink-input.h b/src/pulsecore/sink-input.h
index af2177a..10f19da 100644
--- a/src/pulsecore/sink-input.h
+++ b/src/pulsecore/sink-input.h
@@ -100,10 +100,12 @@ struct pa_sink_input {
     pa_cvolume volume;             /* The volume clients are informed about */
     pa_cvolume reference_ratio;    /* The ratio of the stream's volume to the sink's reference volume */
     pa_cvolume real_ratio;         /* The ratio of the stream's volume to the sink's real volume */
-    pa_cvolume volume_factor;      /* An internally used volume factor that can be used by modules to apply effects and suchlike without having that visible to the outside */
-    pa_cvolume soft_volume;        /* The internal software volume we apply to all PCM data while it passes through. Usually calculated as real_ratio * volume_factor */
+    pa_cvolume volume_factor;        /* An internally used volume factor that can be used by modules to apply effects and suchlike without having that visible to the outside. Its value is derived from the individual volume items in the volume_factor_items hashmap */
+    pa_hashmap *volume_factor_items; /* it consists of volume items that are merged into one volume. This hashmap contains those individual items */
+    pa_cvolume soft_volume;          /* The internal software volume we apply to all PCM data while it passes through. Usually calculated as real_ratio * volume_factor */
 
-    pa_cvolume volume_factor_sink; /* A second volume factor in format of the sink this stream is connected to */
+    pa_cvolume volume_factor_sink;        /* A second volume factor in format of the sink this stream is connected to. This value is derived from the volume_factor_sink_items */
+    pa_hashmap *volume_factor_sink_items; /* it consists of volume items that are merged into one volume. This hashmap contains those individual items */
 
     pa_bool_t volume_writable:1;
 
@@ -284,13 +286,14 @@ typedef struct pa_sink_input_new_data {
     pa_idxset *req_formats;
     pa_idxset *nego_formats;
 
-    pa_cvolume volume, volume_factor, volume_factor_sink;
+    pa_cvolume volume;
     pa_bool_t muted:1;
+    pa_hashmap *volume_factor_items, *volume_factor_sink_items;
 
     pa_bool_t sample_spec_is_set:1;
     pa_bool_t channel_map_is_set:1;
 
-    pa_bool_t volume_is_set:1, volume_factor_is_set:1, volume_factor_sink_is_set:1;
+    pa_bool_t volume_is_set:1;
     pa_bool_t muted_is_set:1;
 
     pa_bool_t volume_is_absolute:1;
@@ -305,8 +308,8 @@ void pa_sink_input_new_data_set_sample_spec(pa_sink_input_new_data *data, const
 void pa_sink_input_new_data_set_channel_map(pa_sink_input_new_data *data, const pa_channel_map *map);
 pa_bool_t pa_sink_input_new_data_is_passthrough(pa_sink_input_new_data *data);
 void pa_sink_input_new_data_set_volume(pa_sink_input_new_data *data, const pa_cvolume *volume);
-void pa_sink_input_new_data_apply_volume_factor(pa_sink_input_new_data *data, const pa_cvolume *volume_factor);
-void pa_sink_input_new_data_apply_volume_factor_sink(pa_sink_input_new_data *data, const pa_cvolume *volume_factor);
+void pa_sink_input_new_data_add_volume_factor(pa_sink_input_new_data *data, const char *key, const pa_cvolume *volume_factor);
+void pa_sink_input_new_data_add_volume_factor_sink(pa_sink_input_new_data *data, const char *key, const pa_cvolume *volume_factor);
 void pa_sink_input_new_data_set_muted(pa_sink_input_new_data *data, pa_bool_t mute);
 pa_bool_t pa_sink_input_new_data_set_sink(pa_sink_input_new_data *data, pa_sink *s, pa_bool_t save);
 pa_bool_t pa_sink_input_new_data_set_formats(pa_sink_input_new_data *data, pa_idxset *formats);
@@ -354,6 +357,8 @@ pa_usec_t pa_sink_input_get_latency(pa_sink_input *i, pa_usec_t *sink_latency);
 pa_bool_t pa_sink_input_is_passthrough(pa_sink_input *i);
 pa_bool_t pa_sink_input_is_volume_readable(pa_sink_input *i);
 void pa_sink_input_set_volume(pa_sink_input *i, const pa_cvolume *volume, pa_bool_t save, pa_bool_t absolute);
+void pa_sink_input_add_volume_factor(pa_sink_input *i, const char *key, const pa_cvolume *volume_factor);
+void 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, pa_bool_t absolute);
 
 void pa_sink_input_set_mute(pa_sink_input *i, pa_bool_t mute, pa_bool_t save);
-- 
1.7.11.7



More information about the pulseaudio-discuss mailing list