mirror of
https://gitlab.freedesktop.org/pipewire/pipewire.git
synced 2025-11-05 13:30:02 -05:00
spatializer: move to sofa_plugin
This commit is contained in:
parent
8677cb2fdf
commit
9399fc318e
5 changed files with 315 additions and 282 deletions
|
|
@ -108,6 +108,7 @@ filter_chain_sources = [
|
||||||
'module-filter-chain/biquad.c',
|
'module-filter-chain/biquad.c',
|
||||||
'module-filter-chain/ladspa_plugin.c',
|
'module-filter-chain/ladspa_plugin.c',
|
||||||
'module-filter-chain/builtin_plugin.c',
|
'module-filter-chain/builtin_plugin.c',
|
||||||
|
'module-filter-chain/sofa_plugin.c',
|
||||||
'module-filter-chain/convolver.c'
|
'module-filter-chain/convolver.c'
|
||||||
]
|
]
|
||||||
filter_chain_dependencies = [
|
filter_chain_dependencies = [
|
||||||
|
|
|
||||||
|
|
@ -1205,6 +1205,9 @@ static struct plugin *plugin_load(struct impl *impl, const char *type, const cha
|
||||||
if (spa_streq(type, "builtin")) {
|
if (spa_streq(type, "builtin")) {
|
||||||
pl = load_builtin_plugin(support, n_support, &impl->dsp, path, NULL);
|
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")) {
|
else if (spa_streq(type, "ladspa")) {
|
||||||
pl = load_ladspa_plugin(support, n_support, &impl->dsp, path, NULL);
|
pl = load_ladspa_plugin(support, n_support, &impl->dsp, path, NULL);
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -26,16 +26,10 @@
|
||||||
|
|
||||||
#include <float.h>
|
#include <float.h>
|
||||||
#include <math.h>
|
#include <math.h>
|
||||||
|
|
||||||
#ifdef HAVE_SNDFILE
|
#ifdef HAVE_SNDFILE
|
||||||
#include <sndfile.h>
|
#include <sndfile.h>
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
#ifdef HAVE_LIBMYSOFA
|
|
||||||
#include <mysofa.h>
|
|
||||||
#include <pthread.h>
|
|
||||||
#endif
|
|
||||||
|
|
||||||
#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>
|
||||||
|
|
@ -52,14 +46,6 @@
|
||||||
|
|
||||||
#define MAX_RATES 32u
|
#define MAX_RATES 32u
|
||||||
|
|
||||||
#ifdef HAVE_LIBMYSOFA
|
|
||||||
// > If your program is using several threads, you must use
|
|
||||||
// > appropriate synchronisation mechanisms so only
|
|
||||||
// > a single thread can access the mysofa_open_cached
|
|
||||||
// > and mysofa_close_cached functions at a given time.
|
|
||||||
static pthread_mutex_t libmysofa_mutex = PTHREAD_MUTEX_INITIALIZER;
|
|
||||||
#endif
|
|
||||||
|
|
||||||
static struct dsp_ops *dsp_ops;
|
static struct dsp_ops *dsp_ops;
|
||||||
|
|
||||||
struct builtin {
|
struct builtin {
|
||||||
|
|
@ -861,272 +847,6 @@ static const struct fc_descriptor convolve_desc = {
|
||||||
.cleanup = convolver_cleanup,
|
.cleanup = convolver_cleanup,
|
||||||
};
|
};
|
||||||
|
|
||||||
struct spatializer_impl {
|
|
||||||
unsigned long rate;
|
|
||||||
float *port[64];
|
|
||||||
float old_coords[3];
|
|
||||||
float coords[3];
|
|
||||||
int n_samples, blocksize, tailsize;
|
|
||||||
|
|
||||||
#ifdef HAVE_LIBMYSOFA
|
|
||||||
struct MYSOFA_EASY *sofa;
|
|
||||||
#endif
|
|
||||||
|
|
||||||
struct convolver *l_conv;
|
|
||||||
struct convolver *r_conv;
|
|
||||||
};
|
|
||||||
|
|
||||||
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;
|
|
||||||
char key[256];
|
|
||||||
char filename[PATH_MAX] = "";
|
|
||||||
|
|
||||||
errno = EINVAL;
|
|
||||||
if (config == NULL)
|
|
||||||
return NULL;
|
|
||||||
|
|
||||||
spa_json_init(&it[0], config, strlen(config));
|
|
||||||
if (spa_json_enter_object(&it[0], &it[1]) <= 0)
|
|
||||||
return NULL;
|
|
||||||
|
|
||||||
impl = calloc(1, sizeof(*impl));
|
|
||||||
if (impl == NULL)
|
|
||||||
goto error;
|
|
||||||
|
|
||||||
while (spa_json_get_string(&it[1], key, sizeof(key)) > 0) {
|
|
||||||
if (spa_streq(key, "blocksize")) {
|
|
||||||
if (spa_json_get_int(&it[1], &impl->blocksize) <= 0) {
|
|
||||||
pw_log_error("spatializer:blocksize requires a number");
|
|
||||||
return NULL;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
else if (spa_streq(key, "tailsize")) {
|
|
||||||
if (spa_json_get_int(&it[1], &impl->tailsize) <= 0) {
|
|
||||||
pw_log_error("spatializer:tailsize requires a number");
|
|
||||||
return NULL;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
else if (spa_streq(key, "filename")) {
|
|
||||||
if (spa_json_get_string(&it[1], filename, sizeof(filename)) <= 0) {
|
|
||||||
pw_log_error("spatializer:filename requires a string");
|
|
||||||
return NULL;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
else if (spa_json_next(&it[1], &val) < 0)
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
if (!filename[0]) {
|
|
||||||
pw_log_error("spatializer:filename was not given");
|
|
||||||
return NULL;
|
|
||||||
}
|
|
||||||
|
|
||||||
int ret = MYSOFA_OK;
|
|
||||||
|
|
||||||
pthread_mutex_lock(&libmysofa_mutex);
|
|
||||||
impl->sofa = mysofa_open_cached(filename, SampleRate, &impl->n_samples, &ret);
|
|
||||||
pthread_mutex_unlock(&libmysofa_mutex);
|
|
||||||
|
|
||||||
if (ret != MYSOFA_OK) {
|
|
||||||
pw_log_error("Unable to load HRTF from %s: %d %m", filename, ret);
|
|
||||||
errno = ENOENT;
|
|
||||||
return NULL;
|
|
||||||
}
|
|
||||||
|
|
||||||
for (uint8_t i = 0; i < 3; i++) {
|
|
||||||
impl->old_coords[i] = impl->coords[i] =
|
|
||||||
impl->port[3 + i] ? impl->port[3 + i][0] : 0.0f;
|
|
||||||
impl->old_coords[i] = impl->coords[i] = NAN;
|
|
||||||
}
|
|
||||||
|
|
||||||
mysofa_s2c(impl->coords);
|
|
||||||
|
|
||||||
mysofa_getfilter_float(
|
|
||||||
impl->sofa,
|
|
||||||
impl->coords[0],
|
|
||||||
impl->coords[1],
|
|
||||||
impl->coords[2],
|
|
||||||
left_ir,
|
|
||||||
right_ir,
|
|
||||||
&left_delay,
|
|
||||||
&right_delay
|
|
||||||
);
|
|
||||||
|
|
||||||
// TODO: make use of delay
|
|
||||||
if ((left_delay || right_delay) && (!isnan(left_delay) || !isnan(right_delay))) {
|
|
||||||
pw_log_warn("delay dropped l: %f, r: %f", left_delay, right_delay);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (impl->blocksize <= 0)
|
|
||||||
impl->blocksize = SPA_CLAMP(impl->n_samples, 64, 256);
|
|
||||||
if (impl->tailsize <= 0)
|
|
||||||
impl->tailsize = SPA_CLAMP(4096, impl->blocksize, 32768);
|
|
||||||
|
|
||||||
pw_log_info("using n_samples:%u %d:%d blocksize sofa:%s", impl->n_samples,
|
|
||||||
impl->blocksize, impl->tailsize, filename);
|
|
||||||
|
|
||||||
impl->rate = SampleRate;
|
|
||||||
return impl;
|
|
||||||
error:
|
|
||||||
if (impl->sofa) {
|
|
||||||
pthread_mutex_lock(&libmysofa_mutex);
|
|
||||||
mysofa_close_cached(impl->sofa);
|
|
||||||
pthread_mutex_unlock(&libmysofa_mutex);
|
|
||||||
}
|
|
||||||
|
|
||||||
free(impl);
|
|
||||||
return NULL;
|
|
||||||
#else
|
|
||||||
pw_log_error("libmysofa is required for spatializer, but disabled at compile time");
|
|
||||||
errno = EINVAL;
|
|
||||||
return NULL;
|
|
||||||
#endif
|
|
||||||
}
|
|
||||||
|
|
||||||
static void spatializer_run(void * Instance, unsigned long SampleCount)
|
|
||||||
{
|
|
||||||
#ifdef HAVE_LIBMYSOFA
|
|
||||||
struct spatializer_impl *impl = Instance;
|
|
||||||
|
|
||||||
bool reload = false;
|
|
||||||
for (uint8_t i = 0; i < 3; i++) {
|
|
||||||
if ((impl->port[3 + i] && impl->old_coords[i] != impl->port[3 + i][0])
|
|
||||||
|| isnan(impl->old_coords[i])) {
|
|
||||||
reload = true;
|
|
||||||
}
|
|
||||||
impl->old_coords[i] = impl->coords[i] =
|
|
||||||
impl->port[3 + i][0];
|
|
||||||
}
|
|
||||||
|
|
||||||
if (reload) {
|
|
||||||
float *left_ir = calloc(impl->n_samples, sizeof(float));
|
|
||||||
float *right_ir = calloc(impl->n_samples, sizeof(float));
|
|
||||||
float left_delay;
|
|
||||||
float right_delay;
|
|
||||||
|
|
||||||
mysofa_s2c(impl->coords);
|
|
||||||
|
|
||||||
mysofa_getfilter_float(
|
|
||||||
impl->sofa,
|
|
||||||
impl->coords[0],
|
|
||||||
impl->coords[1],
|
|
||||||
impl->coords[2],
|
|
||||||
left_ir,
|
|
||||||
right_ir,
|
|
||||||
&left_delay,
|
|
||||||
&right_delay
|
|
||||||
);
|
|
||||||
|
|
||||||
// TODO: make use of delay
|
|
||||||
if ((left_delay || right_delay) && (!isnan(left_delay) || !isnan(right_delay))) {
|
|
||||||
pw_log_warn("delay dropped l: %f, r: %f", left_delay, right_delay);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (impl->l_conv)
|
|
||||||
convolver_free(impl->l_conv);
|
|
||||||
impl->l_conv = convolver_new(dsp_ops, impl->blocksize, impl->tailsize, left_ir, impl->n_samples);
|
|
||||||
free(left_ir);
|
|
||||||
if (impl->l_conv == NULL) {
|
|
||||||
pw_log_error("reloading left convolver failed");
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (impl->r_conv)
|
|
||||||
convolver_free(impl->r_conv);
|
|
||||||
impl->r_conv = convolver_new(dsp_ops, impl->blocksize, impl->tailsize, right_ir, impl->n_samples);
|
|
||||||
free(right_ir);
|
|
||||||
if (impl->r_conv == NULL) {
|
|
||||||
pw_log_error("reloading right convolver failed");
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
convolver_run(impl->l_conv, impl->port[2], impl->port[0], SampleCount);
|
|
||||||
convolver_run(impl->r_conv, impl->port[2], impl->port[1], SampleCount);
|
|
||||||
#endif
|
|
||||||
}
|
|
||||||
|
|
||||||
static void spatializer_connect_port(void * Instance, unsigned long Port,
|
|
||||||
float * DataLocation)
|
|
||||||
{
|
|
||||||
struct spatializer_impl *impl = Instance;
|
|
||||||
impl->port[Port] = DataLocation;
|
|
||||||
}
|
|
||||||
|
|
||||||
static void spatializer_cleanup(void * Instance)
|
|
||||||
{
|
|
||||||
struct spatializer_impl *impl = Instance;
|
|
||||||
if (impl->l_conv)
|
|
||||||
convolver_free(impl->l_conv);
|
|
||||||
if (impl->r_conv)
|
|
||||||
convolver_free(impl->r_conv);
|
|
||||||
#ifdef HAVE_LIBMYSOFA
|
|
||||||
if (impl->sofa) {
|
|
||||||
pthread_mutex_lock(&libmysofa_mutex);
|
|
||||||
mysofa_close_cached(impl->sofa);
|
|
||||||
pthread_mutex_unlock(&libmysofa_mutex);
|
|
||||||
}
|
|
||||||
#endif
|
|
||||||
free(impl);
|
|
||||||
}
|
|
||||||
|
|
||||||
static struct fc_port spatializer_ports[] = {
|
|
||||||
{ .index = 0,
|
|
||||||
.name = "Out L",
|
|
||||||
.flags = FC_PORT_OUTPUT | FC_PORT_AUDIO,
|
|
||||||
},
|
|
||||||
{ .index = 1,
|
|
||||||
.name = "Out R",
|
|
||||||
.flags = FC_PORT_OUTPUT | FC_PORT_AUDIO,
|
|
||||||
},
|
|
||||||
{ .index = 2,
|
|
||||||
.name = "In",
|
|
||||||
.flags = FC_PORT_INPUT | FC_PORT_AUDIO,
|
|
||||||
},
|
|
||||||
|
|
||||||
{ .index = 3,
|
|
||||||
.name = "Azimuth",
|
|
||||||
.flags = FC_PORT_INPUT | FC_PORT_CONTROL,
|
|
||||||
.def = 0.0f, .min = 0.0f, .max = 360.0f
|
|
||||||
},
|
|
||||||
{ .index = 4,
|
|
||||||
.name = "Elevation",
|
|
||||||
.flags = FC_PORT_INPUT | FC_PORT_CONTROL,
|
|
||||||
.def = 0.0f, .min = -90.0f, .max = 90.0f
|
|
||||||
},
|
|
||||||
{ .index = 5,
|
|
||||||
.name = "Radius",
|
|
||||||
.flags = FC_PORT_INPUT | FC_PORT_CONTROL,
|
|
||||||
.def = 1.0f, .min = 0.0f, .max = 100.0f
|
|
||||||
},
|
|
||||||
};
|
|
||||||
|
|
||||||
static void spatializer_deactivate(void * Instance)
|
|
||||||
{
|
|
||||||
struct spatializer_impl *impl = Instance;
|
|
||||||
if (impl->l_conv)
|
|
||||||
convolver_reset(impl->l_conv);
|
|
||||||
if (impl->r_conv)
|
|
||||||
convolver_reset(impl->r_conv);
|
|
||||||
}
|
|
||||||
|
|
||||||
static const struct fc_descriptor spatializer_desc = {
|
|
||||||
.name = "spatializer",
|
|
||||||
|
|
||||||
.n_ports = 6,
|
|
||||||
.ports = spatializer_ports,
|
|
||||||
|
|
||||||
.instantiate = spatializer_instantiate,
|
|
||||||
.connect_port = spatializer_connect_port,
|
|
||||||
.deactivate = spatializer_deactivate,
|
|
||||||
.run = spatializer_run,
|
|
||||||
.cleanup = spatializer_cleanup,
|
|
||||||
};
|
|
||||||
|
|
||||||
/** delay */
|
/** delay */
|
||||||
struct delay_impl {
|
struct delay_impl {
|
||||||
unsigned long rate;
|
unsigned long rate;
|
||||||
|
|
@ -1285,8 +1005,6 @@ static const struct fc_descriptor * builtin_descriptor(unsigned long Index)
|
||||||
return &convolve_desc;
|
return &convolve_desc;
|
||||||
case 11:
|
case 11:
|
||||||
return &delay_desc;
|
return &delay_desc;
|
||||||
case 12:
|
|
||||||
return &spatializer_desc;
|
|
||||||
}
|
}
|
||||||
return NULL;
|
return NULL;
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -108,5 +108,7 @@ struct fc_plugin *load_lv2_plugin(const struct spa_support *support, uint32_t n_
|
||||||
struct dsp_ops *dsp, const char *path, const char *config);
|
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 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 dsp_ops *dsp, const char *path, const char *config);
|
||||||
|
struct fc_plugin *load_sofa_plugin(const struct spa_support *support, uint32_t n_support,
|
||||||
|
struct dsp_ops *dsp, const char *path, const char *config);
|
||||||
|
|
||||||
#endif /* PLUGIN_H */
|
#endif /* PLUGIN_H */
|
||||||
|
|
|
||||||
309
src/modules/module-filter-chain/sofa_plugin.c
Normal file
309
src/modules/module-filter-chain/sofa_plugin.c
Normal file
|
|
@ -0,0 +1,309 @@
|
||||||
|
#include "config.h"
|
||||||
|
|
||||||
|
#include <spa/utils/json.h>
|
||||||
|
#include <pipewire/log.h>
|
||||||
|
#include "plugin.h"
|
||||||
|
#include "convolver.h"
|
||||||
|
#include "dsp-ops.h"
|
||||||
|
#include "pffft.h"
|
||||||
|
|
||||||
|
#ifdef HAVE_LIBMYSOFA
|
||||||
|
#include <mysofa.h>
|
||||||
|
#include <pthread.h>
|
||||||
|
|
||||||
|
// > If your program is using several threads, you must use
|
||||||
|
// > appropriate synchronisation mechanisms so only
|
||||||
|
// > a single thread can access the mysofa_open_cached
|
||||||
|
// > and mysofa_close_cached functions at a given time.
|
||||||
|
static pthread_mutex_t libmysofa_mutex = PTHREAD_MUTEX_INITIALIZER;
|
||||||
|
#endif
|
||||||
|
|
||||||
|
static struct dsp_ops *dsp_ops;
|
||||||
|
|
||||||
|
struct spatializer_impl {
|
||||||
|
unsigned long rate;
|
||||||
|
float *port[64];
|
||||||
|
float old_coords[3];
|
||||||
|
float coords[3];
|
||||||
|
int n_samples, blocksize, tailsize;
|
||||||
|
|
||||||
|
#ifdef HAVE_LIBMYSOFA
|
||||||
|
struct MYSOFA_EASY *sofa;
|
||||||
|
#endif
|
||||||
|
|
||||||
|
struct convolver *l_conv;
|
||||||
|
struct convolver *r_conv;
|
||||||
|
};
|
||||||
|
|
||||||
|
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;
|
||||||
|
char key[256];
|
||||||
|
char filename[PATH_MAX] = "";
|
||||||
|
|
||||||
|
errno = EINVAL;
|
||||||
|
if (config == NULL)
|
||||||
|
return NULL;
|
||||||
|
|
||||||
|
spa_json_init(&it[0], config, strlen(config));
|
||||||
|
if (spa_json_enter_object(&it[0], &it[1]) <= 0)
|
||||||
|
return NULL;
|
||||||
|
|
||||||
|
impl = calloc(1, sizeof(*impl));
|
||||||
|
if (impl == NULL) {
|
||||||
|
errno = ENOMEM;
|
||||||
|
goto error;
|
||||||
|
}
|
||||||
|
|
||||||
|
while (spa_json_get_string(&it[1], key, sizeof(key)) > 0) {
|
||||||
|
if (spa_streq(key, "blocksize")) {
|
||||||
|
if (spa_json_get_int(&it[1], &impl->blocksize) <= 0) {
|
||||||
|
pw_log_error("spatializer:blocksize requires a number");
|
||||||
|
errno = EINVAL;
|
||||||
|
goto error;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else if (spa_streq(key, "tailsize")) {
|
||||||
|
if (spa_json_get_int(&it[1], &impl->tailsize) <= 0) {
|
||||||
|
pw_log_error("spatializer:tailsize requires a number");
|
||||||
|
errno = EINVAL;
|
||||||
|
goto error;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else if (spa_streq(key, "filename")) {
|
||||||
|
if (spa_json_get_string(&it[1], filename, sizeof(filename)) <= 0) {
|
||||||
|
pw_log_error("spatializer:filename requires a string");
|
||||||
|
errno = EINVAL;
|
||||||
|
goto error;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else if (spa_json_next(&it[1], &val) < 0)
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
if (!filename[0]) {
|
||||||
|
pw_log_error("spatializer:filename was not given");
|
||||||
|
errno = EINVAL;
|
||||||
|
goto error;
|
||||||
|
}
|
||||||
|
|
||||||
|
int ret = MYSOFA_OK;
|
||||||
|
|
||||||
|
pthread_mutex_lock(&libmysofa_mutex);
|
||||||
|
impl->sofa = mysofa_open_cached(filename, SampleRate, &impl->n_samples, &ret);
|
||||||
|
pthread_mutex_unlock(&libmysofa_mutex);
|
||||||
|
|
||||||
|
if (ret != MYSOFA_OK) {
|
||||||
|
pw_log_error("Unable to load HRTF from %s: %d %m", filename, ret);
|
||||||
|
errno = ENOENT;
|
||||||
|
goto error;
|
||||||
|
}
|
||||||
|
|
||||||
|
for (uint8_t i = 0; i < 3; i++) {
|
||||||
|
impl->old_coords[i] = impl->coords[i] = NAN;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (impl->blocksize <= 0)
|
||||||
|
impl->blocksize = SPA_CLAMP(impl->n_samples, 64, 256);
|
||||||
|
if (impl->tailsize <= 0)
|
||||||
|
impl->tailsize = SPA_CLAMP(4096, impl->blocksize, 32768);
|
||||||
|
|
||||||
|
pw_log_info("using n_samples:%u %d:%d blocksize sofa:%s", impl->n_samples,
|
||||||
|
impl->blocksize, impl->tailsize, filename);
|
||||||
|
|
||||||
|
impl->rate = SampleRate;
|
||||||
|
return impl;
|
||||||
|
error:
|
||||||
|
if (impl->sofa) {
|
||||||
|
pthread_mutex_lock(&libmysofa_mutex);
|
||||||
|
mysofa_close_cached(impl->sofa);
|
||||||
|
pthread_mutex_unlock(&libmysofa_mutex);
|
||||||
|
}
|
||||||
|
free(impl);
|
||||||
|
return NULL;
|
||||||
|
#else
|
||||||
|
pw_log_error("libmysofa is required for spatializer, but disabled at compile time");
|
||||||
|
errno = EINVAL;
|
||||||
|
return NULL;
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
|
||||||
|
static void spatializer_run(void * Instance, unsigned long SampleCount)
|
||||||
|
{
|
||||||
|
#ifdef HAVE_LIBMYSOFA
|
||||||
|
struct spatializer_impl *impl = Instance;
|
||||||
|
|
||||||
|
bool reload = false;
|
||||||
|
for (uint8_t i = 0; i < 3; i++) {
|
||||||
|
if ((impl->port[3 + i] && impl->old_coords[i] != impl->port[3 + i][0])
|
||||||
|
|| isnan(impl->old_coords[i])) {
|
||||||
|
reload = true;
|
||||||
|
}
|
||||||
|
impl->old_coords[i] = impl->coords[i] =
|
||||||
|
impl->port[3 + i][0];
|
||||||
|
}
|
||||||
|
|
||||||
|
if (reload) {
|
||||||
|
float *left_ir = calloc(impl->n_samples, sizeof(float));
|
||||||
|
float *right_ir = calloc(impl->n_samples, sizeof(float));
|
||||||
|
float left_delay;
|
||||||
|
float right_delay;
|
||||||
|
|
||||||
|
mysofa_s2c(impl->coords);
|
||||||
|
mysofa_getfilter_float(
|
||||||
|
impl->sofa,
|
||||||
|
impl->coords[0],
|
||||||
|
impl->coords[1],
|
||||||
|
impl->coords[2],
|
||||||
|
left_ir,
|
||||||
|
right_ir,
|
||||||
|
&left_delay,
|
||||||
|
&right_delay
|
||||||
|
);
|
||||||
|
|
||||||
|
// TODO: make use of delay
|
||||||
|
if ((left_delay || right_delay) && (!isnan(left_delay) || !isnan(right_delay))) {
|
||||||
|
pw_log_warn("delay dropped l: %f, r: %f", left_delay, right_delay);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (impl->l_conv)
|
||||||
|
convolver_free(impl->l_conv);
|
||||||
|
impl->l_conv = convolver_new(dsp_ops, impl->blocksize, impl->tailsize, left_ir, impl->n_samples);
|
||||||
|
free(left_ir);
|
||||||
|
if (impl->l_conv == NULL) {
|
||||||
|
pw_log_error("reloading left convolver failed");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (impl->r_conv)
|
||||||
|
convolver_free(impl->r_conv);
|
||||||
|
impl->r_conv = convolver_new(dsp_ops, impl->blocksize, impl->tailsize, right_ir, impl->n_samples);
|
||||||
|
free(right_ir);
|
||||||
|
if (impl->r_conv == NULL) {
|
||||||
|
pw_log_error("reloading right convolver failed");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
convolver_run(impl->l_conv, impl->port[2], impl->port[0], SampleCount);
|
||||||
|
convolver_run(impl->r_conv, impl->port[2], impl->port[1], SampleCount);
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
|
||||||
|
static void spatializer_connect_port(void * Instance, unsigned long Port,
|
||||||
|
float * DataLocation)
|
||||||
|
{
|
||||||
|
struct spatializer_impl *impl = Instance;
|
||||||
|
impl->port[Port] = DataLocation;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void spatializer_cleanup(void * Instance)
|
||||||
|
{
|
||||||
|
struct spatializer_impl *impl = Instance;
|
||||||
|
if (impl->l_conv)
|
||||||
|
convolver_free(impl->l_conv);
|
||||||
|
if (impl->r_conv)
|
||||||
|
convolver_free(impl->r_conv);
|
||||||
|
|
||||||
|
#ifdef HAVE_LIBMYSOFA
|
||||||
|
if (impl->sofa) {
|
||||||
|
pthread_mutex_lock(&libmysofa_mutex);
|
||||||
|
mysofa_close_cached(impl->sofa);
|
||||||
|
pthread_mutex_unlock(&libmysofa_mutex);
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
free(impl);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void spatializer_deactivate(void * Instance)
|
||||||
|
{
|
||||||
|
struct spatializer_impl *impl = Instance;
|
||||||
|
if (impl->l_conv)
|
||||||
|
convolver_reset(impl->l_conv);
|
||||||
|
if (impl->r_conv)
|
||||||
|
convolver_reset(impl->r_conv);
|
||||||
|
}
|
||||||
|
|
||||||
|
static struct fc_port spatializer_ports[] = {
|
||||||
|
{ .index = 0,
|
||||||
|
.name = "Out L",
|
||||||
|
.flags = FC_PORT_OUTPUT | FC_PORT_AUDIO,
|
||||||
|
},
|
||||||
|
{ .index = 1,
|
||||||
|
.name = "Out R",
|
||||||
|
.flags = FC_PORT_OUTPUT | FC_PORT_AUDIO,
|
||||||
|
},
|
||||||
|
{ .index = 2,
|
||||||
|
.name = "In",
|
||||||
|
.flags = FC_PORT_INPUT | FC_PORT_AUDIO,
|
||||||
|
},
|
||||||
|
|
||||||
|
{ .index = 3,
|
||||||
|
.name = "Azimuth",
|
||||||
|
.flags = FC_PORT_INPUT | FC_PORT_CONTROL,
|
||||||
|
.def = 0.0f, .min = 0.0f, .max = 360.0f
|
||||||
|
},
|
||||||
|
{ .index = 4,
|
||||||
|
.name = "Elevation",
|
||||||
|
.flags = FC_PORT_INPUT | FC_PORT_CONTROL,
|
||||||
|
.def = 0.0f, .min = -90.0f, .max = 90.0f
|
||||||
|
},
|
||||||
|
{ .index = 5,
|
||||||
|
.name = "Radius",
|
||||||
|
.flags = FC_PORT_INPUT | FC_PORT_CONTROL,
|
||||||
|
.def = 1.0f, .min = 0.0f, .max = 100.0f
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
static const struct fc_descriptor spatializer_desc = {
|
||||||
|
.name = "spatializer",
|
||||||
|
|
||||||
|
.n_ports = 6,
|
||||||
|
.ports = spatializer_ports,
|
||||||
|
|
||||||
|
.instantiate = spatializer_instantiate,
|
||||||
|
.connect_port = spatializer_connect_port,
|
||||||
|
.deactivate = spatializer_deactivate,
|
||||||
|
.run = spatializer_run,
|
||||||
|
.cleanup = spatializer_cleanup,
|
||||||
|
};
|
||||||
|
|
||||||
|
static const struct fc_descriptor * sofa_descriptor(unsigned long Index)
|
||||||
|
{
|
||||||
|
switch(Index) {
|
||||||
|
case 0:
|
||||||
|
return &spatializer_desc;
|
||||||
|
}
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
static const struct fc_descriptor *sofa_make_desc(struct fc_plugin *plugin, const char *name)
|
||||||
|
{
|
||||||
|
unsigned long i;
|
||||||
|
for (i = 0; ;i++) {
|
||||||
|
const struct fc_descriptor *d = sofa_descriptor(i);
|
||||||
|
if (d == NULL)
|
||||||
|
break;
|
||||||
|
if (spa_streq(d->name, name))
|
||||||
|
return d;
|
||||||
|
}
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
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,
|
||||||
|
struct dsp_ops *dsp, const char *plugin, const char *config)
|
||||||
|
{
|
||||||
|
dsp_ops = dsp;
|
||||||
|
pffft_select_cpu(dsp->cpu_flags);
|
||||||
|
return &builtin_plugin;
|
||||||
|
}
|
||||||
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue