Combine the functionality of module-role-cork and module-role-duck. If a volume is specified, streams will be ducked, else corked/muted. Also allow multiple instances of the module, so that one instance can be used for corking and one for ducking. --- src/modules/module-role-cork.c | 66 ++++++++++++++++++++++++++++++++---------- 1 file changed, 50 insertions(+), 16 deletions(-) diff --git a/src/modules/module-role-cork.c b/src/modules/module-role-cork.c index d3297d7..f096cf5 100644 --- a/src/modules/module-role-cork.c +++ b/src/modules/module-role-cork.c @@ -34,27 +34,33 @@ #include "module-role-cork-symdef.h" PA_MODULE_AUTHOR("Lennart Poettering"); -PA_MODULE_DESCRIPTION("Mute & cork streams with certain roles while others exist"); +PA_MODULE_DESCRIPTION("Mute & cork or duck streams with certain roles while others exist"); PA_MODULE_VERSION(PACKAGE_VERSION); -PA_MODULE_LOAD_ONCE(true); +PA_MODULE_LOAD_ONCE(false); PA_MODULE_USAGE( - "trigger_roles=<Comma separated list of roles which will trigger a cork> " - "cork_roles=<Comma separated list of roles which will be corked> " - "global=<Should we operate globally or only inside the same device?>"); + "trigger_roles=<Comma separated list of roles which will trigger a cork or duck> " + "cork_roles=<Comma separated list of roles which will be corked or ducked> " + "global=<Should we operate globally or only inside the same device?>" + "volume=<Volume for the attenuated streams. If not set, streams will be corked & muted" +); static const char* const valid_modargs[] = { "trigger_roles", "cork_roles", "global", + "volume", NULL }; struct userdata { pa_core *core; + const char *name; pa_hashmap *cork_state; pa_idxset *trigger_roles; pa_idxset *cork_roles; + pa_volume_t volume; bool global:1; + bool duck:1; pa_hook_slot *sink_input_put_slot, *sink_input_unlink_slot, @@ -94,7 +100,7 @@ static bool shall_cork(struct userdata *u, pa_sink *s, pa_sink_input *ignore) { trigger_role = is_trigger_stream(u, j); if (trigger_role && !j->muted && pa_sink_input_get_state(j) != PA_SINK_INPUT_CORKED) { - pa_log_debug("Found a '%s' stream that will trigger the auto-cork.", trigger_role); + pa_log_debug("Found a '%s' stream that will trigger auto-cork or auto-duck.", trigger_role); return true; } } @@ -132,21 +138,38 @@ static inline void apply_cork_to_sink(struct userdata *u, pa_sink *s, pa_sink_in corked_here = !!pa_hashmap_get(u->cork_state, j); if (cork && !corked && !j->muted) { - pa_log_debug("Found a '%s' stream that should be corked/muted.", cork_role); if (!corked_here) pa_hashmap_put(u->cork_state, j, PA_INT_TO_PTR(1)); - pa_sink_input_set_mute(j, true, false); - pa_sink_input_send_event(j, PA_STREAM_EVENT_REQUEST_CORK, NULL); + + if (u->duck && !corked_here) { + pa_cvolume vol; + vol.channels = 1; + vol.values[0] = u->volume; + + pa_log_debug("Found a '%s' stream that should be ducked.", cork_role); + pa_sink_input_add_volume_factor(j, u->name, &vol); + } + else if (!u->duck) { + pa_log_debug("Found a '%s' stream that should be corked/muted.", cork_role); + pa_sink_input_set_mute(j, true, false); + pa_sink_input_send_event(j, PA_STREAM_EVENT_REQUEST_CORK, NULL); + } } else if (!cork) { pa_hashmap_remove(u->cork_state, j); - if (corked_here && (corked || j->muted)) { - pa_log_debug("Found a '%s' stream that should be uncorked/unmuted.", cork_role); - if (j->muted) - pa_sink_input_set_mute(j, false, false); - if (corked) - pa_sink_input_send_event(j, PA_STREAM_EVENT_REQUEST_UNCORK, NULL); + if (corked_here && u->duck) { + pa_log_debug("Found a '%s' stream that should be unducked", cork_role); + pa_sink_input_remove_volume_factor(j, u->name); } + else if (!u->duck) { + if (corked_here && (corked || j->muted)) { + pa_log_debug("Found a '%s' stream that should be uncorked/unmuted.", cork_role); + if (j->muted) + pa_sink_input_set_mute(j, false, false); + if (corked) + pa_sink_input_send_event(j, PA_STREAM_EVENT_REQUEST_UNCORK, NULL); + } + } } } } @@ -253,6 +276,7 @@ int pa__init(pa_module *m) { m->userdata = u = pa_xnew(struct userdata, 1); u->core = m->core; + u->name = pa_sprintf_malloc("%s#%u", m->name, m->index); u->cork_state = pa_hashmap_new(pa_idxset_trivial_hash_func, pa_idxset_trivial_compare_func); u->trigger_roles = pa_idxset_new(NULL, NULL); @@ -285,7 +309,7 @@ int pa__init(pa_module *m) { } } if (pa_idxset_isempty(u->cork_roles)) { - pa_log_debug("Using roles 'music' and 'video' as cork roles."); + pa_log_debug("Using roles 'music' and 'video' as cork or duck roles."); pa_idxset_put(u->cork_roles, pa_xstrdup("music"), NULL); pa_idxset_put(u->cork_roles, pa_xstrdup("video"), NULL); } @@ -296,6 +320,16 @@ int pa__init(pa_module *m) { } u->global = global; + u->duck = false; + if (pa_modargs_get_value(ma, "volume", NULL)) + u->duck = true; + + 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); -- 2.1.4