module-ladspa-filter: change to graph syntax

Expand the syntax to allow a graph of nodes and links later.
This commit is contained in:
Wim Taymans 2021-05-03 18:13:45 +02:00
parent 22373de5b4
commit 3f5ebdedca

View file

@ -81,7 +81,89 @@ static const struct spa_dict_item module_props[] = {
#include <pipewire/pipewire.h>
#define MAX_NODES 1
#define MAX_LINKS 0
#define MAX_PORTS 64
#define MAX_CONTROLS 256
#define MAX_SAMPLES 8192
struct ladspa_handle {
struct spa_list link;
int ref;
char path[PATH_MAX];
void *handle;
LADSPA_Descriptor_Function desc_func;
struct spa_list descriptor_list;
};
struct ladspa_descriptor {
struct spa_list link;
int ref;
struct ladspa_handle *handle;
char label[256];
const LADSPA_Descriptor *desc;
struct spa_list node_list;
uint32_t n_input;
uint32_t n_output;
uint32_t n_control;
uint32_t n_notify;
unsigned long input[MAX_PORTS];
unsigned long output[MAX_PORTS];
unsigned long control[MAX_PORTS];
unsigned long notify[MAX_PORTS];
LADSPA_Data default_control[MAX_PORTS];
};
struct node {
struct spa_list link;
struct ladspa_descriptor *desc;
uint32_t pending;
uint32_t required;
char name[256];
LADSPA_Data control_data[MAX_PORTS];
LADSPA_Data notify_data[MAX_PORTS];
uint32_t n_hndl;
LADSPA_Handle hndl[MAX_PORTS];
};
struct link {
struct spa_list link;
uint32_t output_node;
uint32_t output_port;
uint32_t input_node;
uint32_t input_port;
LADSPA_Data control_data;
LADSPA_Data audio_data[MAX_SAMPLES];
};
struct graph {
struct impl *impl;
struct spa_list node_list;
struct spa_list link_list;
uint32_t n_input;
const LADSPA_Descriptor *in_desc[MAX_PORTS];
LADSPA_Handle *in_hndl[MAX_PORTS];
uint32_t in_port[MAX_PORTS];
uint32_t n_output;
const LADSPA_Descriptor *out_desc[MAX_PORTS];
LADSPA_Handle *out_hndl[MAX_PORTS];
uint32_t out_port[MAX_PORTS];
uint32_t n_hndl;
const LADSPA_Descriptor *desc[MAX_PORTS];
LADSPA_Handle *hndl[MAX_PORTS];
uint32_t n_control;
struct node *control_node[MAX_CONTROLS];
uint32_t control_index[MAX_CONTROLS];
};
struct impl {
struct pw_context *context;
@ -95,6 +177,8 @@ struct impl {
struct spa_hook core_proxy_listener;
struct spa_hook core_listener;
struct spa_list ladspa_handle_list;
struct pw_properties *capture_props;
struct pw_stream *capture;
struct spa_hook capture_listener;
@ -110,21 +194,7 @@ struct impl {
uint32_t rate;
void *handle;
uint32_t n_input;
uint32_t n_output;
uint32_t n_control;
uint32_t n_notify;
unsigned long input[MAX_PORTS];
unsigned long output[MAX_PORTS];
unsigned long control[MAX_PORTS];
unsigned long notify[MAX_PORTS];
const LADSPA_Descriptor *desc;
uint32_t n_hndl;
LADSPA_Handle hndl[MAX_PORTS];
LADSPA_Data control_data[MAX_PORTS];
LADSPA_Data notify_data[MAX_PORTS];
LADSPA_Data default_control[MAX_PORTS];
struct graph graph;
};
static void do_unload_module(void *obj, void *data, int res, uint32_t id)
@ -151,9 +221,9 @@ static void capture_process(void *d)
{
struct impl *impl = d;
struct pw_buffer *in, *out;
uint32_t i, size = 0, n_hndl = impl->n_hndl;
struct graph *graph = &impl->graph;
uint32_t i, size = 0, n_hndl = graph->n_hndl;
int32_t stride = 0;
const LADSPA_Descriptor *desc = impl->desc;
if ((in = pw_stream_dequeue_buffer(impl->capture)) == NULL)
pw_log_warn("out of capture buffers: %m");
@ -166,22 +236,22 @@ static void capture_process(void *d)
for (i = 0; i < in->buffer->n_datas; i++) {
struct spa_data *ds = &in->buffer->datas[i];
desc->connect_port(impl->hndl[i % n_hndl],
impl->input[i % impl->n_input],
graph->in_desc[i]->connect_port(graph->in_hndl[i],
graph->in_port[i],
SPA_MEMBER(ds->data, ds->chunk->offset, void));
size = SPA_MAX(size, ds->chunk->size);
stride = SPA_MAX(stride, ds->chunk->stride);
}
for (i = 0; i < out->buffer->n_datas; i++) {
struct spa_data *dd = &out->buffer->datas[i];
desc->connect_port(impl->hndl[i % n_hndl],
impl->output[i % impl->n_output], dd->data);
graph->out_desc[i]->connect_port(graph->out_hndl[i],
graph->out_port[i], dd->data);
dd->chunk->offset = 0;
dd->chunk->size = size;
dd->chunk->stride = stride;
}
for (i = 0; i < n_hndl; i++)
desc->run(impl->hndl[i], size / sizeof(float));
graph->desc[i]->run(graph->hndl[i], size / sizeof(float));
done:
if (in != NULL)
@ -190,9 +260,9 @@ done:
pw_stream_queue_buffer(impl->playback, out);
}
static float get_default(struct impl *impl, uint32_t p)
static float get_default(struct impl *impl, struct ladspa_descriptor *desc, uint32_t p)
{
const LADSPA_Descriptor *d = impl->desc;
const LADSPA_Descriptor *d = desc->desc;
LADSPA_PortRangeHintDescriptor hint = d->PortRangeHints[p].HintDescriptor;
LADSPA_Data lower, upper, def;
@ -253,15 +323,19 @@ static float get_default(struct impl *impl, uint32_t p)
return def;
}
static struct spa_pod *get_prop_info(struct impl *impl, struct spa_pod_builder *b, uint32_t idx)
static struct spa_pod *get_prop_info(struct graph *graph, struct spa_pod_builder *b, uint32_t idx)
{
struct spa_pod_frame f[2];
const LADSPA_Descriptor *d = impl->desc;
uint32_t p = impl->control[idx];
struct impl *impl = graph->impl;
struct node *node = graph->control_node[idx];
struct ladspa_descriptor *desc = node->desc;
uint32_t i = graph->control_index[idx];
uint32_t p = desc->control[i];
const LADSPA_Descriptor *d = desc->desc;
LADSPA_PortRangeHintDescriptor hint = d->PortRangeHints[p].HintDescriptor;
float def, upper, lower;
def = get_default(impl, p);
def = get_default(impl, desc, p);
lower = d->PortRangeHints[p].LowerBound;
upper = d->PortRangeHints[p].UpperBound;
@ -289,30 +363,34 @@ static struct spa_pod *get_prop_info(struct impl *impl, struct spa_pod_builder *
return spa_pod_builder_pop(b, &f[0]);
}
static struct spa_pod *get_props_param(struct impl *impl, struct spa_pod_builder *b)
static struct spa_pod *get_props_param(struct graph *graph, struct spa_pod_builder *b)
{
struct spa_pod_frame f[2];
uint32_t i;
spa_pod_builder_push_object(b, &f[0],
SPA_TYPE_OBJECT_Props, SPA_PARAM_Props);
for (i = 0; i < impl->n_control; i++) {
for (i = 0; i < graph->n_control; i++) {
struct node *node = graph->control_node[i];
uint32_t idx = graph->control_index[i];
spa_pod_builder_prop(b, SPA_PROP_START_CUSTOM + i, 0);
spa_pod_builder_float(b, impl->control_data[i]);
spa_pod_builder_float(b, node->control_data[idx]);
}
return spa_pod_builder_pop(b, &f[0]);
}
static int set_control_value(struct impl *impl, const char *name, float *value)
static int set_control_value(struct node *node, const char *name, float *value)
{
uint32_t i;
for (i = 0; i < impl->n_control; i++) {
uint32_t p = impl->control[i];
if (strcmp(impl->desc->PortNames[p], name) == 0) {
float old = impl->control_data[i];
impl->control_data[i] = value ? *value : impl->default_control[i];
pw_log_info("control %d ('%s') to %f", i, name, impl->control_data[i]);
return old == impl->control_data[i] ? 0 : 1;
struct ladspa_descriptor *desc = node->desc;
for (i = 0; i < desc->n_control; i++) {
uint32_t p = desc->control[i];
if (strcmp(desc->desc->PortNames[p], name) == 0) {
float old = node->control_data[i];
node->control_data[i] = value ? *value : desc->default_control[i];
pw_log_info("control %d ('%s') to %f", i, name, node->control_data[i]);
return old == node->control_data[i] ? 0 : 1;
}
}
return 0;
@ -323,6 +401,7 @@ static void param_changed(void *data, uint32_t id, const struct spa_pod *param)
struct impl *impl = data;
const struct spa_pod_prop *prop;
struct spa_pod_object *obj = (struct spa_pod_object *) param;
struct graph *graph = &impl->graph;
int changed = 0;
if (id != SPA_PARAM_Props)
@ -331,20 +410,24 @@ static void param_changed(void *data, uint32_t id, const struct spa_pod *param)
SPA_POD_OBJECT_FOREACH(obj, prop) {
uint32_t idx;
float value;
struct node *node;
if (prop->key < SPA_PROP_START_CUSTOM)
continue;
idx = prop->key - SPA_PROP_START_CUSTOM;
if (idx >= impl->n_control)
if (idx >= graph->n_control)
continue;
if (spa_pod_get_float(&prop->value, &value) < 0)
continue;
if (impl->control_data[idx] != value) {
impl->control_data[idx] = value;
node = graph->control_node[idx];
idx = graph->control_index[idx];
if (node->control_data[idx] != value) {
node->control_data[idx] = value;
changed++;
pw_log_info("control %d to %f", idx, impl->control_data[idx]);
pw_log_info("control %d to %f", idx, node->control_data[idx]);
}
}
if (changed > 0) {
@ -353,7 +436,7 @@ static void param_changed(void *data, uint32_t id, const struct spa_pod *param)
const struct spa_pod *params[1];
spa_pod_builder_init(&b, buffer, sizeof(buffer));
params[0] = get_props_param(impl, &b);
params[0] = get_props_param(graph, &b);
pw_stream_update_params(impl->playback, params, 1);
}
@ -399,6 +482,7 @@ static int setup_streams(struct impl *impl)
uint32_t i, n_params;
const struct spa_pod *params[256];
struct spa_pod_builder b;
struct graph *graph = &impl->graph;
impl->capture = pw_stream_new(impl->core,
"ladspa capture", impl->capture_props);
@ -427,10 +511,10 @@ static int setup_streams(struct impl *impl)
params[n_params++] = spa_format_audio_raw_build(&b,
SPA_PARAM_EnumFormat, &impl->capture_info);
for (i = 0; i < impl->n_control; i++)
params[n_params++] = get_prop_info(impl, &b, i);
for (i = 0; i < graph->n_control; i++)
params[n_params++] = get_prop_info(graph, &b, i);
params[n_params++] = get_props_param(impl, &b);
params[n_params++] = get_props_param(graph, &b);
res = pw_stream_connect(impl->capture,
PW_DIRECTION_INPUT,
@ -480,6 +564,7 @@ static const LADSPA_Descriptor *find_descriptor(LADSPA_Descriptor_Function desc_
return NULL;
}
#if 0
static uint32_t find_port(struct impl *impl, const char *name, int mask)
{
uint32_t p;
@ -516,166 +601,377 @@ static uint32_t collect_ports(struct impl *impl, const char *str, unsigned long
}
return n_ports;
}
#endif
static int parse_control(struct impl *impl, const char *control)
static void ladspa_handle_unref(struct ladspa_handle *hndl)
{
struct spa_json it[2];
char v[256];
if (--hndl->ref > 0)
return;
spa_json_init(&it[0], control, strlen(control));
if (spa_json_enter_object(&it[0], &it[1]) <= 0)
spa_json_init(&it[1], control, strlen(control));
while (spa_json_get_string(&it[1], v, sizeof(v)) > 0) {
float fl;
if (spa_json_get_float(&it[1], &fl) <= 0)
break;
set_control_value(impl, v, &fl);
}
return 0;
dlclose(hndl->handle);
free(hndl);
}
static int load_ladspa(struct impl *impl, struct pw_properties *props)
static struct ladspa_handle *ladspa_handle_load(struct impl *impl, const char *plugin)
{
struct ladspa_handle *hndl;
char path[PATH_MAX];
const char *e, *plugin, *label, *inputs, *outputs, *control;
LADSPA_Descriptor_Function desc_func;
const LADSPA_Descriptor *d;
uint32_t i, j, p;
const char *e;
int res;
if ((e = getenv("LADSPA_PATH")) == NULL)
e = "/usr/lib64/ladspa";
if ((plugin = pw_properties_get(props, "ladspa.plugin")) == NULL)
return -EINVAL;
if ((label = pw_properties_get(props, "ladspa.label")) == NULL)
return -EINVAL;
snprintf(path, sizeof(path), "%s/%s.so", e, plugin);
impl->handle = dlopen(path, RTLD_NOW);
if (impl->handle == NULL) {
spa_list_for_each(hndl, &impl->ladspa_handle_list, link) {
if (strcmp(hndl->path, path) == 0) {
hndl->ref++;
return hndl;
}
}
hndl = calloc(1, sizeof(*hndl));
hndl->ref = 1;
snprintf(hndl->path, sizeof(hndl->path), "%s", path);
hndl->handle = dlopen(path, RTLD_NOW);
if (hndl->handle == NULL) {
pw_log_error("plugin dlopen failed %s: %s", path, dlerror());
res = -ENOENT;
goto exit;
}
desc_func = (LADSPA_Descriptor_Function)dlsym(impl->handle,
hndl->desc_func = (LADSPA_Descriptor_Function)dlsym(hndl->handle,
"ladspa_descriptor");
if (desc_func == NULL) {
if (hndl->desc_func == NULL) {
pw_log_error("cannot find descriptor function from %s: %s",
path, dlerror());
res = -ENOSYS;
goto exit;
}
if ((d = find_descriptor(desc_func, label)) == NULL) {
spa_list_init(&hndl->descriptor_list);
return hndl;
exit:
if (hndl->handle != NULL)
dlclose(hndl->handle);
free(hndl);
errno = -res;
return NULL;
}
static void ladspa_descriptor_unref(struct impl *impl, struct ladspa_descriptor *desc)
{
if (--desc->ref > 0)
return;
spa_list_remove(&desc->link);
ladspa_handle_unref(desc->handle);
free(desc);
}
static struct ladspa_descriptor *ladspa_descriptor_load(struct impl *impl,
const char *plugin, const char *label)
{
struct ladspa_handle *hndl;
struct ladspa_descriptor *desc;
const LADSPA_Descriptor *d;
uint32_t i;
unsigned long p;
int res;
if ((hndl = ladspa_handle_load(impl, plugin)) == NULL)
return NULL;
spa_list_for_each(desc, &hndl->descriptor_list, link) {
if (strcmp(desc->label, label) == 0) {
desc->ref++;
return desc;
}
}
desc = calloc(1, sizeof(*desc));
desc->ref = 1;
desc->handle = hndl;
if ((d = find_descriptor(hndl->desc_func, label)) == NULL) {
pw_log_error("cannot find label %s", label);
res = -ENOENT;
goto exit;
}
impl->desc = d;
desc->desc = d;
snprintf(desc->label, sizeof(desc->label), "%s", label);
spa_list_init(&desc->node_list);
pw_properties_setf(props, "ladspa.unique-id", "%lu", impl->desc->UniqueID);
pw_properties_setf(props, "ladspa.name", "%s", impl->desc->Name);
pw_properties_setf(props, "ladspa.maker", "%s", impl->desc->Maker);
pw_properties_setf(props, "ladspa.copyright", "%s", impl->desc->Copyright);
if ((inputs = pw_properties_get(props, "ladspa.inputs")) != NULL) {
if ((impl->n_input = collect_ports(impl, inputs, impl->input,
LADSPA_PORT_INPUT | LADSPA_PORT_AUDIO)) == SPA_ID_INVALID) {
res = -EINVAL;
goto exit;
}
}
if ((outputs = pw_properties_get(props, "ladspa.outputs")) != NULL) {
if ((impl->n_output = collect_ports(impl, outputs, impl->output,
LADSPA_PORT_OUTPUT | LADSPA_PORT_AUDIO)) == SPA_ID_INVALID) {
res = -EINVAL;
goto exit;
}
}
for (p = 0; p < d->PortCount; p++) {
if (LADSPA_IS_PORT_AUDIO(d->PortDescriptors[p])) {
if (inputs == NULL && LADSPA_IS_PORT_INPUT(d->PortDescriptors[p])) {
pw_log_info("using port %d ('%s') as input %d", p,
d->PortNames[p], impl->n_input);
impl->input[impl->n_input++] = p;
if (LADSPA_IS_PORT_INPUT(d->PortDescriptors[p])) {
pw_log_info("using port %lu ('%s') as input %d", p,
d->PortNames[p], desc->n_input);
desc->input[desc->n_input++] = p;
}
else if (outputs == NULL && LADSPA_IS_PORT_OUTPUT(d->PortDescriptors[p])) {
pw_log_info("using port %d ('%s') as output %d", p,
d->PortNames[p], impl->n_output);
impl->output[impl->n_output++] = p;
else if (LADSPA_IS_PORT_OUTPUT(d->PortDescriptors[p])) {
pw_log_info("using port %lu ('%s') as output %d", p,
d->PortNames[p], desc->n_output);
desc->output[desc->n_output++] = p;
}
} else if (LADSPA_IS_PORT_CONTROL(d->PortDescriptors[p])) {
if (LADSPA_IS_PORT_INPUT(d->PortDescriptors[p])) {
pw_log_info("using port %d ('%s') as control %d", p,
d->PortNames[p], impl->n_control);
impl->control[impl->n_control++] = p;
pw_log_info("using port %lu ('%s') as control %d", p,
d->PortNames[p], desc->n_control);
desc->control[desc->n_control++] = p;
}
else if (LADSPA_IS_PORT_OUTPUT(d->PortDescriptors[p])) {
pw_log_info("using port %d ('%s') as notify %d", p,
d->PortNames[p], impl->n_notify);
impl->notify[impl->n_notify++] = p;
pw_log_info("using port %lu ('%s') as notify %d", p,
d->PortNames[p], desc->n_notify);
desc->notify[desc->n_notify++] = p;
}
}
}
if (impl->n_input == 0 || impl->n_output == 0) {
if (desc->n_input == 0 || desc->n_output == 0) {
pw_log_error("plugin has no input or no output ports");
res = -ENOTSUP;
goto exit;
}
for (j = 0; j < impl->n_control; j++) {
p = impl->control[j];
impl->default_control[j] = get_default(impl, p);
impl->control_data[j] = impl->default_control[j];
pw_log_info("control %d ('%s') set to %f", j, d->PortNames[p], impl->control_data[j]);
for (i = 0; i < desc->n_control; i++) {
p = desc->control[i];
desc->default_control[i] = get_default(impl, desc, p);
pw_log_info("control %d ('%s') default to %f", i,
d->PortNames[p], desc->default_control[i]);
}
spa_list_append(&hndl->descriptor_list, &desc->link);
return desc;
exit:
if (hndl != NULL)
ladspa_handle_unref(hndl);
free(desc);
errno = -res;
return NULL;
}
/**
* {
* "Reverb tail" = 2.0
* ...
* }
*/
static int parse_control(struct node *node, struct spa_json *control)
{
struct spa_json it[1];
char key[256];
if (spa_json_enter_object(control, &it[0]) <= 0)
return -EINVAL;
while (spa_json_get_string(&it[0], key, sizeof(key)) > 0) {
float fl;
if (spa_json_get_float(&it[0], &fl) <= 0)
break;
set_control_value(node, key, &fl);
}
return 0;
}
/**
* name = rev
* plugin = g2reverb
* label = G2reverb
* control = [
* ...
* ]
*/
static int load_node(struct graph *graph, struct spa_json *json)
{
struct spa_json it[1];
struct ladspa_descriptor *desc;
struct node *node;
const char *val;
char key[256];
char name[256] = "";
char plugin[256] = "";
char label[256] = "";
bool have_control = false;
uint32_t i;
while (spa_json_get_string(json, key, sizeof(key)) > 0) {
if (strcmp("name", key) == 0) {
if (spa_json_get_string(json, name, sizeof(name)) <= 0)
return -EINVAL;
} else if (strcmp("plugin", key) == 0) {
if (spa_json_get_string(json, plugin, sizeof(plugin)) <= 0)
return -EINVAL;
} else if (strcmp("label", key) == 0) {
if (spa_json_get_string(json, label, sizeof(label)) <= 0)
return -EINVAL;
} else if (strcmp("control", key) == 0) {
if (spa_json_enter_array(json, &it[0]) <= 0)
return -EINVAL;
have_control = true;
} else if (spa_json_next(json, &val) < 0)
break;
}
pw_log_info("loading %s %s", plugin, label);
if ((desc = ladspa_descriptor_load(graph->impl, plugin, label)) == NULL)
return -errno;
node = calloc(1, sizeof(*node));
if (node == NULL)
return -errno;
node->desc = desc;
snprintf(node->name, sizeof(node->name), "%s", name);
for (i = 0; i < desc->n_control; i++)
node->control_data[i] = desc->default_control[i];
if (have_control)
parse_control(node, &it[0]);
spa_list_append(&graph->node_list, &node->link);
return 0;
}
static int setup_graph(struct graph *graph)
{
struct impl *impl = graph->impl;
struct node *node;
uint32_t i, j, n_input, n_output, n_hndl;
int res;
unsigned long p;
graph->n_input = n_input = 0;
graph->n_output = n_output = 0;
graph->n_control = 0;
spa_list_for_each(node, &graph->node_list, link) {
struct ladspa_descriptor *desc = node->desc;
n_input += desc->n_input;
n_output += desc->n_output;
}
if (impl->capture_info.channels == 0)
impl->capture_info.channels = impl->n_input;
impl->capture_info.channels = n_input;
if (impl->playback_info.channels == 0)
impl->playback_info.channels = impl->n_output;
impl->playback_info.channels = n_output;
impl->n_hndl = impl->capture_info.channels / impl->n_input;
if (impl->n_hndl != impl->playback_info.channels / impl->n_output) {
n_hndl = impl->capture_info.channels / n_input;
if (n_hndl != impl->playback_info.channels / n_output) {
pw_log_error("invalid channels");
res = -EINVAL;
goto exit;
}
pw_log_info("using %d instances", impl->n_hndl);
pw_log_info("using %d instances", n_hndl);
if ((control = pw_properties_get(props, "ladspa.control")) != NULL)
parse_control(impl, control);
spa_list_for_each(node, &graph->node_list, link) {
struct ladspa_descriptor *desc = node->desc;
const LADSPA_Descriptor *d = desc->desc;
for (i = 0; i < n_hndl; i++) {
if ((node->hndl[i] = d->instantiate(d, impl->rate)) == NULL) {
pw_log_error("cannot create plugin instance");
res = -ENOMEM;
goto exit;
}
node->n_hndl = i;
for (i = 0; i < impl->n_hndl;i++) {
if ((impl->hndl[i] = d->instantiate(d, impl->rate)) == NULL) {
pw_log_error("cannot create plugin instance");
res = -ENOMEM;
goto exit;
}
for (j = 0; j < desc->n_input; j++) {
p = desc->input[j];
graph->in_desc[graph->n_input] = d;
graph->in_hndl[graph->n_input] = node->hndl[i];
graph->in_port[graph->n_input] = p;
graph->n_input++;
}
for (j = 0; j < desc->n_output; j++) {
p = desc->output[j];
graph->out_desc[graph->n_output] = d;
graph->out_hndl[graph->n_output] = node->hndl[i];
graph->out_port[graph->n_output] = p;
graph->n_output++;
}
for (j = 0; j < impl->n_control; j++) {
p = impl->control[j];
d->connect_port(impl->hndl[i], p, &impl->control_data[j]);
graph->hndl[graph->n_hndl] = node->hndl[i];
graph->desc[graph->n_hndl] = d;
graph->n_hndl++;
for (j = 0; j < desc->n_control; j++) {
p = desc->control[j];
d->connect_port(node->hndl[i], p, &node->control_data[j]);
}
for (j = 0; j < desc->n_notify; j++) {
p = desc->notify[j];
d->connect_port(node->hndl[i], p, &node->notify_data[j]);
}
if (d->activate)
d->activate(node->hndl[i]);
}
for (j = 0; j < impl->n_notify; j++) {
p = impl->notify[j];
d->connect_port(impl->hndl[i], p, &impl->notify_data[j]);
}
if (d->activate)
d->activate(impl->hndl[i]);
}
return 0;
exit:
if (impl->handle != NULL)
dlclose(impl->handle);
impl->handle = NULL;
for (i = 0; i < n_hndl; i++) {
if (node->hndl[i] != NULL)
node->desc->desc->cleanup(node->hndl[i]);
node->hndl[i] = NULL;
}
return res;
}
/**
* ladspa.graph = {
* nodes = [
* { ... } ...
* ]
* links = [
* { ... } ...
* ]
* }
*/
static int load_graph(struct graph *graph, struct pw_properties *props)
{
struct spa_json it[4];
const char *json, *val;
char key[256];
int res;
spa_list_init(&graph->node_list);
spa_list_init(&graph->link_list);
if ((json = pw_properties_get(props, "ladspa.graph")) == NULL)
return -EINVAL;
spa_json_init(&it[0], json, strlen(json));
if (spa_json_enter_object(&it[0], &it[1]) <= 0)
spa_json_init(&it[1], json, strlen(json));
while (spa_json_get_string(&it[1], key, sizeof(key)) > 0) {
if (strcmp("nodes", key) == 0) {
if (spa_json_enter_array(&it[1], &it[2]) <= 0)
return -EINVAL;
while (spa_json_enter_object(&it[2], &it[3]) > 0) {
if ((res = load_node(graph, &it[3])) < 0)
return res;
}
}
else if (strcmp("links", key) == 0) {
if (spa_json_enter_array(&it[1], &it[2]) <= 0)
return -EINVAL;
while (spa_json_enter_object(&it[2], &it[3]) > 0) {
return -ENOTSUP;
}
} else if (spa_json_next(&it[1], &val) < 0)
break;
}
return setup_graph(graph);;
}
static void core_error(void *data, uint32_t id, int seq, int res, const char *message)
{
struct impl *impl = data;
@ -823,6 +1119,9 @@ int pipewire__module_init(struct pw_impl_module *module, const char *args)
impl->context = context;
impl->work = pw_context_get_work_queue(context);
impl->rate = 48000;
impl->graph.impl = impl;
spa_list_init(&impl->ladspa_handle_list);
if (pw_properties_get(props, PW_KEY_NODE_GROUP) == NULL)
pw_properties_setf(props, PW_KEY_NODE_GROUP, "ladspa-filter-%u", id);
@ -846,8 +1145,8 @@ int pipewire__module_init(struct pw_impl_module *module, const char *args)
parse_audio_info(impl->capture_props, &impl->capture_info);
parse_audio_info(impl->playback_props, &impl->playback_info);
if ((res = load_ladspa(impl, props)) < 0) {
pw_log_error("can't load ladspa: %s", spa_strerror(res));
if ((res = load_graph(&impl->graph, props)) < 0) {
pw_log_error("can't load graph: %s", spa_strerror(res));
goto error;
}
copy_props(impl, props, "ladspa.unique-id");
@ -856,11 +1155,11 @@ int pipewire__module_init(struct pw_impl_module *module, const char *args)
copy_props(impl, props, "ladspa.copyright");
if (pw_properties_get(impl->capture_props, PW_KEY_MEDIA_NAME) == NULL)
pw_properties_setf(impl->capture_props, PW_KEY_MEDIA_NAME, "%s input",
impl->desc->Name);
pw_properties_setf(impl->capture_props, PW_KEY_MEDIA_NAME, "ladspa input %u",
id);
if (pw_properties_get(impl->playback_props, PW_KEY_MEDIA_NAME) == NULL)
pw_properties_setf(impl->playback_props, PW_KEY_MEDIA_NAME, "%s output",
impl->desc->Name);
pw_properties_setf(impl->playback_props, PW_KEY_MEDIA_NAME, "ladspa output %u",
id);
impl->core = pw_context_get_object(impl->context, PW_TYPE_INTERFACE_Core);
if (impl->core == NULL) {