resample: add some more options to spa-resample

Make it possible to set the params by name. Dump the resampler config
in verbose mode.
This commit is contained in:
Wim Taymans 2025-11-20 13:19:21 +01:00
parent e8268969ea
commit faf4641625
3 changed files with 69 additions and 30 deletions

View file

@ -13,6 +13,8 @@
SPA_LOG_TOPIC_DEFINE(resample_log_topic, "spa.resample"); SPA_LOG_TOPIC_DEFINE(resample_log_topic, "spa.resample");
#define INHERIT_PARAM(c,q,p) if ((c)->params[p] == 0.0) (c)->params[p] = (q)->params[p];
struct quality { struct quality {
uint32_t n_taps; uint32_t n_taps;
double cutoff_up; /* when upsampling */ double cutoff_up; /* when upsampling */
@ -20,6 +22,7 @@ struct quality {
double params[RESAMPLE_MAX_PARAMS]; double params[RESAMPLE_MAX_PARAMS];
}; };
struct window_info { struct window_info {
uint32_t window; uint32_t window;
void (*func) (struct resample *r, double *w, double t, uint32_t n_taps); void (*func) (struct resample *r, double *w, double t, uint32_t n_taps);
@ -60,9 +63,7 @@ static inline void blackman_window(struct resample *r, double *w, double t, uint
static inline void blackman_config(struct resample *r) static inline void blackman_config(struct resample *r)
{ {
const struct quality *q = &window_info[r->config.window].qualities[r->quality]; const struct quality *q = &window_info[r->config.window].qualities[r->quality];
const uint32_t p0 = RESAMPLE_PARAM_BLACKMAN_ALPHA; INHERIT_PARAM(&r->config, q, RESAMPLE_PARAM_BLACKMAN_ALPHA);
if (r->config.params[p0] == 0.0)
r->config.params[p0] = q->params[p0];
} }
static const struct quality exp_qualities[] = { static const struct quality exp_qualities[] = {
@ -97,9 +98,7 @@ static inline void exp_window(struct resample *r, double *w, double t, uint32_t
static inline void exp_config(struct resample *r) static inline void exp_config(struct resample *r)
{ {
const struct quality *q = &window_info[r->config.window].qualities[r->quality]; const struct quality *q = &window_info[r->config.window].qualities[r->quality];
const uint32_t p0 = RESAMPLE_PARAM_EXP_A; INHERIT_PARAM(&r->config, q, RESAMPLE_PARAM_EXP_A);
if (r->config.params[p0] == 0.0)
r->config.params[p0] = q->params[p0];
} }
#include "dbesi0.c" #include "dbesi0.c"
@ -166,9 +165,9 @@ static inline void kaiser_config(struct resample *r)
static inline void kaiser_config(struct resample *r) static inline void kaiser_config(struct resample *r)
{ {
const struct quality *q = &window_info[r->config.window].qualities[r->quality]; const struct quality *q = &window_info[r->config.window].qualities[r->quality];
const uint32_t p0 = RESAMPLE_PARAM_KAISER_ALPHA; INHERIT_PARAM(&r->config, q, RESAMPLE_PARAM_KAISER_ALPHA);
if (r->config.params[p0] == 0.0) INHERIT_PARAM(&r->config, q, RESAMPLE_PARAM_KAISER_SB_ATT);
r->config.params[p0] = q->params[p0]; INHERIT_PARAM(&r->config, q, RESAMPLE_PARAM_KAISER_TR_BW);
} }
#endif #endif

View file

@ -9,6 +9,7 @@
#include <spa/support/log.h> #include <spa/support/log.h>
#define RESAMPLE_DEFAULT_QUALITY 4 #define RESAMPLE_DEFAULT_QUALITY 4
#define RESAMPLE_WINDOW_DEFAULT RESAMPLE_WINDOW_EXP
#define RESAMPLE_MAX_PARAMS 16 #define RESAMPLE_MAX_PARAMS 16
struct resample_config { struct resample_config {
@ -77,13 +78,14 @@ static const struct resample_window_info {
uint32_t window; uint32_t window;
const char *label; const char *label;
const char *description; const char *description;
uint32_t n_params;
} resample_window_info[] = { } resample_window_info[] = {
[RESAMPLE_WINDOW_EXP] = { RESAMPLE_WINDOW_EXP, [RESAMPLE_WINDOW_EXP] = { RESAMPLE_WINDOW_EXP,
"exponential", "Exponential window", }, "exp", "Exponential window", 1 },
[RESAMPLE_WINDOW_BLACKMAN] = { RESAMPLE_WINDOW_BLACKMAN, [RESAMPLE_WINDOW_BLACKMAN] = { RESAMPLE_WINDOW_BLACKMAN,
"blackman", "Blackman window", }, "blackman", "Blackman window", 1 },
[RESAMPLE_WINDOW_KAISER] = { RESAMPLE_WINDOW_KAISER, [RESAMPLE_WINDOW_KAISER] = { RESAMPLE_WINDOW_KAISER,
"kaiser", "Kaiser window", }, "kaiser", "Kaiser window", 3 },
}; };
static inline uint32_t resample_window_from_label(const char *label) static inline uint32_t resample_window_from_label(const char *label)
@ -95,15 +97,21 @@ static inline uint32_t resample_window_from_label(const char *label)
return RESAMPLE_WINDOW_EXP; return RESAMPLE_WINDOW_EXP;
} }
static inline const char *resample_window_name(uint32_t idx)
{
return resample_window_info[SPA_CLAMP(idx, 0u, SPA_N_ELEMENTS(resample_window_info)-1)].label;
}
static const struct resample_param_info { static const struct resample_param_info {
uint32_t window;
uint32_t idx; uint32_t idx;
const char *label; const char *label;
} resample_param_info[] = { } resample_param_info[] = {
{ RESAMPLE_PARAM_EXP_A, "exp.A" }, { RESAMPLE_WINDOW_EXP, RESAMPLE_PARAM_EXP_A, "exp.A" },
{ RESAMPLE_PARAM_BLACKMAN_ALPHA, "blackman.alpha" }, { RESAMPLE_WINDOW_BLACKMAN, RESAMPLE_PARAM_BLACKMAN_ALPHA, "blackman.alpha" },
{ RESAMPLE_PARAM_KAISER_ALPHA, "kaiser.alpha" }, { RESAMPLE_WINDOW_KAISER, RESAMPLE_PARAM_KAISER_ALPHA, "kaiser.alpha" },
{ RESAMPLE_PARAM_KAISER_SB_ATT, "kaiser.stopband-attenuation" }, { RESAMPLE_WINDOW_KAISER, RESAMPLE_PARAM_KAISER_SB_ATT, "kaiser.stopband-attenuation" },
{ RESAMPLE_PARAM_KAISER_TR_BW, "kaiser.transition-bandwidth" }, { RESAMPLE_WINDOW_KAISER, RESAMPLE_PARAM_KAISER_TR_BW, "kaiser.transition-bandwidth" },
}; };
static inline uint32_t resample_param_from_label(const char *label) static inline uint32_t resample_param_from_label(const char *label)

View file

@ -31,8 +31,7 @@ struct data {
int format; int format;
uint32_t window; uint32_t window;
int quality; int quality;
uint32_t n_taps; struct resample_config config;
double params[4];
int cpu_flags; int cpu_flags;
const char *iname; const char *iname;
@ -65,6 +64,7 @@ static const struct option long_options[] = {
static void show_usage(const char *name, bool is_error) static void show_usage(const char *name, bool is_error)
{ {
FILE *fp; FILE *fp;
uint32_t i;
fp = is_error ? stderr : stdout; fp = is_error ? stderr : stdout;
@ -76,13 +76,27 @@ static void show_usage(const char *name, bool is_error)
fprintf(fp, fprintf(fp,
" -r --rate Output sample rate (default as input)\n" " -r --rate Output sample rate (default as input)\n"
" -f --format Output sample format %s (default as input)\n" " -f --format Output sample format %s (default as input)\n"
" -w --window Window function blackman, exp, kaiser (default kaiser)\n"
" -q --quality Resampler quality (default %u)\n" " -q --quality Resampler quality (default %u)\n"
" -p --param Resampler param\n" " -w --window Window function (default %s)\n",
" -t --taps Resampler taps\n" STR_FMTS, DEFAULT_QUALITY, resample_window_name(RESAMPLE_WINDOW_DEFAULT));
for (i = 0; i < SPA_N_ELEMENTS(resample_window_info); i++) {
fprintf(fp,
" %s: %s\n",
resample_window_info[i].label,
resample_window_info[i].description);
}
fprintf(fp,
" -t --taps Resampler taps (default from quality)\n"
" -p --param Resampler param <name>=<value> (default from quality)\n");
for (i = 0; i < SPA_N_ELEMENTS(resample_param_info); i++) {
fprintf(fp,
" %s\n",
resample_param_info[i].label);
}
fprintf(fp,
" -c --cpuflags CPU flags (default 0)\n" " -c --cpuflags CPU flags (default 0)\n"
"\n", "\n");
STR_FMTS, DEFAULT_QUALITY);
} }
static inline const char * static inline const char *
@ -216,13 +230,23 @@ static int do_conversion(struct data *d)
r.i_rate = d->iinfo.samplerate; r.i_rate = d->iinfo.samplerate;
r.o_rate = d->oinfo.samplerate; r.o_rate = d->oinfo.samplerate;
r.quality = d->quality < 0 ? DEFAULT_QUALITY : d->quality; r.quality = d->quality < 0 ? DEFAULT_QUALITY : d->quality;
r.config.window = d->window; r.config = d->config;
r.config.n_taps = d->n_taps;
r.config.params[0] = d->params[0];
if ((res = resample_native_init(&r)) < 0) { if ((res = resample_native_init(&r)) < 0) {
fprintf(stderr, "can't init converter: %s\n", spa_strerror(res)); fprintf(stderr, "can't init converter: %s\n", spa_strerror(res));
return res; return res;
} }
if (d->verbose) {
fprintf(stdout, "window:%s cutoff:%f n_taps:%u\n",
resample_window_name(r.config.window),
r.config.cutoff, r.config.n_taps);
for (i = 0; i < SPA_N_ELEMENTS(resample_param_info); i++) {
if (resample_param_info[i].window != r.config.window)
continue;
fprintf(stdout, " param:%s %f\n",
resample_param_info[i].label,
r.config.params[resample_param_info[i].idx]);
}
}
for (j = 0; j < channels; j++) for (j = 0; j < channels; j++)
src[j] = &in[MAX_SAMPLES * j]; src[j] = &in[MAX_SAMPLES * j];
@ -333,13 +357,21 @@ int main(int argc, char *argv[])
data.cpu_flags = strtol(optarg, NULL, 0); data.cpu_flags = strtol(optarg, NULL, 0);
break; break;
case 'w': case 'w':
data.window = resample_window_from_label(optarg); data.config.window = resample_window_from_label(optarg);
break; break;
case 'p': case 'p':
data.params[0] = atof(optarg); {
char *eq;
if ((eq = strchr(optarg, '=')) != NULL) {
uint32_t idx;
*eq = 0;
idx = resample_param_from_label(optarg);
data.config.params[idx] = atof(eq+1);
}
break; break;
}
case 't': case 't':
data.n_taps = atoi(optarg); data.config.n_taps = atoi(optarg);
break; break;
default: default:
fprintf(stderr, "error: unknown option '%c'\n", c); fprintf(stderr, "error: unknown option '%c'\n", c);