mirror of
https://gitlab.freedesktop.org/pipewire/pipewire.git
synced 2025-10-31 22:25:38 -04:00
filter-chain: add builtin plugins
Add a simple mixer builtin plugin. Free memory on module unload.
This commit is contained in:
parent
3c7222d84b
commit
00e85a252c
2 changed files with 224 additions and 58 deletions
|
|
@ -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);
|
||||
}
|
||||
|
||||
|
|
|
|||
115
src/modules/module-filter-chain/builtin.h
Normal file
115
src/modules/module-filter-chain/builtin.h
Normal 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;
|
||||
}
|
||||
|
||||
Loading…
Add table
Add a link
Reference in a new issue