<div dir="ltr"><div class="gmail_default" style="font-family:verdana,sans-serif">Hi, All</div><div class="gmail_default" style="font-family:verdana,sans-serif"><br></div><div class="gmail_default" style="font-family:verdana,sans-serif">I want to get reviewed the below patch for role-ducking.<br></div><div class="gmail_default" style="font-family:verdana,sans-serif">I think it'll be useful if there are several volume levels(%/db) to be applied for each ducking role.</div><div class="gmail_default" style="font-family:verdana,sans-serif"><br></div><div class="gmail_default" style="font-family:verdana,sans-serif">Could you give any feedback about it?</div><div class="gmail_default" style="font-family:verdana,sans-serif"><br></div><div class="gmail_default" style="font-family:verdana,sans-serif">And I knew there are patches regarding role-ducking/cork from Mr.Chini. Regardless it merges with upstream codes, I just want to get checked this concept.<br></div><div class="gmail_default" style="font-family:verdana,sans-serif">(Actually, I'm planning to apply this feature to Tizen 3.0 TV profile. There is an use-case that needs it.)<br></div><div class="gmail_default" style="font-family:verdana,sans-serif"><br></div><div class="gmail_default" style="font-family:verdana,sans-serif"><br></div><div class="gmail_default" style="font-family:verdana,sans-serif">Best regards,<br></div><div class="gmail_default" style="font-family:verdana,sans-serif">Sangchul Lee</div><div class="gmail_default" style="font-family:verdana,sans-serif"><br></div><div class="gmail_extra"><br><div class="gmail_quote">2016-02-19 22:22 GMT+09:00 Sangchul Lee <span dir="ltr"><<a href="mailto:sangchul1011@gmail.com" target="_blank">sangchul1011@gmail.com</a>></span>:<br><blockquote class="gmail_quote" style="margin:0 0 0 .8ex;border-left:1px #ccc solid;padding-left:1ex">Now, trigger_roles, ducking_roles and volume can be divided into several groups by slash.<br>
That means each group can be affected by its own volume policy.<br>
And it works in the same way as before without any slash.<br>
<br>
Signed-off-by: Sangchul Lee <<a href="mailto:sc11.lee@samsung.com" target="_blank">sc11.lee@samsung.com</a>><br>
---<br>
 src/modules/module-role-ducking.c | 240 ++++++++++++++++++++++++++++----------<br>
 1 file changed, 180 insertions(+), 60 deletions(-)<br>
<br>
diff --git a/src/modules/module-role-ducking.c b/src/modules/module-role-ducking.c<br>
index ee31b8c..fe02aa7 100644<br>
--- a/src/modules/module-role-ducking.c<br>
+++ b/src/modules/module-role-ducking.c<br>
@@ -39,10 +39,10 @@ PA_MODULE_DESCRIPTION("Apply a ducking effect based on streams roles");<br>
 PA_MODULE_VERSION(PACKAGE_VERSION);<br>
 PA_MODULE_LOAD_ONCE(true);<br>
 PA_MODULE_USAGE(<br>
-        "trigger_roles=<Comma separated list of roles which will trigger a ducking> "<br>
-        "ducking_roles=<Comma separated list of roles which will be ducked> "<br>
+        "trigger_roles=<Comma(and slash) separated list of roles which will trigger a ducking. Slash can divide the roles into groups>"<br>
+        "ducking_roles=<Comma(and slash) separated list of roles which will be ducked. Slash can divide the roles into groups>"<br>
         "global=<Should we operate globally or only inside the same device?>"<br>
-        "volume=<Volume for the attenuated streams. Default: -20dB"<br>
+        "volume=<Volume for the attenuated streams. Default: -20dB. If trigger_roles and ducking_roles are separated by slash, use slash for dividing volume group>"<br>
 );<br>
<br>
 static const char* const valid_modargs[] = {<br>
@@ -56,11 +56,12 @@ static const char* const valid_modargs[] = {<br>
 struct userdata {<br>
     pa_core *core;<br>
     const char *name;<br>
-    pa_idxset *trigger_roles;<br>
-    pa_idxset *ducking_roles;<br>
-    pa_idxset *ducked_inputs;<br>
+    uint32_t n_group;<br>
+    pa_idxset **trigger_roles;<br>
+    pa_idxset **ducking_roles;<br>
+    pa_idxset **ducked_inputs;<br>
+    char **volumes;<br>
     bool global;<br>
-    pa_volume_t volume;<br>
     pa_hook_slot<br>
         *sink_input_put_slot,<br>
         *sink_input_unlink_slot,<br>
@@ -68,7 +69,7 @@ struct userdata {<br>
         *sink_input_move_finish_slot;<br>
 };<br>
<br>
-static bool sink_has_trigger_streams(struct userdata *u, pa_sink *s, pa_sink_input *ignore) {<br>
+static bool sink_has_trigger_streams(struct userdata *u, pa_sink *s, pa_sink_input *ignore, uint32_t group_idx) {<br>
     pa_sink_input *j;<br>
     uint32_t idx, role_idx;<br>
     const char *trigger_role;<br>
@@ -85,7 +86,7 @@ static bool sink_has_trigger_streams(struct userdata *u, pa_sink *s, pa_sink_inp<br>
         if (!(role = pa_proplist_gets(j->proplist, PA_PROP_MEDIA_ROLE)))<br>
             continue;<br>
<br>
-        PA_IDXSET_FOREACH(trigger_role, u->trigger_roles, role_idx) {<br>
+        PA_IDXSET_FOREACH(trigger_role, u->trigger_roles[group_idx], role_idx) {<br>
             if (pa_streq(role, trigger_role)) {<br>
                 pa_log_debug("Found a '%s' stream that will trigger the ducking.", trigger_role);<br>
                 return true;<br>
@@ -96,7 +97,7 @@ static bool sink_has_trigger_streams(struct userdata *u, pa_sink *s, pa_sink_inp<br>
     return false;<br>
 }<br>
<br>
-static bool sinks_have_trigger_streams(struct userdata *u, pa_sink *s, pa_sink_input *ignore) {<br>
+static bool sinks_have_trigger_streams(struct userdata *u, pa_sink *s, pa_sink_input *ignore, uint32_t group_idx) {<br>
     bool ret = false;<br>
<br>
     pa_assert(u);<br>
@@ -104,23 +105,26 @@ static bool sinks_have_trigger_streams(struct userdata *u, pa_sink *s, pa_sink_i<br>
     if (u->global) {<br>
         uint32_t idx;<br>
         PA_IDXSET_FOREACH(s, u->core->sinks, idx)<br>
-            if ((ret = sink_has_trigger_streams(u, s, ignore)))<br>
+            if ((ret = sink_has_trigger_streams(u, s, ignore, group_idx)))<br>
                 break;<br>
     } else<br>
-        ret = sink_has_trigger_streams(u, s, ignore);<br>
+        ret = sink_has_trigger_streams(u, s, ignore, group_idx);<br>
<br>
     return ret;<br>
 }<br>
<br>
-static void apply_ducking_to_sink(struct userdata *u, pa_sink *s, pa_sink_input *ignore, bool duck) {<br>
+static void apply_ducking_to_sink(struct userdata *u, pa_sink *s, pa_sink_input *ignore, bool duck, uint32_t group_idx) {<br>
     pa_sink_input *j;<br>
     uint32_t idx, role_idx;<br>
     const char *ducking_role;<br>
     bool trigger = false;<br>
+    char *name = NULL;<br>
<br>
     pa_assert(u);<br>
     pa_sink_assert_ref(s);<br>
<br>
+    name = pa_sprintf_malloc("%s_group_%u", u->name, group_idx);<br>
+<br>
     PA_IDXSET_FOREACH(j, s->inputs, idx) {<br>
         const char *role;<br>
         pa_sink_input *i;<br>
@@ -131,44 +135,47 @@ static void apply_ducking_to_sink(struct userdata *u, pa_sink *s, pa_sink_input<br>
         if (!(role = pa_proplist_gets(j->proplist, PA_PROP_MEDIA_ROLE)))<br>
             continue;<br>
<br>
-        PA_IDXSET_FOREACH(ducking_role, u->ducking_roles, role_idx) {<br>
+        PA_IDXSET_FOREACH(ducking_role, u->ducking_roles[group_idx], role_idx) {<br>
             if ((trigger = pa_streq(role, ducking_role)))<br>
                 break;<br>
         }<br>
         if (!trigger)<br>
             continue;<br>
<br>
-        i = pa_idxset_get_by_data(u->ducked_inputs, j, NULL);<br>
+        i = pa_idxset_get_by_data(u->ducked_inputs[group_idx], j, NULL);<br>
         if (duck && !i) {<br>
             pa_cvolume vol;<br>
             vol.channels = 1;<br>
-            vol.values[0] = u->volume;<br>
+            pa_parse_volume(u->volumes[group_idx], &vol.values[0]);<br>
<br>
-            pa_log_debug("Found a '%s' stream that should be ducked.", ducking_role);<br>
-            pa_sink_input_add_volume_factor(j, u->name, &vol);<br>
-            pa_idxset_put(u->ducked_inputs, j, NULL);<br>
+            pa_log_debug("Found a '%s' stream that should be ducked by group '%u'.", ducking_role, group_idx);<br>
+            pa_sink_input_add_volume_factor(j, name, &vol);<br>
+            pa_idxset_put(u->ducked_inputs[group_idx], j, NULL);<br>
         } else if (!duck && i) { /* This stream should not longer be ducked */<br>
-            pa_log_debug("Found a '%s' stream that should be unducked", ducking_role);<br>
-            pa_idxset_remove_by_data(u->ducked_inputs, j, NULL);<br>
-            pa_sink_input_remove_volume_factor(j, u->name);<br>
+            pa_log_debug("Found a '%s' stream that should be unducked by group '%u'", ducking_role, group_idx);<br>
+            pa_idxset_remove_by_data(u->ducked_inputs[group_idx], j, NULL);<br>
+            pa_sink_input_remove_volume_factor(j, name);<br>
         }<br>
     }<br>
+<br>
+    pa_xfree(name);<br>
 }<br>
<br>
-static void apply_ducking(struct userdata *u, pa_sink *s, pa_sink_input *ignore, bool duck) {<br>
+static void apply_ducking(struct userdata *u, pa_sink *s, pa_sink_input *ignore, bool duck, uint32_t group_idx) {<br>
     pa_assert(u);<br>
<br>
     if (u->global) {<br>
         uint32_t idx;<br>
         PA_IDXSET_FOREACH(s, u->core->sinks, idx)<br>
-            apply_ducking_to_sink(u, s, ignore, duck);<br>
+            apply_ducking_to_sink(u, s, ignore, duck, group_idx);<br>
     } else<br>
-        apply_ducking_to_sink(u, s, ignore, duck);<br>
+        apply_ducking_to_sink(u, s, ignore, duck, group_idx);<br>
 }<br>
<br>
 static pa_hook_result_t process(struct userdata *u, pa_sink_input *i, bool duck) {<br>
     bool should_duck = false;<br>
     const char *role;<br>
+    uint32_t j;<br>
<br>
     pa_assert(u);<br>
     pa_sink_input_assert_ref(i);<br>
@@ -179,8 +186,10 @@ static pa_hook_result_t process(struct userdata *u, pa_sink_input *i, bool duck)<br>
     if (!i->sink)<br>
         return PA_HOOK_OK;<br>
<br>
-    should_duck = sinks_have_trigger_streams(u, i->sink, duck ? NULL : i);<br>
-    apply_ducking(u, i->sink, duck ? NULL : i, should_duck);<br>
+    for (j = 0; j < u->n_group; j++) {<br>
+        should_duck = sinks_have_trigger_streams(u, i->sink, duck ? NULL : i, j);<br>
+        apply_ducking(u, i->sink, duck ? NULL : i, should_duck, j);<br>
+    }<br>
<br>
     return PA_HOOK_OK;<br>
 }<br>
@@ -193,9 +202,13 @@ static pa_hook_result_t sink_input_put_cb(pa_core *core, pa_sink_input *i, struc<br>
 }<br>
<br>
 static pa_hook_result_t sink_input_unlink_cb(pa_core *core, pa_sink_input *i, struct userdata *u) {<br>
+    uint32_t j;<br>
+<br>
     pa_sink_input_assert_ref(i);<br>
<br>
-    pa_idxset_remove_by_data(u->ducked_inputs, i, NULL);<br>
+    for (j = 0; j < u->n_group; j++)<br>
+        pa_idxset_remove_by_data(u->ducked_inputs[j], i, NULL);<br>
+<br>
     return process(u, i, false);<br>
 }<br>
<br>
@@ -217,6 +230,12 @@ int pa__init(pa_module *m) {<br>
     pa_modargs *ma = NULL;<br>
     struct userdata *u;<br>
     const char *roles;<br>
+    const char *volumes;<br>
+    pa_volume_t volume;<br>
+    uint32_t group_count_tr = 0;<br>
+    uint32_t group_count_du = 0;<br>
+    uint32_t group_count_vol = 0;<br>
+    uint32_t i = 0;<br>
<br>
     pa_assert(m);<br>
<br>
@@ -230,41 +249,127 @@ int pa__init(pa_module *m) {<br>
     u->core = m->core;<br>
     u->name = m->name;<br>
<br>
-    u->ducked_inputs = pa_idxset_new(NULL, NULL);<br>
-<br>
-    u->trigger_roles = pa_idxset_new(NULL, NULL);<br>
     roles = pa_modargs_get_value(ma, "trigger_roles", NULL);<br>
     if (roles) {<br>
         const char *split_state = NULL;<br>
         char *n = NULL;<br>
-        while ((n = pa_split(roles, ",", &split_state))) {<br>
-            if (n[0] != '\0')<br>
-                pa_idxset_put(u->trigger_roles, n, NULL);<br>
-            else<br>
-                pa_xfree(n);<br>
+        while ((n = pa_split(roles, "/", &split_state))) {<br>
+            group_count_tr++;<br>
+            pa_xfree(n);<br>
         }<br>
     }<br>
-    if (pa_idxset_isempty(u->trigger_roles)) {<br>
+    roles = pa_modargs_get_value(ma, "ducking_roles", NULL);<br>
+    if (roles) {<br>
+        const char *split_state = NULL;<br>
+        char *n = NULL;<br>
+        while ((n = pa_split(roles, "/", &split_state))) {<br>
+            group_count_du++;<br>
+            pa_xfree(n);<br>
+        }<br>
+    }<br>
+    volumes = pa_modargs_get_value(ma, "volume", NULL);<br>
+    if (volumes) {<br>
+        const char *split_state = NULL;<br>
+        char *n = NULL;<br>
+        while ((n = pa_split(volumes, "/", &split_state))) {<br>
+            group_count_vol++;<br>
+            pa_xfree(n);<br>
+        }<br>
+    }<br>
+<br>
+    if ((group_count_tr > 1 || group_count_du > 1 || group_count_vol > 1) &&<br>
+        ((group_count_tr != group_count_du) || (group_count_tr != group_count_vol))) {<br>
+        pa_log("Invalid number of groups");<br>
+        goto fail;<br>
+    }<br>
+<br>
+    if (group_count_tr > 0)<br>
+        u->n_group = group_count_tr;<br>
+    else<br>
+        u->n_group = 1;<br>
+<br>
+    u->trigger_roles = pa_xmalloc0(u->n_group * sizeof(pa_idxset*));<br>
+    u->ducking_roles = pa_xmalloc0(u->n_group * sizeof(pa_idxset*));<br>
+    u->ducked_inputs = pa_xmalloc0(u->n_group * sizeof(pa_idxset*));<br>
+    u->volumes = pa_xmalloc0(u->n_group * sizeof(char*));<br>
+    while (i < u->n_group) {<br>
+        u->trigger_roles[i] = pa_idxset_new(NULL, NULL);<br>
+        u->ducking_roles[i] = pa_idxset_new(NULL, NULL);<br>
+        u->ducked_inputs[i++] = pa_idxset_new(NULL, NULL);<br>
+    }<br>
+<br>
+    roles = pa_modargs_get_value(ma, "trigger_roles", NULL);<br>
+    if (roles) {<br>
+        const char *group_split_state = NULL;<br>
+        char *roles_in_group = NULL;<br>
+        i = 0;<br>
+        while ((roles_in_group = pa_split(roles, "/", &group_split_state))) {<br>
+            if (roles_in_group[0] != '\0') {<br>
+                const char *split_state = NULL;<br>
+                char *n = NULL;<br>
+                while ((n = pa_split(roles_in_group, ",", &split_state))) {<br>
+                    if (n[0] != '\0')<br>
+                        pa_idxset_put(u->trigger_roles[i], n, NULL);<br>
+                    else<br>
+                        pa_xfree(n);<br>
+                }<br>
+                i++;<br>
+            } else<br>
+                pa_xfree(roles_in_group);<br>
+        }<br>
+    }<br>
+    if (pa_idxset_isempty(u->trigger_roles[0])) {<br>
         pa_log_debug("Using role 'phone' as trigger role.");<br>
-        pa_idxset_put(u->trigger_roles, pa_xstrdup("phone"), NULL);<br>
+        pa_idxset_put(u->trigger_roles[0], pa_xstrdup("phone"), NULL);<br>
     }<br>
<br>
-    u->ducking_roles = pa_idxset_new(NULL, NULL);<br>
     roles = pa_modargs_get_value(ma, "ducking_roles", NULL);<br>
     if (roles) {<br>
-        const char *split_state = NULL;<br>
+        const char *group_split_state = NULL;<br>
+        char *roles_in_group = NULL;<br>
+        i = 0;<br>
+        while ((roles_in_group = pa_split(roles, "/", &group_split_state))) {<br>
+            if (roles_in_group[0] != '\0') {<br>
+                const char *split_state = NULL;<br>
+                char *n = NULL;<br>
+                while ((n = pa_split(roles_in_group, ",", &split_state))) {<br>
+                    if (n[0] != '\0')<br>
+                        pa_idxset_put(u->ducking_roles[i], n, NULL);<br>
+                    else<br>
+                        pa_xfree(n);<br>
+                }<br>
+                i++;<br>
+            } else<br>
+                pa_xfree(roles_in_group);<br>
+        }<br>
+    }<br>
+    if (pa_idxset_isempty(u->ducking_roles[0])) {<br>
+        pa_log_debug("Using roles 'music' and 'video' as ducking roles.");<br>
+        pa_idxset_put(u->ducking_roles[0], pa_xstrdup("music"), NULL);<br>
+        pa_idxset_put(u->ducking_roles[0], pa_xstrdup("video"), NULL);<br>
+    }<br>
+<br>
+    volumes = pa_modargs_get_value(ma, "volume", NULL);<br>
+    if (volumes) {<br>
+        const char *group_split_state = NULL;<br>
         char *n = NULL;<br>
-        while ((n = pa_split(roles, ",", &split_state))) {<br>
+        i = 0;<br>
+        while ((n = pa_split(volumes, "/", &group_split_state))) {<br>
+            pa_log_debug("%s", n);<br>
             if (n[0] != '\0')<br>
-                pa_idxset_put(u->ducking_roles, n, NULL);<br>
+                u->volumes[i++] = n;<br>
             else<br>
                 pa_xfree(n);<br>
         }<br>
     }<br>
-    if (pa_idxset_isempty(u->ducking_roles)) {<br>
-        pa_log_debug("Using roles 'music' and 'video' as ducking roles.");<br>
-        pa_idxset_put(u->ducking_roles, pa_xstrdup("music"), NULL);<br>
-        pa_idxset_put(u->ducking_roles, pa_xstrdup("video"), NULL);<br>
+    if (!u->volumes[0])<br>
+        u->volumes[0] = pa_xstrdup("-20db");<br>
+<br>
+    for (i = 0; i < u->n_group; i++) {<br>
+        if (pa_parse_volume(u->volumes[i], &volume) == -1) {<br>
+            pa_log("Failed to parse a volume parameter: volume");<br>
+            goto fail;<br>
+        }<br>
     }<br>
<br>
     u->global = false;<br>
@@ -273,12 +378,6 @@ int pa__init(pa_module *m) {<br>
         goto fail;<br>
     }<br>
<br>
-    u->volume = pa_sw_volume_from_dB(-20);<br>
-    if (pa_modargs_get_value_volume(ma, "volume", &u->volume) < 0) {<br>
-        pa_log("Failed to parse a volume parameter: volume");<br>
-        goto fail;<br>
-    }<br>
-<br>
     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);<br>
     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);<br>
     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);<br>
@@ -300,23 +399,44 @@ fail:<br>
 void pa__done(pa_module *m) {<br>
     struct userdata* u;<br>
     pa_sink_input *i;<br>
+    uint32_t j;<br>
+    uint32_t k;<br>
<br>
     pa_assert(m);<br>
<br>
     if (!(u = m->userdata))<br>
         return;<br>
<br>
-    if (u->trigger_roles)<br>
-        pa_idxset_free(u->trigger_roles, pa_xfree);<br>
+    if (u->trigger_roles) {<br>
+        for (j = 0; j < u->n_group; j++)<br>
+            pa_idxset_free(u->trigger_roles[j], pa_xfree);<br>
+        pa_xfree(u->trigger_roles);<br>
+    }<br>
<br>
-    if (u->ducking_roles)<br>
-        pa_idxset_free(u->ducking_roles, pa_xfree);<br>
+    if (u->ducking_roles) {<br>
+        for (j = 0; j < u->n_group; j++)<br>
+            pa_idxset_free(u->ducking_roles[j], pa_xfree);<br>
+        pa_xfree(u->ducking_roles);<br>
+    }<br>
<br>
-    if (u->ducked_inputs) {<br>
-        while ((i = pa_idxset_steal_first(u->ducked_inputs, NULL)))<br>
-            pa_sink_input_remove_volume_factor(i, u->name);<br>
+    if (u->volumes) {<br>
+        for (j = 0; j < u->n_group; j++)<br>
+            pa_xfree(u->volumes[j]);<br>
+        pa_xfree(u->volumes);<br>
+    }<br>
<br>
-        pa_idxset_free(u->ducked_inputs, NULL);<br>
+    if (u->ducked_inputs) {<br>
+        char *name = NULL;<br>
+        for (j = 0; j < u->n_group; j++) {<br>
+            while ((i = pa_idxset_steal_first(u->ducked_inputs[j], NULL)))<br>
+                for (k = 0; k < u->n_group; k++) {<br>
+                    name = pa_sprintf_malloc("%s_group_%u", u->name, k);<br>
+                    pa_sink_input_remove_volume_factor(i, name);<br>
+                    pa_xfree(name);<br>
+                }<br>
+            pa_idxset_free(u->ducked_inputs[j], NULL);<br>
+        }<br>
+        pa_xfree(u->ducked_inputs);<br>
     }<br>
<br>
     if (u->sink_input_put_slot)<br>
<span><font color="#888888">--<br>
2.1.4<br>
<br>
</font></span></blockquote></div><br></div></div>