filter-chain: add builtin plugins

Add a simple mixer builtin plugin.
Free memory on module unload.
This commit is contained in:
Wim Taymans 2021-05-09 12:00:30 +02:00
parent 3c7222d84b
commit 00e85a252c
2 changed files with 224 additions and 58 deletions

View file

@ -114,7 +114,6 @@ struct ladspa_descriptor {
struct ladspa_handle *handle;
char label[256];
const LADSPA_Descriptor *desc;
struct spa_list node_list;
uint32_t n_input;
uint32_t n_output;
@ -171,6 +170,17 @@ struct link {
struct port *input;
};
struct graph_port {
const LADSPA_Descriptor *desc;
LADSPA_Handle hndl;
uint32_t port;
};
struct graph_hndl {
const LADSPA_Descriptor *desc;
LADSPA_Handle hndl;
};
struct graph {
struct impl *impl;
@ -178,18 +188,13 @@ struct graph {
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];
struct graph_port input[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];
struct graph_port output[MAX_PORTS];
uint32_t n_hndl;
const LADSPA_Descriptor *desc[MAX_PORTS];
LADSPA_Handle hndl[MAX_PORTS];
struct graph_hndl hndl[MAX_HNDL];
uint32_t n_control;
struct port *control_port[MAX_CONTROLS];
@ -230,6 +235,8 @@ struct impl {
struct graph graph;
};
#include "module-filter-chain/builtin.h"
static void do_unload_module(void *obj, void *data, int res, uint32_t id)
{
struct impl *impl = data;
@ -269,22 +276,24 @@ static void capture_process(void *d)
for (i = 0; i < in->buffer->n_datas; i++) {
struct spa_data *ds = &in->buffer->datas[i];
graph->in_desc[i]->connect_port(graph->in_hndl[i],
graph->in_port[i],
struct graph_port *port = &graph->input[i];
port->desc->connect_port(port->hndl, port->port,
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];
graph->out_desc[i]->connect_port(graph->out_hndl[i],
graph->out_port[i], dd->data);
struct graph_port *port = &graph->output[i];
port->desc->connect_port(port->hndl, port->port, dd->data);
dd->chunk->offset = 0;
dd->chunk->size = size;
dd->chunk->stride = stride;
}
for (i = 0; i < n_hndl; i++)
graph->desc[i]->run(graph->hndl[i], size / sizeof(float));
for (i = 0; i < n_hndl; i++) {
struct graph_hndl *hndl = &graph->hndl[i];
hndl->desc->run(hndl->hndl, size / sizeof(float));
}
done:
if (in != NULL)
@ -670,7 +679,7 @@ static void ladspa_handle_unref(struct ladspa_handle *hndl)
{
if (--hndl->ref > 0)
return;
if (hndl->handle)
dlclose(hndl->handle);
free(hndl);
}
@ -698,6 +707,9 @@ static struct ladspa_handle *ladspa_handle_load(struct impl *impl, const char *p
hndl->ref = 1;
snprintf(hndl->path, sizeof(hndl->path), "%s", path);
if (strcmp(plugin, "builtin") == 0) {
hndl->desc_func = builtin_ladspa_descriptor;
} else {
hndl->handle = dlopen(path, RTLD_NOW);
if (hndl->handle == NULL) {
pw_log_error("plugin dlopen failed %s: %s", path, dlerror());
@ -707,7 +719,7 @@ static struct ladspa_handle *ladspa_handle_load(struct impl *impl, const char *p
hndl->desc_func = (LADSPA_Descriptor_Function)dlsym(hndl->handle,
"ladspa_descriptor");
}
if (hndl->desc_func == NULL) {
pw_log_error("cannot find descriptor function from %s: %s",
path, dlerror());
@ -726,7 +738,7 @@ exit:
return NULL;
}
static void ladspa_descriptor_unref(struct impl *impl, struct ladspa_descriptor *desc)
static void ladspa_descriptor_unref(struct ladspa_descriptor *desc)
{
if (--desc->ref > 0)
return;
@ -767,7 +779,6 @@ static struct ladspa_descriptor *ladspa_descriptor_load(struct impl *impl,
}
desc->desc = d;
snprintf(desc->label, sizeof(desc->label), "%s", label);
spa_list_init(&desc->node_list);
for (p = 0; p < d->PortCount; p++) {
if (LADSPA_IS_PORT_AUDIO(d->PortDescriptors[p])) {
@ -888,6 +899,11 @@ static int parse_link(struct graph *graph, struct spa_json *json)
link->output = out_port;
link->input = in_port;
if (in_port->n_links > 0) {
pw_log_info("Can't have more than 1 link to %s, use a mixer", input);
return -ENOTSUP;
}
pw_log_info("linking %s:%s -> %s:%s",
out_port->node->name,
out_port->node->desc->desc->PortNames[out_port->p],
@ -906,6 +922,17 @@ static int parse_link(struct graph *graph, struct spa_json *json)
return 0;
}
static void link_free(struct link *link)
{
spa_list_remove(&link->input_link);
link->input->n_links--;
link->input->node->n_deps--;
spa_list_remove(&link->output_link);
link->output->n_links--;
spa_list_remove(&link->link);
free(link);
}
/**
* type = ladspa
* name = rev
@ -935,8 +962,6 @@ static int load_node(struct graph *graph, struct spa_json *json)
pw_log_error("type expects a string");
return -EINVAL;
}
if (strcmp(type, "ladspa") != 0)
return -ENOTSUP;
} else if (strcmp("name", key) == 0) {
if (spa_json_get_string(json, name, sizeof(name)) <= 0) {
pw_log_error("name expects a string");
@ -959,8 +984,12 @@ static int load_node(struct graph *graph, struct spa_json *json)
break;
}
pw_log_info("loading %s %s", plugin, label);
if (strcmp(type, "builtin") == 0) {
snprintf(plugin, sizeof(plugin), "%s", "builtin");
} else if (strcmp(type, "ladspa") != 0)
return -ENOTSUP;
pw_log_info("loading %s %s", plugin, label);
if ((desc = ladspa_descriptor_load(graph->impl, plugin, label)) == NULL)
return -errno;
@ -1009,6 +1038,23 @@ static int load_node(struct graph *graph, struct spa_json *json)
return 0;
}
static void node_free(struct node *node)
{
uint32_t i, j;
const LADSPA_Descriptor *d = node->desc->desc;
spa_list_remove(&node->link);
for (i = 0; i < node->n_hndl; i++) {
for (j = 0; j < node->desc->n_output; j++)
free(node->output_port[j].audio_data[i]);
if (d->activate)
d->activate(node->hndl[i]);
d->cleanup(node->hndl[i]);
}
ladspa_descriptor_unref(node->desc);
free(node);
}
static struct node *find_next_node(struct graph *graph)
{
struct node *node;
@ -1028,13 +1074,6 @@ static int setup_input_port(struct graph *graph, struct port *port)
struct link *link;
uint32_t i, n_hndl = port->node->n_hndl;
if (port->n_links > 1) {
pw_log_warn("mixing not implemented yet");
/* FIXME, add a mixer node here, connect all linked peer
* data to the input, make output data, connect to the input
* port */
}
spa_list_for_each(link, &port->link_list, input_link) {
struct port *peer = link->output;
for (i = 0; i < n_hndl; i++) {
@ -1078,6 +1117,8 @@ static int setup_graph(struct graph *graph, struct spa_json *inputs, struct spa_
struct impl *impl = graph->impl;
struct node *node, *first, *last;
struct port *port;
struct graph_port *gp;
struct graph_hndl *gh;
uint32_t i, j, n_input, n_output, n_hndl = 0;
int res;
unsigned long p;
@ -1177,12 +1218,12 @@ static int setup_graph(struct graph *graph, struct spa_json *inputs, struct spa_
desc = first->desc;
d = desc->desc;
for (j = 0; j < desc->n_input; j++) {
gp = &graph->input[graph->n_input++];
pw_log_info("input port %s[%d]:%s",
first->name, i, d->PortNames[desc->input[j]]);
graph->in_desc[graph->n_input] = d;
graph->in_hndl[graph->n_input] = first->hndl[i];
graph->in_port[graph->n_input] = desc->input[j];
graph->n_input++;
gp->desc = d;
gp->hndl = first->hndl[i];
gp->port = desc->input[j];
}
} else {
struct spa_json it = *inputs;
@ -1192,26 +1233,26 @@ static int setup_graph(struct graph *graph, struct spa_json *inputs, struct spa_
pw_log_error("input port %s not found", v);
goto error;
}
gp = &graph->input[graph->n_input++];
desc = port->node->desc;
d = desc->desc;
pw_log_info("input port %s[%d]:%s",
port->node->name, i, d->PortNames[port->p]);
graph->in_desc[graph->n_input] = d;
graph->in_hndl[graph->n_input] = port->node->hndl[i];
graph->in_port[graph->n_input] = port->p;
graph->n_input++;
gp->desc = d;
gp->hndl = port->node->hndl[i];
gp->port = port->p;
}
}
if (outputs == NULL) {
desc = last->desc;
d = desc->desc;
for (j = 0; j < desc->n_output; j++) {
gp = &graph->output[graph->n_output++];
pw_log_info("output port %s[%d]:%s",
last->name, i, d->PortNames[desc->output[j]]);
graph->out_desc[graph->n_output] = d;
graph->out_hndl[graph->n_output] = last->hndl[i];
graph->out_port[graph->n_output] = desc->output[j];
graph->n_output++;
gp->desc = d;
gp->hndl = last->hndl[i];
gp->port = desc->output[j];
}
} else {
struct spa_json it = *outputs;
@ -1221,15 +1262,14 @@ static int setup_graph(struct graph *graph, struct spa_json *inputs, struct spa_
pw_log_error("output port %s not found", v);
goto error;
}
gp = &graph->output[graph->n_output++];
desc = port->node->desc;
d = desc->desc;
pw_log_info("output port %s[%d]:%s",
port->node->name, i, d->PortNames[port->p]);
graph->out_desc[graph->n_output] = d;
graph->out_hndl[graph->n_output] = port->node->hndl[i];
graph->out_port[graph->n_output] = port->p;
graph->n_output++;
gp->desc = d;
gp->hndl = port->node->hndl[i];
gp->port = port->p;
}
}
}
@ -1247,9 +1287,9 @@ static int setup_graph(struct graph *graph, struct spa_json *inputs, struct spa_
setup_input_port(graph, &node->input_port[i]);
for (i = 0; i < n_hndl; i++) {
graph->hndl[graph->n_hndl] = node->hndl[i];
graph->desc[graph->n_hndl] = d;
graph->n_hndl++;
gh = &graph->hndl[graph->n_hndl++];
gh->hndl = node->hndl[i];
gh->desc = d;
}
for (i = 0; i < desc->n_output; i++)
@ -1338,6 +1378,16 @@ static int load_graph(struct graph *graph, struct pw_properties *props)
return setup_graph(graph, pinputs, poutputs);
}
static void graph_free(struct graph *graph)
{
struct link *link;
struct node *node;
spa_list_consume(link, &graph->link_list, link)
link_free(link);
spa_list_consume(node, &graph->node_list, link)
node_free(node);
}
static void core_error(void *data, uint32_t id, int seq, int res, const char *message)
{
struct impl *impl = data;
@ -1379,6 +1429,7 @@ static void impl_destroy(struct impl *impl)
if (impl->playback_props)
pw_properties_free(impl->playback_props);
pw_work_queue_cancel(impl->work, impl, SPA_ID_INVALID);
graph_free(&impl->graph);
free(impl);
}

View file

@ -0,0 +1,115 @@
/* PipeWire
*
* Copyright © 2021 Wim Taymans
*
* Permission is hereby granted, free of charge, to any person obtaining a
* copy of this software and associated documentation files (the "Software"),
* to deal in the Software without restriction, including without limitation
* the rights to use, copy, modify, merge, publish, distribute, sublicense,
* and/or sell copies of the Software, and to permit persons to whom the
* Software is furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice (including the next
* paragraph) shall be included in all copies or substantial portions of the
* Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
* THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
* FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
* DEALINGS IN THE SOFTWARE.
*/
struct builtin {
LADSPA_Data *port[64];
};
static LADSPA_Handle builtin_instantiate(const struct _LADSPA_Descriptor * Descriptor,
unsigned long SampleRate)
{
struct builtin *impl;
impl = calloc(1, sizeof(*impl));
if (impl == NULL)
return NULL;
return impl;
}
static void builtin_connect_port(LADSPA_Handle Instance, unsigned long Port,
LADSPA_Data * DataLocation)
{
struct builtin *impl = Instance;
impl->port[Port] = DataLocation;
}
static void builtin_cleanup(LADSPA_Handle Instance)
{
struct builtin *impl = Instance;
free(impl);
}
/** mixer */
static void mixer_run(LADSPA_Handle Instance, unsigned long SampleCount)
{
struct builtin *impl = Instance;
unsigned long i;
float gain1 = impl->port[3][0];
float gain2 = impl->port[4][0];
float *in1 = impl->port[1], *in2 = impl->port[2], *out = impl->port[0];
if (gain1 == 0.0f && gain2 == 0.0f) {
memset(out, 0, SampleCount * sizeof(float));
} else if (gain1 == 1.0f && gain2 == 1.0f) {
for (i = 0; i < SampleCount; i++)
out[i] = in1[i] + in2[i];
} else {
for (i = 0; i < SampleCount; i++)
out[i] = in1[i] * gain1 + in2[i] * gain2;
}
}
static const LADSPA_PortDescriptor mixer_port_desc[] = {
LADSPA_PORT_OUTPUT | LADSPA_PORT_AUDIO,
LADSPA_PORT_INPUT | LADSPA_PORT_AUDIO,
LADSPA_PORT_INPUT | LADSPA_PORT_AUDIO,
LADSPA_PORT_INPUT | LADSPA_PORT_CONTROL,
LADSPA_PORT_INPUT | LADSPA_PORT_CONTROL,
};
static const char * const mixer_port_names[] = {
"Out", "In 1", "In 2", "Gain 1", "Gain 2"
};
static const LADSPA_PortRangeHint mixer_range_hints[] = {
{ 0, }, { 0, }, { 0, },
{ LADSPA_HINT_BOUNDED_BELOW | LADSPA_HINT_BOUNDED_ABOVE | LADSPA_HINT_DEFAULT_1, 0.0, 10.0 },
{ LADSPA_HINT_BOUNDED_BELOW | LADSPA_HINT_BOUNDED_ABOVE | LADSPA_HINT_DEFAULT_1, 0.0, 10.0 }
};
static const LADSPA_Descriptor mixer_desc = {
.Label = "mixer",
.Name = "Mix 2 inputs",
.Maker = "PipeWire",
.Copyright = "MIT",
.PortCount = 5,
.PortDescriptors = mixer_port_desc,
.PortNames = mixer_port_names,
.PortRangeHints = mixer_range_hints,
.instantiate = builtin_instantiate,
.connect_port = builtin_connect_port,
.run = mixer_run,
.cleanup = builtin_cleanup,
};
static const LADSPA_Descriptor * builtin_ladspa_descriptor(unsigned long Index)
{
switch(Index) {
case 0:
return &mixer_desc;
}
return NULL;
}