<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>