From faf4641625705a2cdf67d2e766d5fe23095c5b0b Mon Sep 17 00:00:00 2001 From: Wim Taymans Date: Thu, 20 Nov 2025 13:19:21 +0100 Subject: [PATCH] resample: add some more options to spa-resample Make it possible to set the params by name. Dump the resampler config in verbose mode. --- spa/plugins/audioconvert/resample-native.c | 17 +++---- spa/plugins/audioconvert/resample.h | 24 ++++++--- spa/plugins/audioconvert/spa-resample.c | 58 +++++++++++++++++----- 3 files changed, 69 insertions(+), 30 deletions(-) diff --git a/spa/plugins/audioconvert/resample-native.c b/spa/plugins/audioconvert/resample-native.c index 982bf8714..c2729b45b 100644 --- a/spa/plugins/audioconvert/resample-native.c +++ b/spa/plugins/audioconvert/resample-native.c @@ -13,6 +13,8 @@ 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 { uint32_t n_taps; double cutoff_up; /* when upsampling */ @@ -20,6 +22,7 @@ struct quality { double params[RESAMPLE_MAX_PARAMS]; }; + struct window_info { uint32_t window; 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) { const struct quality *q = &window_info[r->config.window].qualities[r->quality]; - const uint32_t p0 = RESAMPLE_PARAM_BLACKMAN_ALPHA; - if (r->config.params[p0] == 0.0) - r->config.params[p0] = q->params[p0]; + INHERIT_PARAM(&r->config, q, RESAMPLE_PARAM_BLACKMAN_ALPHA); } 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) { const struct quality *q = &window_info[r->config.window].qualities[r->quality]; - const uint32_t p0 = RESAMPLE_PARAM_EXP_A; - if (r->config.params[p0] == 0.0) - r->config.params[p0] = q->params[p0]; + INHERIT_PARAM(&r->config, q, RESAMPLE_PARAM_EXP_A); } #include "dbesi0.c" @@ -166,9 +165,9 @@ 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 uint32_t p0 = RESAMPLE_PARAM_KAISER_ALPHA; - if (r->config.params[p0] == 0.0) - r->config.params[p0] = q->params[p0]; + INHERIT_PARAM(&r->config, q, RESAMPLE_PARAM_KAISER_ALPHA); + INHERIT_PARAM(&r->config, q, RESAMPLE_PARAM_KAISER_SB_ATT); + INHERIT_PARAM(&r->config, q, RESAMPLE_PARAM_KAISER_TR_BW); } #endif diff --git a/spa/plugins/audioconvert/resample.h b/spa/plugins/audioconvert/resample.h index 86d6194d5..a031fff16 100644 --- a/spa/plugins/audioconvert/resample.h +++ b/spa/plugins/audioconvert/resample.h @@ -9,6 +9,7 @@ #include #define RESAMPLE_DEFAULT_QUALITY 4 +#define RESAMPLE_WINDOW_DEFAULT RESAMPLE_WINDOW_EXP #define RESAMPLE_MAX_PARAMS 16 struct resample_config { @@ -77,13 +78,14 @@ static const struct resample_window_info { uint32_t window; const char *label; const char *description; + uint32_t n_params; } resample_window_info[] = { [RESAMPLE_WINDOW_EXP] = { RESAMPLE_WINDOW_EXP, - "exponential", "Exponential window", }, + "exp", "Exponential window", 1 }, [RESAMPLE_WINDOW_BLACKMAN] = { RESAMPLE_WINDOW_BLACKMAN, - "blackman", "Blackman window", }, + "blackman", "Blackman window", 1 }, [RESAMPLE_WINDOW_KAISER] = { RESAMPLE_WINDOW_KAISER, - "kaiser", "Kaiser window", }, + "kaiser", "Kaiser window", 3 }, }; 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; } +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 { + uint32_t window; uint32_t idx; const char *label; } resample_param_info[] = { - { RESAMPLE_PARAM_EXP_A, "exp.A" }, - { RESAMPLE_PARAM_BLACKMAN_ALPHA, "blackman.alpha" }, - { RESAMPLE_PARAM_KAISER_ALPHA, "kaiser.alpha" }, - { RESAMPLE_PARAM_KAISER_SB_ATT, "kaiser.stopband-attenuation" }, - { RESAMPLE_PARAM_KAISER_TR_BW, "kaiser.transition-bandwidth" }, + { RESAMPLE_WINDOW_EXP, RESAMPLE_PARAM_EXP_A, "exp.A" }, + { RESAMPLE_WINDOW_BLACKMAN, RESAMPLE_PARAM_BLACKMAN_ALPHA, "blackman.alpha" }, + { RESAMPLE_WINDOW_KAISER, RESAMPLE_PARAM_KAISER_ALPHA, "kaiser.alpha" }, + { RESAMPLE_WINDOW_KAISER, RESAMPLE_PARAM_KAISER_SB_ATT, "kaiser.stopband-attenuation" }, + { RESAMPLE_WINDOW_KAISER, RESAMPLE_PARAM_KAISER_TR_BW, "kaiser.transition-bandwidth" }, }; static inline uint32_t resample_param_from_label(const char *label) diff --git a/spa/plugins/audioconvert/spa-resample.c b/spa/plugins/audioconvert/spa-resample.c index 4c9ae21ea..d9b2c7dd2 100644 --- a/spa/plugins/audioconvert/spa-resample.c +++ b/spa/plugins/audioconvert/spa-resample.c @@ -31,8 +31,7 @@ struct data { int format; uint32_t window; int quality; - uint32_t n_taps; - double params[4]; + struct resample_config config; int cpu_flags; const char *iname; @@ -65,6 +64,7 @@ static const struct option long_options[] = { static void show_usage(const char *name, bool is_error) { FILE *fp; + uint32_t i; fp = is_error ? stderr : stdout; @@ -76,13 +76,27 @@ static void show_usage(const char *name, bool is_error) fprintf(fp, " -r --rate Output sample rate (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" - " -p --param Resampler param\n" - " -t --taps Resampler taps\n" + " -w --window Window function (default %s)\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 = (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" - "\n", - STR_FMTS, DEFAULT_QUALITY); + "\n"); } static inline const char * @@ -216,13 +230,23 @@ static int do_conversion(struct data *d) r.i_rate = d->iinfo.samplerate; r.o_rate = d->oinfo.samplerate; r.quality = d->quality < 0 ? DEFAULT_QUALITY : d->quality; - r.config.window = d->window; - r.config.n_taps = d->n_taps; - r.config.params[0] = d->params[0]; + r.config = d->config; if ((res = resample_native_init(&r)) < 0) { fprintf(stderr, "can't init converter: %s\n", spa_strerror(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++) src[j] = &in[MAX_SAMPLES * j]; @@ -333,13 +357,21 @@ int main(int argc, char *argv[]) data.cpu_flags = strtol(optarg, NULL, 0); break; case 'w': - data.window = resample_window_from_label(optarg); + data.config.window = resample_window_from_label(optarg); break; 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; + } case 't': - data.n_taps = atoi(optarg); + data.config.n_taps = atoi(optarg); break; default: fprintf(stderr, "error: unknown option '%c'\n", c);