filter-chain: don't let plugins depend on pipewire, just spa

This commit is contained in:
Wim Taymans 2024-11-11 16:36:09 +01:00
parent 44340fde05
commit 02edfba21a
4 changed files with 221 additions and 158 deletions

View file

@ -15,10 +15,9 @@
#include <spa/utils/json.h> #include <spa/utils/json.h>
#include <spa/utils/result.h> #include <spa/utils/result.h>
#include <spa/support/cpu.h> #include <spa/support/cpu.h>
#include <spa/support/log.h>
#include <spa/plugins/audioconvert/resample.h> #include <spa/plugins/audioconvert/resample.h>
#include <pipewire/log.h>
#include "plugin.h" #include "plugin.h"
#include "biquad.h" #include "biquad.h"
@ -30,11 +29,16 @@
struct plugin { struct plugin {
struct fc_plugin plugin; struct fc_plugin plugin;
struct dsp_ops *dsp_ops; struct dsp_ops *dsp;
struct spa_log *log;
}; };
struct builtin { struct builtin {
struct plugin *plugin; struct plugin *plugin;
struct dsp_ops *dsp;
struct spa_log *log;
unsigned long rate; unsigned long rate;
float *port[64]; float *port[64];
@ -80,7 +84,7 @@ static void copy_run(void * Instance, unsigned long SampleCount)
{ {
struct builtin *impl = Instance; struct builtin *impl = Instance;
float *in = impl->port[1], *out = impl->port[0]; float *in = impl->port[1], *out = impl->port[0];
dsp_ops_copy(impl->plugin->dsp_ops, out, in, SampleCount); dsp_ops_copy(impl->dsp, out, in, SampleCount);
} }
static struct fc_port copy_ports[] = { static struct fc_port copy_ports[] = {
@ -129,7 +133,7 @@ static void mixer_run(void * Instance, unsigned long SampleCount)
src[n_src] = in; src[n_src] = in;
gains[n_src++] = gain; gains[n_src++] = gain;
} }
dsp_ops_mix_gain(impl->plugin->dsp_ops, out, src, gains, n_src, SampleCount); dsp_ops_mix_gain(impl->dsp, out, src, gains, n_src, SampleCount);
} }
static struct fc_port mixer_ports[] = { static struct fc_port mixer_ports[] = {
@ -320,6 +324,8 @@ static void *bq_instantiate(const struct fc_plugin *plugin, const struct fc_desc
return NULL; return NULL;
impl->plugin = (struct plugin *) plugin; impl->plugin = (struct plugin *) plugin;
impl->log = impl->plugin->log;
impl->dsp = impl->plugin->dsp;
impl->rate = SampleRate; impl->rate = SampleRate;
impl->b0 = impl->a0 = 1.0f; impl->b0 = impl->a0 = 1.0f;
impl->type = bq_type_from_name(Descriptor->name); impl->type = bq_type_from_name(Descriptor->name);
@ -327,19 +333,19 @@ static void *bq_instantiate(const struct fc_plugin *plugin, const struct fc_desc
return impl; return impl;
if (config == NULL) { if (config == NULL) {
pw_log_error("biquads:bq_raw requires a config section"); spa_log_error(impl->log, "biquads:bq_raw requires a config section");
goto error; goto error;
} }
if (spa_json_begin_object(&it[0], config, strlen(config)) <= 0) { if (spa_json_begin_object(&it[0], config, strlen(config)) <= 0) {
pw_log_error("biquads:config section must be an object"); spa_log_error(impl->log, "biquads:config section must be an object");
goto error; goto error;
} }
while ((len = spa_json_object_next(&it[0], key, sizeof(key), &val)) > 0) { while ((len = spa_json_object_next(&it[0], key, sizeof(key), &val)) > 0) {
if (spa_streq(key, "coefficients")) { if (spa_streq(key, "coefficients")) {
if (!spa_json_is_array(val, len)) { if (!spa_json_is_array(val, len)) {
pw_log_error("biquads:coefficients require an array"); spa_log_error(impl->log, "biquads:coefficients require an array");
goto error; goto error;
} }
spa_json_enter(&it[0], &it[1]); spa_json_enter(&it[0], &it[1]);
@ -351,48 +357,48 @@ static void *bq_instantiate(const struct fc_plugin *plugin, const struct fc_desc
while ((len = spa_json_object_next(&it[2], key, sizeof(key), &val)) > 0) { while ((len = spa_json_object_next(&it[2], key, sizeof(key), &val)) > 0) {
if (spa_streq(key, "rate")) { if (spa_streq(key, "rate")) {
if (spa_json_parse_int(val, len, &rate) <= 0) { if (spa_json_parse_int(val, len, &rate) <= 0) {
pw_log_error("biquads:rate requires a number"); spa_log_error(impl->log, "biquads:rate requires a number");
goto error; goto error;
} }
} }
else if (spa_streq(key, "b0")) { else if (spa_streq(key, "b0")) {
if (spa_json_parse_float(val, len, &b0) <= 0) { if (spa_json_parse_float(val, len, &b0) <= 0) {
pw_log_error("biquads:b0 requires a float"); spa_log_error(impl->log, "biquads:b0 requires a float");
goto error; goto error;
} }
} }
else if (spa_streq(key, "b1")) { else if (spa_streq(key, "b1")) {
if (spa_json_parse_float(val, len, &b1) <= 0) { if (spa_json_parse_float(val, len, &b1) <= 0) {
pw_log_error("biquads:b1 requires a float"); spa_log_error(impl->log, "biquads:b1 requires a float");
goto error; goto error;
} }
} }
else if (spa_streq(key, "b2")) { else if (spa_streq(key, "b2")) {
if (spa_json_parse_float(val, len, &b2) <= 0) { if (spa_json_parse_float(val, len, &b2) <= 0) {
pw_log_error("biquads:b2 requires a float"); spa_log_error(impl->log, "biquads:b2 requires a float");
goto error; goto error;
} }
} }
else if (spa_streq(key, "a0")) { else if (spa_streq(key, "a0")) {
if (spa_json_parse_float(val, len, &a0) <= 0) { if (spa_json_parse_float(val, len, &a0) <= 0) {
pw_log_error("biquads:a0 requires a float"); spa_log_error(impl->log, "biquads:a0 requires a float");
goto error; goto error;
} }
} }
else if (spa_streq(key, "a1")) { else if (spa_streq(key, "a1")) {
if (spa_json_parse_float(val, len, &a1) <= 0) { if (spa_json_parse_float(val, len, &a1) <= 0) {
pw_log_error("biquads:a1 requires a float"); spa_log_error(impl->log, "biquads:a1 requires a float");
goto error; goto error;
} }
} }
else if (spa_streq(key, "a2")) { else if (spa_streq(key, "a2")) {
if (spa_json_parse_float(val, len, &a2) <= 0) { if (spa_json_parse_float(val, len, &a2) <= 0) {
pw_log_error("biquads:a0 requires a float"); spa_log_error(impl->log, "biquads:a0 requires a float");
goto error; goto error;
} }
} }
else { else {
pw_log_warn("biquads: ignoring coefficients key: '%s'", key); spa_log_warn(impl->log, "biquads: ignoring coefficients key: '%s'", key);
} }
} }
if (labs((long)rate - (long)SampleRate) < if (labs((long)rate - (long)SampleRate) <
@ -403,7 +409,7 @@ static void *bq_instantiate(const struct fc_plugin *plugin, const struct fc_desc
} }
} }
else { else {
pw_log_warn("biquads: ignoring config key: '%s'", key); spa_log_warn(impl->log, "biquads: ignoring config key: '%s'", key);
} }
} }
@ -532,7 +538,7 @@ static void bq_run(void *Instance, unsigned long samples)
if (impl->freq != freq || impl->Q != Q || impl->gain != gain) if (impl->freq != freq || impl->Q != Q || impl->gain != gain)
bq_freq_update(impl, impl->type, freq, Q, gain); bq_freq_update(impl, impl->type, freq, Q, gain);
} }
dsp_ops_biquad_run(impl->plugin->dsp_ops, bq, out, in, samples); dsp_ops_biquad_run(impl->dsp, bq, out, in, samples);
} }
/** bq_lowpass */ /** bq_lowpass */
@ -665,6 +671,9 @@ static const struct fc_descriptor bq_raw_desc = {
/** convolve */ /** convolve */
struct convolver_impl { struct convolver_impl {
struct plugin *plugin; struct plugin *plugin;
struct spa_log *log;
struct dsp_ops *dsp;
unsigned long rate; unsigned long rate;
float *port[2]; float *port[2];
@ -707,7 +716,7 @@ static float *read_samples_from_sf(SNDFILE *f, SF_INFO info, float gain, int del
} }
#endif #endif
static float *read_closest(char **filenames, float gain, float delay_sec, int offset, static float *read_closest(struct plugin *pl, char **filenames, float gain, float delay_sec, int offset,
int length, int channel, long unsigned *rate, int *n_samples) int length, int channel, long unsigned *rate, int *n_samples)
{ {
#ifdef HAVE_SNDFILE #ifdef HAVE_SNDFILE
@ -729,24 +738,24 @@ static float *read_closest(char **filenames, float gain, float delay_sec, int of
if (labs((long)infos[i].samplerate - (long)*rate) < diff) { if (labs((long)infos[i].samplerate - (long)*rate) < diff) {
best = i; best = i;
diff = labs((long)infos[i].samplerate - (long)*rate); diff = labs((long)infos[i].samplerate - (long)*rate);
pw_log_debug("new closest match: %d", infos[i].samplerate); spa_log_debug(pl->log, "new closest match: %d", infos[i].samplerate);
} }
} }
if (fs[best] != NULL) { if (fs[best] != NULL) {
pw_log_info("loading best rate:%u %s", infos[best].samplerate, filenames[best]); spa_log_info(pl->log, "loading best rate:%u %s", infos[best].samplerate, filenames[best]);
samples = read_samples_from_sf(fs[best], infos[best], gain, samples = read_samples_from_sf(fs[best], infos[best], gain,
(int) (delay_sec * infos[best].samplerate), offset, length, (int) (delay_sec * infos[best].samplerate), offset, length,
channel, rate, n_samples); channel, rate, n_samples);
} else { } else {
char buf[PATH_MAX]; char buf[PATH_MAX];
pw_log_error("Can't open any sample file (CWD %s):", spa_log_error(pl->log, "Can't open any sample file (CWD %s):",
getcwd(buf, sizeof(buf))); getcwd(buf, sizeof(buf)));
for (i = 0; i < MAX_RATES && filenames[i] && filenames[i][0]; i++) { for (i = 0; i < MAX_RATES && filenames[i] && filenames[i][0]; i++) {
fs[i] = sf_open(filenames[i], SFM_READ, &infos[i]); fs[i] = sf_open(filenames[i], SFM_READ, &infos[i]);
if (fs[i] == NULL) if (fs[i] == NULL)
pw_log_error(" failed file %s: %s", filenames[i], sf_strerror(fs[i])); spa_log_error(pl->log, " failed file %s: %s", filenames[i], sf_strerror(fs[i]));
else else
pw_log_warn(" unexpectedly opened file %s", filenames[i]); spa_log_warn(pl->log, " unexpectedly opened file %s", filenames[i]);
} }
} }
for (i = 0; i < MAX_RATES; i++) for (i = 0; i < MAX_RATES; i++)
@ -755,7 +764,7 @@ static float *read_closest(char **filenames, float gain, float delay_sec, int of
return samples; return samples;
#else #else
pw_log_error("compiled without sndfile support, can't load samples: " spa_log_error(pl->log, "compiled without sndfile support, can't load samples: "
"using dirac impulse"); "using dirac impulse");
float *samples = calloc(1, sizeof(float)); float *samples = calloc(1, sizeof(float));
samples[0] = gain; samples[0] = gain;
@ -764,7 +773,7 @@ static float *read_closest(char **filenames, float gain, float delay_sec, int of
#endif #endif
} }
static float *create_hilbert(const char *filename, float gain, int rate, float delay_sec, int offset, static float *create_hilbert(struct plugin *pl, const char *filename, float gain, int rate, float delay_sec, int offset,
int length, int *n_samples) int length, int *n_samples)
{ {
float *samples, v; float *samples, v;
@ -795,7 +804,7 @@ static float *create_hilbert(const char *filename, float gain, int rate, float d
return samples; return samples;
} }
static float *create_dirac(const char *filename, float gain, int rate, float delay_sec, int offset, static float *create_dirac(struct plugin *pl, const char *filename, float gain, int rate, float delay_sec, int offset,
int length, int *n_samples) int length, int *n_samples)
{ {
float *samples; float *samples;
@ -814,7 +823,7 @@ static float *create_dirac(const char *filename, float gain, int rate, float del
return samples; return samples;
} }
static float *resample_buffer(struct dsp_ops *dsp_ops, float *samples, int *n_samples, static float *resample_buffer(struct plugin *pl, float *samples, int *n_samples,
unsigned long in_rate, unsigned long out_rate, uint32_t quality) unsigned long in_rate, unsigned long out_rate, uint32_t quality)
{ {
#ifdef HAVE_SPA_PLUGINS #ifdef HAVE_SPA_PLUGINS
@ -828,10 +837,10 @@ static float *resample_buffer(struct dsp_ops *dsp_ops, float *samples, int *n_sa
r.channels = 1; r.channels = 1;
r.i_rate = in_rate; r.i_rate = in_rate;
r.o_rate = out_rate; r.o_rate = out_rate;
r.cpu_flags = dsp_ops->cpu_flags; r.cpu_flags = pl->dsp->cpu_flags;
r.quality = quality; r.quality = quality;
if ((res = resample_native_init(&r)) < 0) { if ((res = resample_native_init(&r)) < 0) {
pw_log_error("resampling failed: %s", spa_strerror(res)); spa_log_error(pl->log, "resampling failed: %s", spa_strerror(res));
errno = -res; errno = -res;
return NULL; return NULL;
} }
@ -846,11 +855,11 @@ static float *resample_buffer(struct dsp_ops *dsp_ops, float *samples, int *n_sa
out_len = out_n_samples; out_len = out_n_samples;
out_buf = out_samples; out_buf = out_samples;
pw_log_info("Resampling filter: rate: %lu => %lu, n_samples: %u => %u, q:%u", spa_log_info(pl->log, "Resampling filter: rate: %lu => %lu, n_samples: %u => %u, q:%u",
in_rate, out_rate, in_len, out_len, quality); in_rate, out_rate, in_len, out_len, quality);
resample_process(&r, (void*)&in_buf, &in_len, (void*)&out_buf, &out_len); resample_process(&r, (void*)&in_buf, &in_len, (void*)&out_buf, &out_len);
pw_log_debug("resampled: %u -> %u samples", in_len, out_len); spa_log_debug(pl->log, "resampled: %u -> %u samples", in_len, out_len);
total_out += out_len; total_out += out_len;
in_len = resample_delay(&r); in_len = resample_delay(&r);
@ -861,9 +870,9 @@ static float *resample_buffer(struct dsp_ops *dsp_ops, float *samples, int *n_sa
out_buf = out_samples + total_out; out_buf = out_samples + total_out;
out_len = out_n_samples - total_out; out_len = out_n_samples - total_out;
pw_log_debug("flushing resampler: %u in %u out", in_len, out_len); spa_log_debug(pl->log, "flushing resampler: %u in %u out", in_len, out_len);
resample_process(&r, (void*)&in_buf, &in_len, (void*)&out_buf, &out_len); resample_process(&r, (void*)&in_buf, &in_len, (void*)&out_buf, &out_len);
pw_log_debug("flushed: %u -> %u samples", in_len, out_len); spa_log_debug(pl->log, "flushed: %u -> %u samples", in_len, out_len);
total_out += out_len; total_out += out_len;
free(in_buf); free(in_buf);
@ -884,7 +893,7 @@ error:
free(out_samples); free(out_samples);
return NULL; return NULL;
#else #else
pw_log_error("compiled without spa-plugins support, can't resample"); spa_log_error(impl->log, "compiled without spa-plugins support, can't resample");
float *out_samples = calloc(*n_samples, sizeof(float)); float *out_samples = calloc(*n_samples, sizeof(float));
memcpy(out_samples, samples, *n_samples * sizeof(float)); memcpy(out_samples, samples, *n_samples * sizeof(float));
return out_samples; return out_samples;
@ -895,6 +904,7 @@ static void * convolver_instantiate(const struct fc_plugin *plugin, const struct
unsigned long SampleRate, int index, const char *config) unsigned long SampleRate, int index, const char *config)
{ {
struct convolver_impl *impl; struct convolver_impl *impl;
struct plugin *pl = (struct plugin*)plugin;
float *samples; float *samples;
int offset = 0, length = 0, channel = index, n_samples = 0, len; int offset = 0, length = 0, channel = index, n_samples = 0, len;
uint32_t i = 0; uint32_t i = 0;
@ -909,31 +919,31 @@ static void * convolver_instantiate(const struct fc_plugin *plugin, const struct
errno = EINVAL; errno = EINVAL;
if (config == NULL) { if (config == NULL) {
pw_log_error("convolver: requires a config section"); spa_log_error(pl->log, "convolver: requires a config section");
return NULL; return NULL;
} }
if (spa_json_begin_object(&it[0], config, strlen(config)) <= 0) { if (spa_json_begin_object(&it[0], config, strlen(config)) <= 0) {
pw_log_error("convolver:config must be an object"); spa_log_error(pl->log, "convolver:config must be an object");
return NULL; return NULL;
} }
while ((len = spa_json_object_next(&it[0], key, sizeof(key), &val)) > 0) { while ((len = spa_json_object_next(&it[0], key, sizeof(key), &val)) > 0) {
if (spa_streq(key, "blocksize")) { if (spa_streq(key, "blocksize")) {
if (spa_json_parse_int(val, len, &blocksize) <= 0) { if (spa_json_parse_int(val, len, &blocksize) <= 0) {
pw_log_error("convolver:blocksize requires a number"); spa_log_error(pl->log, "convolver:blocksize requires a number");
return NULL; return NULL;
} }
} }
else if (spa_streq(key, "tailsize")) { else if (spa_streq(key, "tailsize")) {
if (spa_json_parse_int(val, len, &tailsize) <= 0) { if (spa_json_parse_int(val, len, &tailsize) <= 0) {
pw_log_error("convolver:tailsize requires a number"); spa_log_error(pl->log, "convolver:tailsize requires a number");
return NULL; return NULL;
} }
} }
else if (spa_streq(key, "gain")) { else if (spa_streq(key, "gain")) {
if (spa_json_parse_float(val, len, &gain) <= 0) { if (spa_json_parse_float(val, len, &gain) <= 0) {
pw_log_error("convolver:gain requires a number"); spa_log_error(pl->log, "convolver:gain requires a number");
return NULL; return NULL;
} }
} }
@ -942,7 +952,7 @@ static void * convolver_instantiate(const struct fc_plugin *plugin, const struct
if (spa_json_parse_int(val, len, &delay_i) > 0) { if (spa_json_parse_int(val, len, &delay_i) > 0) {
delay = delay_i / (float)SampleRate; delay = delay_i / (float)SampleRate;
} else if (spa_json_parse_float(val, len, &delay) <= 0) { } else if (spa_json_parse_float(val, len, &delay) <= 0) {
pw_log_error("convolver:delay requires a number"); spa_log_error(pl->log, "convolver:delay requires a number");
return NULL; return NULL;
} }
} }
@ -956,7 +966,7 @@ static void * convolver_instantiate(const struct fc_plugin *plugin, const struct
} }
} }
else if (spa_json_parse_stringn(val, len, v, sizeof(v)) <= 0) { else if (spa_json_parse_stringn(val, len, v, sizeof(v)) <= 0) {
pw_log_error("convolver:filename requires a string or an array"); spa_log_error(pl->log, "convolver:filename requires a string or an array");
return NULL; return NULL;
} else { } else {
filenames[0] = strdup(v); filenames[0] = strdup(v);
@ -964,34 +974,34 @@ static void * convolver_instantiate(const struct fc_plugin *plugin, const struct
} }
else if (spa_streq(key, "offset")) { else if (spa_streq(key, "offset")) {
if (spa_json_parse_int(val, len, &offset) <= 0) { if (spa_json_parse_int(val, len, &offset) <= 0) {
pw_log_error("convolver:offset requires a number"); spa_log_error(pl->log, "convolver:offset requires a number");
return NULL; return NULL;
} }
} }
else if (spa_streq(key, "length")) { else if (spa_streq(key, "length")) {
if (spa_json_parse_int(val, len, &length) <= 0) { if (spa_json_parse_int(val, len, &length) <= 0) {
pw_log_error("convolver:length requires a number"); spa_log_error(pl->log, "convolver:length requires a number");
return NULL; return NULL;
} }
} }
else if (spa_streq(key, "channel")) { else if (spa_streq(key, "channel")) {
if (spa_json_parse_int(val, len, &channel) <= 0) { if (spa_json_parse_int(val, len, &channel) <= 0) {
pw_log_error("convolver:channel requires a number"); spa_log_error(pl->log, "convolver:channel requires a number");
return NULL; return NULL;
} }
} }
else if (spa_streq(key, "resample_quality")) { else if (spa_streq(key, "resample_quality")) {
if (spa_json_parse_int(val, len, &resample_quality) <= 0) { if (spa_json_parse_int(val, len, &resample_quality) <= 0) {
pw_log_error("convolver:resample_quality requires a number"); spa_log_error(pl->log, "convolver:resample_quality requires a number");
return NULL; return NULL;
} }
} }
else { else {
pw_log_warn("convolver: ignoring config key: '%s'", key); spa_log_warn(pl->log, "convolver: ignoring config key: '%s'", key);
} }
} }
if (filenames[0] == NULL) { if (filenames[0] == NULL) {
pw_log_error("convolver:filename was not given"); spa_log_error(pl->log, "convolver:filename was not given");
return NULL; return NULL;
} }
@ -1001,18 +1011,17 @@ static void * convolver_instantiate(const struct fc_plugin *plugin, const struct
offset = 0; offset = 0;
if (spa_streq(filenames[0], "/hilbert")) { if (spa_streq(filenames[0], "/hilbert")) {
samples = create_hilbert(filenames[0], gain, SampleRate, delay, offset, samples = create_hilbert(pl, filenames[0], gain, SampleRate, delay, offset,
length, &n_samples); length, &n_samples);
} else if (spa_streq(filenames[0], "/dirac")) { } else if (spa_streq(filenames[0], "/dirac")) {
samples = create_dirac(filenames[0], gain, SampleRate, delay, offset, samples = create_dirac(pl, filenames[0], gain, SampleRate, delay, offset,
length, &n_samples); length, &n_samples);
} else { } else {
rate = SampleRate; rate = SampleRate;
samples = read_closest(filenames, gain, delay, offset, samples = read_closest(pl, filenames, gain, delay, offset,
length, channel, &rate, &n_samples); length, channel, &rate, &n_samples);
if (samples != NULL && rate != SampleRate) { if (samples != NULL && rate != SampleRate) {
struct plugin *p = (struct plugin *) plugin; samples = resample_buffer(pl, samples, &n_samples,
samples = resample_buffer(p->dsp_ops, samples, &n_samples,
rate, SampleRate, resample_quality); rate, SampleRate, resample_quality);
} }
} }
@ -1031,17 +1040,19 @@ static void * convolver_instantiate(const struct fc_plugin *plugin, const struct
if (tailsize <= 0) if (tailsize <= 0)
tailsize = SPA_CLAMP(4096, blocksize, 32768); tailsize = SPA_CLAMP(4096, blocksize, 32768);
pw_log_info("using n_samples:%u %d:%d blocksize delay:%f", n_samples, spa_log_info(pl->log, "using n_samples:%u %d:%d blocksize delay:%f", n_samples,
blocksize, tailsize, delay); blocksize, tailsize, delay);
impl = calloc(1, sizeof(*impl)); impl = calloc(1, sizeof(*impl));
if (impl == NULL) if (impl == NULL)
goto error; goto error;
impl->plugin = (struct plugin *) plugin; impl->plugin = pl;
impl->log = pl->log;
impl->dsp = pl->dsp;
impl->rate = SampleRate; impl->rate = SampleRate;
impl->conv = convolver_new(impl->plugin->dsp_ops, blocksize, tailsize, samples, n_samples); impl->conv = convolver_new(impl->dsp, blocksize, tailsize, samples, n_samples);
if (impl->conv == NULL) if (impl->conv == NULL)
goto error; goto error;
@ -1110,6 +1121,10 @@ static const struct fc_descriptor convolve_desc = {
/** delay */ /** delay */
struct delay_impl { struct delay_impl {
struct plugin *plugin; struct plugin *plugin;
struct dsp_ops *dsp;
struct spa_log *log;
unsigned long rate; unsigned long rate;
float *port[4]; float *port[4];
@ -1130,6 +1145,7 @@ static void delay_cleanup(void * Instance)
static void *delay_instantiate(const struct fc_plugin *plugin, const struct fc_descriptor * Descriptor, static void *delay_instantiate(const struct fc_plugin *plugin, const struct fc_descriptor * Descriptor,
unsigned long SampleRate, int index, const char *config) unsigned long SampleRate, int index, const char *config)
{ {
struct plugin *pl = (struct plugin*) plugin;
struct delay_impl *impl; struct delay_impl *impl;
struct spa_json it[1]; struct spa_json it[1];
const char *val; const char *val;
@ -1138,24 +1154,24 @@ static void *delay_instantiate(const struct fc_plugin *plugin, const struct fc_d
int len; int len;
if (config == NULL) { if (config == NULL) {
pw_log_error("delay: requires a config section"); spa_log_error(pl->log, "delay: requires a config section");
errno = EINVAL; errno = EINVAL;
return NULL; return NULL;
} }
if (spa_json_begin_object(&it[0], config, strlen(config)) <= 0) { if (spa_json_begin_object(&it[0], config, strlen(config)) <= 0) {
pw_log_error("delay:config must be an object"); spa_log_error(pl->log, "delay:config must be an object");
return NULL; return NULL;
} }
while ((len = spa_json_object_next(&it[0], key, sizeof(key), &val)) > 0) { while ((len = spa_json_object_next(&it[0], key, sizeof(key), &val)) > 0) {
if (spa_streq(key, "max-delay")) { if (spa_streq(key, "max-delay")) {
if (spa_json_parse_float(val, len, &max_delay) <= 0) { if (spa_json_parse_float(val, len, &max_delay) <= 0) {
pw_log_error("delay:max-delay requires a number"); spa_log_error(pl->log, "delay:max-delay requires a number");
return NULL; return NULL;
} }
} else { } else {
pw_log_warn("delay: ignoring config key: '%s'", key); spa_log_warn(pl->log, "delay: ignoring config key: '%s'", key);
} }
} }
if (max_delay <= 0.0f) if (max_delay <= 0.0f)
@ -1165,10 +1181,12 @@ static void *delay_instantiate(const struct fc_plugin *plugin, const struct fc_d
if (impl == NULL) if (impl == NULL)
return NULL; return NULL;
impl->plugin = (struct plugin *) plugin; impl->plugin = pl;
impl->dsp = pl->dsp;
impl->log = pl->log;
impl->rate = SampleRate; impl->rate = SampleRate;
impl->buffer_samples = SPA_ROUND_UP_N((uint32_t)(max_delay * impl->rate), 64); impl->buffer_samples = SPA_ROUND_UP_N((uint32_t)(max_delay * impl->rate), 64);
pw_log_info("max-delay:%f seconds rate:%lu samples:%d", max_delay, impl->rate, impl->buffer_samples); spa_log_info(impl->log, "max-delay:%f seconds rate:%lu samples:%d", max_delay, impl->rate, impl->buffer_samples);
impl->buffer = calloc(impl->buffer_samples * 2 + 64, sizeof(float)); impl->buffer = calloc(impl->buffer_samples * 2 + 64, sizeof(float));
if (impl->buffer == NULL) { if (impl->buffer == NULL) {
@ -1200,7 +1218,7 @@ static void delay_run(void * Instance, unsigned long SampleCount)
impl->delay_samples = SPA_CLAMP((uint32_t)(delay * impl->rate), 0u, impl->buffer_samples-1); impl->delay_samples = SPA_CLAMP((uint32_t)(delay * impl->rate), 0u, impl->buffer_samples-1);
impl->delay = delay; impl->delay = delay;
} }
dsp_ops_delay(impl->plugin->dsp_ops, impl->buffer, &impl->ptr, impl->buffer_samples, dsp_ops_delay(impl->dsp, impl->buffer, &impl->ptr, impl->buffer_samples,
impl->delay_samples, out, in, SampleCount); impl->delay_samples, out, in, SampleCount);
} }
@ -1334,7 +1352,7 @@ static void linear_run(void * Instance, unsigned long SampleCount)
float *ctrl = impl->port[3], *notify = impl->port[2]; float *ctrl = impl->port[3], *notify = impl->port[2];
if (in != NULL && out != NULL) if (in != NULL && out != NULL)
dsp_ops_linear(impl->plugin->dsp_ops, out, in, mult, add, SampleCount); dsp_ops_linear(impl->dsp, out, in, mult, add, SampleCount);
if (ctrl != NULL && notify != NULL) if (ctrl != NULL && notify != NULL)
notify[0] = ctrl[0] * mult + add; notify[0] = ctrl[0] * mult + add;
@ -1579,7 +1597,7 @@ static void mult_run(void * Instance, unsigned long SampleCount)
src[n_src++] = in; src[n_src++] = in;
} }
dsp_ops_mult(impl->plugin->dsp_ops, out, src, n_src, SampleCount); dsp_ops_mult(impl->dsp, out, src, n_src, SampleCount);
} }
static struct fc_port mult_ports[] = { static struct fc_port mult_ports[] = {
@ -1706,6 +1724,10 @@ static const struct fc_descriptor sine_desc = {
#define PARAM_EQ_MAX 64 #define PARAM_EQ_MAX 64
struct param_eq_impl { struct param_eq_impl {
struct plugin *plugin; struct plugin *plugin;
struct dsp_ops *dsp;
struct spa_log *log;
unsigned long rate; unsigned long rate;
float *port[8*2]; float *port[8*2];
@ -1713,7 +1735,8 @@ struct param_eq_impl {
struct biquad bq[PARAM_EQ_MAX * 8]; struct biquad bq[PARAM_EQ_MAX * 8];
}; };
static int load_eq_bands(const char *filename, int rate, struct biquad *bq, uint32_t max_bq, uint32_t *n_bq) static int load_eq_bands(struct plugin *pl, const char *filename, int rate,
struct biquad *bq, uint32_t max_bq, uint32_t *n_bq)
{ {
FILE *f = NULL; FILE *f = NULL;
char *line = NULL; char *line = NULL;
@ -1728,7 +1751,7 @@ static int load_eq_bands(const char *filename, int rate, struct biquad *bq, uint
if ((f = fopen(filename, "r")) == NULL) { if ((f = fopen(filename, "r")) == NULL) {
res = -errno; res = -errno;
pw_log_error("failed to open param_eq file '%s': %m", filename); spa_log_error(pl->log, "failed to open param_eq file '%s': %m", filename);
goto exit; goto exit;
} }
/* /*
@ -1746,7 +1769,7 @@ static int load_eq_bands(const char *filename, int rate, struct biquad *bq, uint
nread = getline(&line, &linelen, f); nread = getline(&line, &linelen, f);
if (nread != -1 && sscanf(line, "%*s %6s %*s", gain) == 1) { if (nread != -1 && sscanf(line, "%*s %6s %*s", gain) == 1) {
if (spa_json_parse_float(gain, strlen(gain), &vg)) { if (spa_json_parse_float(gain, strlen(gain), &vg)) {
pw_log_info("%d %s freq:0 q:1.0 gain:%f", n, spa_log_info(pl->log, "%d %s freq:0 q:1.0 gain:%f", n,
bq_name_from_type(BQ_HIGHSHELF), vg); bq_name_from_type(BQ_HIGHSHELF), vg);
biquad_set(&bq[n++], BQ_HIGHSHELF, 0.0f, 1.0f, vg); biquad_set(&bq[n++], BQ_HIGHSHELF, 0.0f, 1.0f, vg);
} }
@ -1784,7 +1807,7 @@ static int load_eq_bands(const char *filename, int rate, struct biquad *bq, uint
if (spa_json_parse_float(gain, strlen(gain), &vg) && if (spa_json_parse_float(gain, strlen(gain), &vg) &&
spa_json_parse_float(q, strlen(q), &vq)) { spa_json_parse_float(q, strlen(q), &vq)) {
pw_log_info("%d %s freq:%d q:%f gain:%f", n, spa_log_info(pl->log, "%d %s freq:%d q:%f gain:%f", n,
bq_name_from_type(type), freq, vq, vg); bq_name_from_type(type), freq, vq, vg);
biquad_set(&bq[n++], type, freq * 2.0f / rate, vq, vg); biquad_set(&bq[n++], type, freq * 2.0f / rate, vq, vg);
} }
@ -1810,7 +1833,8 @@ exit:
* { type=bq_peaking freq=1600 gain=2.3 q=2.700 } * { type=bq_peaking freq=1600 gain=2.3 q=2.700 }
* ] * ]
*/ */
static int parse_filters(struct spa_json *iter, int rate, struct biquad *bq, uint32_t max_bq, uint32_t *n_bq) static int parse_filters(struct plugin *pl, struct spa_json *iter, int rate,
struct biquad *bq, uint32_t max_bq, uint32_t *n_bq)
{ {
struct spa_json it[1]; struct spa_json it[1];
const char *val; const char *val;
@ -1826,37 +1850,37 @@ static int parse_filters(struct spa_json *iter, int rate, struct biquad *bq, uin
while ((len = spa_json_object_next(&it[0], key, sizeof(key), &val)) > 0) { while ((len = spa_json_object_next(&it[0], key, sizeof(key), &val)) > 0) {
if (spa_streq(key, "type")) { if (spa_streq(key, "type")) {
if (spa_json_parse_stringn(val, len, type_str, sizeof(type_str)) <= 0) { if (spa_json_parse_stringn(val, len, type_str, sizeof(type_str)) <= 0) {
pw_log_error("param_eq:type requires a string"); spa_log_error(pl->log, "param_eq:type requires a string");
return -EINVAL; return -EINVAL;
} }
type = bq_type_from_name(type_str); type = bq_type_from_name(type_str);
} }
else if (spa_streq(key, "freq")) { else if (spa_streq(key, "freq")) {
if (spa_json_parse_float(val, len, &freq) <= 0) { if (spa_json_parse_float(val, len, &freq) <= 0) {
pw_log_error("param_eq:rate requires a number"); spa_log_error(pl->log, "param_eq:rate requires a number");
return -EINVAL; return -EINVAL;
} }
} }
else if (spa_streq(key, "q")) { else if (spa_streq(key, "q")) {
if (spa_json_parse_float(val, len, &q) <= 0) { if (spa_json_parse_float(val, len, &q) <= 0) {
pw_log_error("param_eq:q requires a float"); spa_log_error(pl->log, "param_eq:q requires a float");
return -EINVAL; return -EINVAL;
} }
} }
else if (spa_streq(key, "gain")) { else if (spa_streq(key, "gain")) {
if (spa_json_parse_float(val, len, &gain) <= 0) { if (spa_json_parse_float(val, len, &gain) <= 0) {
pw_log_error("param_eq:gain requires a float"); spa_log_error(pl->log, "param_eq:gain requires a float");
return -EINVAL; return -EINVAL;
} }
} }
else { else {
pw_log_warn("param_eq: ignoring filter key: '%s'", key); spa_log_warn(pl->log, "param_eq: ignoring filter key: '%s'", key);
} }
} }
if (n == max_bq) if (n == max_bq)
return -ENOSPC; return -ENOSPC;
pw_log_info("%d %s freq:%f q:%f gain:%f", n, spa_log_info(pl->log, "%d %s freq:%f q:%f gain:%f", n,
bq_name_from_type(type), freq, q, gain); bq_name_from_type(type), freq, q, gain);
biquad_set(&bq[n++], type, freq * 2 / rate, q, gain); biquad_set(&bq[n++], type, freq * 2 / rate, q, gain);
} }
@ -1875,6 +1899,7 @@ static int parse_filters(struct spa_json *iter, int rate, struct biquad *bq, uin
static void *param_eq_instantiate(const struct fc_plugin *plugin, const struct fc_descriptor * Descriptor, static void *param_eq_instantiate(const struct fc_plugin *plugin, const struct fc_descriptor * Descriptor,
unsigned long SampleRate, int index, const char *config) unsigned long SampleRate, int index, const char *config)
{ {
struct plugin *pl = (struct plugin *) plugin;
struct spa_json it[3]; struct spa_json it[3];
const char *val; const char *val;
char key[256], filename[PATH_MAX]; char key[256], filename[PATH_MAX];
@ -1883,13 +1908,13 @@ static void *param_eq_instantiate(const struct fc_plugin *plugin, const struct f
uint32_t i, n_bq = 0; uint32_t i, n_bq = 0;
if (config == NULL) { if (config == NULL) {
pw_log_error("param_eq: requires a config section"); spa_log_error(pl->log, "param_eq: requires a config section");
errno = EINVAL; errno = EINVAL;
return NULL; return NULL;
} }
if (spa_json_begin_object(&it[0], config, strlen(config)) <= 0) { if (spa_json_begin_object(&it[0], config, strlen(config)) <= 0) {
pw_log_error("param_eq: config must be an object"); spa_log_error(pl->log, "param_eq: config must be an object");
return NULL; return NULL;
} }
@ -1897,7 +1922,9 @@ static void *param_eq_instantiate(const struct fc_plugin *plugin, const struct f
if (impl == NULL) if (impl == NULL)
return NULL; return NULL;
impl->plugin = (struct plugin *) plugin; impl->plugin = pl;
impl->dsp = pl->dsp;
impl->log = pl->log;
impl->rate = SampleRate; impl->rate = SampleRate;
for (i = 0; i < SPA_N_ELEMENTS(impl->bq); i++) for (i = 0; i < SPA_N_ELEMENTS(impl->bq); i++)
biquad_set(&impl->bq[i], BQ_NONE, 0.0f, 0.0f, 0.0f); biquad_set(&impl->bq[i], BQ_NONE, 0.0f, 0.0f, 0.0f);
@ -1908,23 +1935,23 @@ static void *param_eq_instantiate(const struct fc_plugin *plugin, const struct f
if (spa_strstartswith(key, "filename")) { if (spa_strstartswith(key, "filename")) {
if (spa_json_parse_stringn(val, len, filename, sizeof(filename)) <= 0) { if (spa_json_parse_stringn(val, len, filename, sizeof(filename)) <= 0) {
pw_log_error("param_eq: filename requires a string"); spa_log_error(impl->log, "param_eq: filename requires a string");
goto error; goto error;
} }
if (spa_atoi32(key+8, &idx, 0)) if (spa_atoi32(key+8, &idx, 0))
bq = &impl->bq[(SPA_CLAMP(idx, 1, 8) - 1) * PARAM_EQ_MAX]; bq = &impl->bq[(SPA_CLAMP(idx, 1, 8) - 1) * PARAM_EQ_MAX];
res = load_eq_bands(filename, impl->rate, bq, PARAM_EQ_MAX, &n_bq); res = load_eq_bands(pl, filename, impl->rate, bq, PARAM_EQ_MAX, &n_bq);
if (res < 0) { if (res < 0) {
pw_log_error("param_eq: failed to parse configuration from '%s'", filename); spa_log_error(impl->log, "param_eq: failed to parse configuration from '%s'", filename);
goto error; goto error;
} }
pw_log_info("loaded %d biquads for channel %d", n_bq, idx); spa_log_info(impl->log, "loaded %d biquads for channel %d", n_bq, idx);
impl->n_bq = SPA_MAX(impl->n_bq, n_bq); impl->n_bq = SPA_MAX(impl->n_bq, n_bq);
} }
else if (spa_strstartswith(key, "filters")) { else if (spa_strstartswith(key, "filters")) {
if (!spa_json_is_array(val, len)) { if (!spa_json_is_array(val, len)) {
pw_log_error("param_eq:filters require an array"); spa_log_error(impl->log, "param_eq:filters require an array");
goto error; goto error;
} }
spa_json_enter(&it[0], &it[1]); spa_json_enter(&it[0], &it[1]);
@ -1932,15 +1959,15 @@ static void *param_eq_instantiate(const struct fc_plugin *plugin, const struct f
if (spa_atoi32(key+7, &idx, 0)) if (spa_atoi32(key+7, &idx, 0))
bq = &impl->bq[(SPA_CLAMP(idx, 1, 8) - 1) * PARAM_EQ_MAX]; bq = &impl->bq[(SPA_CLAMP(idx, 1, 8) - 1) * PARAM_EQ_MAX];
res = parse_filters(&it[1], impl->rate, bq, PARAM_EQ_MAX, &n_bq); res = parse_filters(pl, &it[1], impl->rate, bq, PARAM_EQ_MAX, &n_bq);
if (res < 0) { if (res < 0) {
pw_log_error("param_eq: failed to parse configuration"); spa_log_error(impl->log, "param_eq: failed to parse configuration");
goto error; goto error;
} }
pw_log_info("parsed %d biquads for channel %d", n_bq, idx); spa_log_info(impl->log, "parsed %d biquads for channel %d", n_bq, idx);
impl->n_bq = SPA_MAX(impl->n_bq, n_bq); impl->n_bq = SPA_MAX(impl->n_bq, n_bq);
} else { } else {
pw_log_warn("param_eq: ignoring config key: '%s'", key); spa_log_warn(impl->log, "param_eq: ignoring config key: '%s'", key);
} }
if (idx == 0) { if (idx == 0) {
for (i = 1; i < 8; i++) for (i = 1; i < 8; i++)
@ -1964,7 +1991,7 @@ static void param_eq_connect_port(void * Instance, unsigned long Port,
static void param_eq_run(void * Instance, unsigned long SampleCount) static void param_eq_run(void * Instance, unsigned long SampleCount)
{ {
struct param_eq_impl *impl = Instance; struct param_eq_impl *impl = Instance;
dsp_ops_biquadn_run(impl->plugin->dsp_ops, impl->bq, impl->n_bq, PARAM_EQ_MAX, dsp_ops_biquadn_run(impl->dsp, impl->bq, impl->n_bq, PARAM_EQ_MAX,
&impl->port[8], (const float**)impl->port, 8, SampleCount); &impl->port[8], (const float**)impl->port, 8, SampleCount);
} }
@ -2124,7 +2151,8 @@ struct fc_plugin *load_builtin_plugin(const struct spa_support *support, uint32_
struct plugin *impl = calloc (1, sizeof (struct plugin)); struct plugin *impl = calloc (1, sizeof (struct plugin));
impl->plugin.make_desc = builtin_make_desc; impl->plugin.make_desc = builtin_make_desc;
impl->plugin.unload = builtin_plugin_unload; impl->plugin.unload = builtin_plugin_unload;
impl->dsp_ops = dsp; impl->dsp = dsp;
pffft_select_cpu(dsp->cpu_flags); pffft_select_cpu(dsp->cpu_flags);
impl->log = spa_support_find(support, n_support, SPA_TYPE_INTERFACE_Log);
return (struct fc_plugin *) impl; return (struct fc_plugin *) impl;
} }

View file

@ -11,15 +11,16 @@
#include <spa/utils/defs.h> #include <spa/utils/defs.h>
#include <spa/utils/list.h> #include <spa/utils/list.h>
#include <spa/utils/string.h> #include <spa/utils/string.h>
#include <spa/support/log.h>
#include <pipewire/log.h>
#include <pipewire/utils.h>
#include "plugin.h" #include "plugin.h"
#include "ladspa.h" #include "ladspa.h"
struct plugin { struct plugin {
struct fc_plugin plugin; struct fc_plugin plugin;
struct spa_log *log;
void *handle; void *handle;
LADSPA_Descriptor_Function desc_func; LADSPA_Descriptor_Function desc_func;
}; };
@ -172,47 +173,70 @@ static void ladspa_unload(struct fc_plugin *plugin)
free(p); free(p);
} }
static struct fc_plugin *ladspa_handle_load_by_path(const char *path) static struct fc_plugin *ladspa_handle_load_by_path(struct spa_log *log, const char *path)
{ {
struct plugin *p; struct plugin *p;
int res; int res;
void *handle = NULL;
LADSPA_Descriptor_Function desc_func;
p = calloc(1, sizeof(*p)); handle = dlopen(path, RTLD_NOW);
if (!p) if (handle == NULL) {
return NULL; spa_log_debug(log, "failed to open '%s': %s", path, dlerror());
p->handle = dlopen(path, RTLD_NOW);
if (!p->handle) {
pw_log_debug("failed to open '%s': %s", path, dlerror());
res = -ENOENT; res = -ENOENT;
goto exit; goto exit;
} }
pw_log_info("successfully opened '%s'", path); spa_log_info(log, "successfully opened '%s'", path);
p->desc_func = (LADSPA_Descriptor_Function) dlsym(p->handle, "ladspa_descriptor"); desc_func = (LADSPA_Descriptor_Function) dlsym(handle, "ladspa_descriptor");
if (!p->desc_func) { if (desc_func == NULL) {
pw_log_warn("cannot find descriptor function in '%s': %s", path, dlerror()); spa_log_warn(log, "cannot find descriptor function in '%s': %s", path, dlerror());
res = -ENOSYS; res = -ENOSYS;
goto exit; 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.make_desc = ladspa_make_desc; p->plugin.make_desc = ladspa_make_desc;
p->plugin.unload = ladspa_unload; p->plugin.unload = ladspa_unload;
return &p->plugin; return &p->plugin;
exit: exit:
if (p->handle) if (handle)
dlclose(p->handle); dlclose(handle);
free(p);
errno = -res; errno = -res;
return NULL; return NULL;
} }
static inline const char *split_walk(const char *str, const char *delimiter, size_t * len, const char **state)
{
const char *s = *state ? *state : str;
s += strspn(s, delimiter);
if (*s == '\0')
return NULL;
*len = strcspn(s, delimiter);
*state = s + *len;
return s;
}
struct fc_plugin *load_ladspa_plugin(const struct spa_support *support, uint32_t n_support, struct fc_plugin *load_ladspa_plugin(const struct spa_support *support, uint32_t n_support,
struct dsp_ops *dsp, const char *plugin, const struct spa_dict *info) struct dsp_ops *dsp, const char *plugin, const struct spa_dict *info)
{ {
struct fc_plugin *pl = NULL; struct fc_plugin *pl = NULL;
struct spa_log *log;
log = spa_support_find(support, n_support, SPA_TYPE_INTERFACE_Log);
if (plugin[0] != '/') { if (plugin[0] != '/') {
const char *search_dirs, *p, *state = NULL; const char *search_dirs, *p, *state = NULL;
@ -230,7 +254,7 @@ struct fc_plugin *load_ladspa_plugin(const struct spa_support *support, uint32_t
*/ */
errno = ENAMETOOLONG; errno = ENAMETOOLONG;
while ((p = pw_split_walk(search_dirs, ":", &len, &state))) { while ((p = split_walk(search_dirs, ":", &len, &state))) {
int pathlen; int pathlen;
if (len >= sizeof(path)) if (len >= sizeof(path))
@ -240,17 +264,17 @@ struct fc_plugin *load_ladspa_plugin(const struct spa_support *support, uint32_t
if (pathlen < 0 || (size_t) pathlen >= sizeof(path)) if (pathlen < 0 || (size_t) pathlen >= sizeof(path))
continue; continue;
pl = ladspa_handle_load_by_path(path); pl = ladspa_handle_load_by_path(log, path);
if (pl != NULL) if (pl != NULL)
break; break;
} }
} }
else { else {
pl = ladspa_handle_load_by_path(plugin); pl = ladspa_handle_load_by_path(log, plugin);
} }
if (pl == NULL) if (pl == NULL)
pw_log_error("failed to load plugin '%s': %s", plugin, strerror(errno)); spa_log_error(log, "failed to load plugin '%s': %s", plugin, strerror(errno));
return pl; return pl;
} }

View file

@ -5,16 +5,13 @@
#include <dlfcn.h> #include <dlfcn.h>
#include <math.h> #include <math.h>
#include <lilv/lilv.h>
#include <spa/utils/defs.h> #include <spa/utils/defs.h>
#include <spa/utils/list.h> #include <spa/utils/list.h>
#include <spa/utils/string.h> #include <spa/utils/string.h>
#include <spa/support/loop.h> #include <spa/support/loop.h>
#include <spa/support/log.h>
#include <pipewire/log.h>
#include <pipewire/utils.h>
#include <pipewire/array.h>
#include <lilv/lilv.h>
#if defined __has_include #if defined __has_include
# if __has_include (<lv2/atom/atom.h>) # if __has_include (<lv2/atom/atom.h>)
@ -42,45 +39,48 @@
static struct context *_context; static struct context *_context;
typedef struct URITable { typedef struct URITable {
struct pw_array array; char **data;
size_t alloc;
size_t len;
} URITable; } URITable;
static void uri_table_init(URITable *table) static void uri_table_init(URITable *table)
{ {
pw_array_init(&table->array, 1024); table->data = NULL;
table->len = table->alloc = 0;
} }
static void uri_table_destroy(URITable *table) static void uri_table_destroy(URITable *table)
{ {
char **p; size_t i;
pw_array_for_each(p, &table->array) for (i = 0; i < table->len; i++)
free(*p); free(table->data[i]);
pw_array_clear(&table->array); free(table->data);
uri_table_init(table);
} }
static LV2_URID uri_table_map(LV2_URID_Map_Handle handle, const char *uri) static LV2_URID uri_table_map(LV2_URID_Map_Handle handle, const char *uri)
{ {
URITable *table = (URITable*)handle; URITable *table = (URITable*)handle;
char **p; size_t i;
size_t i = 0;
pw_array_for_each(p, &table->array) { for (i = 0; i < table->len; i++)
i++; if (spa_streq(table->data[i], uri))
if (spa_streq(*p, uri)) return i+1;
goto done;
if (table->len == table->alloc) {
table->alloc += 64;
table->data = realloc(table->data, table->alloc * sizeof(char *));
} }
pw_array_add_ptr(&table->array, strdup(uri)); table->data[table->len++] = strdup(uri);
i = pw_array_get_len(&table->array, char*); return table->len;
done:
return i;
} }
static const char *uri_table_unmap(LV2_URID_Map_Handle handle, LV2_URID urid) static const char *uri_table_unmap(LV2_URID_Map_Handle handle, LV2_URID urid)
{ {
URITable *table = (URITable*)handle; URITable *table = (URITable*)handle;
if (urid > 0 && urid <= table->len)
if (urid > 0 && urid <= pw_array_get_len(&table->array, char*)) return table->data[urid-1];
return *pw_array_get_unchecked(&table->array, urid - 1, char*);
return NULL; return NULL;
} }
@ -88,6 +88,7 @@ struct context {
int ref; int ref;
LilvWorld *world; LilvWorld *world;
struct spa_log *log;
struct spa_loop *data_loop; struct spa_loop *data_loop;
struct spa_loop *main_loop; struct spa_loop *main_loop;
@ -185,6 +186,7 @@ static struct context *context_new(const struct spa_support *support, uint32_t n
c->atom_Int = context_map(c, LV2_ATOM__Int); c->atom_Int = context_map(c, LV2_ATOM__Int);
c->atom_Float = context_map(c, LV2_ATOM__Float); 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->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); c->main_loop = spa_support_find(support, n_support, SPA_TYPE_INTERFACE_Loop);
@ -475,7 +477,7 @@ struct fc_plugin *pipewire__filter_chain_plugin_load(const struct spa_support *s
uri = lilv_new_uri(c->world, plugin_uri); uri = lilv_new_uri(c->world, plugin_uri);
if (uri == NULL) { if (uri == NULL) {
pw_log_warn("invalid URI %s", plugin_uri); spa_log_warn(c->log, "invalid URI %s", plugin_uri);
res = -EINVAL; res = -EINVAL;
goto error_unref; goto error_unref;
} }
@ -485,7 +487,7 @@ struct fc_plugin *pipewire__filter_chain_plugin_load(const struct spa_support *s
lilv_node_free(uri); lilv_node_free(uri);
if (plugin == NULL) { if (plugin == NULL) {
pw_log_warn("can't load plugin %s", plugin_uri); spa_log_warn(c->log, "can't load plugin %s", plugin_uri);
res = -EINVAL; res = -EINVAL;
goto error_unref; goto error_unref;
} }

View file

@ -4,8 +4,7 @@
#include <spa/utils/json.h> #include <spa/utils/json.h>
#include <spa/support/loop.h> #include <spa/support/loop.h>
#include <spa/support/log.h>
#include <pipewire/log.h>
#include "plugin.h" #include "plugin.h"
#include "convolver.h" #include "convolver.h"
@ -16,7 +15,9 @@
struct plugin { struct plugin {
struct fc_plugin plugin; struct fc_plugin plugin;
struct dsp_ops *dsp_ops;
struct dsp_ops *dsp;
struct spa_log *log;
struct spa_loop *data_loop; struct spa_loop *data_loop;
struct spa_loop *main_loop; struct spa_loop *main_loop;
uint32_t quantum_limit; uint32_t quantum_limit;
@ -24,6 +25,10 @@ struct plugin {
struct spatializer_impl { struct spatializer_impl {
struct plugin *plugin; struct plugin *plugin;
struct dsp_ops *dsp;
struct spa_log *log;
unsigned long rate; unsigned long rate;
float *port[6]; float *port[6];
int n_samples, blocksize, tailsize; int n_samples, blocksize, tailsize;
@ -39,6 +44,7 @@ static void * spatializer_instantiate(const struct fc_plugin *plugin, const stru
unsigned long SampleRate, int index, const char *config) unsigned long SampleRate, int index, const char *config)
{ {
struct spatializer_impl *impl; struct spatializer_impl *impl;
struct plugin *pl = (struct plugin*) plugin;
struct spa_json it[1]; struct spa_json it[1];
const char *val; const char *val;
char key[256]; char key[256];
@ -47,12 +53,12 @@ static void * spatializer_instantiate(const struct fc_plugin *plugin, const stru
errno = EINVAL; errno = EINVAL;
if (config == NULL) { if (config == NULL) {
pw_log_error("spatializer: no config was given"); spa_log_error(pl->log, "spatializer: no config was given");
return NULL; return NULL;
} }
if (spa_json_begin_object(&it[0], config, strlen(config)) <= 0) { if (spa_json_begin_object(&it[0], config, strlen(config)) <= 0) {
pw_log_error("spatializer: expected object in config"); spa_log_error(pl->log, "spatializer: expected object in config");
return NULL; return NULL;
} }
@ -62,33 +68,35 @@ static void * spatializer_instantiate(const struct fc_plugin *plugin, const stru
return NULL; return NULL;
} }
impl->plugin = (struct plugin *) plugin; impl->plugin = pl;
impl->dsp = pl->dsp;
impl->log = pl->log;
while ((len = spa_json_object_next(&it[0], key, sizeof(key), &val)) > 0) { while ((len = spa_json_object_next(&it[0], key, sizeof(key), &val)) > 0) {
if (spa_streq(key, "blocksize")) { if (spa_streq(key, "blocksize")) {
if (spa_json_parse_int(val, len, &impl->blocksize) <= 0) { if (spa_json_parse_int(val, len, &impl->blocksize) <= 0) {
pw_log_error("spatializer:blocksize requires a number"); spa_log_error(impl->log, "spatializer:blocksize requires a number");
errno = EINVAL; errno = EINVAL;
goto error; goto error;
} }
} }
else if (spa_streq(key, "tailsize")) { else if (spa_streq(key, "tailsize")) {
if (spa_json_parse_int(val, len, &impl->tailsize) <= 0) { if (spa_json_parse_int(val, len, &impl->tailsize) <= 0) {
pw_log_error("spatializer:tailsize requires a number"); spa_log_error(impl->log, "spatializer:tailsize requires a number");
errno = EINVAL; errno = EINVAL;
goto error; goto error;
} }
} }
else if (spa_streq(key, "filename")) { else if (spa_streq(key, "filename")) {
if (spa_json_parse_stringn(val, len, filename, sizeof(filename)) <= 0) { if (spa_json_parse_stringn(val, len, filename, sizeof(filename)) <= 0) {
pw_log_error("spatializer:filename requires a string"); spa_log_error(impl->log, "spatializer:filename requires a string");
errno = EINVAL; errno = EINVAL;
goto error; goto error;
} }
} }
} }
if (!filename[0]) { if (!filename[0]) {
pw_log_error("spatializer:filename was not given"); spa_log_error(impl->log, "spatializer:filename was not given");
errno = EINVAL; errno = EINVAL;
goto error; goto error;
} }
@ -166,7 +174,7 @@ static void * spatializer_instantiate(const struct fc_plugin *plugin, const stru
reason = "Internal error"; reason = "Internal error";
break; break;
} }
pw_log_error("Unable to load HRTF from %s: %s (%d)", filename, reason, ret); spa_log_error(impl->log, "Unable to load HRTF from %s: %s (%d)", filename, reason, ret);
goto error; goto error;
} }
@ -175,7 +183,7 @@ static void * spatializer_instantiate(const struct fc_plugin *plugin, const stru
if (impl->tailsize <= 0) if (impl->tailsize <= 0)
impl->tailsize = SPA_CLAMP(4096, impl->blocksize, 32768); impl->tailsize = SPA_CLAMP(4096, impl->blocksize, 32768);
pw_log_info("using n_samples:%u %d:%d blocksize sofa:%s", impl->n_samples, spa_log_info(impl->log, "using n_samples:%u %d:%d blocksize sofa:%s", impl->n_samples,
impl->blocksize, impl->tailsize, filename); impl->blocksize, impl->tailsize, filename);
impl->tmp[0] = calloc(impl->plugin->quantum_limit, sizeof(float)); impl->tmp[0] = calloc(impl->plugin->quantum_limit, sizeof(float));
@ -219,7 +227,7 @@ static void spatializer_reload(void * Instance)
for (uint8_t i = 0; i < 3; i++) for (uint8_t i = 0; i < 3; i++)
coords[i] = impl->port[3 + i][0]; coords[i] = impl->port[3 + i][0];
pw_log_info("making spatializer with %f %f %f", coords[0], coords[1], coords[2]); spa_log_info(impl->log, "making spatializer with %f %f %f", coords[0], coords[1], coords[2]);
mysofa_s2c(coords); mysofa_s2c(coords);
mysofa_getfilter_float( mysofa_getfilter_float(
@ -235,23 +243,23 @@ static void spatializer_reload(void * Instance)
// TODO: make use of delay // TODO: make use of delay
if ((left_delay != 0.0f || right_delay != 0.0f) && (!isnan(left_delay) || !isnan(right_delay))) if ((left_delay != 0.0f || right_delay != 0.0f) && (!isnan(left_delay) || !isnan(right_delay)))
pw_log_warn("delay dropped l: %f, r: %f", left_delay, right_delay); spa_log_warn(impl->log, "delay dropped l: %f, r: %f", left_delay, right_delay);
if (impl->l_conv[2]) if (impl->l_conv[2])
convolver_free(impl->l_conv[2]); convolver_free(impl->l_conv[2]);
if (impl->r_conv[2]) if (impl->r_conv[2])
convolver_free(impl->r_conv[2]); convolver_free(impl->r_conv[2]);
impl->l_conv[2] = convolver_new(impl->plugin->dsp_ops, impl->blocksize, impl->tailsize, impl->l_conv[2] = convolver_new(impl->dsp, impl->blocksize, impl->tailsize,
left_ir, impl->n_samples); left_ir, impl->n_samples);
impl->r_conv[2] = convolver_new(impl->plugin->dsp_ops, impl->blocksize, impl->tailsize, impl->r_conv[2] = convolver_new(impl->dsp, impl->blocksize, impl->tailsize,
right_ir, impl->n_samples); right_ir, impl->n_samples);
free(left_ir); free(left_ir);
free(right_ir); free(right_ir);
if (impl->l_conv[2] == NULL || impl->r_conv[2] == NULL) { if (impl->l_conv[2] == NULL || impl->r_conv[2] == NULL) {
pw_log_error("reloading left or right convolver failed"); spa_log_error(impl->log, "reloading left or right convolver failed");
return; return;
} }
spa_loop_invoke(impl->plugin->data_loop, do_switch, 1, NULL, 0, true, impl); spa_loop_invoke(impl->plugin->data_loop, do_switch, 1, NULL, 0, true, impl);
@ -438,11 +446,12 @@ struct fc_plugin *pipewire__filter_chain_plugin_load(const struct spa_support *s
if (spa_streq(k, "clock.quantum-limit")) if (spa_streq(k, "clock.quantum-limit"))
spa_atou32(s, &impl->quantum_limit, 0); spa_atou32(s, &impl->quantum_limit, 0);
} }
impl->dsp_ops = dsp; impl->dsp = dsp;
pffft_select_cpu(dsp->cpu_flags); pffft_select_cpu(dsp->cpu_flags);
impl->data_loop = spa_support_find(support, n_support, SPA_TYPE_INTERFACE_DataLoop); 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->main_loop = spa_support_find(support, n_support, SPA_TYPE_INTERFACE_Loop);
impl->log = spa_support_find(support, n_support, SPA_TYPE_INTERFACE_Log);
return (struct fc_plugin *) impl; return (struct fc_plugin *) impl;
} }