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;
|
struct ladspa_handle *handle;
|
||||||
char label[256];
|
char label[256];
|
||||||
const LADSPA_Descriptor *desc;
|
const LADSPA_Descriptor *desc;
|
||||||
struct spa_list node_list;
|
|
||||||
|
|
||||||
uint32_t n_input;
|
uint32_t n_input;
|
||||||
uint32_t n_output;
|
uint32_t n_output;
|
||||||
|
|
@ -171,6 +170,17 @@ struct link {
|
||||||
struct port *input;
|
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 graph {
|
||||||
struct impl *impl;
|
struct impl *impl;
|
||||||
|
|
||||||
|
|
@ -178,18 +188,13 @@ struct graph {
|
||||||
struct spa_list link_list;
|
struct spa_list link_list;
|
||||||
|
|
||||||
uint32_t n_input;
|
uint32_t n_input;
|
||||||
const LADSPA_Descriptor *in_desc[MAX_PORTS];
|
struct graph_port input[MAX_PORTS];
|
||||||
LADSPA_Handle in_hndl[MAX_PORTS];
|
|
||||||
uint32_t in_port[MAX_PORTS];
|
|
||||||
|
|
||||||
uint32_t n_output;
|
uint32_t n_output;
|
||||||
const LADSPA_Descriptor *out_desc[MAX_PORTS];
|
struct graph_port output[MAX_PORTS];
|
||||||
LADSPA_Handle out_hndl[MAX_PORTS];
|
|
||||||
uint32_t out_port[MAX_PORTS];
|
|
||||||
|
|
||||||
uint32_t n_hndl;
|
uint32_t n_hndl;
|
||||||
const LADSPA_Descriptor *desc[MAX_PORTS];
|
struct graph_hndl hndl[MAX_HNDL];
|
||||||
LADSPA_Handle hndl[MAX_PORTS];
|
|
||||||
|
|
||||||
uint32_t n_control;
|
uint32_t n_control;
|
||||||
struct port *control_port[MAX_CONTROLS];
|
struct port *control_port[MAX_CONTROLS];
|
||||||
|
|
@ -230,6 +235,8 @@ struct impl {
|
||||||
struct graph graph;
|
struct graph graph;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
#include "module-filter-chain/builtin.h"
|
||||||
|
|
||||||
static void do_unload_module(void *obj, void *data, int res, uint32_t id)
|
static void do_unload_module(void *obj, void *data, int res, uint32_t id)
|
||||||
{
|
{
|
||||||
struct impl *impl = data;
|
struct impl *impl = data;
|
||||||
|
|
@ -269,22 +276,24 @@ static void capture_process(void *d)
|
||||||
|
|
||||||
for (i = 0; i < in->buffer->n_datas; i++) {
|
for (i = 0; i < in->buffer->n_datas; i++) {
|
||||||
struct spa_data *ds = &in->buffer->datas[i];
|
struct spa_data *ds = &in->buffer->datas[i];
|
||||||
graph->in_desc[i]->connect_port(graph->in_hndl[i],
|
struct graph_port *port = &graph->input[i];
|
||||||
graph->in_port[i],
|
port->desc->connect_port(port->hndl, port->port,
|
||||||
SPA_MEMBER(ds->data, ds->chunk->offset, void));
|
SPA_MEMBER(ds->data, ds->chunk->offset, void));
|
||||||
size = SPA_MAX(size, ds->chunk->size);
|
size = SPA_MAX(size, ds->chunk->size);
|
||||||
stride = SPA_MAX(stride, ds->chunk->stride);
|
stride = SPA_MAX(stride, ds->chunk->stride);
|
||||||
}
|
}
|
||||||
for (i = 0; i < out->buffer->n_datas; i++) {
|
for (i = 0; i < out->buffer->n_datas; i++) {
|
||||||
struct spa_data *dd = &out->buffer->datas[i];
|
struct spa_data *dd = &out->buffer->datas[i];
|
||||||
graph->out_desc[i]->connect_port(graph->out_hndl[i],
|
struct graph_port *port = &graph->output[i];
|
||||||
graph->out_port[i], dd->data);
|
port->desc->connect_port(port->hndl, port->port, dd->data);
|
||||||
dd->chunk->offset = 0;
|
dd->chunk->offset = 0;
|
||||||
dd->chunk->size = size;
|
dd->chunk->size = size;
|
||||||
dd->chunk->stride = stride;
|
dd->chunk->stride = stride;
|
||||||
}
|
}
|
||||||
for (i = 0; i < n_hndl; i++)
|
for (i = 0; i < n_hndl; i++) {
|
||||||
graph->desc[i]->run(graph->hndl[i], size / sizeof(float));
|
struct graph_hndl *hndl = &graph->hndl[i];
|
||||||
|
hndl->desc->run(hndl->hndl, size / sizeof(float));
|
||||||
|
}
|
||||||
|
|
||||||
done:
|
done:
|
||||||
if (in != NULL)
|
if (in != NULL)
|
||||||
|
|
@ -670,8 +679,8 @@ static void ladspa_handle_unref(struct ladspa_handle *hndl)
|
||||||
{
|
{
|
||||||
if (--hndl->ref > 0)
|
if (--hndl->ref > 0)
|
||||||
return;
|
return;
|
||||||
|
if (hndl->handle)
|
||||||
dlclose(hndl->handle);
|
dlclose(hndl->handle);
|
||||||
free(hndl);
|
free(hndl);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -698,16 +707,19 @@ static struct ladspa_handle *ladspa_handle_load(struct impl *impl, const char *p
|
||||||
hndl->ref = 1;
|
hndl->ref = 1;
|
||||||
snprintf(hndl->path, sizeof(hndl->path), "%s", path);
|
snprintf(hndl->path, sizeof(hndl->path), "%s", path);
|
||||||
|
|
||||||
hndl->handle = dlopen(path, RTLD_NOW);
|
if (strcmp(plugin, "builtin") == 0) {
|
||||||
if (hndl->handle == NULL) {
|
hndl->desc_func = builtin_ladspa_descriptor;
|
||||||
pw_log_error("plugin dlopen failed %s: %s", path, dlerror());
|
} else {
|
||||||
res = -ENOENT;
|
hndl->handle = dlopen(path, RTLD_NOW);
|
||||||
goto exit;
|
if (hndl->handle == NULL) {
|
||||||
|
pw_log_error("plugin dlopen failed %s: %s", path, dlerror());
|
||||||
|
res = -ENOENT;
|
||||||
|
goto exit;
|
||||||
|
}
|
||||||
|
|
||||||
|
hndl->desc_func = (LADSPA_Descriptor_Function)dlsym(hndl->handle,
|
||||||
|
"ladspa_descriptor");
|
||||||
}
|
}
|
||||||
|
|
||||||
hndl->desc_func = (LADSPA_Descriptor_Function)dlsym(hndl->handle,
|
|
||||||
"ladspa_descriptor");
|
|
||||||
|
|
||||||
if (hndl->desc_func == NULL) {
|
if (hndl->desc_func == NULL) {
|
||||||
pw_log_error("cannot find descriptor function from %s: %s",
|
pw_log_error("cannot find descriptor function from %s: %s",
|
||||||
path, dlerror());
|
path, dlerror());
|
||||||
|
|
@ -726,7 +738,7 @@ exit:
|
||||||
return NULL;
|
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)
|
if (--desc->ref > 0)
|
||||||
return;
|
return;
|
||||||
|
|
@ -767,7 +779,6 @@ static struct ladspa_descriptor *ladspa_descriptor_load(struct impl *impl,
|
||||||
}
|
}
|
||||||
desc->desc = d;
|
desc->desc = d;
|
||||||
snprintf(desc->label, sizeof(desc->label), "%s", label);
|
snprintf(desc->label, sizeof(desc->label), "%s", label);
|
||||||
spa_list_init(&desc->node_list);
|
|
||||||
|
|
||||||
for (p = 0; p < d->PortCount; p++) {
|
for (p = 0; p < d->PortCount; p++) {
|
||||||
if (LADSPA_IS_PORT_AUDIO(d->PortDescriptors[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->output = out_port;
|
||||||
link->input = in_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",
|
pw_log_info("linking %s:%s -> %s:%s",
|
||||||
out_port->node->name,
|
out_port->node->name,
|
||||||
out_port->node->desc->desc->PortNames[out_port->p],
|
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;
|
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
|
* type = ladspa
|
||||||
* name = rev
|
* name = rev
|
||||||
|
|
@ -935,8 +962,6 @@ static int load_node(struct graph *graph, struct spa_json *json)
|
||||||
pw_log_error("type expects a string");
|
pw_log_error("type expects a string");
|
||||||
return -EINVAL;
|
return -EINVAL;
|
||||||
}
|
}
|
||||||
if (strcmp(type, "ladspa") != 0)
|
|
||||||
return -ENOTSUP;
|
|
||||||
} else if (strcmp("name", key) == 0) {
|
} else if (strcmp("name", key) == 0) {
|
||||||
if (spa_json_get_string(json, name, sizeof(name)) <= 0) {
|
if (spa_json_get_string(json, name, sizeof(name)) <= 0) {
|
||||||
pw_log_error("name expects a string");
|
pw_log_error("name expects a string");
|
||||||
|
|
@ -959,8 +984,12 @@ static int load_node(struct graph *graph, struct spa_json *json)
|
||||||
break;
|
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)
|
if ((desc = ladspa_descriptor_load(graph->impl, plugin, label)) == NULL)
|
||||||
return -errno;
|
return -errno;
|
||||||
|
|
||||||
|
|
@ -1009,6 +1038,23 @@ static int load_node(struct graph *graph, struct spa_json *json)
|
||||||
return 0;
|
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)
|
static struct node *find_next_node(struct graph *graph)
|
||||||
{
|
{
|
||||||
struct node *node;
|
struct node *node;
|
||||||
|
|
@ -1028,13 +1074,6 @@ static int setup_input_port(struct graph *graph, struct port *port)
|
||||||
struct link *link;
|
struct link *link;
|
||||||
uint32_t i, n_hndl = port->node->n_hndl;
|
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) {
|
spa_list_for_each(link, &port->link_list, input_link) {
|
||||||
struct port *peer = link->output;
|
struct port *peer = link->output;
|
||||||
for (i = 0; i < n_hndl; i++) {
|
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 impl *impl = graph->impl;
|
||||||
struct node *node, *first, *last;
|
struct node *node, *first, *last;
|
||||||
struct port *port;
|
struct port *port;
|
||||||
|
struct graph_port *gp;
|
||||||
|
struct graph_hndl *gh;
|
||||||
uint32_t i, j, n_input, n_output, n_hndl = 0;
|
uint32_t i, j, n_input, n_output, n_hndl = 0;
|
||||||
int res;
|
int res;
|
||||||
unsigned long p;
|
unsigned long p;
|
||||||
|
|
@ -1177,12 +1218,12 @@ static int setup_graph(struct graph *graph, struct spa_json *inputs, struct spa_
|
||||||
desc = first->desc;
|
desc = first->desc;
|
||||||
d = desc->desc;
|
d = desc->desc;
|
||||||
for (j = 0; j < desc->n_input; j++) {
|
for (j = 0; j < desc->n_input; j++) {
|
||||||
|
gp = &graph->input[graph->n_input++];
|
||||||
pw_log_info("input port %s[%d]:%s",
|
pw_log_info("input port %s[%d]:%s",
|
||||||
first->name, i, d->PortNames[desc->input[j]]);
|
first->name, i, d->PortNames[desc->input[j]]);
|
||||||
graph->in_desc[graph->n_input] = d;
|
gp->desc = d;
|
||||||
graph->in_hndl[graph->n_input] = first->hndl[i];
|
gp->hndl = first->hndl[i];
|
||||||
graph->in_port[graph->n_input] = desc->input[j];
|
gp->port = desc->input[j];
|
||||||
graph->n_input++;
|
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
struct spa_json it = *inputs;
|
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);
|
pw_log_error("input port %s not found", v);
|
||||||
goto error;
|
goto error;
|
||||||
}
|
}
|
||||||
|
gp = &graph->input[graph->n_input++];
|
||||||
desc = port->node->desc;
|
desc = port->node->desc;
|
||||||
d = desc->desc;
|
d = desc->desc;
|
||||||
pw_log_info("input port %s[%d]:%s",
|
pw_log_info("input port %s[%d]:%s",
|
||||||
port->node->name, i, d->PortNames[port->p]);
|
port->node->name, i, d->PortNames[port->p]);
|
||||||
graph->in_desc[graph->n_input] = d;
|
gp->desc = d;
|
||||||
graph->in_hndl[graph->n_input] = port->node->hndl[i];
|
gp->hndl = port->node->hndl[i];
|
||||||
graph->in_port[graph->n_input] = port->p;
|
gp->port = port->p;
|
||||||
graph->n_input++;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (outputs == NULL) {
|
if (outputs == NULL) {
|
||||||
desc = last->desc;
|
desc = last->desc;
|
||||||
d = desc->desc;
|
d = desc->desc;
|
||||||
for (j = 0; j < desc->n_output; j++) {
|
for (j = 0; j < desc->n_output; j++) {
|
||||||
|
gp = &graph->output[graph->n_output++];
|
||||||
pw_log_info("output port %s[%d]:%s",
|
pw_log_info("output port %s[%d]:%s",
|
||||||
last->name, i, d->PortNames[desc->output[j]]);
|
last->name, i, d->PortNames[desc->output[j]]);
|
||||||
graph->out_desc[graph->n_output] = d;
|
gp->desc = d;
|
||||||
graph->out_hndl[graph->n_output] = last->hndl[i];
|
gp->hndl = last->hndl[i];
|
||||||
graph->out_port[graph->n_output] = desc->output[j];
|
gp->port = desc->output[j];
|
||||||
graph->n_output++;
|
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
struct spa_json it = *outputs;
|
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);
|
pw_log_error("output port %s not found", v);
|
||||||
goto error;
|
goto error;
|
||||||
}
|
}
|
||||||
|
gp = &graph->output[graph->n_output++];
|
||||||
desc = port->node->desc;
|
desc = port->node->desc;
|
||||||
d = desc->desc;
|
d = desc->desc;
|
||||||
pw_log_info("output port %s[%d]:%s",
|
pw_log_info("output port %s[%d]:%s",
|
||||||
port->node->name, i, d->PortNames[port->p]);
|
port->node->name, i, d->PortNames[port->p]);
|
||||||
graph->out_desc[graph->n_output] = d;
|
gp->desc = d;
|
||||||
graph->out_hndl[graph->n_output] = port->node->hndl[i];
|
gp->hndl = port->node->hndl[i];
|
||||||
graph->out_port[graph->n_output] = port->p;
|
gp->port = port->p;
|
||||||
graph->n_output++;
|
|
||||||
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -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]);
|
setup_input_port(graph, &node->input_port[i]);
|
||||||
|
|
||||||
for (i = 0; i < n_hndl; i++) {
|
for (i = 0; i < n_hndl; i++) {
|
||||||
graph->hndl[graph->n_hndl] = node->hndl[i];
|
gh = &graph->hndl[graph->n_hndl++];
|
||||||
graph->desc[graph->n_hndl] = d;
|
gh->hndl = node->hndl[i];
|
||||||
graph->n_hndl++;
|
gh->desc = d;
|
||||||
}
|
}
|
||||||
|
|
||||||
for (i = 0; i < desc->n_output; i++)
|
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);
|
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)
|
static void core_error(void *data, uint32_t id, int seq, int res, const char *message)
|
||||||
{
|
{
|
||||||
struct impl *impl = data;
|
struct impl *impl = data;
|
||||||
|
|
@ -1379,6 +1429,7 @@ static void impl_destroy(struct impl *impl)
|
||||||
if (impl->playback_props)
|
if (impl->playback_props)
|
||||||
pw_properties_free(impl->playback_props);
|
pw_properties_free(impl->playback_props);
|
||||||
pw_work_queue_cancel(impl->work, impl, SPA_ID_INVALID);
|
pw_work_queue_cancel(impl->work, impl, SPA_ID_INVALID);
|
||||||
|
graph_free(&impl->graph);
|
||||||
free(impl);
|
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