mirror of
https://gitlab.freedesktop.org/pipewire/pipewire.git
synced 2025-11-02 09:01:50 -05:00
filter-chain: allow multiple filters for convolver
Choose the closest match to reduce resampling loss. If match is not equal, resample. Keep the backwards compatibility retaining `filename` property. When both set, `filenames` takes priority
This commit is contained in:
parent
134a20c3c5
commit
2251b9b1d9
1 changed files with 100 additions and 22 deletions
|
|
@ -23,6 +23,7 @@
|
|||
*/
|
||||
|
||||
#include "config.h"
|
||||
#include "pipewire/private.h"
|
||||
|
||||
#include <float.h>
|
||||
#include <math.h>
|
||||
|
|
@ -439,22 +440,12 @@ struct convolver_impl {
|
|||
struct convolver *conv;
|
||||
};
|
||||
|
||||
static float *read_samples(const char *filename, float gain, int delay, int offset,
|
||||
int length, int channel, long unsigned *rate, int *n_samples)
|
||||
{
|
||||
float *samples;
|
||||
#ifdef HAVE_SNDFILE
|
||||
SF_INFO info;
|
||||
SNDFILE *f;
|
||||
static float *read_samples_from_sf(SNDFILE *f, SF_INFO info, float gain, int delay,
|
||||
int offset, int length, int channel, long unsigned *rate, int *n_samples) {
|
||||
float *samples;
|
||||
int i, n;
|
||||
|
||||
spa_zero(info);
|
||||
f = sf_open(filename, SFM_READ, &info) ;
|
||||
if (f == NULL) {
|
||||
pw_log_error("can't open %s", filename);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
if (length <= 0)
|
||||
length = info.frames;
|
||||
else
|
||||
|
|
@ -467,13 +458,12 @@ static float *read_samples(const char *filename, float gain, int delay, int offs
|
|||
return NULL;
|
||||
|
||||
samples = calloc(n * info.channels, sizeof(float));
|
||||
if (samples == NULL)
|
||||
if (samples == NULL)
|
||||
return NULL;
|
||||
|
||||
if (offset > 0)
|
||||
sf_seek(f, offset, SEEK_SET);
|
||||
sf_readf_float(f, samples + (delay * info.channels), length);
|
||||
sf_close(f);
|
||||
|
||||
channel = channel % info.channels;
|
||||
|
||||
|
|
@ -483,16 +473,77 @@ static float *read_samples(const char *filename, float gain, int delay, int offs
|
|||
*n_samples = n;
|
||||
*rate = info.samplerate;
|
||||
return samples;
|
||||
}
|
||||
#endif
|
||||
|
||||
static float *read_samples(const char *filename, float gain, int delay, int offset,
|
||||
int length, int channel, long unsigned *rate, int *n_samples)
|
||||
{
|
||||
#ifdef HAVE_SNDFILE
|
||||
SF_INFO info;
|
||||
SNDFILE *f;
|
||||
|
||||
spa_zero(info);
|
||||
f = sf_open(filename, SFM_READ, &info) ;
|
||||
if (f == NULL) {
|
||||
pw_log_error("can't open %s", filename);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
float *samples = read_samples_from_sf(f, info, gain, delay, offset,
|
||||
length, channel, rate, n_samples);
|
||||
|
||||
sf_close(f);
|
||||
return samples;
|
||||
#else
|
||||
pw_log_error("compiled without sndfile support, can't load samples: "
|
||||
"using dirac impulse");
|
||||
samples = calloc(1, sizeof(float));
|
||||
float *samples = calloc(1, sizeof(float));
|
||||
samples[0] = gain;
|
||||
*n_samples = 1;
|
||||
return samples;
|
||||
#endif
|
||||
}
|
||||
|
||||
static float *read_closest(char **filenames, float gain, int delay, int offset,
|
||||
int length, int channel, long unsigned *rate, int *n_samples)
|
||||
{
|
||||
#ifdef HAVE_SNDFILE
|
||||
SF_INFO infos[MAX_RATES];
|
||||
SNDFILE *fs[MAX_RATES];
|
||||
|
||||
spa_zero(infos);
|
||||
spa_zero(fs);
|
||||
|
||||
int diff = INT_MAX;
|
||||
uint32_t best = 0, i;
|
||||
|
||||
for (i = 0; i < MAX_RATES && filenames[i] && filenames[i][0]; i++) {
|
||||
fs[i] = sf_open(filenames[i], SFM_READ, &infos[i]);
|
||||
if (!fs[i])
|
||||
continue;
|
||||
|
||||
if (labs((long)infos[i].samplerate - (long)*rate) < diff) {
|
||||
best = i;
|
||||
diff = labs((long)infos[i].samplerate - (long)*rate);
|
||||
pw_log_debug("new closest match: %d", infos[i].samplerate);
|
||||
}
|
||||
}
|
||||
|
||||
float *samples = read_samples_from_sf(fs[best], infos[best], gain, delay,
|
||||
offset, length, channel, rate, n_samples);
|
||||
|
||||
for (i = 0; i < MAX_RATES; i++)
|
||||
if (fs[i])
|
||||
sf_close(fs[i]);
|
||||
|
||||
return samples;
|
||||
#else
|
||||
return read_samples(filenames[0], gain, delay, offset,
|
||||
length, channel, rate, n_samples);
|
||||
#endif
|
||||
}
|
||||
|
||||
static float *create_hilbert(const char *filename, float gain, int delay, int offset,
|
||||
int length, int *n_samples)
|
||||
{
|
||||
|
|
@ -617,16 +668,21 @@ static void * convolver_instantiate(const struct fc_descriptor * Descriptor,
|
|||
struct convolver_impl *impl;
|
||||
float *samples;
|
||||
int offset = 0, length = 0, channel = index, n_samples;
|
||||
uint32_t i = 0;
|
||||
struct spa_json it[2];
|
||||
struct spa_json filenames_json;
|
||||
const char *val;
|
||||
char key[256];
|
||||
char key[256], v[256];
|
||||
char filename[PATH_MAX] = "";
|
||||
char *filenames[MAX_RATES];
|
||||
int blocksize = 0, tailsize = 0;
|
||||
int delay = 0;
|
||||
int resample_quality = RESAMPLE_DEFAULT_QUALITY;
|
||||
float gain = 1.0f;
|
||||
unsigned long rate;
|
||||
|
||||
spa_zero(filenames);
|
||||
|
||||
errno = EINVAL;
|
||||
if (config == NULL)
|
||||
return NULL;
|
||||
|
|
@ -666,6 +722,17 @@ static void * convolver_instantiate(const struct fc_descriptor * Descriptor,
|
|||
return NULL;
|
||||
}
|
||||
}
|
||||
else if (spa_streq(key, "filenames")) {
|
||||
if (spa_json_enter_array(&it[1], &filenames_json) <= 0) {
|
||||
pw_log_error("convolver:filenames requires an array");
|
||||
return NULL;
|
||||
}
|
||||
while (spa_json_get_string(&filenames_json, v, sizeof(v)) > 0 && i < MAX_RATES) {
|
||||
filenames[i] = calloc(strlen(v) + 1, sizeof(char));
|
||||
strncpy(filenames[i], v, strlen(v));
|
||||
i++;
|
||||
}
|
||||
}
|
||||
else if (spa_streq(key, "offset")) {
|
||||
if (spa_json_get_int(&it[1], &offset) <= 0) {
|
||||
pw_log_error("convolver:offset requires a number");
|
||||
|
|
@ -693,8 +760,8 @@ static void * convolver_instantiate(const struct fc_descriptor * Descriptor,
|
|||
else if (spa_json_next(&it[1], &val) < 0)
|
||||
break;
|
||||
}
|
||||
if (!filename[0]) {
|
||||
pw_log_error("convolver:filename was not given");
|
||||
if (!filename[0] && !filenames[0]) {
|
||||
pw_log_error("convolver:filename nor filenames was given");
|
||||
return NULL;
|
||||
}
|
||||
|
||||
|
|
@ -703,7 +770,14 @@ static void * convolver_instantiate(const struct fc_descriptor * Descriptor,
|
|||
if (offset < 0)
|
||||
offset = 0;
|
||||
|
||||
if (spa_streq(filename, "/hilbert")) {
|
||||
if (filenames[0]) {
|
||||
rate = SampleRate;
|
||||
samples = read_closest(filenames, gain, delay, offset,
|
||||
length, channel, &rate, &n_samples);
|
||||
if (rate != SampleRate)
|
||||
samples = resample_buffer(samples, &n_samples,
|
||||
rate, SampleRate, resample_quality);
|
||||
} else if (spa_streq(filename, "/hilbert")) {
|
||||
samples = create_hilbert(filename, gain, delay, offset,
|
||||
length, &n_samples);
|
||||
} else if (spa_streq(filename, "/dirac")) {
|
||||
|
|
@ -722,13 +796,17 @@ static void * convolver_instantiate(const struct fc_descriptor * Descriptor,
|
|||
return NULL;
|
||||
}
|
||||
|
||||
for (i = 0; i < MAX_RATES; i++)
|
||||
if (filenames[i])
|
||||
free(filenames[i]);
|
||||
|
||||
if (blocksize <= 0)
|
||||
blocksize = SPA_CLAMP(n_samples, 64, 256);
|
||||
if (tailsize <= 0)
|
||||
tailsize = SPA_CLAMP(4096, blocksize, 32768);
|
||||
|
||||
pw_log_info("using n_samples:%u %d:%d blocksize ir:%s", n_samples,
|
||||
blocksize, tailsize, filename);
|
||||
pw_log_info("using n_samples:%u %d:%d blocksize", n_samples,
|
||||
blocksize, tailsize);
|
||||
|
||||
impl = calloc(1, sizeof(*impl));
|
||||
if (impl == NULL)
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue