[pulseaudio-discuss] [PATCH] role-ducking: Add support for ducking group
Sangchul Lee
sangchul1011 at gmail.com
Thu Mar 3 14:30:22 UTC 2016
Hi, All
I want to get reviewed the below patch for role-ducking.
I think it'll be useful if there are several volume levels(%/db) to be
applied for each ducking role.
Could you give any feedback about it?
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.
(Actually, I'm planning to apply this feature to Tizen 3.0 TV profile.
There is an use-case that needs it.)
Best regards,
Sangchul Lee
2016-02-19 22:22 GMT+09:00 Sangchul Lee <sangchul1011 at gmail.com>:
> 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 in the same way as before without any slash.
>
> Signed-off-by: Sangchul Lee <sc11.lee at samsung.com>
> ---
> src/modules/module-role-ducking.c | 240
> ++++++++++++++++++++++++++++----------
> 1 file changed, 180 insertions(+), 60 deletions(-)
>
> diff --git a/src/modules/module-role-ducking.c
> b/src/modules/module-role-ducking.c
> index ee31b8c..fe02aa7 100644
> --- a/src/modules/module-role-ducking.c
> +++ b/src/modules/module-role-ducking.c
> @@ -39,10 +39,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[] = {
> @@ -56,11 +56,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,
> @@ -68,7 +69,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;
> @@ -85,7 +86,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;
> @@ -96,7 +97,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);
> @@ -104,23 +105,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;
> @@ -131,44 +135,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);
> @@ -179,8 +186,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;
> }
> @@ -193,9 +202,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);
> }
>
> @@ -217,6 +230,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);
>
> @@ -230,41 +249,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;
> @@ -273,12 +378,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);
> @@ -300,23 +399,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)
> --
> 2.1.4
>
>
-------------- next part --------------
An HTML attachment was scrubbed...
URL: <https://lists.freedesktop.org/archives/pulseaudio-discuss/attachments/20160303/0c3d4e65/attachment-0001.html>
More information about the pulseaudio-discuss
mailing list