filter-chain: instantiate the nodes when rate is known

Instantiate the graph nodes when the samplerate is known instead of
using a fixed samplerate of 48KHz.

Warn when the convolver samplerate does not match the graph rate. We
might want to resample the IR later.
This commit is contained in:
Wim Taymans 2022-09-19 16:52:36 +02:00
parent 000a6608cf
commit 3faca67ffe
5 changed files with 136 additions and 126 deletions

View file

@ -577,6 +577,10 @@ struct impl {
struct graph graph; struct graph graph;
}; };
static int graph_instantiate(struct graph *graph);
static void graph_cleanup(struct graph *graph);
static void capture_destroy(void *d) static void capture_destroy(void *d)
{ {
struct impl *impl = d; struct impl *impl = d;
@ -919,13 +923,14 @@ static void graph_reset(struct graph *graph)
for (i = 0; i < graph->n_hndl; i++) { for (i = 0; i < graph->n_hndl; i++) {
struct graph_hndl *hndl = &graph->hndl[i]; struct graph_hndl *hndl = &graph->hndl[i];
const struct fc_descriptor *d = hndl->desc; const struct fc_descriptor *d = hndl->desc;
if (hndl->hndl == NULL || *hndl->hndl == NULL)
continue;
if (d->deactivate) if (d->deactivate)
d->deactivate(*hndl->hndl); d->deactivate(*hndl->hndl);
if (d->activate) if (d->activate)
d->activate(*hndl->hndl); d->activate(*hndl->hndl);
} }
} }
static void param_props_changed(struct impl *impl, const struct spa_pod *param) static void param_props_changed(struct impl *impl, const struct spa_pod *param)
{ {
struct spa_pod_object *obj = (struct spa_pod_object *) param; struct spa_pod_object *obj = (struct spa_pod_object *) param;
@ -1000,8 +1005,15 @@ static void param_changed(void *data, uint32_t id, const struct spa_pod *param)
switch (id) { switch (id) {
case SPA_PARAM_Format: case SPA_PARAM_Format:
if (param == NULL) if (param == NULL) {
graph_reset(graph); graph_cleanup(graph);
} else {
struct spa_audio_info_raw info;
spa_zero(info);
spa_format_audio_raw_parse(param, &info);
impl->rate = info.rate;
graph_instantiate(graph);
}
break; break;
case SPA_PARAM_Props: case SPA_PARAM_Props:
if (param != NULL) if (param != NULL)
@ -1489,6 +1501,7 @@ static int load_node(struct graph *graph, struct spa_json *json)
bool have_config = false; bool have_config = false;
uint32_t i; uint32_t i;
int res; int res;
float *data;
while (spa_json_get_string(json, key, sizeof(key)) > 0) { while (spa_json_get_string(json, key, sizeof(key)) > 0) {
if (spa_streq("type", key)) { if (spa_streq("type", key)) {
@ -1563,6 +1576,14 @@ static int load_node(struct graph *graph, struct spa_json *json)
port->idx = i; port->idx = i;
port->external = SPA_ID_INVALID; port->external = SPA_ID_INVALID;
port->p = desc->output[i]; port->p = desc->output[i];
if ((data = port->audio_data[i]) == NULL) {
data = calloc(1, MAX_SAMPLES * sizeof(float));
if (data == NULL) {
pw_log_error("cannot create port data: %m");
return -errno;
}
}
port->audio_data[i] = data;
spa_list_init(&port->link_list); spa_list_init(&port->link_list);
} }
for (i = 0; i < desc->n_control; i++) { for (i = 0; i < desc->n_control; i++) {
@ -1593,21 +1614,31 @@ static int load_node(struct graph *graph, struct spa_json *json)
return 0; return 0;
} }
static void node_free(struct node *node) static void node_cleanup(struct node *node)
{ {
uint32_t i, j;
const struct fc_descriptor *d = node->desc->desc; const struct fc_descriptor *d = node->desc->desc;
uint32_t i;
spa_list_remove(&node->link);
for (i = 0; i < node->n_hndl; i++) { 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 (node->hndl[i] == NULL) if (node->hndl[i] == NULL)
continue; continue;
if (d->deactivate) if (d->deactivate)
d->deactivate(node->hndl[i]); d->deactivate(node->hndl[i]);
d->cleanup(node->hndl[i]); d->cleanup(node->hndl[i]);
node->hndl[i] = NULL;
} }
}
static void node_free(struct node *node)
{
uint32_t i, j;
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]);
}
node_cleanup(node);
descriptor_unref(node->desc); descriptor_unref(node->desc);
free(node->input_port); free(node->input_port);
free(node->output_port); free(node->output_port);
@ -1616,6 +1647,78 @@ static void node_free(struct node *node)
free(node); free(node);
} }
static void graph_cleanup(struct graph *graph)
{
struct node *node;
spa_list_for_each(node, &graph->node_list, link)
node_cleanup(node);
}
static int graph_instantiate(struct graph *graph)
{
struct impl *impl = graph->impl;
struct node *node;
struct port *port;
struct link *link;
struct descriptor *desc;
const struct fc_descriptor *d;
uint32_t i, j;
int res;
spa_list_for_each(node, &graph->node_list, link) {
float *sd = silence_data, *dd = discard_data;
node_cleanup(node);
desc = node->desc;
d = desc->desc;
if (d->flags & FC_DESCRIPTOR_SUPPORTS_NULL_DATA)
sd = dd = NULL;
for (i = 0; i < node->n_hndl; i++) {
pw_log_info("instantiate %s %d rate:%lu", d->name, i, impl->rate);
if ((node->hndl[i] = d->instantiate(d, impl->rate, i, node->config)) == NULL) {
pw_log_error("cannot create plugin instance: %m");
res = -errno;
goto error;
}
for (j = 0; j < desc->n_input; j++) {
port = &node->input_port[j];
d->connect_port(node->hndl[i], port->p, sd);
spa_list_for_each(link, &port->link_list, input_link) {
struct port *peer = link->output;
pw_log_info("connect input port %s[%d]:%s %p",
node->name, i, d->ports[port->p].name,
peer->audio_data[i]);
d->connect_port(node->hndl[i], port->p, peer->audio_data[i]);
}
}
for (j = 0; j < desc->n_output; j++) {
port = &node->output_port[j];
pw_log_info("connect output port %s[%d]:%s %p",
node->name, i, d->ports[port->p].name,
port->audio_data[i]);
d->connect_port(node->hndl[i], port->p, port->audio_data[i]);
}
for (j = 0; j < desc->n_control; j++) {
port = &node->control_port[j];
d->connect_port(node->hndl[i], port->p, &port->control_data);
}
for (j = 0; j < desc->n_notify; j++) {
port = &node->notify_port[j];
d->connect_port(node->hndl[i], port->p, &port->control_data);
}
if (d->activate)
d->activate(node->hndl[i]);
}
}
return 0;
error:
graph_cleanup(graph);
return res;
}
static struct node *find_next_node(struct graph *graph) static struct node *find_next_node(struct graph *graph)
{ {
struct node *node; struct node *node;
@ -1628,61 +1731,16 @@ static struct node *find_next_node(struct graph *graph)
return NULL; return NULL;
} }
static int setup_input_port(struct graph *graph, struct port *port)
{
struct descriptor *desc = port->node->desc;
const struct fc_descriptor *d = desc->desc;
struct link *link;
uint32_t i, n_hndl = port->node->n_hndl;
spa_list_for_each(link, &port->link_list, input_link) {
struct port *peer = link->output;
for (i = 0; i < n_hndl; i++) {
pw_log_info("connect input port %s[%d]:%s %p",
port->node->name, i, d->ports[port->p].name,
peer->audio_data[i]);
d->connect_port(port->node->hndl[i], port->p, peer->audio_data[i]);
}
}
return 0;
}
static int setup_output_port(struct graph *graph, struct port *port)
{
struct descriptor *desc = port->node->desc;
const struct fc_descriptor *d = desc->desc;
struct link *link;
uint32_t i, n_hndl = port->node->n_hndl;
for (i = 0; i < n_hndl; i++) {
float *data;
if ((data = port->audio_data[i]) == NULL) {
data = calloc(1, MAX_SAMPLES * sizeof(float));
if (data == NULL)
return -errno;
}
port->audio_data[i] = data;
pw_log_info("connect output port %s[%d]:%s %p",
port->node->name, i, d->ports[port->p].name,
port->audio_data[i]);
d->connect_port(port->node->hndl[i], port->p, data);
}
spa_list_for_each(link, &port->link_list, output_link)
link->input->node->n_deps--;
return 0;
}
static int setup_graph(struct graph *graph, struct spa_json *inputs, struct spa_json *outputs) static int setup_graph(struct graph *graph, struct spa_json *inputs, struct spa_json *outputs)
{ {
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 link *link;
struct graph_port *gp; struct graph_port *gp;
struct graph_hndl *gh; struct graph_hndl *gh;
uint32_t i, j, n_nodes, n_input, n_output, n_control, n_hndl = 0; uint32_t i, j, n_nodes, n_input, n_output, n_control, n_hndl = 0;
int res; int res;
unsigned long p;
struct descriptor *desc; struct descriptor *desc;
const struct fc_descriptor *d; const struct fc_descriptor *d;
char v[256]; char v[256];
@ -1753,52 +1811,11 @@ static int setup_graph(struct graph *graph, struct spa_json *inputs, struct spa_
n_control = 0; n_control = 0;
n_nodes = 0; n_nodes = 0;
spa_list_for_each(node, &graph->node_list, link) { spa_list_for_each(node, &graph->node_list, link) {
float *sd = silence_data, *dd = discard_data; node->n_hndl = n_hndl;
desc = node->desc; desc = node->desc;
d = desc->desc;
if (d->flags & FC_DESCRIPTOR_SUPPORTS_NULL_DATA)
sd = dd = NULL;
for (i = 0; i < n_hndl; i++) {
pw_log_info("instantiate %s %d", d->name, i);
if ((node->hndl[i] = d->instantiate(d, &impl->rate, i, node->config)) == NULL) {
pw_log_error("cannot create plugin instance: %m");
res = -errno;
goto error;
}
node->n_hndl = i + 1;
for (j = 0; j < desc->n_input; j++) {
p = desc->input[j];
d->connect_port(node->hndl[i], p, sd);
}
for (j = 0; j < desc->n_output; j++) {
p = desc->output[j];
d->connect_port(node->hndl[i], p, dd);
}
for (j = 0; j < desc->n_control; j++) {
port = &node->control_port[j];
d->connect_port(node->hndl[i], port->p, &port->control_data);
}
for (j = 0; j < desc->n_notify; j++) {
port = &node->notify_port[j];
d->connect_port(node->hndl[i], port->p, &port->control_data);
}
if (d->activate)
d->activate(node->hndl[i]);
}
n_control += desc->n_control; n_control += desc->n_control;
n_nodes++; n_nodes++;
} }
pw_log_info("suggested rate:%lu capture:%d playback:%d", impl->rate,
impl->capture_info.rate, impl->playback_info.rate);
if (impl->capture_info.rate == 0)
impl->capture_info.rate = impl->rate;
if (impl->playback_info.rate == 0)
impl->playback_info.rate = impl->rate;
graph->n_input = 0; graph->n_input = 0;
graph->input = calloc(n_input * n_hndl, sizeof(struct graph_port)); graph->input = calloc(n_input * n_hndl, sizeof(struct graph_port));
graph->n_output = 0; graph->n_output = 0;
@ -1916,17 +1933,14 @@ static int setup_graph(struct graph *graph, struct spa_json *inputs, struct spa_
desc = node->desc; desc = node->desc;
d = desc->desc; d = desc->desc;
for (i = 0; i < desc->n_input; i++)
setup_input_port(graph, &node->input_port[i]);
for (i = 0; i < n_hndl; i++) { for (i = 0; i < n_hndl; i++) {
gh = &graph->hndl[graph->n_hndl++]; gh = &graph->hndl[graph->n_hndl++];
gh->hndl = &node->hndl[i]; gh->hndl = &node->hndl[i];
gh->desc = d; gh->desc = d;
}
for (i = 0; i < desc->n_output; i++) spa_list_for_each(link, &node->output_port[i].link_list, output_link)
setup_output_port(graph, &node->output_port[i]); link->input->node->n_deps--;
}
/* collect all control ports on the graph */ /* collect all control ports on the graph */
for (i = 0; i < desc->n_control; i++) { for (i = 0; i < desc->n_control; i++) {
@ -1934,17 +1948,8 @@ static int setup_graph(struct graph *graph, struct spa_json *inputs, struct spa_
graph->n_control++; graph->n_control++;
} }
} }
return 0; res = 0;
error: error:
spa_list_for_each(node, &graph->node_list, link) {
for (i = 0; i < node->n_hndl; i++) {
if (node->hndl[i] != NULL)
node->desc->desc->cleanup(node->hndl[i]);
node->hndl[i] = NULL;
}
node->n_hndl = 0;
}
return res; return res;
} }
@ -2190,7 +2195,6 @@ int pipewire__module_init(struct pw_impl_module *module, const char *args)
impl->module = module; impl->module = module;
impl->context = context; impl->context = context;
impl->rate = 48000;
impl->graph.impl = impl; impl->graph.impl = impl;
spa_list_init(&impl->plugin_list); spa_list_init(&impl->plugin_list);

View file

@ -52,7 +52,7 @@ struct builtin {
}; };
static void *builtin_instantiate(const struct fc_descriptor * Descriptor, static void *builtin_instantiate(const struct fc_descriptor * Descriptor,
unsigned long *SampleRate, int index, const char *config) unsigned long SampleRate, int index, const char *config)
{ {
struct builtin *impl; struct builtin *impl;
@ -60,7 +60,7 @@ static void *builtin_instantiate(const struct fc_descriptor * Descriptor,
if (impl == NULL) if (impl == NULL)
return NULL; return NULL;
impl->rate = *SampleRate; impl->rate = SampleRate;
return impl; return impl;
} }
@ -576,7 +576,7 @@ static float *create_dirac(const char *filename, float gain, int delay, int offs
} }
static void * convolver_instantiate(const struct fc_descriptor * Descriptor, static void * convolver_instantiate(const struct fc_descriptor * Descriptor,
unsigned long *SampleRate, int index, const char *config) unsigned long SampleRate, int index, const char *config)
{ {
struct convolver_impl *impl; struct convolver_impl *impl;
float *samples; float *samples;
@ -588,6 +588,7 @@ static void * convolver_instantiate(const struct fc_descriptor * Descriptor,
int blocksize = 0, tailsize = 0; int blocksize = 0, tailsize = 0;
int delay = 0; int delay = 0;
float gain = 1.0f; float gain = 1.0f;
unsigned long rate;
if (config == NULL) if (config == NULL)
return NULL; return NULL;
@ -647,8 +648,13 @@ static void * convolver_instantiate(const struct fc_descriptor * Descriptor,
samples = create_dirac(filename, gain, delay, offset, samples = create_dirac(filename, gain, delay, offset,
length, &n_samples); length, &n_samples);
} else { } else {
rate = SampleRate;
samples = read_samples(filename, gain, delay, offset, samples = read_samples(filename, gain, delay, offset,
length, channel, SampleRate, &n_samples); length, channel, &rate, &n_samples);
if (rate != SampleRate) {
pw_log_warn("Convolver samplerate %lu doesn't match filter rate %lu. "
"Consider forcing a filter rate.", rate, SampleRate);
}
} }
if (samples == NULL) if (samples == NULL)
return NULL; return NULL;
@ -664,7 +670,7 @@ static void * convolver_instantiate(const struct fc_descriptor * Descriptor,
if (impl == NULL) if (impl == NULL)
goto error; goto error;
impl->rate = *SampleRate; impl->rate = SampleRate;
impl->conv = convolver_new(blocksize, tailsize, samples, n_samples); impl->conv = convolver_new(blocksize, tailsize, samples, n_samples);
if (impl->conv == NULL) if (impl->conv == NULL)
@ -750,7 +756,7 @@ static void delay_cleanup(void * Instance)
} }
static void *delay_instantiate(const struct fc_descriptor * Descriptor, static void *delay_instantiate(const struct fc_descriptor * Descriptor,
unsigned long *SampleRate, int index, const char *config) unsigned long SampleRate, int index, const char *config)
{ {
struct delay_impl *impl; struct delay_impl *impl;
struct spa_json it[2]; struct spa_json it[2];
@ -782,7 +788,7 @@ static void *delay_instantiate(const struct fc_descriptor * Descriptor,
if (impl == NULL) if (impl == NULL)
return NULL; return NULL;
impl->rate = *SampleRate; impl->rate = SampleRate;
impl->buffer_samples = max_delay * impl->rate; impl->buffer_samples = max_delay * impl->rate;
pw_log_info("%lu %d", impl->rate, impl->buffer_samples); pw_log_info("%lu %d", impl->rate, impl->buffer_samples);

View file

@ -47,10 +47,10 @@ struct descriptor {
}; };
static void *ladspa_instantiate(const struct fc_descriptor *desc, static void *ladspa_instantiate(const struct fc_descriptor *desc,
unsigned long *SampleRate, int index, const char *config) unsigned long SampleRate, int index, const char *config)
{ {
struct descriptor *d = (struct descriptor *)desc; struct descriptor *d = (struct descriptor *)desc;
return d->d->instantiate(d->d, *SampleRate); return d->d->instantiate(d->d, SampleRate);
} }
static const LADSPA_Descriptor *find_desc(LADSPA_Descriptor_Function desc_func, const char *name) static const LADSPA_Descriptor *find_desc(LADSPA_Descriptor_Function desc_func, const char *name)

View file

@ -298,7 +298,7 @@ work_schedule(LV2_Worker_Schedule_Handle handle, uint32_t size, const void *data
} }
static void *lv2_instantiate(const struct fc_descriptor *desc, static void *lv2_instantiate(const struct fc_descriptor *desc,
unsigned long *SampleRate, int index, const char *config) unsigned long SampleRate, int index, const char *config)
{ {
struct descriptor *d = (struct descriptor*)desc; struct descriptor *d = (struct descriptor*)desc;
struct plugin *p = d->p; struct plugin *p = d->p;
@ -308,7 +308,7 @@ static void *lv2_instantiate(const struct fc_descriptor *desc,
static const int32_t min_block_length = 1; static const int32_t min_block_length = 1;
static const int32_t max_block_length = 8192; static const int32_t max_block_length = 8192;
static const int32_t seq_size = 32768; static const int32_t seq_size = 32768;
float fsample_rate = *SampleRate; float fsample_rate = SampleRate;
i = calloc(1, sizeof(*i)); i = calloc(1, sizeof(*i));
if (i == NULL) if (i == NULL)
@ -350,7 +350,7 @@ static void *lv2_instantiate(const struct fc_descriptor *desc,
i->options_feature.data = i->options; i->options_feature.data = i->options;
i->features[n_features++] = &i->options_feature; i->features[n_features++] = &i->options_feature;
i->instance = lilv_plugin_instantiate(p->p, *SampleRate, i->features); i->instance = lilv_plugin_instantiate(p->p, SampleRate, i->features);
if (i->instance == NULL) { if (i->instance == NULL) {
free(i); free(i);
return NULL; return NULL;

View file

@ -72,7 +72,7 @@ struct fc_descriptor {
struct fc_port *ports; struct fc_port *ports;
void *(*instantiate) (const struct fc_descriptor *desc, void *(*instantiate) (const struct fc_descriptor *desc,
unsigned long *SampleRate, int index, const char *config); unsigned long SampleRate, int index, const char *config);
void (*cleanup) (void *instance); void (*cleanup) (void *instance);