filter-chain: make filter-graph SPA plugins

Make SPA plugins from all the filter-graph plugins and use the plugin
loader to load them.

Because they are not in the standard plugin path in development, add
the module dir to the plugin path for now.
This commit is contained in:
Wim Taymans 2024-11-13 10:20:54 +01:00
parent 201455eecd
commit 2e157f7248
11 changed files with 648 additions and 326 deletions

View file

@ -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) \

View file

@ -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"

View file

@ -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

View file

@ -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);

View file

@ -11,6 +11,8 @@
#include <spa/utils/defs.h>
#include <spa/param/audio/format-utils.h>
#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,

View file

@ -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);

View file

@ -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;
}

View file

@ -15,13 +15,11 @@
#include "config.h"
#include "audio-plugin.h"
#include "filter-graph.h"
#include <spa/utils/result.h>
#include <spa/utils/string.h>
#include <spa/utils/json.h>
#include <spa/support/cpu.h>
#include <spa/support/plugin-loader.h>
#include <spa/param/latency-utils.h>
#include <spa/param/tag-utils.h>
#include <spa/param/audio/raw.h>
@ -32,7 +30,9 @@
#include <spa/debug/types.h>
#include <spa/debug/log.h>
#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;
}

View file

@ -8,6 +8,7 @@
#include <math.h>
#include <limits.h>
#include <spa/utils/result.h>
#include <spa/utils/defs.h>
#include <spa/utils/list.h>
#include <spa/utils/string.h>
@ -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;
}

View file

@ -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;
}

View file

@ -9,11 +9,11 @@
#include "audio-plugin.h"
#include "convolver.h"
#include "audio-dsp.h"
#include "pffft.h"
#include <mysofa.h>
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;
}