role-ducking: Add support for ducking group 39/59839/3 accepted/tizen/common/20160301.120602 accepted/tizen/ivi/20160225.081404 accepted/tizen/mobile/20160225.081316 accepted/tizen/tv/20160225.081329 accepted/tizen/wearable/20160225.081349 submit/tizen/20160224.091054 submit/tizen_common/20160229.190608
authorSangchul Lee <sc11.lee@samsung.com>
Fri, 5 Feb 2016 04:47:34 +0000 (13:47 +0900)
committerSangchul Lee <sc11.lee@samsung.com>
Wed, 24 Feb 2016 06:42:45 +0000 (15:42 +0900)
Now, trigger_roles, ducking_roles and volume can be divided into several groups by slash.
That means each group can be affected by its own volume policy.
And it works the same way as before without any slash.

[Version] 5.0-59
[Profile] Common
[Issue Type] Feature Enhancement

Change-Id: I27475733968d36ed5c0df7b98e6e53c09e6c3de6
Signed-off-by: Sangchul Lee <sc11.lee@samsung.com>
packaging/pulseaudio.spec
src/modules/module-role-ducking.c

index 719649a..d2ab864 100644 (file)
@@ -10,7 +10,7 @@
 Name:             pulseaudio
 Summary:          Improved Linux sound server
 Version:          5.0
-Release:          58
+Release:          59
 Group:            Multimedia/Audio
 License:          LGPL-2.1+
 URL:              http://pulseaudio.org
index 5054589..28f3032 100644 (file)
@@ -41,10 +41,10 @@ PA_MODULE_DESCRIPTION("Apply a ducking effect based on streams roles");
 PA_MODULE_VERSION(PACKAGE_VERSION);
 PA_MODULE_LOAD_ONCE(true);
 PA_MODULE_USAGE(
-        "trigger_roles=<Comma separated list of roles which will trigger a ducking> "
-        "ducking_roles=<Comma separated list of roles which will be ducked> "
+        "trigger_roles=<Comma(and slash) separated list of roles which will trigger a ducking. Slash can divide the roles into groups>"
+        "ducking_roles=<Comma(and slash) separated list of roles which will be ducked. Slash can divide the roles into groups>"
         "global=<Should we operate globally or only inside the same device?>"
-        "volume=<Volume for the attenuated streams. Default: -20dB"
+        "volume=<Volume for the attenuated streams. Default: -20dB. If trigger_roles and ducking_roles are separated by slash, use slash for dividing volume group>"
 );
 
 static const char* const valid_modargs[] = {
@@ -58,11 +58,12 @@ static const char* const valid_modargs[] = {
 struct userdata {
     pa_core *core;
     const char *name;
-    pa_idxset *trigger_roles;
-    pa_idxset *ducking_roles;
-    pa_idxset *ducked_inputs;
+    uint32_t n_group;
+    pa_idxset **trigger_roles;
+    pa_idxset **ducking_roles;
+    pa_idxset **ducked_inputs;
+    char **volumes;
     bool global;
-    pa_volume_t volume;
     pa_hook_slot
         *sink_input_put_slot,
         *sink_input_unlink_slot,
@@ -70,7 +71,7 @@ struct userdata {
         *sink_input_move_finish_slot;
 };
 
-static bool sink_has_trigger_streams(struct userdata *u, pa_sink *s, pa_sink_input *ignore) {
+static bool sink_has_trigger_streams(struct userdata *u, pa_sink *s, pa_sink_input *ignore, uint32_t group_idx) {
     pa_sink_input *j;
     uint32_t idx, role_idx;
     const char *trigger_role;
@@ -87,7 +88,7 @@ static bool sink_has_trigger_streams(struct userdata *u, pa_sink *s, pa_sink_inp
         if (!(role = pa_proplist_gets(j->proplist, PA_PROP_MEDIA_ROLE)))
             continue;
 
-        PA_IDXSET_FOREACH(trigger_role, u->trigger_roles, role_idx) {
+        PA_IDXSET_FOREACH(trigger_role, u->trigger_roles[group_idx], role_idx) {
             if (pa_streq(role, trigger_role)) {
                 pa_log_debug("Found a '%s' stream that will trigger the ducking.", trigger_role);
                 return true;
@@ -98,7 +99,7 @@ static bool sink_has_trigger_streams(struct userdata *u, pa_sink *s, pa_sink_inp
     return false;
 }
 
-static bool sinks_have_trigger_streams(struct userdata *u, pa_sink *s, pa_sink_input *ignore) {
+static bool sinks_have_trigger_streams(struct userdata *u, pa_sink *s, pa_sink_input *ignore, uint32_t group_idx) {
     bool ret = false;
 
     pa_assert(u);
@@ -106,23 +107,26 @@ static bool sinks_have_trigger_streams(struct userdata *u, pa_sink *s, pa_sink_i
     if (u->global) {
         uint32_t idx;
         PA_IDXSET_FOREACH(s, u->core->sinks, idx)
-            if ((ret = sink_has_trigger_streams(u, s, ignore)))
+            if ((ret = sink_has_trigger_streams(u, s, ignore, group_idx)))
                 break;
     } else
-        ret = sink_has_trigger_streams(u, s, ignore);
+        ret = sink_has_trigger_streams(u, s, ignore, group_idx);
 
     return ret;
 }
 
-static void apply_ducking_to_sink(struct userdata *u, pa_sink *s, pa_sink_input *ignore, bool duck) {
+static void apply_ducking_to_sink(struct userdata *u, pa_sink *s, pa_sink_input *ignore, bool duck, uint32_t group_idx) {
     pa_sink_input *j;
     uint32_t idx, role_idx;
     const char *ducking_role;
     bool trigger = false;
+    char *name = NULL;
 
     pa_assert(u);
     pa_sink_assert_ref(s);
 
+    name = pa_sprintf_malloc("%s_group_%u", u->name, group_idx);
+
     PA_IDXSET_FOREACH(j, s->inputs, idx) {
         const char *role;
         pa_sink_input *i;
@@ -133,44 +137,47 @@ static void apply_ducking_to_sink(struct userdata *u, pa_sink *s, pa_sink_input
         if (!(role = pa_proplist_gets(j->proplist, PA_PROP_MEDIA_ROLE)))
             continue;
 
-        PA_IDXSET_FOREACH(ducking_role, u->ducking_roles, role_idx) {
+        PA_IDXSET_FOREACH(ducking_role, u->ducking_roles[group_idx], role_idx) {
             if ((trigger = pa_streq(role, ducking_role)))
                 break;
         }
         if (!trigger)
             continue;
 
-        i = pa_idxset_get_by_data(u->ducked_inputs, j, NULL);
+        i = pa_idxset_get_by_data(u->ducked_inputs[group_idx], j, NULL);
         if (duck && !i) {
             pa_cvolume vol;
             vol.channels = 1;
-            vol.values[0] = u->volume;
+            pa_parse_volume(u->volumes[group_idx], &vol.values[0]);
 
-            pa_log_debug("Found a '%s' stream that should be ducked.", ducking_role);
-            pa_sink_input_add_volume_factor(j, u->name, &vol);
-            pa_idxset_put(u->ducked_inputs, j, NULL);
+            pa_log_debug("Found a '%s' stream that should be ducked by group '%u'.", ducking_role, group_idx);
+            pa_sink_input_add_volume_factor(j, name, &vol);
+            pa_idxset_put(u->ducked_inputs[group_idx], j, NULL);
         } else if (!duck && i) { /* This stream should not longer be ducked */
-            pa_log_debug("Found a '%s' stream that should be unducked", ducking_role);
-            pa_idxset_remove_by_data(u->ducked_inputs, j, NULL);
-            pa_sink_input_remove_volume_factor(j, u->name);
+            pa_log_debug("Found a '%s' stream that should be unducked by group '%u'", ducking_role, group_idx);
+            pa_idxset_remove_by_data(u->ducked_inputs[group_idx], j, NULL);
+            pa_sink_input_remove_volume_factor(j, name);
         }
     }
+
+    pa_xfree(name);
 }
 
-static void apply_ducking(struct userdata *u, pa_sink *s, pa_sink_input *ignore, bool duck) {
+static void apply_ducking(struct userdata *u, pa_sink *s, pa_sink_input *ignore, bool duck, uint32_t group_idx) {
     pa_assert(u);
 
     if (u->global) {
         uint32_t idx;
         PA_IDXSET_FOREACH(s, u->core->sinks, idx)
-            apply_ducking_to_sink(u, s, ignore, duck);
+            apply_ducking_to_sink(u, s, ignore, duck, group_idx);
     } else
-        apply_ducking_to_sink(u, s, ignore, duck);
+        apply_ducking_to_sink(u, s, ignore, duck, group_idx);
 }
 
 static pa_hook_result_t process(struct userdata *u, pa_sink_input *i, bool duck) {
     bool should_duck = false;
     const char *role;
+    uint32_t j;
 
     pa_assert(u);
     pa_sink_input_assert_ref(i);
@@ -184,8 +191,10 @@ static pa_hook_result_t process(struct userdata *u, pa_sink_input *i, bool duck)
     if (!i->sink)
         return PA_HOOK_OK;
 
-    should_duck = sinks_have_trigger_streams(u, i->sink, duck ? NULL : i);
-    apply_ducking(u, i->sink, duck ? NULL : i, should_duck);
+    for (j = 0; j < u->n_group; j++) {
+        should_duck = sinks_have_trigger_streams(u, i->sink, duck ? NULL : i, j);
+        apply_ducking(u, i->sink, duck ? NULL : i, should_duck, j);
+    }
 
     return PA_HOOK_OK;
 }
@@ -198,9 +207,13 @@ static pa_hook_result_t sink_input_put_cb(pa_core *core, pa_sink_input *i, struc
 }
 
 static pa_hook_result_t sink_input_unlink_cb(pa_core *core, pa_sink_input *i, struct userdata *u) {
+    uint32_t j;
+
     pa_sink_input_assert_ref(i);
 
-    pa_idxset_remove_by_data(u->ducked_inputs, i, NULL);
+    for (j = 0; j < u->n_group; j++)
+        pa_idxset_remove_by_data(u->ducked_inputs[j], i, NULL);
+
     return process(u, i, false);
 }
 
@@ -222,6 +235,12 @@ int pa__init(pa_module *m) {
     pa_modargs *ma = NULL;
     struct userdata *u;
     const char *roles;
+    const char *volumes;
+    pa_volume_t volume;
+    uint32_t group_count_tr = 0;
+    uint32_t group_count_du = 0;
+    uint32_t group_count_vol = 0;
+    uint32_t i = 0;
 
     pa_assert(m);
 
@@ -235,41 +254,127 @@ int pa__init(pa_module *m) {
     u->core = m->core;
     u->name = m->name;
 
-    u->ducked_inputs = pa_idxset_new(NULL, NULL);
-
-    u->trigger_roles = pa_idxset_new(NULL, NULL);
     roles = pa_modargs_get_value(ma, "trigger_roles", NULL);
     if (roles) {
         const char *split_state = NULL;
         char *n = NULL;
-        while ((n = pa_split(roles, ",", &split_state))) {
-            if (n[0] != '\0')
-                pa_idxset_put(u->trigger_roles, n, NULL);
-            else
-                pa_xfree(n);
+        while ((n = pa_split(roles, "/", &split_state))) {
+            group_count_tr++;
+            pa_xfree(n);
         }
     }
-    if (pa_idxset_isempty(u->trigger_roles)) {
+    roles = pa_modargs_get_value(ma, "ducking_roles", NULL);
+    if (roles) {
+        const char *split_state = NULL;
+        char *n = NULL;
+        while ((n = pa_split(roles, "/", &split_state))) {
+            group_count_du++;
+            pa_xfree(n);
+        }
+    }
+    volumes = pa_modargs_get_value(ma, "volume", NULL);
+    if (volumes) {
+        const char *split_state = NULL;
+        char *n = NULL;
+        while ((n = pa_split(volumes, "/", &split_state))) {
+            group_count_vol++;
+            pa_xfree(n);
+        }
+    }
+
+    if ((group_count_tr > 1 || group_count_du > 1 || group_count_vol > 1) &&
+        ((group_count_tr != group_count_du) || (group_count_tr != group_count_vol))) {
+        pa_log("Invalid number of groups");
+        goto fail;
+    }
+
+    if (group_count_tr > 0)
+        u->n_group = group_count_tr;
+    else
+        u->n_group = 1;
+
+    u->trigger_roles = pa_xmalloc0(u->n_group * sizeof(pa_idxset*));
+    u->ducking_roles = pa_xmalloc0(u->n_group * sizeof(pa_idxset*));
+    u->ducked_inputs = pa_xmalloc0(u->n_group * sizeof(pa_idxset*));
+    u->volumes = pa_xmalloc0(u->n_group * sizeof(char*));
+    while (i < u->n_group) {
+        u->trigger_roles[i] = pa_idxset_new(NULL, NULL);
+        u->ducking_roles[i] = pa_idxset_new(NULL, NULL);
+        u->ducked_inputs[i++] = pa_idxset_new(NULL, NULL);
+    }
+
+    roles = pa_modargs_get_value(ma, "trigger_roles", NULL);
+    if (roles) {
+        const char *group_split_state = NULL;
+        char *roles_in_group = NULL;
+        i = 0;
+        while ((roles_in_group = pa_split(roles, "/", &group_split_state))) {
+            if (roles_in_group[0] != '\0') {
+                const char *split_state = NULL;
+                char *n = NULL;
+                while ((n = pa_split(roles_in_group, ",", &split_state))) {
+                    if (n[0] != '\0')
+                        pa_idxset_put(u->trigger_roles[i], n, NULL);
+                    else
+                        pa_xfree(n);
+                }
+                i++;
+            } else
+                pa_xfree(roles_in_group);
+        }
+    }
+    if (pa_idxset_isempty(u->trigger_roles[0])) {
         pa_log_debug("Using role 'phone' as trigger role.");
-        pa_idxset_put(u->trigger_roles, pa_xstrdup("phone"), NULL);
+        pa_idxset_put(u->trigger_roles[0], pa_xstrdup("phone"), NULL);
     }
 
-    u->ducking_roles = pa_idxset_new(NULL, NULL);
     roles = pa_modargs_get_value(ma, "ducking_roles", NULL);
     if (roles) {
-        const char *split_state = NULL;
+        const char *group_split_state = NULL;
+        char *roles_in_group = NULL;
+        i = 0;
+        while ((roles_in_group = pa_split(roles, "/", &group_split_state))) {
+            if (roles_in_group[0] != '\0') {
+                const char *split_state = NULL;
+                char *n = NULL;
+                while ((n = pa_split(roles_in_group, ",", &split_state))) {
+                    if (n[0] != '\0')
+                        pa_idxset_put(u->ducking_roles[i], n, NULL);
+                    else
+                        pa_xfree(n);
+                }
+                i++;
+            } else
+                pa_xfree(roles_in_group);
+        }
+    }
+    if (pa_idxset_isempty(u->ducking_roles[0])) {
+        pa_log_debug("Using roles 'music' and 'video' as ducking roles.");
+        pa_idxset_put(u->ducking_roles[0], pa_xstrdup("music"), NULL);
+        pa_idxset_put(u->ducking_roles[0], pa_xstrdup("video"), NULL);
+    }
+
+    volumes = pa_modargs_get_value(ma, "volume", NULL);
+    if (volumes) {
+        const char *group_split_state = NULL;
         char *n = NULL;
-        while ((n = pa_split(roles, ",", &split_state))) {
+        i = 0;
+        while ((n = pa_split(volumes, "/", &group_split_state))) {
+            pa_log_debug("%s", n);
             if (n[0] != '\0')
-                pa_idxset_put(u->ducking_roles, n, NULL);
+                u->volumes[i++] = n;
             else
                 pa_xfree(n);
         }
     }
-    if (pa_idxset_isempty(u->ducking_roles)) {
-        pa_log_debug("Using roles 'music' and 'video' as ducking roles.");
-        pa_idxset_put(u->ducking_roles, pa_xstrdup("music"), NULL);
-        pa_idxset_put(u->ducking_roles, pa_xstrdup("video"), NULL);
+    if (!u->volumes[0])
+        u->volumes[0] = pa_xstrdup("-20db");
+
+    for (i = 0; i < u->n_group; i++) {
+        if (pa_parse_volume(u->volumes[i], &volume) == -1) {
+            pa_log("Failed to parse a volume parameter: volume");
+            goto fail;
+        }
     }
 
     u->global = false;
@@ -278,12 +383,6 @@ int pa__init(pa_module *m) {
         goto fail;
     }
 
-    u->volume = pa_sw_volume_from_dB(-20);
-    if (pa_modargs_get_value_volume(ma, "volume", &u->volume) < 0) {
-        pa_log("Failed to parse a volume parameter: volume");
-        goto fail;
-    }
-
     u->sink_input_put_slot = pa_hook_connect(&m->core->hooks[PA_CORE_HOOK_SINK_INPUT_PUT], PA_HOOK_LATE, (pa_hook_cb_t) sink_input_put_cb, u);
     u->sink_input_unlink_slot = pa_hook_connect(&m->core->hooks[PA_CORE_HOOK_SINK_INPUT_UNLINK], PA_HOOK_LATE, (pa_hook_cb_t) sink_input_unlink_cb, u);
     u->sink_input_move_start_slot = pa_hook_connect(&m->core->hooks[PA_CORE_HOOK_SINK_INPUT_MOVE_START], PA_HOOK_LATE, (pa_hook_cb_t) sink_input_move_start_cb, u);
@@ -305,23 +404,44 @@ fail:
 void pa__done(pa_module *m) {
     struct userdata* u;
     pa_sink_input *i;
+    uint32_t j;
+    uint32_t k;
 
     pa_assert(m);
 
     if (!(u = m->userdata))
         return;
 
-    if (u->trigger_roles)
-        pa_idxset_free(u->trigger_roles, pa_xfree);
+    if (u->trigger_roles) {
+        for (j = 0; j < u->n_group; j++)
+            pa_idxset_free(u->trigger_roles[j], pa_xfree);
+        pa_xfree(u->trigger_roles);
+    }
 
-    if (u->ducking_roles)
-        pa_idxset_free(u->ducking_roles, pa_xfree);
+    if (u->ducking_roles) {
+        for (j = 0; j < u->n_group; j++)
+            pa_idxset_free(u->ducking_roles[j], pa_xfree);
+        pa_xfree(u->ducking_roles);
+    }
 
-    if (u->ducked_inputs) {
-        while ((i = pa_idxset_steal_first(u->ducked_inputs, NULL)))
-            pa_sink_input_remove_volume_factor(i, u->name);
+    if (u->volumes) {
+        for (j = 0; j < u->n_group; j++)
+            pa_xfree(u->volumes[j]);
+        pa_xfree(u->volumes);
+    }
 
-        pa_idxset_free(u->ducked_inputs, NULL);
+    if (u->ducked_inputs) {
+        char *name = NULL;
+        for (j = 0; j < u->n_group; j++) {
+            while ((i = pa_idxset_steal_first(u->ducked_inputs[j], NULL)))
+                for (k = 0; k < u->n_group; k++) {
+                    name = pa_sprintf_malloc("%s_group_%u", u->name, k);
+                    pa_sink_input_remove_volume_factor(i, name);
+                    pa_xfree(name);
+                }
+            pa_idxset_free(u->ducked_inputs[j], NULL);
+        }
+        pa_xfree(u->ducked_inputs);
     }
 
     if (u->sink_input_put_slot)