On Fri, 2016-02-19 at 22:22 +0900, Sangchul Lee wrote: > Â struct userdata { > Â Â Â Â Â pa_core *core; > Â Â Â Â Â const char *name; > -Â Â Â Â pa_idxset *trigger_roles; > -Â Â Â Â pa_idxset *ducking_roles; > -Â Â Â Â pa_idxset *ducked_inputs; > +Â Â Â Â uint32_t n_group; I'd prefer "n_groups". > +Â Â Â Â pa_idxset **trigger_roles; > +Â Â Â Â pa_idxset **ducking_roles; > +Â Â Â Â pa_idxset **ducked_inputs; > +Â Â Â Â char **volumes; > Â Â Â Â Â bool global; > -Â Â Â Â pa_volume_t volume; Why is the type for volumes changed from pa_volume_t to char*? It's better to parse the volumes just once during the initialization rather than every time the volume values are needed. Rather than having four arrays containing the various attributes of a group, it's better to define a struct for a group and have just one array for storing the group structs. You could also add the volume factor name to the group struct so that it doesn't have to be constructed every time it's needed. > @@ -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); This code makes "foo/" being counted as one group and "foo//" as two groups. It would be more consistent to consider "foo/" to be two groups and "foo//" to be three groups. However, the best way of fixing this would be to fix pa_split() to not ignore the empty string after a trailing delimiter, and I don't want to require you to do that change (it would also require reviewing all other code that is currently using pa_split() to make sure that nothing breaks by the change). If you want to fix pa_split(), then that's awesome, but if not, then let's just ignore this small problem. > Â Â Â Â Â Â Â Â Â } > Â Â Â Â Â } > -Â Â Â Â 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) { Since you're going to loop exactly u->n_group times, a for loop would be the idiomatic choice. > +Â Â Â Â Â Â Â Â 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); If an empty string is given as a role name, I think that should be an error. > +Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â } > +Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â i++; > +Â Â Â Â Â Â Â Â Â Â Â Â } else > +Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â pa_xfree(roles_in_group); Likewise, an empty group string should be interpreted as an empty role name, which should be an error. > +Â Â Â Â Â Â Â Â } > +Â Â Â Â } > +Â Â Â Â 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); Same comments as above regarding empty strings. > +Â Â Â Â Â Â Â Â } > +Â Â Â Â } > +Â Â Â Â 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); This log message seems like something that you meant to remove from the final version. > Â Â Â Â Â Â Â Â Â Â Â Â Â 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"); This message looks strange to me (I know it was there already earlier). I think "Failed to parse volume" would be better. -- Tanu