diff --git a/Makefile.in b/Makefile.in index 104619316..a5cc649b6 100644 --- a/Makefile.in +++ b/Makefile.in @@ -16,7 +16,7 @@ clean: ninja -C $(BUILD_ROOT) clean run: all - SPA_PLUGIN_DIR=$(BUILD_ROOT)/spa/plugins \ + SPA_PLUGIN_DIR=$(BUILD_ROOT)/spa/plugins:$(BUILD_ROOT)/src/modules \ SPA_DATA_DIR=$(SOURCE_ROOT)/spa/plugins \ PIPEWIRE_MODULE_DIR=$(BUILD_ROOT)/src/modules \ PATH=$(BUILD_ROOT)/src/examples:$(PATH) \ diff --git a/pw-uninstalled.sh b/pw-uninstalled.sh index 1bb6c55c2..4167c4f4a 100755 --- a/pw-uninstalled.sh +++ b/pw-uninstalled.sh @@ -37,7 +37,7 @@ fi # the config file read by the daemon export PIPEWIRE_CONFIG_DIR="${BUILDDIR}/src/daemon" # the directory with SPA plugins -export SPA_PLUGIN_DIR="${BUILDDIR}/spa/plugins" +export SPA_PLUGIN_DIR="${BUILDDIR}/spa/plugins:${BUILDDIR}/src/modules" export SPA_DATA_DIR="${SCRIPT_DIR}/spa/plugins" # the directory with pipewire modules export PIPEWIRE_MODULE_DIR="${BUILDDIR}/src/modules" diff --git a/src/modules/meson.build b/src/modules/meson.build index fe3511310..219fa6170 100644 --- a/src/modules/meson.build +++ b/src/modules/meson.build @@ -126,9 +126,6 @@ simd_dependencies += filter_graph_c filter_graph = static_library('filter_graph', ['module-filter-chain/biquad.c', - 'module-filter-chain/ladspa_plugin.c', - 'module-filter-chain/builtin_plugin.c', - 'module-filter-chain/convolver.c', 'module-filter-chain/filter-graph.c' ], include_directories : [configinc], dependencies : [ spa_dep, sndfile_dep, plugin_dependencies ], @@ -150,25 +147,41 @@ pipewire_module_filter_chain = shared_library('pipewire-module-filter-chain', dependencies : [spa_dep, mathlib, dl_lib, pipewire_dep], ) -if libmysofa_dep.found() -pipewire_module_filter_graph_sofa = shared_library('pipewire-filter-graph-plugin-sofa', - [ 'module-filter-chain/sofa_plugin.c' ], +pipewire_module_filter_graph_builtin = shared_library('spa-filter-graph-plugin-builtin', + [ 'module-filter-chain/builtin_plugin.c', + 'module-filter-chain/convolver.c' ], include_directories : [configinc], install : true, - install_dir : modules_install_dir, - install_rpath: modules_install_dir, - link_with : simd_dependencies, + install_dir : spa_plugindir / 'filter-graph', + dependencies : [ filter_graph_dependencies ] +) + +pipewire_module_filter_graph_ladspa = shared_library('spa-filter-graph-plugin-ladspa', + [ 'module-filter-chain/ladspa_plugin.c' ], + include_directories : [configinc], + install : true, + install : true, + install_dir : spa_plugindir / 'filter-graph', + dependencies : [ filter_graph_dependencies ] +) + +if libmysofa_dep.found() +pipewire_module_filter_graph_sofa = shared_library('spa-filter-graph-plugin-sofa', + [ 'module-filter-chain/sofa_plugin.c', + 'module-filter-chain/convolver.c' ], + include_directories : [configinc], + install : true, + install_dir : spa_plugindir / 'filter-graph', dependencies : [ filter_graph_dependencies, libmysofa_dep ] ) endif if lilv_lib.found() -pipewire_module_filter_graph_lv2 = shared_library('pipewire-filter-graph-plugin-lv2', +pipewire_module_filter_graph_lv2 = shared_library('spa-filter-graph-plugin-lv2', [ 'module-filter-chain/lv2_plugin.c' ], include_directories : [configinc], install : true, - install_dir : modules_install_dir, - install_rpath: modules_install_dir, + install_dir : spa_plugindir / 'filter-graph', dependencies : [ filter_graph_dependencies, lilv_lib ] ) endif diff --git a/src/modules/module-filter-chain.c b/src/modules/module-filter-chain.c index 5a536a5e7..5add83ef0 100644 --- a/src/modules/module-filter-chain.c +++ b/src/modules/module-filter-chain.c @@ -1167,6 +1167,10 @@ static void impl_destroy(struct impl *impl) if (impl->core && impl->do_disconnect) pw_core_disconnect(impl->core); + if (impl->handle) + spa_handle_clear(impl->handle); + free(impl->handle); + pw_properties_free(impl->capture_props); pw_properties_free(impl->playback_props); diff --git a/src/modules/module-filter-chain/audio-dsp.c b/src/modules/module-filter-chain/audio-dsp.c index 1ba0b981c..fadd85349 100644 --- a/src/modules/module-filter-chain/audio-dsp.c +++ b/src/modules/module-filter-chain/audio-dsp.c @@ -11,6 +11,8 @@ #include #include +#include "pffft.h" + #include "audio-dsp-impl.h" struct dsp_info { @@ -111,6 +113,7 @@ struct spa_fga_dsp * spa_fga_dsp_new(uint32_t cpu_flags) if (dsp == NULL) return NULL; + pffft_select_cpu(cpu_flags); dsp->cpu_flags = cpu_flags; dsp->iface = SPA_INTERFACE_INIT( SPA_TYPE_INTERFACE_FILTER_GRAPH_AudioDSP, diff --git a/src/modules/module-filter-chain/audio-plugin.h b/src/modules/module-filter-chain/audio-plugin.h index 124873487..1e2e31051 100644 --- a/src/modules/module-filter-chain/audio-plugin.h +++ b/src/modules/module-filter-chain/audio-plugin.h @@ -22,7 +22,6 @@ struct spa_fga_plugin_methods { uint32_t version; const struct spa_fga_descriptor *(*make_desc) (void *plugin, const char *name); - void (*free) (void *plugin); }; struct spa_fga_port { @@ -99,7 +98,6 @@ static inline void spa_fga_descriptor_free(const struct spa_fga_descriptor *desc #define spa_fga_plugin_make_desc(o,...) spa_fga_plugin_method_r(o,make_desc,0,__VA_ARGS__) -#define spa_fga_plugin_free(o,...) spa_fga_plugin_method(o,free,0,##__VA_ARGS__) typedef struct spa_fga_plugin *(spa_filter_graph_audio_plugin_load_func_t)(const struct spa_support *support, uint32_t n_support, const char *path, const struct spa_dict *info); diff --git a/src/modules/module-filter-chain/builtin_plugin.c b/src/modules/module-filter-chain/builtin_plugin.c index fd152ca9f..1ba7741c9 100644 --- a/src/modules/module-filter-chain/builtin_plugin.c +++ b/src/modules/module-filter-chain/builtin_plugin.c @@ -21,14 +21,15 @@ #include "audio-plugin.h" #include "biquad.h" -#include "pffft.h" #include "convolver.h" #include "audio-dsp.h" #define MAX_RATES 32u struct plugin { + struct spa_handle handle; struct spa_fga_plugin plugin; + struct spa_fga_dsp *dsp; struct spa_log *log; }; @@ -55,13 +56,14 @@ struct builtin { static void *builtin_instantiate(const struct spa_fga_plugin *plugin, const struct spa_fga_descriptor * Descriptor, unsigned long SampleRate, int index, const char *config) { + struct plugin *pl = SPA_CONTAINER_OF(plugin, struct plugin, plugin); struct builtin *impl; impl = calloc(1, sizeof(*impl)); if (impl == NULL) return NULL; - impl->plugin = (struct plugin *) plugin; + impl->plugin = pl; impl->rate = SampleRate; impl->dsp = impl->plugin->dsp; impl->log = impl->plugin->log; @@ -314,6 +316,7 @@ static void bq_raw_update(struct builtin *impl, float b0, float b1, float b2, static void *bq_instantiate(const struct spa_fga_plugin *plugin, const struct spa_fga_descriptor * Descriptor, unsigned long SampleRate, int index, const char *config) { + struct plugin *pl = SPA_CONTAINER_OF(plugin, struct plugin, plugin); struct builtin *impl; struct spa_json it[3]; const char *val; @@ -325,7 +328,7 @@ static void *bq_instantiate(const struct spa_fga_plugin *plugin, const struct sp if (impl == NULL) return NULL; - impl->plugin = (struct plugin *) plugin; + impl->plugin = pl; impl->log = impl->plugin->log; impl->dsp = impl->plugin->dsp; impl->rate = SampleRate; @@ -905,8 +908,8 @@ error: static void * convolver_instantiate(const struct spa_fga_plugin *plugin, const struct spa_fga_descriptor * Descriptor, unsigned long SampleRate, int index, const char *config) { + struct plugin *pl = SPA_CONTAINER_OF(plugin, struct plugin, plugin); struct convolver_impl *impl; - struct plugin *pl = (struct plugin*)plugin; float *samples; int offset = 0, length = 0, channel = index, n_samples = 0, len; uint32_t i = 0; @@ -1147,7 +1150,7 @@ static void delay_cleanup(void * Instance) static void *delay_instantiate(const struct spa_fga_plugin *plugin, const struct spa_fga_descriptor * Descriptor, unsigned long SampleRate, int index, const char *config) { - struct plugin *pl = (struct plugin*) plugin; + struct plugin *pl = SPA_CONTAINER_OF(plugin, struct plugin, plugin); struct delay_impl *impl; struct spa_json it[1]; const char *val; @@ -1901,7 +1904,7 @@ static int parse_filters(struct plugin *pl, struct spa_json *iter, int rate, static void *param_eq_instantiate(const struct spa_fga_plugin *plugin, const struct spa_fga_descriptor * Descriptor, unsigned long SampleRate, int index, const char *config) { - struct plugin *pl = (struct plugin *) plugin; + struct plugin *pl = SPA_CONTAINER_OF(plugin, struct plugin, plugin); struct spa_json it[3]; const char *val; char key[256], filename[PATH_MAX]; @@ -2142,30 +2145,121 @@ static const struct spa_fga_descriptor *builtin_plugin_make_desc(void *plugin, c return NULL; } -static void builtin_plugin_free(void *p) -{ - free(p); -} - static struct spa_fga_plugin_methods impl_plugin = { SPA_VERSION_FGA_PLUGIN_METHODS, .make_desc = builtin_plugin_make_desc, - .free = builtin_plugin_free }; -struct spa_fga_plugin *load_builtin_plugin(const struct spa_support *support, uint32_t n_support, - const char *plugin, const struct spa_dict *info) +static int impl_get_interface(struct spa_handle *handle, const char *type, void **interface) { - struct plugin *impl = calloc (1, sizeof (struct plugin)); + struct plugin *impl; + + spa_return_val_if_fail(handle != NULL, -EINVAL); + spa_return_val_if_fail(interface != NULL, -EINVAL); + + impl = (struct plugin *) handle; + + if (spa_streq(type, SPA_TYPE_INTERFACE_FILTER_GRAPH_AudioPlugin)) + *interface = &impl->plugin; + else + return -ENOENT; + + return 0; +} + +static int impl_clear(struct spa_handle *handle) +{ + return 0; +} + +static size_t +impl_get_size(const struct spa_handle_factory *factory, + const struct spa_dict *params) +{ + return sizeof(struct plugin); +} + +static int +impl_init(const struct spa_handle_factory *factory, + struct spa_handle *handle, + const struct spa_dict *info, + const struct spa_support *support, + uint32_t n_support) +{ + struct plugin *impl; + + handle->get_interface = impl_get_interface; + handle->clear = impl_clear; + + impl = (struct plugin *) handle; impl->plugin.iface = SPA_INTERFACE_INIT( SPA_TYPE_INTERFACE_FILTER_GRAPH_AudioPlugin, SPA_VERSION_FGA_PLUGIN, &impl_plugin, impl); - impl->dsp = spa_support_find(support, n_support, SPA_TYPE_INTERFACE_FILTER_GRAPH_AudioDSP); - pffft_select_cpu(impl->dsp->cpu_flags); impl->log = spa_support_find(support, n_support, SPA_TYPE_INTERFACE_Log); + impl->dsp = spa_support_find(support, n_support, SPA_TYPE_INTERFACE_FILTER_GRAPH_AudioDSP); - return (struct spa_fga_plugin *) impl; + for (uint32_t i = 0; info && i < info->n_items; i++) { + const char *k = info->items[i].key; + const char *s = info->items[i].value; + if (spa_streq(k, "filter.graph.audio.dsp")) + sscanf(s, "pointer:%p", &impl->dsp); + } + if (impl->dsp == NULL) { + spa_log_error(impl->log, "%p: could not find DSP functions", impl); + return -EINVAL; + } + return 0; +} + +static const struct spa_interface_info impl_interfaces[] = { + {SPA_TYPE_INTERFACE_FILTER_GRAPH_AudioPlugin,}, +}; + +static int +impl_enum_interface_info(const struct spa_handle_factory *factory, + const struct spa_interface_info **info, + uint32_t *index) +{ + spa_return_val_if_fail(factory != NULL, -EINVAL); + spa_return_val_if_fail(info != NULL, -EINVAL); + spa_return_val_if_fail(index != NULL, -EINVAL); + + switch (*index) { + case 0: + *info = &impl_interfaces[*index]; + break; + default: + return 0; + } + (*index)++; + return 1; +} + +struct spa_handle_factory spa_fga_plugin_builtin_factory = { + SPA_VERSION_HANDLE_FACTORY, + "filter.graph.plugin.builtin", + NULL, + impl_get_size, + impl_init, + impl_enum_interface_info, +}; + +SPA_EXPORT +int spa_handle_factory_enum(const struct spa_handle_factory **factory, uint32_t *index) +{ + spa_return_val_if_fail(factory != NULL, -EINVAL); + spa_return_val_if_fail(index != NULL, -EINVAL); + + switch (*index) { + case 0: + *factory = &spa_fga_plugin_builtin_factory; + break; + default: + return 0; + } + (*index)++; + return 1; } diff --git a/src/modules/module-filter-chain/filter-graph.c b/src/modules/module-filter-chain/filter-graph.c index 3d2b5cfa3..9d9693aa0 100644 --- a/src/modules/module-filter-chain/filter-graph.c +++ b/src/modules/module-filter-chain/filter-graph.c @@ -15,13 +15,11 @@ #include "config.h" -#include "audio-plugin.h" -#include "filter-graph.h" - #include #include #include #include +#include #include #include #include @@ -32,7 +30,9 @@ #include #include -#include "module-filter-chain/audio-dsp-impl.h" +#include "audio-plugin.h" +#include "filter-graph.h" +#include "audio-dsp-impl.h" #undef SPA_LOG_TOPIC_DEFAULT #define SPA_LOG_TOPIC_DEFAULT &log_topic @@ -49,29 +49,19 @@ SPA_LOG_TOPIC_DEFINE_STATIC(log_topic, "spa.filter-graph"); #define spa_filter_graph_emit_apply_props(hooks,...) spa_filter_graph_emit(hooks,apply_props, 0, __VA_ARGS__) #define spa_filter_graph_emit_props_changed(hooks,...) spa_filter_graph_emit(hooks,props_changed, 0, __VA_ARGS__) - -struct spa_fga_plugin *load_ladspa_plugin(const struct spa_support *support, uint32_t n_support, - const char *path, const struct spa_dict *info); -struct spa_fga_plugin *load_builtin_plugin(const struct spa_support *support, uint32_t n_support, - const char *path, const struct spa_dict *info); - struct plugin { struct spa_list link; + struct impl *impl; + int ref; char type[256]; char path[PATH_MAX]; + struct spa_handle *hndl; struct spa_fga_plugin *plugin; struct spa_list descriptor_list; }; -struct plugin_func { - struct spa_list link; - char type[256]; - spa_filter_graph_audio_plugin_load_func_t *func; - void *hndl; -}; - struct descriptor { struct spa_list link; int ref; @@ -195,12 +185,10 @@ struct impl { struct spa_filter_graph filter_graph; struct spa_hook_list hooks; - struct spa_support support[16]; - uint32_t n_support; - struct spa_log *log; struct spa_cpu *cpu; struct spa_fga_dsp *dsp; + struct spa_plugin_loader *loader; struct graph graph; @@ -212,7 +200,6 @@ struct impl { uint32_t out_ports; struct spa_list plugin_list; - struct spa_list plugin_func_list; float *silence_data; float *discard_data; @@ -731,40 +718,18 @@ static uint32_t count_array(struct spa_json *json) static void plugin_unref(struct plugin *hndl) { + struct impl *impl = hndl->impl; + if (--hndl->ref > 0) return; - spa_fga_plugin_free(hndl->plugin); - spa_list_remove(&hndl->link); + spa_handle_clear(hndl->hndl); + if (hndl->hndl) + spa_plugin_loader_unload(impl->loader, hndl->hndl); free(hndl); } - -static struct plugin_func *add_plugin_func(struct impl *impl, const char *type, - spa_filter_graph_audio_plugin_load_func_t *func, void *hndl) -{ - struct plugin_func *pl; - - pl = calloc(1, sizeof(*pl)); - if (pl == NULL) - return NULL; - - snprintf(pl->type, sizeof(pl->type), "%s", type); - pl->func = func; - pl->hndl = hndl; - spa_list_append(&impl->plugin_func_list, &pl->link); - return pl; -} - -static void free_plugin_func(struct plugin_func *pl) -{ - spa_list_remove(&pl->link); - if (pl->hndl) - dlclose(pl->hndl); - free(pl); -} - static inline const char *split_walk(const char *str, const char *delimiter, size_t * len, const char **state) { const char *s = *state ? *state : str; @@ -779,106 +744,71 @@ static inline const char *split_walk(const char *str, const char *delimiter, siz return s; } -static spa_filter_graph_audio_plugin_load_func_t *find_plugin_func(struct impl *impl, const char *type) -{ - spa_filter_graph_audio_plugin_load_func_t *func = NULL; - void *hndl = NULL; - int res; - struct plugin_func *pl; - char module[PATH_MAX]; - const char *module_dir; - const char *state = NULL, *p; - size_t len; - - spa_list_for_each(pl, &impl->plugin_func_list, link) { - if (spa_streq(pl->type, type)) - return pl->func; - } - module_dir = getenv("PIPEWIRE_MODULE_DIR"); - if (module_dir == NULL) - module_dir = MODULEDIR; - spa_log_debug(impl->log, "moduledir set to: %s", module_dir); - - while ((p = split_walk(module_dir, ":", &len, &state))) { - if ((res = spa_scnprintf(module, sizeof(module), - "%.*s/libpipewire-filter-graph-plugin-%s.so", - (int)len, p, type)) <= 0) - continue; - - hndl = dlopen(module, RTLD_NOW | RTLD_LOCAL); - if (hndl != NULL) - break; - - spa_log_debug(impl->log, "open plugin module %s failed: %s", module, dlerror()); - } - if (hndl == NULL) { - errno = ENOENT; - return NULL; - } - func = dlsym(hndl, SPA_FILTER_GRAPH_AUDIO_PLUGIN_LOAD_FUNC_NAME); - if (func != NULL) { - spa_log_info(impl->log, "opened plugin module %s", module); - pl = add_plugin_func(impl, type, func, hndl); - if (pl == NULL) - goto error_close; - } else { - errno = ENOSYS; - spa_log_error(impl->log, "%s is not a filter graph plugin: %m", module); - goto error_close; - } - return func; - -error_close: - dlclose(hndl); - return NULL; -} - static struct plugin *plugin_load(struct impl *impl, const char *type, const char *path) { - struct spa_fga_plugin *pl = NULL; - struct plugin *hndl; - spa_filter_graph_audio_plugin_load_func_t *plugin_func; + struct spa_handle *hndl = NULL; + struct plugin *plugin; + char module[PATH_MAX]; + char factory_name[256], dsp_ptr[256]; + void *iface; + int res; - spa_list_for_each(hndl, &impl->plugin_list, link) { - if (spa_streq(hndl->type, type) && - spa_streq(hndl->path, path)) { - hndl->ref++; - return hndl; + spa_list_for_each(plugin, &impl->plugin_list, link) { + if (spa_streq(plugin->type, type) && + spa_streq(plugin->path, path)) { + plugin->ref++; + return plugin; } } - plugin_func = find_plugin_func(impl, type); - if (plugin_func == NULL) { + spa_scnprintf(module, sizeof(module), + //"filter-graph/libspa-filter-graph-plugin-%s", type); + "libspa-filter-graph-plugin-%s", type); + spa_scnprintf(factory_name, sizeof(factory_name), + "filter.graph.plugin.%s", type); + spa_scnprintf(dsp_ptr, sizeof(dsp_ptr), + "pointer:%p", impl->dsp); + + hndl = spa_plugin_loader_load(impl->loader, factory_name, + &SPA_DICT_ITEMS( + SPA_DICT_ITEM(SPA_KEY_LIBRARY_NAME, module), + SPA_DICT_ITEM("filter.graph.path", path), + SPA_DICT_ITEM("filter.graph.audio.dsp", dsp_ptr))); + + if (hndl == NULL) { + res = -errno; spa_log_error(impl->log, "can't load plugin type '%s': %m", type); - pl = NULL; - } else { - char quantum[64]; - snprintf(quantum, sizeof(quantum), "%d", impl->quantum_limit); - pl = plugin_func(impl->support, impl->n_support, path, - &SPA_DICT_ITEMS( - SPA_DICT_ITEM("clock.quantum-limit", quantum) - )); - } - if (pl == NULL) goto exit; + } + if ((res = spa_handle_get_interface(hndl, SPA_TYPE_INTERFACE_FILTER_GRAPH_AudioPlugin, &iface)) < 0) { + spa_log_error(impl->log, "can't find iface '%s': %s", + SPA_TYPE_INTERFACE_FILTER_GRAPH_AudioPlugin, spa_strerror(res)); + goto exit; + } + plugin = calloc(1, sizeof(*plugin)); + if (!plugin) { + res = -errno; + goto exit; + } - hndl = calloc(1, sizeof(*hndl)); - if (!hndl) - return NULL; - - hndl->ref = 1; - snprintf(hndl->type, sizeof(hndl->type), "%s", type); - snprintf(hndl->path, sizeof(hndl->path), "%s", path); + plugin->ref = 1; + snprintf(plugin->type, sizeof(plugin->type), "%s", type); + snprintf(plugin->path, sizeof(plugin->path), "%s", path); spa_log_info(impl->log, "successfully opened '%s':'%s'", type, path); - hndl->plugin = pl; + plugin->impl = impl; + plugin->hndl = hndl; + plugin->plugin = iface; - spa_list_init(&hndl->descriptor_list); - spa_list_append(&impl->plugin_list, &hndl->link); + spa_list_init(&plugin->descriptor_list); + spa_list_append(&impl->plugin_list, &plugin->link); - return hndl; + return plugin; exit: + if (hndl) + spa_plugin_loader_unload(impl->loader, hndl); + errno = -res; return NULL; } @@ -902,17 +832,17 @@ static void descriptor_unref(struct descriptor *desc) static struct descriptor *descriptor_load(struct impl *impl, const char *type, const char *plugin, const char *label) { - struct plugin *hndl; + struct plugin *pl; struct descriptor *desc; const struct spa_fga_descriptor *d; uint32_t i, n_input, n_output, n_control, n_notify; unsigned long p; int res; - if ((hndl = plugin_load(impl, type, plugin)) == NULL) + if ((pl = plugin_load(impl, type, plugin)) == NULL) return NULL; - spa_list_for_each(desc, &hndl->descriptor_list, link) { + spa_list_for_each(desc, &pl->descriptor_list, link) { if (spa_streq(desc->label, label)) { desc->ref++; @@ -923,17 +853,17 @@ static struct descriptor *descriptor_load(struct impl *impl, const char *type, * so we need to unref handle here since we're merely reusing * thedescriptor, not creating a new one */ - plugin_unref(hndl); + plugin_unref(pl); return desc; } } desc = calloc(1, sizeof(*desc)); desc->ref = 1; - desc->plugin = hndl; + desc->plugin = pl; spa_list_init(&desc->link); - if ((d = spa_fga_plugin_make_desc(hndl->plugin, label)) == NULL) { + if ((d = spa_fga_plugin_make_desc(pl->plugin, label)) == NULL) { spa_log_error(impl->log, "cannot find label %s", label); res = -ENOENT; goto exit; @@ -1000,7 +930,7 @@ static struct descriptor *descriptor_load(struct impl *impl, const char *type, spa_log_info(impl->log, "control %d ('%s') default to %f", i, d->ports[p].name, desc->default_control[i]); } - spa_list_append(&hndl->descriptor_list, &desc->link); + spa_list_append(&pl->descriptor_list, &desc->link); return desc; @@ -2045,11 +1975,11 @@ static int impl_get_interface(struct spa_handle *handle, const char *type, void static int impl_clear(struct spa_handle *handle) { struct impl *impl = (struct impl *) handle; - struct plugin_func *pl; graph_free(&impl->graph); - spa_list_consume(pl, &impl->plugin_func_list, link) - free_plugin_func(pl); + + if (impl->dsp) + spa_fga_dsp_free(impl->dsp); free(impl->silence_data); free(impl->discard_data); @@ -2083,21 +2013,14 @@ impl_init(const struct spa_handle_factory *factory, impl->log = spa_support_find(support, n_support, SPA_TYPE_INTERFACE_Log); spa_log_topic_init(impl->log, &log_topic); - for (i = 0; i < SPA_MIN(n_support, 15u); i++) - impl->support[i] = support[i]; - impl->n_support = n_support; - impl->cpu = spa_support_find(support, n_support, SPA_TYPE_INTERFACE_CPU); impl->max_align = spa_cpu_get_max_align(impl->cpu); impl->dsp = spa_fga_dsp_new(impl->cpu ? spa_cpu_get_flags(impl->cpu) : 0); - impl->support[impl->n_support++] = SPA_SUPPORT_INIT(SPA_TYPE_INTERFACE_FILTER_GRAPH_AudioDSP, impl->dsp); + + impl->loader = spa_support_find(support, n_support, SPA_TYPE_INTERFACE_PluginLoader); spa_list_init(&impl->plugin_list); - spa_list_init(&impl->plugin_func_list); - - add_plugin_func(impl, "builtin", load_builtin_plugin, NULL); - add_plugin_func(impl, "ladspa", load_ladspa_plugin, NULL); for (i = 0; info && i < info->n_items; i++) { const char *k = info->items[i].key; @@ -2174,3 +2097,20 @@ struct spa_handle_factory spa_filter_graph_factory = { impl_init, impl_enum_interface_info, }; + +SPA_EXPORT +int spa_handle_factory_enum(const struct spa_handle_factory **factory, uint32_t *index) +{ + spa_return_val_if_fail(factory != NULL, -EINVAL); + spa_return_val_if_fail(index != NULL, -EINVAL); + + switch (*index) { + case 0: + *factory = &spa_filter_graph_factory; + break; + default: + return 0; + } + (*index)++; + return 1; +} diff --git a/src/modules/module-filter-chain/ladspa_plugin.c b/src/modules/module-filter-chain/ladspa_plugin.c index f6fe5085f..122091708 100644 --- a/src/modules/module-filter-chain/ladspa_plugin.c +++ b/src/modules/module-filter-chain/ladspa_plugin.c @@ -8,6 +8,7 @@ #include #include +#include #include #include #include @@ -17,11 +18,12 @@ #include "ladspa.h" struct plugin { + struct spa_handle handle; struct spa_fga_plugin plugin; struct spa_log *log; - void *handle; + void *hndl; LADSPA_Descriptor_Function desc_func; }; @@ -165,64 +167,41 @@ static const struct spa_fga_descriptor *ladspa_plugin_make_desc(void *plugin, co return &desc->desc; } -static void ladspa_plugin_free(void *plugin) -{ - struct plugin *p = (struct plugin *)plugin; - if (p->handle) - dlclose(p->handle); - free(p); -} - static struct spa_fga_plugin_methods impl_plugin = { SPA_VERSION_FGA_PLUGIN_METHODS, .make_desc = ladspa_plugin_make_desc, - .free = ladspa_plugin_free }; -static struct spa_fga_plugin *ladspa_handle_load_by_path(struct spa_log *log, const char *path) +static int ladspa_handle_load_by_path(struct plugin *impl, const char *path) { - struct plugin *p; int res; void *handle = NULL; LADSPA_Descriptor_Function desc_func; handle = dlopen(path, RTLD_NOW); if (handle == NULL) { - spa_log_debug(log, "failed to open '%s': %s", path, dlerror()); + spa_log_debug(impl->log, "failed to open '%s': %s", path, dlerror()); res = -ENOENT; goto exit; } - spa_log_info(log, "successfully opened '%s'", path); + spa_log_info(impl->log, "successfully opened '%s'", path); desc_func = (LADSPA_Descriptor_Function) dlsym(handle, "ladspa_descriptor"); if (desc_func == NULL) { - spa_log_warn(log, "cannot find descriptor function in '%s': %s", path, dlerror()); + spa_log_warn(impl->log, "cannot find descriptor function in '%s': %s", path, dlerror()); res = -ENOSYS; goto exit; } - p = calloc(1, sizeof(*p)); - if (!p) { - res = -errno; - goto exit; - } - p->log = log; - p->handle = handle; - p->desc_func = desc_func; - - p->plugin.iface = SPA_INTERFACE_INIT( - SPA_TYPE_INTERFACE_FILTER_GRAPH_AudioPlugin, - SPA_VERSION_FGA_PLUGIN, - &impl_plugin, p); - - return &p->plugin; + impl->hndl = handle; + impl->desc_func = desc_func; + return 0; exit: if (handle) dlclose(handle); - errno = -res; - return NULL; + return res; } static inline const char *split_walk(const char *str, const char *delimiter, size_t * len, const char **state) @@ -239,17 +218,13 @@ static inline const char *split_walk(const char *str, const char *delimiter, siz return s; } -struct spa_fga_plugin *load_ladspa_plugin(const struct spa_support *support, uint32_t n_support, - const char *plugin, const struct spa_dict *info) +static int load_ladspa_plugin(struct plugin *impl, const char *path) { - struct spa_fga_plugin *pl = NULL; - struct spa_log *log; + int res = -ENOENT; - log = spa_support_find(support, n_support, SPA_TYPE_INTERFACE_Log); - - if (plugin[0] != '/') { + if (path[0] != '/') { const char *search_dirs, *p, *state = NULL; - char path[PATH_MAX]; + char filename[PATH_MAX]; size_t len; search_dirs = getenv("LADSPA_PATH"); @@ -261,29 +236,150 @@ struct spa_fga_plugin *load_ladspa_plugin(const struct spa_support *support, uin * is never called, which can only happen if the supplied * LADSPA_PATH contains too long paths */ - errno = ENAMETOOLONG; + res = -ENAMETOOLONG; while ((p = split_walk(search_dirs, ":", &len, &state))) { - int pathlen; + int namelen; - if (len >= sizeof(path)) + if (len >= sizeof(filename)) continue; - pathlen = snprintf(path, sizeof(path), "%.*s/%s.so", (int) len, p, plugin); - if (pathlen < 0 || (size_t) pathlen >= sizeof(path)) + namelen = snprintf(filename, sizeof(filename), "%.*s/%s.so", (int) len, p, path); + if (namelen < 0 || (size_t) namelen >= sizeof(filename)) continue; - pl = ladspa_handle_load_by_path(log, path); - if (pl != NULL) + res = ladspa_handle_load_by_path(impl, filename); + if (res >= 0) break; } } else { - pl = ladspa_handle_load_by_path(log, plugin); + res = ladspa_handle_load_by_path(impl, path); + } + return res; +} + +static int impl_get_interface(struct spa_handle *handle, const char *type, void **interface) +{ + struct plugin *impl; + + spa_return_val_if_fail(handle != NULL, -EINVAL); + spa_return_val_if_fail(interface != NULL, -EINVAL); + + impl = (struct plugin *) handle; + + if (spa_streq(type, SPA_TYPE_INTERFACE_FILTER_GRAPH_AudioPlugin)) + *interface = &impl->plugin; + else + return -ENOENT; + + return 0; +} + +static int impl_clear(struct spa_handle *handle) +{ + struct plugin *impl = (struct plugin *)handle; + if (impl->hndl) + dlclose(impl->hndl); + impl->hndl = NULL; + return 0; +} + +static size_t +impl_get_size(const struct spa_handle_factory *factory, + const struct spa_dict *params) +{ + return sizeof(struct plugin); +} + +static int +impl_init(const struct spa_handle_factory *factory, + struct spa_handle *handle, + const struct spa_dict *info, + const struct spa_support *support, + uint32_t n_support) +{ + struct plugin *impl; + uint32_t i; + int res; + const char *path = NULL; + + handle->get_interface = impl_get_interface; + handle->clear = impl_clear; + + impl = (struct plugin *) handle; + + impl->log = spa_support_find(support, n_support, SPA_TYPE_INTERFACE_Log); + + for (i = 0; info && i < info->n_items; i++) { + const char *k = info->items[i].key; + const char *s = info->items[i].value; + if (spa_streq(k, "filter.graph.path")) + path = s; + } + if (path == NULL) + return -EINVAL; + + if ((res = load_ladspa_plugin(impl, path)) < 0) { + spa_log_error(impl->log, "failed to load plugin '%s': %s", + path, spa_strerror(res)); + return res; } - if (pl == NULL) - spa_log_error(log, "failed to load plugin '%s': %s", plugin, strerror(errno)); + impl->plugin.iface = SPA_INTERFACE_INIT( + SPA_TYPE_INTERFACE_FILTER_GRAPH_AudioPlugin, + SPA_VERSION_FGA_PLUGIN, + &impl_plugin, impl); - return pl; + return 0; +} + +static const struct spa_interface_info impl_interfaces[] = { + { SPA_TYPE_INTERFACE_FILTER_GRAPH_AudioPlugin }, +}; + +static int +impl_enum_interface_info(const struct spa_handle_factory *factory, + const struct spa_interface_info **info, + uint32_t *index) +{ + spa_return_val_if_fail(factory != NULL, -EINVAL); + spa_return_val_if_fail(info != NULL, -EINVAL); + spa_return_val_if_fail(index != NULL, -EINVAL); + + switch (*index) { + case 0: + *info = &impl_interfaces[*index]; + break; + default: + return 0; + } + (*index)++; + return 1; +} + +struct spa_handle_factory spa_fga_plugin_ladspa_factory = { + SPA_VERSION_HANDLE_FACTORY, + "filter.graph.plugin.ladspa", + NULL, + impl_get_size, + impl_init, + impl_enum_interface_info, +}; + +SPA_EXPORT +int spa_handle_factory_enum(const struct spa_handle_factory **factory, uint32_t *index) +{ + spa_return_val_if_fail(factory != NULL, -EINVAL); + spa_return_val_if_fail(index != NULL, -EINVAL); + + switch (*index) { + case 0: + *factory = &spa_fga_plugin_ladspa_factory; + break; + default: + return 0; + } + (*index)++; + return 1; } diff --git a/src/modules/module-filter-chain/lv2_plugin.c b/src/modules/module-filter-chain/lv2_plugin.c index 6948eab9e..a2af22d6a 100644 --- a/src/modules/module-filter-chain/lv2_plugin.c +++ b/src/modules/module-filter-chain/lv2_plugin.c @@ -88,10 +88,6 @@ struct context { int ref; LilvWorld *world; - struct spa_log *log; - struct spa_loop *data_loop; - struct spa_loop *main_loop; - LilvNode *lv2_InputPort; LilvNode *lv2_OutputPort; LilvNode *lv2_AudioPort; @@ -145,7 +141,7 @@ static const LV2_Feature buf_size_features[3] = { { LV2_BUF_SIZE__boundedBlockLength, NULL }, }; -static struct context *context_new(const struct spa_support *support, uint32_t n_support) +static struct context *context_new(void) { struct context *c; @@ -186,20 +182,16 @@ static struct context *context_new(const struct spa_support *support, uint32_t n c->atom_Int = context_map(c, LV2_ATOM__Int); c->atom_Float = context_map(c, LV2_ATOM__Float); - c->log = spa_support_find(support, n_support, SPA_TYPE_INTERFACE_Log); - 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: context_free(c); return NULL; } -static struct context *context_ref(const struct spa_support *support, uint32_t n_support) +static struct context *context_ref(void) { if (_context == NULL) { - _context = context_new(support, n_support); + _context = context_new(); if (_context == NULL) return NULL; } @@ -216,7 +208,13 @@ static void context_unref(struct context *context) } struct plugin { + struct spa_handle handle; struct spa_fga_plugin plugin; + + struct spa_log *log; + struct spa_loop *data_loop; + struct spa_loop *main_loop; + struct context *c; const LilvPlugin *p; }; @@ -228,6 +226,8 @@ struct descriptor { struct instance { struct descriptor *desc; + struct plugin *p; + LilvInstance *instance; LV2_Worker_Schedule work_schedule; LV2_Feature work_schedule_feature; @@ -256,8 +256,7 @@ 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); + spa_loop_invoke(i->p->data_loop, do_respond, 1, data, size, false, i); return LV2_WORKER_SUCCESS; } @@ -275,8 +274,7 @@ 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); + spa_loop_invoke(i->p->main_loop, do_schedule, 1, data, size, false, i); return LV2_WORKER_SUCCESS; } @@ -299,6 +297,7 @@ static void *lv2_instantiate(const struct spa_fga_plugin *plugin, const struct s i->block_length = 1024; i->desc = d; + i->p = p; i->features[n_features++] = &c->map_feature; i->features[n_features++] = &c->unmap_feature; i->features[n_features++] = &buf_size_features[0]; @@ -453,68 +452,153 @@ static const struct spa_fga_descriptor *lv2_plugin_make_desc(void *plugin, const return &desc->desc; } -static void lv2_plugin_free(void *plugin) -{ - struct plugin *p = (struct plugin *)plugin; - context_unref(p->c); - free(p); -} - static struct spa_fga_plugin_methods impl_plugin = { SPA_VERSION_FGA_PLUGIN_METHODS, .make_desc = lv2_plugin_make_desc, - .free = lv2_plugin_free +}; + +static int impl_get_interface(struct spa_handle *handle, const char *type, void **interface) +{ + struct plugin *impl; + + spa_return_val_if_fail(handle != NULL, -EINVAL); + spa_return_val_if_fail(interface != NULL, -EINVAL); + + impl = (struct plugin *) handle; + + if (spa_streq(type, SPA_TYPE_INTERFACE_FILTER_GRAPH_AudioPlugin)) + *interface = &impl->plugin; + else + return -ENOENT; + + return 0; +} + +static int impl_clear(struct spa_handle *handle) +{ + struct plugin *p = (struct plugin *)handle; + context_unref(p->c); + return 0; +} + +static size_t +impl_get_size(const struct spa_handle_factory *factory, + const struct spa_dict *params) +{ + return sizeof(struct plugin); +} + +static int +impl_init(const struct spa_handle_factory *factory, + struct spa_handle *handle, + const struct spa_dict *info, + const struct spa_support *support, + uint32_t n_support) +{ + struct plugin *impl; + uint32_t i; + int res; + const char *path = NULL; + const LilvPlugins *plugins; + LilvNode *uri; + + handle->get_interface = impl_get_interface; + handle->clear = impl_clear; + + impl = (struct plugin *) handle; + impl->log = spa_support_find(support, n_support, SPA_TYPE_INTERFACE_Log); + impl->data_loop = spa_support_find(support, n_support, SPA_TYPE_INTERFACE_DataLoop); + impl->main_loop = spa_support_find(support, n_support, SPA_TYPE_INTERFACE_Loop); + + for (i = 0; info && i < info->n_items; i++) { + const char *k = info->items[i].key; + const char *s = info->items[i].value; + if (spa_streq(k, "filter.graph.path")) + path = s; + } + if (path == NULL) + return -EINVAL; + + impl->c = context_ref(); + if (impl->c == NULL) + return -EINVAL; + + uri = lilv_new_uri(impl->c->world, path); + if (uri == NULL) { + spa_log_warn(impl->log, "invalid URI %s", path); + res = -EINVAL; + goto error_cleanup; + } + + plugins = lilv_world_get_all_plugins(impl->c->world); + impl->p = lilv_plugins_get_by_uri(plugins, uri); + lilv_node_free(uri); + + if (impl->p == NULL) { + spa_log_warn(impl->log, "can't load plugin %s", path); + res = -EINVAL; + goto error_cleanup; + } + impl->plugin.iface = SPA_INTERFACE_INIT( + SPA_TYPE_INTERFACE_FILTER_GRAPH_AudioPlugin, + SPA_VERSION_FGA_PLUGIN, + &impl_plugin, impl); + + return 0; + +error_cleanup: + if (impl->c) + context_unref(impl->c); + return res; +} + + +static const struct spa_interface_info impl_interfaces[] = { + { SPA_TYPE_INTERFACE_FILTER_GRAPH_AudioPlugin }, +}; + +static int +impl_enum_interface_info(const struct spa_handle_factory *factory, + const struct spa_interface_info **info, + uint32_t *index) +{ + spa_return_val_if_fail(factory != NULL, -EINVAL); + spa_return_val_if_fail(info != NULL, -EINVAL); + spa_return_val_if_fail(index != NULL, -EINVAL); + + switch (*index) { + case 0: + *info = &impl_interfaces[*index]; + break; + default: + return 0; + } + (*index)++; + return 1; +} + +static struct spa_handle_factory spa_fga_plugin_lv2_factory = { + SPA_VERSION_HANDLE_FACTORY, + "filter.graph.plugin.lv2", + NULL, + impl_get_size, + impl_init, + impl_enum_interface_info, }; SPA_EXPORT -struct spa_fga_plugin *spa_filter_graph_audio_plugin_load(const struct spa_support *support, uint32_t n_support, - const char *plugin_uri, const struct spa_dict *info) +int spa_handle_factory_enum(const struct spa_handle_factory **factory, uint32_t *index) { - struct context *c; - const LilvPlugins *plugins; - const LilvPlugin *plugin; - LilvNode *uri; - int res; - struct plugin *p; + spa_return_val_if_fail(factory != NULL, -EINVAL); + spa_return_val_if_fail(index != NULL, -EINVAL); - c = context_ref(support, n_support); - if (c == NULL) - return NULL; - - uri = lilv_new_uri(c->world, plugin_uri); - if (uri == NULL) { - spa_log_warn(c->log, "invalid URI %s", plugin_uri); - res = -EINVAL; - goto error_unref; + switch (*index) { + case 0: + *factory = &spa_fga_plugin_lv2_factory; + break; + default: + return 0; } - - plugins = lilv_world_get_all_plugins(c->world); - plugin = lilv_plugins_get_by_uri(plugins, uri); - lilv_node_free(uri); - - if (plugin == NULL) { - spa_log_warn(c->log, "can't load plugin %s", plugin_uri); - res = -EINVAL; - goto error_unref; - } - - p = calloc(1, sizeof(*p)); - if (!p) { - res = -errno; - goto error_unref; - } - p->p = plugin; - p->c = c; - - p->plugin.iface = SPA_INTERFACE_INIT( - SPA_TYPE_INTERFACE_FILTER_GRAPH_AudioPlugin, - SPA_VERSION_FGA_PLUGIN, - &impl_plugin, p); - - return &p->plugin; - -error_unref: - context_unref(c); - errno = -res; - return NULL; + (*index)++; + return 1; } diff --git a/src/modules/module-filter-chain/sofa_plugin.c b/src/modules/module-filter-chain/sofa_plugin.c index 70b556f71..de2367817 100644 --- a/src/modules/module-filter-chain/sofa_plugin.c +++ b/src/modules/module-filter-chain/sofa_plugin.c @@ -9,11 +9,11 @@ #include "audio-plugin.h" #include "convolver.h" #include "audio-dsp.h" -#include "pffft.h" #include struct plugin { + struct spa_handle handle; struct spa_fga_plugin plugin; struct spa_fga_dsp *dsp; @@ -43,8 +43,8 @@ struct spatializer_impl { static void * spatializer_instantiate(const struct spa_fga_plugin *plugin, const struct spa_fga_descriptor * Descriptor, unsigned long SampleRate, int index, const char *config) { + struct plugin *pl = SPA_CONTAINER_OF(plugin, struct plugin, plugin); struct spatializer_impl *impl; - struct plugin *pl = (struct plugin*) plugin; struct spa_json it[1]; const char *val; char key[256]; @@ -424,23 +424,53 @@ static const struct spa_fga_descriptor *sofa_plugin_make_desc(void *plugin, cons return NULL; } -static void sofa_plugin_free(void *plugin) -{ - struct plugin *impl = plugin; - free(impl); -} - static struct spa_fga_plugin_methods impl_plugin = { SPA_VERSION_FGA_PLUGIN_METHODS, .make_desc = sofa_plugin_make_desc, - .free = sofa_plugin_free }; -SPA_EXPORT -struct spa_fga_plugin *spa_filter_graph_audio_plugin_load(const struct spa_support *support, uint32_t n_support, - const char *plugin, const struct spa_dict *info) +static int impl_get_interface(struct spa_handle *handle, const char *type, void **interface) { - struct plugin *impl = calloc(1, sizeof (struct plugin)); + struct plugin *impl; + + spa_return_val_if_fail(handle != NULL, -EINVAL); + spa_return_val_if_fail(interface != NULL, -EINVAL); + + impl = (struct plugin *) handle; + + if (spa_streq(type, SPA_TYPE_INTERFACE_FILTER_GRAPH_AudioPlugin)) + *interface = &impl->plugin; + else + return -ENOENT; + + return 0; +} + +static int impl_clear(struct spa_handle *handle) +{ + return 0; +} + +static size_t +impl_get_size(const struct spa_handle_factory *factory, + const struct spa_dict *params) +{ + return sizeof(struct plugin); +} + +static int +impl_init(const struct spa_handle_factory *factory, + struct spa_handle *handle, + const struct spa_dict *info, + const struct spa_support *support, + uint32_t n_support) +{ + struct plugin *impl; + + handle->get_interface = impl_get_interface; + handle->clear = impl_clear; + + impl = (struct plugin *) handle; impl->plugin.iface = SPA_INTERFACE_INIT( SPA_TYPE_INTERFACE_FILTER_GRAPH_AudioPlugin, @@ -449,17 +479,77 @@ struct spa_fga_plugin *spa_filter_graph_audio_plugin_load(const struct spa_suppo impl->quantum_limit = 8192u; + impl->log = spa_support_find(support, n_support, SPA_TYPE_INTERFACE_Log); + impl->data_loop = spa_support_find(support, n_support, SPA_TYPE_INTERFACE_DataLoop); + impl->main_loop = spa_support_find(support, n_support, SPA_TYPE_INTERFACE_Loop); + impl->dsp = spa_support_find(support, n_support, SPA_TYPE_INTERFACE_FILTER_GRAPH_AudioDSP); + for (uint32_t i = 0; info && i < info->n_items; i++) { const char *k = info->items[i].key; const char *s = info->items[i].value; if (spa_streq(k, "clock.quantum-limit")) spa_atou32(s, &impl->quantum_limit, 0); + if (spa_streq(k, "filter.graph.audio.dsp")) + sscanf(s, "pointer:%p", &impl->dsp); } - impl->data_loop = spa_support_find(support, n_support, SPA_TYPE_INTERFACE_DataLoop); - impl->main_loop = spa_support_find(support, n_support, SPA_TYPE_INTERFACE_Loop); - impl->log = spa_support_find(support, n_support, SPA_TYPE_INTERFACE_Log); - impl->dsp = spa_support_find(support, n_support, SPA_TYPE_INTERFACE_FILTER_GRAPH_AudioDSP); - pffft_select_cpu(impl->dsp->cpu_flags); - return (struct spa_fga_plugin *) impl; + if (impl->data_loop == NULL || impl->main_loop == NULL) { + spa_log_error(impl->log, "%p: could not find a data/main loop", impl); + return -EINVAL; + } + if (impl->dsp == NULL) { + spa_log_error(impl->log, "%p: could not find DSP functions", impl); + return -EINVAL; + } + return 0; +} + +static const struct spa_interface_info impl_interfaces[] = { + {SPA_TYPE_INTERFACE_FILTER_GRAPH_AudioPlugin,}, +}; + +static int +impl_enum_interface_info(const struct spa_handle_factory *factory, + const struct spa_interface_info **info, + uint32_t *index) +{ + spa_return_val_if_fail(factory != NULL, -EINVAL); + spa_return_val_if_fail(info != NULL, -EINVAL); + spa_return_val_if_fail(index != NULL, -EINVAL); + + switch (*index) { + case 0: + *info = &impl_interfaces[*index]; + break; + default: + return 0; + } + (*index)++; + return 1; +} + +struct spa_handle_factory spa_fga_sofa_plugin_factory = { + SPA_VERSION_HANDLE_FACTORY, + "filter.graph.plugin.sofa", + NULL, + impl_get_size, + impl_init, + impl_enum_interface_info, +}; + +SPA_EXPORT +int spa_handle_factory_enum(const struct spa_handle_factory **factory, uint32_t *index) +{ + spa_return_val_if_fail(factory != NULL, -EINVAL); + spa_return_val_if_fail(index != NULL, -EINVAL); + + switch (*index) { + case 0: + *factory = &spa_fga_sofa_plugin_factory; + break; + default: + return 0; + } + (*index)++; + return 1; }