2 This file is part of PulseAudio.
4 Copyright 2017 KimJeongYeon <jeongyeon.kim@samsung.com>
6 PulseAudio is free software; you can redistribute it and/or modify
7 it under the terms of the GNU Lesser General Public License as published
8 by the Free Software Foundation; either version 2.1 of the License,
9 or (at your option) any later version.
11 PulseAudio is distributed in the hope that it will be useful, but
12 WITHOUT ANY WARRANTY; without even the implied warranty of
13 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
14 General Public License for more details.
16 You should have received a copy of the GNU Lesser General Public License
17 along with PulseAudio; if not, write to the Free Software
18 Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
26 #include "stream-manager-priv.h"
27 #include "stream-manager-filter-priv.h"
29 #define MODULE_FILTER_APPLY "module-filter-apply"
30 #define PA_PROP_FILTER_APPLY_GROUP PA_PROP_FILTER_APPLY".%s.group"
31 #define PA_PROP_FILTER_APPLY_PARAMETERS PA_PROP_FILTER_APPLY".%s.parameters"
33 /* Based on parse_control_parameters() of module-ladspa-sink.c */
34 static int32_t parse_filter_control_parameters(const char *cdata, double *read_values, bool *use_default,
35 const uint32_t max_n_control) {
37 const char *state = NULL;
40 pa_assert(read_values);
41 pa_assert(use_default);
43 pa_log_debug("Trying to read control values");
48 pa_log_debug("cdata: '%s'", cdata);
50 while ((k = pa_split(cdata, ",", &state)) && p < max_n_control) {
54 pa_log_debug("Read empty control value (p=%d)", p);
55 use_default[p++] = true;
60 if (pa_atod(k, &f) < 0) {
61 pa_log_debug("Failed to parse control value '%s' (p=%d)", k, p);
68 pa_log_debug("Read control value %f (p=%d)", f, p);
70 use_default[p] = false;
74 /* The previous loop doesn't take the last control value into account
75 * if it is left empty, so we do it here. */
76 if (*cdata == 0 || cdata[strlen(cdata) - 1] == ',') {
77 if (p < max_n_control)
78 use_default[p] = true;
82 if (p > max_n_control || k) {
83 pa_log("Too many control values passed, %d expected.", max_n_control);
88 /* Return number of controls */
95 /* Set property to ladspa-sink */
96 static int32_t control_filter_ladspa(DBusConnection *conn, filter_info *f, uint32_t s_idx) {
97 DBusMessage *msg = NULL;
99 DBusMessageIter msg_iter, variant_iter, struct_iter;
101 double *control = NULL;
102 dbus_bool_t *use_default = NULL;
104 char *ladspa_object_path;
105 const char *ladspa_interface = "org.PulseAudio.Ext.Ladspa1";
106 const char *ladspa_propname = "AlgorithmParameters";
111 dbus_error_init(&err);
113 ladspa_object_path = pa_sprintf_malloc("/org/pulseaudio/core1/sink%d", s_idx);
115 if (!(msg = dbus_message_new_method_call("org.pulseaudio.Server",
117 "org.freedesktop.DBus.Properties",
119 pa_log_error("failed dbus method call");
124 dbus_message_iter_init_append(msg, &msg_iter);
126 dbus_message_iter_append_basic(&msg_iter, DBUS_TYPE_STRING, &ladspa_interface);
127 dbus_message_iter_append_basic(&msg_iter, DBUS_TYPE_STRING, &ladspa_propname);
129 /* Variant has struct of two arrays, signature : (adab) */
130 dbus_message_iter_open_container(&msg_iter, DBUS_TYPE_VARIANT, "(adab)", &variant_iter);
131 dbus_message_iter_open_container(&variant_iter, DBUS_TYPE_STRUCT, NULL, &struct_iter);
133 /* Copying because of the D-Bus type mapping */
134 control = pa_xnew(double, f->n_controls);
135 use_default = pa_xnew(dbus_bool_t, f->n_controls);
137 for (i = 0; i < f->n_controls; i++) {
138 control[i] = f->controls.values[i];
139 use_default[i] = (dbus_bool_t)f->controls.use_default[i];
142 pa_dbus_append_basic_array(&struct_iter, DBUS_TYPE_DOUBLE, control, f->n_controls);
143 pa_dbus_append_basic_array(&struct_iter, DBUS_TYPE_BOOLEAN, use_default, f->n_controls);
145 dbus_message_iter_close_container(&variant_iter, &struct_iter);
146 dbus_message_iter_close_container(&msg_iter, &variant_iter);
148 dbus_message_set_no_reply(msg, true);
149 if (!dbus_connection_send(conn, msg, NULL)) {
150 pa_log_error("Failed to method call org.freedesktop.DBus.Properties.Set, %s", err.message);
159 dbus_message_unref(msg);
160 dbus_error_free(&err);
163 pa_xfree(use_default);
164 pa_xfree(ladspa_object_path);
169 static void update_prop_filter_apply_group(pa_sink_input *si, filter_info *f, const char *filter_apply) {
170 char *prop_group = NULL;
173 pa_assert(filter_apply);
175 prop_group = pa_sprintf_malloc(PA_PROP_FILTER_APPLY_GROUP, filter_apply);
178 pa_proplist_unset(si->proplist, prop_group);
180 pa_proplist_sets(si->proplist, prop_group, f->group);
182 pa_xfree(prop_group);
185 static void update_prop_filter_apply_params(pa_sink_input *si, filter_info *f, const char *filter_apply) {
186 char *prop_parameters = NULL;
189 pa_assert(filter_apply);
191 prop_parameters = pa_sprintf_malloc(PA_PROP_FILTER_APPLY_PARAMETERS, filter_apply);
194 pa_proplist_unset(si->proplist, prop_parameters);
195 else if (f->parameters)
196 pa_proplist_sets(si->proplist, prop_parameters, f->parameters);
198 pa_xfree(prop_parameters);
201 /* Invoked from dbus method call or hook fire */
202 int32_t apply_filter_to_sink_input(pa_sink_input *si, filter_info *f, bool need_to_hook) {
205 if (f && (!f->filter_apply || pa_streq(f->filter_apply, ""))) {
206 pa_log_error("Try to applying empty filter module");
211 const char *filter_apply = NULL;
212 /* Unload filter using module-filter-apply */
213 if ((filter_apply = pa_proplist_gets(si->proplist, PA_PROP_FILTER_APPLY))) {
214 update_prop_filter_apply_group(si, f, filter_apply);
215 update_prop_filter_apply_params(si, f, filter_apply);
216 pa_proplist_unset(si->proplist, PA_PROP_FILTER_APPLY);
219 /* Load filter using module-filter-apply */
220 pa_proplist_sets(si->proplist, PA_PROP_FILTER_APPLY, f->filter_apply);
221 update_prop_filter_apply_group(si, f, f->filter_apply);
222 update_prop_filter_apply_params(si, f, f->filter_apply);
226 pa_hook_fire(&si->core->hooks[PA_CORE_HOOK_SINK_INPUT_PROPLIST_CHANGED], si);
231 /* Invoked from dbus method call */
232 int32_t update_filter(pa_stream_manager *m, const char *filter_name, const char *filter_parameters, const char *filter_group,
233 const char *stream_type) {
234 filter_info *f = NULL, *f_new = NULL;
236 pa_sink_input *si = NULL;
237 const char *role = NULL;
238 int32_t ret_apply = 0;
241 if (pa_safe_streq(filter_name, "")) {
242 pa_log_error("Empty filter name");
246 f = pa_hashmap_get(m->filter_infos, (const void*)stream_type);
248 pa_log_info("Remove existing filter_info[%p] of stream_type[%s]", f, stream_type);
249 pa_hashmap_remove_and_free(m->filter_infos, (const void*)stream_type);
252 /* Add updated filter_info to hashmap */
253 f_key = pa_xstrdup(stream_type);
254 f_new = pa_xmalloc0(sizeof(filter_info));
255 f_new->filter_apply = pa_xstrdup(filter_name);
256 f_new->group = pa_xstrdup(filter_group);
257 f_new->parameters = pa_xstrdup(filter_parameters);
258 pa_hashmap_put(m->filter_infos, (void*)f_key, f_new);
259 pa_log_debug("Apply filter(module-%s) to streams of %s type", f_new->filter_apply, stream_type);
261 /* Remove filter_info from hashmap */
263 pa_log_debug("Remove filter from streams of %s type", stream_type);
266 /* Apply or remove filter using module-filter-apply */
267 PA_IDXSET_FOREACH(si, m->core->sink_inputs, idx) {
268 role = pa_proplist_gets(si->proplist, PA_PROP_MEDIA_ROLE);
269 if (pa_safe_streq(role, stream_type)) {
270 if ((ret_apply = apply_filter_to_sink_input(si, f_new, true)) < 0) {
271 pa_log_error("Failed to %s filter %s sink-input(%d)", f_new ? "apply" : "remove", f_new ? "to" : "from",
281 #define MAX_ELEMENT_LENGTH 64
282 #define MAX_PARAMETERS_LENGTH 256
283 static void update_filter_parameters(filter_info *f) {
284 const char *split_state = NULL;
285 const char *control_element = "control=";
286 char *param_element = NULL;
287 char control_values[MAX_ELEMENT_LENGTH] = {'\0',};
288 char result_buf[MAX_PARAMETERS_LENGTH] = {'\0',};
295 if (f->n_controls == 0)
298 while ((param_element = pa_split(f->parameters, " ", &split_state))) {
299 len = strlen(result_buf);
300 if (!strncmp(param_element, control_element, strlen(control_element))) {
301 for (i = 0; i < f->n_controls; i++) {
302 c_len = strlen(control_values);
303 pa_snprintf(control_values + c_len, MAX_ELEMENT_LENGTH - c_len,
304 (i == f->n_controls - 1) ? "%1.1f" : "%1.1f,", f->controls.values[i]);
306 pa_snprintf(result_buf + len, MAX_PARAMETERS_LENGTH - len, "%s%s", control_element, control_values);
308 pa_snprintf(result_buf + len, MAX_PARAMETERS_LENGTH - len, "%s ", param_element);
310 pa_xfree((void *)param_element);
313 pa_xfree((void *)f->parameters);
314 f->parameters = pa_xstrdup(result_buf);
316 pa_log_info("new filter parameters: %s", f->parameters);
319 /* Invoked from dbus method call */
320 int32_t control_filter(pa_stream_manager *m, const char *filter_name, const char *filter_controls, const char *stream_type,
321 DBusConnection *conn) {
322 filter_info *f = NULL;
324 pa_sink_input *si = NULL;
325 const char *role = NULL;
329 if (pa_streq(filter_name, "") || pa_streq(filter_controls, "")) {
330 pa_log_error("Empty filter name or controls");
334 /* Search filter_info according to stream type, then get index of sink */
335 if (!(f = pa_hashmap_get(m->filter_infos, (const void*)stream_type))) {
336 pa_log_error("No filter information or sink to control");
340 if (pa_safe_streq(f->filter_apply, filter_name)) {
341 PA_IDXSET_FOREACH(si, m->core->sink_inputs, si_idx) {
342 role = pa_proplist_gets(si->proplist, PA_PROP_MEDIA_ROLE);
343 if (pa_safe_streq(role, stream_type)) {
351 if (pa_safe_streq(f->filter_apply, "ladspa-sink") || (s && pa_safe_streq(s->module->name, "module-ladspa-sink"))) {
352 double read_values[FILTER_MAX_CONTROLS];
353 bool use_default[FILTER_MAX_CONTROLS];
356 /* Parse control parameters (e.g, "0.0, 0.1, 3.0") */
357 n_controls = parse_filter_control_parameters(filter_controls, read_values, use_default, FILTER_MAX_CONTROLS);
358 if (n_controls > 0) {
359 /* Save control parameters to filter_info */
361 for (i = 0; i < n_controls; i++) {
362 f->controls.values[i] = read_values[i];
363 f->controls.use_default[i] = use_default[i];
364 f->n_controls = (uint32_t)n_controls;
367 /* update parameters */
368 update_filter_parameters(f);
370 PA_IDXSET_FOREACH(si, m->core->sink_inputs, si_idx) {
371 role = pa_proplist_gets(si->proplist, PA_PROP_MEDIA_ROLE);
372 if (pa_safe_streq(role, stream_type))
373 update_prop_filter_apply_params(si, f, f->filter_apply);
377 pa_log_error("No filter controls to filter");
381 /* Send control parameters to ladspa-sink */
383 control_filter_ladspa(conn, f, s_idx);
385 pa_log_error("Sorry, it is able to control ladspa-sink only");
392 static pa_sink* get_master_sink_of_filter(pa_stream_manager *m, const char *filter_name, const char *filter_group) {
394 pa_sink *master_sink;
396 const char *role = NULL;
397 const char *filter_apply = NULL;
400 pa_assert(filter_name);
401 pa_assert(filter_group);
403 /* We can not find sink_master directly only with module_name and stream_role,
404 * therefore, we use sink-input information instead of it */
405 PA_IDXSET_FOREACH(i, m->core->sink_inputs, idx) {
406 role = pa_proplist_gets(i->proplist, PA_PROP_MEDIA_ROLE);
407 filter_apply = pa_proplist_gets(i->proplist, PA_PROP_FILTER_APPLY);
408 if (pa_safe_streq(filter_name, filter_apply) && pa_safe_streq(role, filter_group)) {
409 if ((master_sink = pa_sink_get_master(i->sink))) {
410 pa_log_info("Found master sink name[%s] of [module-%s, filter_group:%s]",
411 master_sink->name, filter_name, filter_group);
417 pa_log_error("Failed to get master sink of [module-%s, filter_group:%s]", filter_name, filter_group);
422 int32_t reload_filter(pa_stream_manager *m, const char *stream_role, pa_sink *new_master_sink) {
423 filter_info *f = NULL;
424 const char *filter_name = NULL;
425 const char *filter_params = NULL;
426 const char *filter_group = NULL;
427 const char *role = NULL;
428 pa_sink *prev_master_sink = NULL;
434 pa_assert(stream_role);
435 pa_assert(new_master_sink);
437 if (!(f = pa_hashmap_get(m->filter_infos, stream_role))) {
438 pa_log_error("could not find any filter info for [%s]", stream_role);
442 filter_name = pa_xstrdup(f->filter_apply);
443 filter_params = pa_xstrdup(f->parameters);
444 filter_group = pa_xstrdup(f->group);
445 pa_log_debug("found filter: name[%s], params[%s], group[%s] for [%s]", filter_name, filter_params, filter_group, stream_role);
447 if (!(prev_master_sink = get_master_sink_of_filter(m, filter_name, filter_group))) {
452 if (prev_master_sink == new_master_sink) {
453 pa_log_info("master sink is same as before, [%s], skip it", new_master_sink->name);
458 if (update_filter(m, NULL, NULL, NULL, stream_role)) {
459 pa_log_error("failed to update filter to unload,");
464 /* move all streams belongs to stream_role */
465 PA_IDXSET_FOREACH(i, prev_master_sink->inputs, idx) {
466 role = pa_proplist_gets(i->proplist, PA_PROP_MEDIA_ROLE);
467 if (pa_safe_streq(role, stream_role))
468 pa_sink_input_move_to(i, new_master_sink, false);
471 pa_log_info("name:%s, params:%s, group:%s", filter_name, filter_params, filter_group);
472 if (update_filter(m, filter_name, filter_params, filter_group, stream_role)) {
473 pa_log_error("failed to update filter to load");
477 pa_log_info("reload filter successfully for [stream_role:%s, sink:%s]", stream_role, new_master_sink->name);
480 pa_xfree((void *)filter_name);
481 pa_xfree((void *)filter_params);
482 pa_xfree((void *)filter_group);
487 static void filter_info_key_free_cb(char *key) {
491 static void filter_info_value_free_cb(filter_info *f) {
494 pa_xfree((void *)f->filter_apply);
495 pa_xfree((void *)f->group);
496 pa_xfree((void *)f->parameters);
501 void init_filters(pa_stream_manager *m) {
502 pa_module *i, *module = NULL;
507 m->filter_infos = pa_hashmap_new_full(pa_idxset_string_hash_func, pa_idxset_string_compare_func,
508 (pa_free_cb_t)filter_info_key_free_cb, (pa_free_cb_t)filter_info_value_free_cb);
510 /* Load module-filter-apply */
511 PA_IDXSET_FOREACH(i, m->core->modules, idx) {
512 if (pa_streq(i->name, MODULE_FILTER_APPLY)) {
514 pa_log("module-filter-apply loaded already");
519 char *args = pa_sprintf_malloc("autoclean_interval=%d", 0);
520 pa_module_load(m->core, MODULE_FILTER_APPLY, args);
521 pa_log("module-filter-apply loaded");
526 void deinit_filters(pa_stream_manager *m) {
532 /* Unload module-filter-apply */
533 PA_IDXSET_FOREACH(i, m->core->modules, idx) {
534 if (pa_streq(i->name, MODULE_FILTER_APPLY)) {
535 pa_module_unload(m->core, i, true);
536 pa_log("module-filter-apply unloaded");
542 pa_hashmap_free(m->filter_infos);