From 8416d66ab3e1ab62fa6f39002e56a158da21cdeb Mon Sep 17 00:00:00 2001 From: Wim Taymans Date: Mon, 13 Dec 2021 11:28:25 +0100 Subject: [PATCH] filter-chain: improve lv2 support Add support for Worker. Add Options feature. Pass support in plugin load. --- src/modules/module-filter-chain.c | 18 +- .../module-filter-chain/builtin_plugin.c | 13 +- .../module-filter-chain/ladspa_plugin.c | 3 +- src/modules/module-filter-chain/lv2_plugin.c | 183 +++++++++++++++--- src/modules/module-filter-chain/plugin.h | 12 +- 5 files changed, 183 insertions(+), 46 deletions(-) diff --git a/src/modules/module-filter-chain.c b/src/modules/module-filter-chain.c index c1cc4004e..400449324 100644 --- a/src/modules/module-filter-chain.c +++ b/src/modules/module-filter-chain.c @@ -39,7 +39,6 @@ #include #include #include -#include #include #include @@ -739,6 +738,8 @@ static struct plugin *plugin_load(struct impl *impl, const char *type, const cha struct fc_plugin *pl = NULL; struct plugin *hndl; int res = 0; + const struct spa_support *support; + uint32_t n_support; spa_list_for_each(hndl, &impl->plugin_list, link) { if (spa_streq(hndl->type, type) && @@ -747,16 +748,17 @@ static struct plugin *plugin_load(struct impl *impl, const char *type, const cha return hndl; } } + support = pw_context_get_support(impl->context, &n_support); if (spa_streq(type, "builtin")) { - pl = load_builtin_plugin(path, NULL); + pl = load_builtin_plugin(support, n_support, path, NULL); } else if (spa_streq(type, "ladspa")) { - pl = load_ladspa_plugin(path, NULL); + pl = load_ladspa_plugin(support, n_support, path, NULL); } #ifdef HAVE_LILV else if (spa_streq(type, "lv2")) { - pl = load_lv2_plugin(path, NULL); + pl = load_lv2_plugin(support, n_support, path, NULL); } #endif if (pl == NULL) @@ -1280,6 +1282,7 @@ static int setup_graph(struct graph *graph, struct spa_json *inputs, struct spa_ 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"); res = -ENOMEM; @@ -1650,9 +1653,6 @@ int pipewire__module_init(struct pw_impl_module *module, const char *args) struct impl *impl; uint32_t id = pw_global_get_id(pw_impl_module_get_global(module)); const char *str; - const struct spa_support *support; - uint32_t n_support; - struct spa_cpu *cpu_iface; int res; PW_LOG_TOPIC_INIT(mod_topic); @@ -1663,10 +1663,6 @@ int pipewire__module_init(struct pw_impl_module *module, const char *args) pw_log_debug("module %p: new %s", impl, args); - support = pw_context_get_support(context, &n_support); - cpu_iface = spa_support_find(support, n_support, SPA_TYPE_INTERFACE_CPU); - init_builtin_plugin(cpu_iface ? spa_cpu_get_flags(cpu_iface) : 0); - if (args) props = pw_properties_new_string(args); else diff --git a/src/modules/module-filter-chain/builtin_plugin.c b/src/modules/module-filter-chain/builtin_plugin.c index 3eeb38b1d..9b68a7fcb 100644 --- a/src/modules/module-filter-chain/builtin_plugin.c +++ b/src/modules/module-filter-chain/builtin_plugin.c @@ -30,6 +30,8 @@ #endif #include +#include + #include #include "plugin.h" @@ -771,12 +773,11 @@ static struct fc_plugin builtin_plugin = { .make_desc = builtin_make_desc }; -struct fc_plugin *load_builtin_plugin(const char *plugin, const char *config) +struct fc_plugin *load_builtin_plugin(const struct spa_support *support, uint32_t n_support, + const char *plugin, const char *config) { + struct spa_cpu *cpu_iface; + cpu_iface = spa_support_find(support, n_support, SPA_TYPE_INTERFACE_CPU); + pffft_select_cpu(cpu_iface ? spa_cpu_get_flags(cpu_iface) : 0); return &builtin_plugin; } - -void init_builtin_plugin(uint32_t cpu_flags) -{ - pffft_select_cpu(cpu_flags); -} diff --git a/src/modules/module-filter-chain/ladspa_plugin.c b/src/modules/module-filter-chain/ladspa_plugin.c index 7e2cde416..eb56de305 100644 --- a/src/modules/module-filter-chain/ladspa_plugin.c +++ b/src/modules/module-filter-chain/ladspa_plugin.c @@ -226,7 +226,8 @@ exit: return NULL; } -struct fc_plugin *load_ladspa_plugin(const char *plugin, const char *config) +struct fc_plugin *load_ladspa_plugin(const struct spa_support *support, uint32_t n_support, + const char *plugin, const char *config) { struct fc_plugin *pl = NULL; diff --git a/src/modules/module-filter-chain/lv2_plugin.c b/src/modules/module-filter-chain/lv2_plugin.c index 1a9fbd791..b999e15e9 100644 --- a/src/modules/module-filter-chain/lv2_plugin.c +++ b/src/modules/module-filter-chain/lv2_plugin.c @@ -28,6 +28,7 @@ #include #include #include +#include #include #include @@ -36,6 +37,9 @@ #include #include #include +#include "lv2/lv2plug.in/ns/ext/worker/worker.h" +#include "lv2/lv2plug.in/ns/ext/options/options.h" +#include "lv2/lv2plug.in/ns/ext/parameters/parameters.h" #include "plugin.h" @@ -61,17 +65,18 @@ static void uri_table_destroy(URITable *table) static LV2_URID uri_table_map(LV2_URID_Map_Handle handle, const char *uri) { URITable *table = (URITable*)handle; - char *p; + char **p; size_t i = 0; pw_array_for_each(p, &table->array) { - if (spa_streq(p, uri)) - return i + 1; i++; + if (spa_streq(*p, uri)) + goto done; } - pw_array_add_ptr(&table->array, strdup(uri)); - return pw_array_get_len(&table->array, char*); + i = pw_array_get_len(&table->array, char*); +done: + return i; } static const char *uri_table_unmap(LV2_URID_Map_Handle handle, LV2_URID urid) @@ -87,6 +92,9 @@ struct context { int ref; LilvWorld *world; + struct spa_loop *data_loop; + struct spa_loop *main_loop; + LilvNode *lv2_InputPort; LilvNode *lv2_OutputPort; LilvNode *lv2_AudioPort; @@ -98,18 +106,25 @@ struct context { LilvNode *powerOf2BlockLength; LilvNode *fixedBlockLength; LilvNode *boundedBlockLength; + LilvNode* worker_schedule; + LilvNode* worker_iface; URITable uri_table; LV2_URID_Map map; - LV2_URID_Unmap unmap; LV2_Feature map_feature; + LV2_URID_Unmap unmap; LV2_Feature unmap_feature; - const LV2_Feature *features[5]; + + LV2_URID atom_Int; + LV2_URID atom_Float; }; +#define context_map(c,uri) ((c)->map.map((c)->map.handle,(uri))) + static void context_free(struct context *c) { if (c->world) { + lilv_node_free(c->worker_schedule); lilv_node_free(c->powerOf2BlockLength); lilv_node_free(c->fixedBlockLength); lilv_node_free(c->boundedBlockLength); @@ -133,7 +148,7 @@ static const LV2_Feature buf_size_features[3] = { { LV2_BUF_SIZE__boundedBlockLength, NULL }, }; -static struct context *context_new(void) +static struct context *context_new(const struct spa_support *support, uint32_t n_support) { struct context *c; @@ -159,6 +174,8 @@ static struct context *context_new(void) c->powerOf2BlockLength = lilv_new_uri(c->world, LV2_BUF_SIZE__powerOf2BlockLength); c->fixedBlockLength = lilv_new_uri(c->world, LV2_BUF_SIZE__fixedBlockLength); c->boundedBlockLength = lilv_new_uri(c->world, LV2_BUF_SIZE__boundedBlockLength); + c->worker_schedule = lilv_new_uri(c->world, LV2_WORKER__schedule); + c->worker_iface = lilv_new_uri(c->world, LV2_WORKER__interface); c->map.handle = &c->uri_table; c->map.map = uri_table_map; @@ -168,11 +185,12 @@ static struct context *context_new(void) c->unmap.unmap = uri_table_unmap; c->unmap_feature.URI = LV2_URID_UNMAP_URI; c->unmap_feature.data = &c->unmap; - c->features[0] = &c->map_feature; - c->features[1] = &c->unmap_feature; - c->features[2] = &buf_size_features[0]; - c->features[3] = &buf_size_features[1]; - c->features[4] = &buf_size_features[2]; + + c->atom_Int = context_map(c, LV2_ATOM__Int); + c->atom_Float = context_map(c, LV2_ATOM__Float); + + c->data_loop = spa_support_find(support, n_support, SPA_TYPE_INTERFACE_DataLoop); + c->main_loop = spa_support_find(support, n_support, SPA_TYPE_INTERFACE_Loop); return c; error: @@ -180,10 +198,10 @@ error: return NULL; } -static struct context *context_ref(void) +static struct context *context_ref(const struct spa_support *support, uint32_t n_support) { if (_context == NULL) { - _context = context_new(); + _context = context_new(support, n_support); if (_context == NULL) return NULL; } @@ -210,38 +228,156 @@ struct descriptor { struct plugin *p; }; +struct instance { + struct descriptor *desc; + LilvInstance *instance; + LV2_Worker_Schedule work_schedule; + LV2_Feature work_schedule_feature; + LV2_Options_Option options[6]; + LV2_Feature options_feature; + + const LV2_Feature *features[7]; + + const LV2_Worker_Interface *work_iface; + + int32_t block_length; +}; + +static int +do_respond(struct spa_loop *loop, bool async, uint32_t seq, const void *data, + size_t size, void *user_data) +{ + struct instance *i = (struct instance*)user_data; + i->work_iface->work_response(i->instance->lv2_handle, size, data); + return 0; +} + +/** Called by the plugin to respond to non-RT work. */ +static LV2_Worker_Status +work_respond(LV2_Worker_Respond_Handle handle, uint32_t size, const void *data) +{ + struct instance *i = (struct instance*)handle; + struct context *c = i->desc->p->c; + spa_loop_invoke(c->data_loop, do_respond, 1, data, size, false, i); + return LV2_WORKER_SUCCESS; +} + +static int +do_schedule(struct spa_loop *loop, bool async, uint32_t seq, const void *data, + size_t size, void *user_data) +{ + struct instance *i = (struct instance*)user_data; + i->work_iface->work(i->instance->lv2_handle, work_respond, i, size, data); + return 0; +} + +/** Called by the plugin to schedule non-RT work. */ +static LV2_Worker_Status +work_schedule(LV2_Worker_Schedule_Handle handle, uint32_t size, const void *data) +{ + struct instance *i = (struct instance*)handle; + struct context *c = i->desc->p->c; + spa_loop_invoke(c->main_loop, do_schedule, 1, data, size, false, i); + return LV2_WORKER_SUCCESS; +} + static void *lv2_instantiate(const struct fc_descriptor *desc, unsigned long *SampleRate, int index, const char *config) { struct descriptor *d = (struct descriptor*)desc; struct plugin *p = d->p; struct context *c = p->c; - return lilv_plugin_instantiate(p->p, *SampleRate, c->features); + struct instance *i; + uint32_t n_features = 0; + static const int32_t min_block_length = 1; + static const int32_t max_block_length = 8192; + static const int32_t seq_size = 32768; + float fsample_rate = *SampleRate; + + i = calloc(1, sizeof(*i)); + if (i == NULL) + return NULL; + + i->block_length = 1024; + i->desc = d; + i->features[n_features++] = &c->map_feature; + i->features[n_features++] = &c->unmap_feature; + i->features[n_features++] = &buf_size_features[0]; + i->features[n_features++] = &buf_size_features[1]; + i->features[n_features++] = &buf_size_features[2]; + if (lilv_plugin_has_feature(p->p, c->worker_schedule)) { + i->work_schedule.handle = i; + i->work_schedule.schedule_work = work_schedule; + i->work_schedule_feature.URI = LV2_WORKER__schedule; + i->work_schedule_feature.data = &i->work_schedule; + i->features[n_features++] = &i->work_schedule_feature; + } + + i->options[0] = (LV2_Options_Option) { LV2_OPTIONS_INSTANCE, 0, + context_map(c, LV2_BUF_SIZE__minBlockLength), sizeof(int32_t), + c->atom_Int, &min_block_length }; + i->options[1] = (LV2_Options_Option) { LV2_OPTIONS_INSTANCE, 0, + context_map(c, LV2_BUF_SIZE__maxBlockLength), sizeof(int32_t), + c->atom_Int, &max_block_length }; + i->options[2] = (LV2_Options_Option) { LV2_OPTIONS_INSTANCE, 0, + context_map(c, LV2_BUF_SIZE__sequenceSize), sizeof(int32_t), + c->atom_Int, &seq_size }; + i->options[3] = (LV2_Options_Option) { LV2_OPTIONS_INSTANCE, 0, + context_map(c, "http://lv2plug.in/ns/ext/buf-size#nominalBlockLength"), sizeof(int32_t), + c->atom_Int, &i->block_length }, + i->options[4] = (LV2_Options_Option) { LV2_OPTIONS_INSTANCE, 0, + context_map(c, LV2_PARAMETERS__sampleRate), sizeof(float), + c->atom_Float, &fsample_rate }; + i->options[5] = (LV2_Options_Option) { LV2_OPTIONS_INSTANCE, 0, 0, 0, 0, NULL }; + + i->options_feature.URI = LV2_OPTIONS__options; + i->options_feature.data = i->options; + i->features[n_features++] = &i->options_feature; + + i->instance = lilv_plugin_instantiate(p->p, *SampleRate, i->features); + if (i->instance == NULL) { + free(i); + return NULL; + } + if (lilv_plugin_has_extension_data(p->p, c->worker_iface)) { + i->work_iface = (const LV2_Worker_Interface*) + lilv_instance_get_extension_data(i->instance, LV2_WORKER__interface); + } + + return i; } static void lv2_cleanup(void *instance) { - lilv_instance_free(instance); + struct instance *i = instance; + lilv_instance_free(i->instance); + free(i); } static void lv2_connect_port(void *instance, unsigned long port, float *data) { - lilv_instance_connect_port(instance, port, data); + struct instance *i = instance; + lilv_instance_connect_port(i->instance, port, data); } static void lv2_activate(void *instance) { - lilv_instance_activate(instance); + struct instance *i = instance; + lilv_instance_activate(i->instance); } static void lv2_deactivate(void *instance) { - lilv_instance_deactivate(instance); + struct instance *i = instance; + lilv_instance_deactivate(i->instance); } static void lv2_run(void *instance, unsigned long SampleCount) { - lilv_instance_run(instance, SampleCount); + struct instance *i = instance; + lilv_instance_run(i->instance, SampleCount); + if (i->work_iface != NULL && i->work_iface->end_run != NULL) + i->work_iface->end_run(i->instance); } static void lv2_free(struct fc_descriptor *desc) @@ -319,7 +455,8 @@ static void lv2_unload(struct fc_plugin *plugin) free(p); } -struct fc_plugin *load_lv2_plugin(const char *plugin_uri, const char *config) +struct fc_plugin *load_lv2_plugin(const struct spa_support *support, uint32_t n_support, + const char *plugin_uri, const char *config) { struct context *c; const LilvPlugins *plugins; @@ -328,7 +465,7 @@ struct fc_plugin *load_lv2_plugin(const char *plugin_uri, const char *config) int res; struct plugin *p; - c = context_ref(); + c = context_ref(support, n_support); if (c == NULL) return NULL; diff --git a/src/modules/module-filter-chain/plugin.h b/src/modules/module-filter-chain/plugin.h index b51684abe..583bb78fe 100644 --- a/src/modules/module-filter-chain/plugin.h +++ b/src/modules/module-filter-chain/plugin.h @@ -28,6 +28,7 @@ #include #include +#include #include #include #include @@ -93,8 +94,9 @@ static inline void fc_descriptor_free(struct fc_descriptor *desc) desc->free(desc); } -struct fc_plugin *load_ladspa_plugin(const char *path, const char *config); -struct fc_plugin *load_lv2_plugin(const char *path, const char *config); -struct fc_plugin *load_builtin_plugin(const char *path, const char *config); - -void init_builtin_plugin(uint32_t cpu_flags); +struct fc_plugin *load_ladspa_plugin(const struct spa_support *support, uint32_t n_support, + const char *path, const char *config); +struct fc_plugin *load_lv2_plugin(const struct spa_support *support, uint32_t n_support, + const char *path, const char *config); +struct fc_plugin *load_builtin_plugin(const struct spa_support *support, uint32_t n_support, + const char *path, const char *config);