filter-chain: move sofa and lv2 to external modules

dlopen lv2 and sofa plugin modules instead of hardcoding them into the+
filter-chain. This also makes it possible to add more plugin module
types externally.
This commit is contained in:
Wim Taymans 2023-06-29 14:04:40 +02:00
parent 5d177acc53
commit 98f138dbe0
8 changed files with 145 additions and 65 deletions

View file

@ -295,7 +295,6 @@ summary({'sndfile': sndfile_dep.found()}, bool_yn: true, section: 'pw-cat/pw-pla
cdata.set('HAVE_SNDFILE', sndfile_dep.found())
libmysofa_dep = dependency('libmysofa', required : get_option('libmysofa'))
summary({'libmysofa': libmysofa_dep.found()}, bool_yn: true, section: 'filter-chain')
cdata.set('HAVE_LIBMYSOFA', libmysofa_dep.found())
pulseaudio_dep = dependency('libpulse', required : get_option('libpulse'))
summary({'libpulse': pulseaudio_dep.found()}, bool_yn: true, section: 'Streaming between daemons')
avahi_dep = dependency('avahi-client', required : get_option('avahi'))
@ -403,7 +402,6 @@ summary({'OpenSSL (for raop-sink)': openssl_lib.found()}, bool_yn: true)
lilv_lib = dependency('lilv-0', required: get_option('lv2'))
summary({'lilv (for lv2 plugins)': lilv_lib.found()}, bool_yn: true)
cdata.set('HAVE_LILV', lilv_lib.found())
libffado_dep = dependency('libffado', required: get_option('libffado'))
summary({'ffado': libffado_dep.found()}, bool_yn: true)

View file

@ -117,20 +117,12 @@ filter_chain_sources = [
'module-filter-chain/biquad.c',
'module-filter-chain/ladspa_plugin.c',
'module-filter-chain/builtin_plugin.c',
'module-filter-chain/sofa_plugin.c',
'module-filter-chain/convolver.c'
]
filter_chain_dependencies = [
mathlib, dl_lib, pipewire_dep, sndfile_dep, audioconvert_dep, libmysofa_dep
mathlib, dl_lib, pipewire_dep, sndfile_dep, audioconvert_dep
]
if lilv_lib.found()
filter_chain_sources += [
'module-filter-chain/lv2_plugin.c'
]
filter_chain_dependencies += [ lilv_lib ]
endif
pipewire_module_filter_chain = shared_library('pipewire-module-filter-chain',
filter_chain_sources,
include_directories : [configinc],
@ -141,6 +133,31 @@ pipewire_module_filter_chain = shared_library('pipewire-module-filter-chain',
dependencies : filter_chain_dependencies,
)
if libmysofa_dep.found()
pipewire_module_filter_chain_sofa = shared_library('pipewire-module-filter-chain-sofa',
[ 'module-filter-chain/sofa_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,
dependencies : [ filter_chain_dependencies, libmysofa_dep ]
)
endif
if lilv_lib.found()
pipewire_module_filter_chain_lv2 = shared_library('pipewire-module-filter-chain-lv2',
[ 'module-filter-chain/lv2_plugin.c' ],
include_directories : [configinc],
install : true,
install_dir : modules_install_dir,
install_rpath: modules_install_dir,
dependencies : [ filter_chain_dependencies, lilv_lib ]
)
endif
pipewire_module_combine_stream = shared_library('pipewire-module-combine-stream',
[ 'module-combine-stream.c' ],
include_directories : [configinc],

View file

@ -523,6 +523,11 @@ static const struct spa_dict_item module_props[] = {
static float silence_data[MAX_SAMPLES];
static float discard_data[MAX_SAMPLES];
struct fc_plugin *load_ladspa_plugin(const struct spa_support *support, uint32_t n_support,
struct dsp_ops *dsp, const char *path, const char *config);
struct fc_plugin *load_builtin_plugin(const struct spa_support *support, uint32_t n_support,
struct dsp_ops *dsp, const char *path, const char *config);
struct plugin {
struct spa_list link;
int ref;
@ -533,6 +538,13 @@ struct plugin {
struct spa_list descriptor_list;
};
struct plugin_func {
struct spa_list link;
char type[256];
fc_plugin_load_func *func;
void *hndl;
};
struct descriptor {
struct spa_list link;
int ref;
@ -647,6 +659,7 @@ struct impl {
struct dsp_ops dsp;
struct spa_list plugin_list;
struct spa_list plugin_func_list;
struct pw_properties *capture_props;
struct pw_stream *capture;
@ -1348,12 +1361,92 @@ static void plugin_unref(struct plugin *hndl)
free(hndl);
}
static struct plugin_func *add_plugin_func(struct impl *impl, const char *type,
fc_plugin_load_func *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 fc_plugin_load_func *find_plugin_func(struct impl *impl, const char *type)
{
fc_plugin_load_func *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;
pw_log_debug("moduledir set to: %s", module_dir);
while ((p = pw_split_walk(module_dir, ":", &len, &state))) {
if ((res = spa_scnprintf(module, sizeof(module),
"%.*s/libpipewire-module-filter-chain-%s.so",
(int)len, p, type)) <= 0)
continue;
hndl = dlopen(module, RTLD_NOW | RTLD_LOCAL);
if (hndl != NULL)
break;
pw_log_debug("open plugin module %s failed: %s", module, dlerror());
}
if (hndl == NULL) {
errno = ENOENT;
return NULL;
}
func = dlsym(hndl, FC_PLUGIN_LOAD_FUNC);
if (func != NULL) {
pw_log_info("opened plugin module %s", module);
pl = add_plugin_func(impl, type, func, hndl);
if (pl == NULL)
goto error_close;
} else {
errno = ENOSYS;
pw_log_error("%s is not a filter chain 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 fc_plugin *pl = NULL;
struct plugin *hndl;
const struct spa_support *support;
uint32_t n_support;
fc_plugin_load_func *plugin_func;
spa_list_for_each(hndl, &impl->plugin_list, link) {
if (spa_streq(hndl->type, type) &&
@ -1364,27 +1457,12 @@ static struct plugin *plugin_load(struct impl *impl, const char *type, const cha
}
support = pw_context_get_support(impl->context, &n_support);
if (spa_streq(type, "builtin")) {
pl = load_builtin_plugin(support, n_support, &impl->dsp, path, NULL);
}
else if (spa_streq(type, "sofa")) {
pl = load_sofa_plugin(support, n_support, &impl->dsp, path, NULL);
}
else if (spa_streq(type, "ladspa")) {
pl = load_ladspa_plugin(support, n_support, &impl->dsp, path, NULL);
}
else if (spa_streq(type, "lv2")) {
#ifdef HAVE_LILV
pl = load_lv2_plugin(support, n_support, &impl->dsp, path, NULL);
#else
pw_log_error("filter-chain is compiled without lv2 support");
plugin_func = find_plugin_func(impl, type);
if (plugin_func == NULL) {
pw_log_error("can't load plugin type '%s': %m", type);
pl = NULL;
errno = ENOTSUP;
#endif
} else {
pw_log_error("invalid plugin type '%s'", type);
pl = NULL;
errno = EINVAL;
pl = plugin_func(support, n_support, &impl->dsp, path, NULL);
}
if (pl == NULL)
goto exit;
@ -1397,7 +1475,7 @@ static struct plugin *plugin_load(struct impl *impl, const char *type, const cha
snprintf(hndl->type, sizeof(hndl->type), "%s", type);
snprintf(hndl->path, sizeof(hndl->path), "%s", path);
pw_log_info("successfully opened '%s'", path);
pw_log_info("successfully opened '%s':'%s'", type, path);
hndl->plugin = pl;
@ -2374,6 +2452,8 @@ static const struct pw_proxy_events core_proxy_events = {
static void impl_destroy(struct impl *impl)
{
struct plugin_func *pl;
/* disconnect both streams before destroying any of them */
if (impl->capture)
pw_stream_disconnect(impl->capture);
@ -2391,6 +2471,8 @@ static void impl_destroy(struct impl *impl)
pw_properties_free(impl->capture_props);
pw_properties_free(impl->playback_props);
graph_free(&impl->graph);
spa_list_consume(pl, &impl->plugin_func_list, link)
free_plugin_func(pl);
free(impl);
}
@ -2502,6 +2584,10 @@ int pipewire__module_init(struct pw_impl_module *module, const char *args)
impl->graph.impl = impl;
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);
support = pw_context_get_support(impl->context, &n_support);

View file

@ -10,6 +10,7 @@
#include <sndfile.h>
#endif
#include <unistd.h>
#include <limits.h>
#include <spa/utils/json.h>
#include <spa/utils/result.h>

View file

@ -6,6 +6,7 @@
#include <dlfcn.h>
#include <math.h>
#include <limits.h>
#include <spa/utils/defs.h>
#include <spa/utils/list.h>

View file

@ -451,7 +451,8 @@ static void lv2_unload(struct fc_plugin *plugin)
free(p);
}
struct fc_plugin *load_lv2_plugin(const struct spa_support *support, uint32_t n_support,
SPA_EXPORT
struct fc_plugin *pipewire__filter_chain_plugin_load(const struct spa_support *support, uint32_t n_support,
struct dsp_ops *ops, const char *plugin_uri, const char *config)
{
struct context *c;

View file

@ -7,14 +7,8 @@
#include <stdint.h>
#include <stddef.h>
#include <errno.h>
#include <stdio.h>
#include <limits.h>
#include <spa/support/plugin.h>
#include <spa/utils/defs.h>
#include <spa/utils/list.h>
#include <spa/utils/string.h>
#include "dsp-ops.h"
@ -83,13 +77,10 @@ static inline void fc_descriptor_free(const struct fc_descriptor *desc)
desc->free(desc);
}
struct fc_plugin *load_ladspa_plugin(const struct spa_support *support, uint32_t n_support,
struct dsp_ops *dsp, const char *path, const char *config);
struct fc_plugin *load_lv2_plugin(const struct spa_support *support, uint32_t n_support,
struct dsp_ops *dsp, const char *path, const char *config);
struct fc_plugin *load_builtin_plugin(const struct spa_support *support, uint32_t n_support,
struct dsp_ops *dsp, const char *path, const char *config);
struct fc_plugin *load_sofa_plugin(const struct spa_support *support, uint32_t n_support,
#define FC_PLUGIN_LOAD_FUNC "pipewire__filter_chain_plugin_load"
typedef struct fc_plugin *(fc_plugin_load_func)(const struct spa_support *support, uint32_t n_support,
struct dsp_ops *dsp, const char *path, const char *config);
#endif /* PLUGIN_H */

View file

@ -1,19 +1,20 @@
#include "config.h"
#include <limits.h>
#include <spa/utils/json.h>
#include <spa/support/loop.h>
#include <pipewire/log.h>
#include "plugin.h"
#include "convolver.h"
#include "dsp-ops.h"
#include "pffft.h"
#ifdef HAVE_LIBMYSOFA
#include <mysofa.h>
#define MAX_SAMPLES 8192u
#endif
static struct dsp_ops *dsp_ops;
static struct spa_loop *data_loop;
@ -25,9 +26,7 @@ struct spatializer_impl {
int n_samples, blocksize, tailsize;
float *tmp[2];
#ifdef HAVE_LIBMYSOFA
struct MYSOFA_EASY *sofa;
#endif
unsigned int interpolate:1;
struct convolver *l_conv[3];
struct convolver *r_conv[3];
@ -36,7 +35,6 @@ struct spatializer_impl {
static void * spatializer_instantiate(const struct fc_descriptor * Descriptor,
unsigned long SampleRate, int index, const char *config)
{
#ifdef HAVE_LIBMYSOFA
struct spatializer_impl *impl;
struct spa_json it[2];
const char *val;
@ -115,14 +113,8 @@ error:
mysofa_close_cached(impl->sofa);
free(impl);
return NULL;
#else
pw_log_error("libmysofa is required for spatializer, but disabled at compile time");
errno = EINVAL;
return NULL;
#endif
}
#ifdef HAVE_LIBMYSOFA
static int
do_switch(struct spa_loop *loop, bool async, uint32_t seq, const void *data,
size_t size, void *user_data)
@ -205,11 +197,9 @@ do_free(struct spa_loop *loop, bool async, uint32_t seq, const void *data,
convolver_free(fd->item[1]);
return 0;
}
#endif
static void spatializer_run(void * Instance, unsigned long SampleCount)
{
#ifdef HAVE_LIBMYSOFA
struct spatializer_impl *impl = Instance;
if (impl->interpolate) {
@ -239,7 +229,6 @@ static void spatializer_run(void * Instance, unsigned long SampleCount)
convolver_run(impl->l_conv[0], impl->port[2], impl->port[0], SampleCount);
convolver_run(impl->r_conv[0], impl->port[2], impl->port[1], SampleCount);
}
#endif
}
static void spatializer_connect_port(void * Instance, unsigned long Port,
@ -261,10 +250,8 @@ static void spatializer_cleanup(void * Instance)
if (impl->r_conv[i])
convolver_free(impl->r_conv[i]);
}
#ifdef HAVE_LIBMYSOFA
if (impl->sofa)
mysofa_close_cached(impl->sofa);
#endif
free(impl->tmp[0]);
free(impl->tmp[1]);
@ -273,10 +260,8 @@ static void spatializer_cleanup(void * Instance)
static void spatializer_control_changed(void * Instance)
{
#ifdef HAVE_LIBMYSOFA
pw_log_info("control changed");
spatializer_reload(Instance);
#endif
}
static void spatializer_deactivate(void * Instance)
@ -361,7 +346,8 @@ static struct fc_plugin builtin_plugin = {
.make_desc = sofa_make_desc
};
struct fc_plugin *load_sofa_plugin(const struct spa_support *support, uint32_t n_support,
SPA_EXPORT
struct fc_plugin *pipewire__filter_chain_plugin_load(const struct spa_support *support, uint32_t n_support,
struct dsp_ops *dsp, const char *plugin, const char *config)
{
dsp_ops = dsp;
@ -372,4 +358,3 @@ struct fc_plugin *load_sofa_plugin(const struct spa_support *support, uint32_t n
return &builtin_plugin;
}