diff --git a/spa/plugins/audioconvert/audioconvert.c b/spa/plugins/audioconvert/audioconvert.c index ebb057218..7665975d0 100644 --- a/spa/plugins/audioconvert/audioconvert.c +++ b/spa/plugins/audioconvert/audioconvert.c @@ -100,6 +100,7 @@ struct props { unsigned int mix_disabled:1; unsigned int resample_disabled:1; unsigned int resample_quality; + uint32_t resample_window; double rate; char wav_path[512]; unsigned int lock_volumes:1; @@ -122,6 +123,7 @@ static void props_reset(struct props *props) props->mix_disabled = false; props->resample_disabled = false; props->resample_quality = RESAMPLE_DEFAULT_QUALITY; + props->resample_window = RESAMPLE_WINDOW_DEFAULT; props->rate = 1.0; spa_zero(props->wav_path); props->lock_volumes = false; @@ -1478,6 +1480,8 @@ static int audioconvert_set_param(struct impl *this, const char *k, const char * this->props.resample_quality = atoi(s); else if (spa_streq(k, "resample.disable")) this->props.resample_disabled = spa_atob(s); + else if (spa_streq(k, "resample.window")) + this->props.resample_window = resample_window_from_label(s); else if (spa_streq(k, "dither.noise")) spa_atou32(s, &this->dir[1].conv.noise_bits, 0); else if (spa_streq(k, "dither.method")) @@ -2289,6 +2293,8 @@ static int setup_resample(struct impl *this) this->resample.o_rate = out->format.info.raw.rate; this->resample.log = this->log; this->resample.quality = this->props.resample_quality; + spa_zero(this->resample.config); + this->resample.config.window = this->props.resample_window; this->resample.cpu_flags = this->cpu_flags; this->rate_adjust = this->props.rate != 1.0; diff --git a/spa/plugins/audioconvert/dbesi0.c b/spa/plugins/audioconvert/dbesi0.c new file mode 100644 index 000000000..958eba717 --- /dev/null +++ b/spa/plugins/audioconvert/dbesi0.c @@ -0,0 +1,147 @@ +/* Copyright(C) 1996 Takuya OOURA + +You may use, copy, modify this code for any purpose and +without fee. + +Package home: http://www.kurims.kyoto-u.ac.jp/~ooura/bessel.html +*/ + +/* Bessel I_0(x) function in double precision */ + +#include + +static double +dbesi0 (double x) +{ + int k; + double w, t, y; + static double a[65] = { + 8.5246820682016865877e-11, 2.5966600546497407288e-9, + 7.9689994568640180274e-8, 1.9906710409667748239e-6, + 4.0312469446528002532e-5, 6.4499871606224265421e-4, + 0.0079012345761930579108, 0.071111111109207045212, + 0.444444444444724909, 1.7777777777777532045, + 4.0000000000000011182, 3.99999999999999998, + 1.0000000000000000001, + 1.1520919130377195927e-10, 2.2287613013610985225e-9, + 8.1903951930694585113e-8, 1.9821560631611544984e-6, + 4.0335461940910133184e-5, 6.4495330974432203401e-4, + 0.0079013012611467520626, 0.071111038160875566622, + 0.44444450319062699316, 1.7777777439146450067, + 4.0000000132337935071, 3.9999999968569015366, + 1.0000000003426703174, + 1.5476870780515238488e-10, 1.2685004214732975355e-9, + 9.2776861851114223267e-8, 1.9063070109379044378e-6, + 4.0698004389917945832e-5, 6.4370447244298070713e-4, + 0.0079044749458444976958, 0.071105052411749363882, + 0.44445280640924755082, 1.7777694934432109713, + 4.0000055808824003386, 3.9999977081165740932, + 1.0000004333949319118, + 2.0675200625006793075e-10, -6.1689554705125681442e-10, + 1.2436765915401571654e-7, 1.5830429403520613423e-6, + 4.2947227560776583326e-5, 6.3249861665073441312e-4, + 0.0079454472840953930811, 0.070994327785661860575, + 0.44467219586283000332, 1.7774588182255374745, + 4.0003038986252717972, 3.9998233869142057195, + 1.0000472932961288324, + 2.7475684794982708655e-10, -3.8991472076521332023e-9, + 1.9730170483976049388e-7, 5.9651531561967674521e-7, + 5.1992971474748995357e-5, 5.7327338675433770752e-4, + 0.0082293143836530412024, 0.069990934858728039037, + 0.44726764292723985087, 1.7726685170014087784, + 4.0062907863712704432, 3.9952750700487845355, + 1.0016354346654179322 + }; + static double b[70] = { + 6.7852367144945531383e-8, 4.6266061382821826854e-7, + 6.9703135812354071774e-6, 7.6637663462953234134e-5, + 7.9113515222612691636e-4, 0.0073401204731103808981, + 0.060677114958668837046, 0.43994941411651569622, + 2.7420017097661750609, 14.289661921740860534, + 59.820609640320710779, 188.78998681199150629, + 399.8731367825601118, 427.56411572180478514, + 1.8042097874891098754e-7, 1.2277164312044637357e-6, + 1.8484393221474274861e-5, 2.0293995900091309208e-4, + 0.0020918539850246207459, 0.019375315654033949297, + 0.15985869016767185908, 1.1565260527420641724, + 7.1896341224206072113, 37.354773811947484532, + 155.80993164266268457, 489.5211371158540918, + 1030.9147225169564806, 1093.5883545113746958, + 4.8017305613187493564e-7, 3.261317843912380074e-6, + 4.9073137508166159639e-5, 5.3806506676487583755e-4, + 0.0055387918291051866561, 0.051223717488786549025, + 0.42190298621367914765, 3.0463625987357355872, + 18.895299447327733204, 97.915189029455461554, + 407.13940115493494659, 1274.3088990480582632, + 2670.9883037012547506, 2815.7166284662544712, + 1.2789926338424623394e-6, 8.6718263067604918916e-6, + 1.3041508821299929489e-4, 0.001428224737372747892, + 0.014684070635768789378, 0.13561403190404185755, + 1.1152592585977393953, 8.0387088559465389038, + 49.761318895895479206, 257.2684232313529138, + 1066.8543146269566231, 3328.3874581009636362, + 6948.8586598121634874, 7288.4893398212481055, + 3.409350368197032893e-6, 2.3079025203103376076e-5, + 3.4691373283901830239e-4, 0.003794994977222908545, + 0.038974209677945602145, 0.3594948380414878371, + 2.9522878893539528226, 21.246564609514287056, + 131.28727387146173141, 677.38107093296675421, + 2802.3724744545046518, 8718.5731420798254081, + 18141.348781638832286, 18948.925349296308859 + }; + static double c[45] = { + 2.5568678676452702768e-15, 3.0393953792305924324e-14, + 6.3343751991094840009e-13, 1.5041298011833009649e-11, + 4.4569436918556541414e-10, 1.746393051427167951e-8, + 1.0059224011079852317e-6, 1.0729838945088577089e-4, + 0.05150322693642527738, + 5.2527963991711562216e-15, 7.202118481421005641e-15, + 7.2561421229904797156e-13, 1.482312146673104251e-11, + 4.4602670450376245434e-10, 1.7463600061788679671e-8, + 1.005922609132234756e-6, 1.0729838937545111487e-4, + 0.051503226936437300716, + 1.3365917359358069908e-14, -1.2932643065888544835e-13, + 1.7450199447905602915e-12, 1.0419051209056979788e-11, + 4.58047881980598326e-10, 1.7442405450073548966e-8, + 1.0059461453281292278e-6, 1.0729837434500161228e-4, + 0.051503226940658446941, + 5.3771611477352308649e-14, -1.1396193006413731702e-12, + 1.2858641335221653409e-11, -5.9802086004570057703e-11, + 7.3666894305929510222e-10, 1.6731837150730356448e-8, + 1.0070831435812128922e-6, 1.0729733111203704813e-4, + 0.051503227360726294675, + 3.7819492084858931093e-14, -4.8600496888588034879e-13, + 1.6898350504817224909e-12, 4.5884624327524255865e-11, + 1.2521615963377513729e-10, 1.8959658437754727957e-8, + 1.0020716710561353622e-6, 1.073037119856927559e-4, + 0.05150322383300230775 + }; + + w = fabs (x); + if (w < 8.5) { + t = w * w * 0.0625; + k = 13 * ((int) t); + y = (((((((((((a[k] * t + a[k + 1]) * t + + a[k + 2]) * t + a[k + 3]) * t + + a[k + 4]) * t + a[k + 5]) * t + a[k + + 6]) * t + a[k + 7]) * t + a[k + 8]) * t + a[k + + 9]) * t + a[k + 10]) * t + a[k + 11]) * t + a[k + 12]; + } else if (w < 12.5) { + k = (int) w; + t = w - k; + k = 14 * (k - 8); + y = ((((((((((((b[k] * t + b[k + 1]) * t + b[k + 2]) * t + b[k + 3]) * t + + b[k + 4]) * t + b[k + 5]) * t + b[k + + 6]) * t + b[k + 7]) * t + b[k + 8]) * t + + b[k + 9]) * t + b[k + 10]) * t + b[k + 11]) * t + b[k + + 12]) * t + b[k + 13]; + } else { + t = 60 / w; + k = 9 * ((int) t); + y = ((((((((c[k] * t + c[k + 1]) * t + + c[k + 2]) * t + c[k + 3]) * t + c[k + 4]) * t + + c[k + 5]) * t + c[k + 6]) * t + c[k + 7]) * t + + c[k + 8]) * sqrt (t) * exp (w); + } + return y; +} diff --git a/spa/plugins/audioconvert/resample-native.c b/spa/plugins/audioconvert/resample-native.c index 985f25497..23f54070e 100644 --- a/spa/plugins/audioconvert/resample-native.c +++ b/spa/plugins/audioconvert/resample-native.c @@ -15,70 +15,189 @@ SPA_LOG_TOPIC_DEFINE(resample_log_topic, "spa.resample"); struct quality { uint32_t n_taps; - double cutoff; + double cutoff; /* when upsampling */ + double cutoff_factor; /* factor for downsampling */ + double params[32]; }; -static const struct quality window_qualities[] = { - { 8, 0.53, }, - { 16, 0.67, }, - { 24, 0.75, }, - { 32, 0.80, }, - { 48, 0.85, }, /* default */ - { 64, 0.88, }, - { 80, 0.895, }, - { 96, 0.910, }, - { 128, 0.936, }, - { 144, 0.945, }, - { 160, 0.950, }, - { 192, 0.960, }, - { 256, 0.970, }, - { 896, 0.990, }, - { 1024, 0.995, }, +struct window_info { + uint32_t window; + void (*func) (struct resample *r, double *w, double t, uint32_t n_taps); + uint32_t n_qualities; + const struct quality *qualities; + void (*config) (struct resample *r); +}; +struct window_info window_info[]; + +static const struct quality blackman_qualities[] = { + { 8, 0.53, 1.0, { 0.16, }}, + { 16, 0.67, 1.0, { 0.20, }}, + { 24, 0.75, 1.0, { 0.16, }}, + { 32, 0.80, 1.0, { 0.16, }}, + { 48, 0.85, 1.0, { 0.16, }}, /* default */ + { 64, 0.88, 1.0, { 0.16, }}, + { 80, 0.895, 1.0, { 0.16, }}, + { 96, 0.910, 1.0, { 0.16, }}, + { 128, 0.936, 1.0, { 0.16, }}, + { 144, 0.945, 1.0, { 0.16, }}, + { 160, 0.950, 1.0, { 0.16, }}, + { 192, 0.960, 1.0, { 0.16, }}, + { 256, 0.970, 1.0, { 0.16, }}, + { 896, 0.990, 1.0, { 0.16, }}, + { 1024, 0.995, 1.0, { 0.16, }}, }; -static inline double sinc(double x) +static inline void blackman_window(struct resample *r, double *w, double t, uint32_t n_taps) { - if (x < 1e-6) return 1.0; + double x, alpha = r->config.blackman_params.alpha; + uint32_t i, n_taps12 = n_taps/2; + for (i = 0; i < n_taps12; i++, t += 1.0) { + x = 2.0 * M_PI * t / n_taps; + w[i] = (1.0 - alpha) / 2.0 + (1.0 / 2.0) * cos(x) + + (alpha / 2.0) * cos(2.0 * x); + } +} +static inline void blackman_config(struct resample *r) +{ + const struct quality *q = &window_info[r->config.window].qualities[r->quality]; + if (r->config.blackman_params.alpha == 0.0) + r->config.blackman_params.alpha = q->params[0]; +} + +static const struct quality exp_qualities[] = { + { 8, 0.53, 1.0, { 16.97789, }}, + { 16, 0.67, 1.0, { 16.97789, }}, + { 24, 0.75, 1.0, { 16.97789, }}, + { 32, 0.80, 1.0, { 16.97789, }}, + { 48, 0.85, 1.0, { 16.97789, }}, /* default */ + { 64, 0.88, 1.0, { 16.97789, }}, + { 80, 0.895, 1.0, { 16.97789, }}, + { 96, 0.910, 1.0, { 16.97789, }}, + { 128, 0.936, 1.0, { 16.97789, }}, + { 144, 0.945, 1.0, { 16.97789, }}, + { 160, 0.950, 1.0, { 16.97789, }}, + { 192, 0.960, 1.0, { 16.97789, }}, + { 256, 0.970, 1.0, { 16.97789, }}, + { 896, 0.990, 1.0, { 16.97789, }}, + { 1024, 0.995, 1.0, { 16.97789, }}, +}; + +static inline void exp_window(struct resample *r, double *w, double t, uint32_t n_taps) +{ + double x, A = r->config.exp_params.A; + uint32_t i, n_taps12 = n_taps/2; + + for (i = 0; i < n_taps12; i++, t += 1.0) { + x = 2.0 * t / n_taps; + /* doi:10.1109/RME.2008.4595727 with tweak */ + w[i] = (exp(A * sqrt(fmax(0.0, 1.0 - x*x))) - 1) / (exp(A) - 1); + } +} +static inline void exp_config(struct resample *r) +{ + const struct quality *q = &window_info[r->config.window].qualities[r->quality]; + if (r->config.exp_params.A == 0.0) + r->config.exp_params.A = q->params[0]; +} + +#include "dbesi0.c" + +static const struct quality kaiser_qualities[] = { + { 8, 0.53, 1.0, { 5.0, }}, + { 16, 0.67, 1.0, { 5.5, }}, + { 24, 0.75, 1.0, { 6.5, }}, + { 32, 0.80, 1.0, { 7.5, }}, + { 48, 0.85, 1.0, { 8.5, }}, /* default */ + { 64, 0.88, 1.0, { 9.5, }}, + { 80, 0.895, 1.0, { 10.0, }}, + { 96, 0.910, 1.0, { 10.5, }}, + { 128, 0.936, 1.0, { 10.7, }}, + { 144, 0.945, 1.0, { 11.5, }}, + { 160, 0.950, 1.0, { 12.5, }}, + { 192, 0.960, 1.0, { 13.5, }}, + { 256, 0.970, 1.0, { 14.5, }}, + { 896, 0.990, 1.0, { 16.67, }}, + { 1024, 0.995, 1.0, { 17.67, }}, +}; + +static inline void kaiser_window(struct resample *r, double *w, double t, uint32_t n_taps) +{ + double x, alpha = r->config.kaiser_params.alpha, beta = M_PI*alpha, den = dbesi0(beta); + uint32_t i, n_taps12 = n_taps/2; + for (i = 0; i < n_taps12; i++, t += 1.0) { + x = 2.0 * t / n_taps; + w[i] = dbesi0(beta * sqrt(fmax(0.0, 1.0 - x*x))) / den; + } +} + +#if 0 +static inline void kaiser_config(struct resample *r) +{ + double A, B, dw, tr_bw; + uint32_t n; + + A = r->config.kaiser_params.stopband_attenuation; + if (A <= 0.0) + A = 160; + tr_bw = r->config.kaiser_params.transition_bandwidth; + if (tr_bw <= 0.0) + tr_bw = 0.0305; + + /* calculate Beta */ + if (A > 50) + B = 0.1102 * (A - 8.7); + else if (A >= 21) + B = 0.5842 * pow (A - 21, 0.4) + 0.07886 * (A - 21); + else + B = 0.0; + + /* calculate transition width in radians */ + dw = 2 * M_PI * (tr_bw); + /* order of the filter */ + n = (uint32_t)((A - 8.0) / (2.285 * dw)); + + r->config.kaiser_params.alpha = B; + r->config.n_taps = n + 1; +} +#else +static inline void kaiser_config(struct resample *r) +{ + const struct quality *q = &window_info[r->config.window].qualities[r->quality]; + if (r->config.kaiser_params.alpha == 0.0) + r->config.kaiser_params.alpha = q->params[0]; +} +#endif + +struct window_info window_info[] = { + [RESAMPLE_WINDOW_EXP] = { RESAMPLE_WINDOW_EXP, exp_window, + SPA_N_ELEMENTS(exp_qualities), exp_qualities, exp_config }, + [RESAMPLE_WINDOW_BLACKMAN] = { RESAMPLE_WINDOW_BLACKMAN, blackman_window, + SPA_N_ELEMENTS(blackman_qualities), blackman_qualities, blackman_config }, + [RESAMPLE_WINDOW_KAISER] = { RESAMPLE_WINDOW_KAISER, kaiser_window, + SPA_N_ELEMENTS(kaiser_qualities), kaiser_qualities, kaiser_config }, +}; + +static inline double sinc(double x, double cutoff) +{ + if (x < 1e-6) return cutoff; x *= M_PI; - return sin(x) / x; + return sin(x * cutoff) / x; } -static inline double window_blackman(double x, double n_taps) -{ - double alpha = 0.232, r; - x = 2.0 * M_PI * x / n_taps; - r = (1.0 - alpha) / 2.0 + (1.0 / 2.0) * cos(x) + - (alpha / 2.0) * cos(2.0 * x); - return r; -} - -static inline double window_cosh(double x, double n_taps) -{ - double r; - double A = 16.97789; - double x2; - x = 2.0 * x / n_taps; - x2 = x * x; - if (x2 >= 1.0) - return 0.0; - /* doi:10.1109/RME.2008.4595727 with tweak */ - r = (exp(A * sqrt(1 - x2)) - 1) / (exp(A) - 1); - return r; -} - -#define window (1 ? window_cosh : window_blackman) - -static int build_filter(float *taps, uint32_t stride, uint32_t n_taps, uint32_t n_phases, double cutoff) +static int build_filter(struct resample *r, float *taps, uint32_t stride, uint32_t n_taps, + uint32_t n_phases, double cutoff) { uint32_t i, j, n_taps12 = n_taps/2; + double window[n_taps12+1]; for (i = 0; i <= n_phases; i++) { double t = (double) i / (double) n_phases; + window_info[r->config.window].func(r, window, t, n_taps); for (j = 0; j < n_taps12; j++, t += 1.0) { /* exploit symmetry in filter taps */ taps[(n_phases - i) * stride + n_taps12 + j] = taps[i * stride + (n_taps12 - j - 1)] = (float) - (cutoff * sinc(t * cutoff) * window(t, n_taps)); + (sinc(t, cutoff) * window[j]); } } return 0; @@ -352,11 +471,15 @@ int resample_native_init(struct resample *r) { struct native_data *d; const struct quality *q; - double scale; - uint32_t c, n_taps, n_phases, filter_size, in_rate, out_rate, gcd, filter_stride; + double scale, cutoff_factor; + uint32_t i, n_taps, n_phases, filter_size, in_rate, out_rate, gcd, filter_stride; uint32_t history_stride, history_size, oversample; + struct resample_config *c = &r->config; - r->quality = SPA_CLAMP(r->quality, 0, (int) SPA_N_ELEMENTS(window_qualities) - 1); + if (c->window == RESAMPLE_WINDOW_DEFAULT) + c->window = RESAMPLE_WINDOW_EXP; + + r->quality = SPA_CLAMP(r->quality, 0, (int)(window_info[c->window].n_qualities - 1)); r->free = impl_native_free; r->update_rate = impl_native_update_rate; r->in_len = impl_native_in_len; @@ -366,17 +489,22 @@ int resample_native_init(struct resample *r) r->delay = impl_native_delay; r->phase = impl_native_phase; - q = &window_qualities[r->quality]; + window_info[c->window].config(r); + + q = &window_info[c->window].qualities[r->quality]; + cutoff_factor = r->o_rate < r->i_rate ? q->cutoff_factor : 1.0; + c->cutoff = c->cutoff <= 0.0 ? q->cutoff * cutoff_factor: c->cutoff; + n_taps = c->n_taps == 0 ? q->n_taps : c->n_taps; gcd = calc_gcd(r->i_rate, r->o_rate); in_rate = r->i_rate / gcd; out_rate = r->o_rate / gcd; - scale = SPA_MIN(q->cutoff * out_rate / in_rate, q->cutoff); + scale = SPA_MIN(c->cutoff * out_rate / in_rate, c->cutoff); /* multiple of 8 taps to ease simd optimizations */ - n_taps = SPA_ROUND_UP_N((uint32_t)ceil(q->n_taps / scale), 8); + n_taps = SPA_ROUND_UP_N((uint32_t)ceil(n_taps / scale), 8); n_taps = SPA_MIN(n_taps, 1u << 18); /* try to get at least 256 phases so that interpolation is @@ -400,7 +528,7 @@ int resample_native_init(struct resample *r) return -errno; r->data = d; - d->n_taps = n_taps; + c->n_taps = d->n_taps = n_taps; d->n_phases = n_phases; d->in_rate = UINT32_TO_FIXP(in_rate); d->out_rate = out_rate; @@ -411,25 +539,25 @@ int resample_native_init(struct resample *r) d->history = SPA_PTROFF(d->hist_mem, history_size, float*); d->filter_stride = filter_stride / sizeof(float); d->filter_stride_os = d->filter_stride * oversample; - for (c = 0; c < r->channels; c++) - d->history[c] = SPA_PTROFF(d->hist_mem, c * history_stride, float); + for (i = 0; i < r->channels; i++) + d->history[i] = SPA_PTROFF(d->hist_mem, i * history_stride, float); #ifndef RESAMPLE_DISABLE_PRECOMP /* See if we have precomputed coefficients */ - for (c = 0; precomp_coeffs[c].filter; c++) { - if (precomp_coeffs[c].in_rate == r->i_rate && - precomp_coeffs[c].out_rate == r->o_rate && - precomp_coeffs[c].quality == r->quality) + for (i = 0; precomp_coeffs[i].filter; i++) { + if (precomp_coeffs[i].in_rate == r->i_rate && + precomp_coeffs[i].out_rate == r->o_rate && + precomp_coeffs[i].quality == r->quality) break; } - if (precomp_coeffs[c].filter) { + if (precomp_coeffs[i].filter) { spa_log_debug(r->log, "using precomputed filter for %u->%u(%u)", r->i_rate, r->o_rate, r->quality); - spa_memcpy(d->filter, precomp_coeffs[c].filter, filter_size); + spa_memcpy(d->filter, precomp_coeffs[i].filter, filter_size); } else { #endif - build_filter(d->filter, d->filter_stride, n_taps, n_phases, scale); + build_filter(r, d->filter, d->filter_stride, n_taps, n_phases, scale); #ifndef RESAMPLE_DISABLE_PRECOMP } #endif @@ -440,8 +568,8 @@ int resample_native_init(struct resample *r) return -ENOTSUP; } - spa_log_debug(r->log, "native %p: q:%d in:%d out:%d gcd:%d n_taps:%d n_phases:%d features:%08x:%08x", - r, r->quality, r->i_rate, r->o_rate, gcd, n_taps, n_phases, + spa_log_info(r->log, "native %p: q:%d w:%d in:%d out:%d gcd:%d n_taps:%d n_phases:%d features:%08x:%08x", + r, r->quality, c->window, r->i_rate, r->o_rate, gcd, n_taps, n_phases, r->cpu_flags, d->info->cpu_flags); r->cpu_flags = d->info->cpu_flags; diff --git a/spa/plugins/audioconvert/resample.h b/spa/plugins/audioconvert/resample.h index 5308fa828..eb9cf3563 100644 --- a/spa/plugins/audioconvert/resample.h +++ b/spa/plugins/audioconvert/resample.h @@ -10,6 +10,32 @@ #define RESAMPLE_DEFAULT_QUALITY 4 +struct resample_config { +#define RESAMPLE_WINDOW_DEFAULT 0 +#define RESAMPLE_WINDOW_EXP 1 +#define RESAMPLE_WINDOW_BLACKMAN 2 +#define RESAMPLE_WINDOW_KAISER 3 + uint32_t window; + + uint32_t n_taps; + double cutoff; + + union { + double params[32]; + struct { + double A; + } exp_params; + struct { + double alpha; + } blackman_params; + struct { + double stopband_attenuation; + double transition_bandwidth; + double alpha; + } kaiser_params; + }; +}; + struct resample { struct spa_log *log; #define RESAMPLE_OPTION_PREFILL (1<<0) @@ -23,6 +49,8 @@ struct resample { double rate; int quality; + struct resample_config config; + void (*free) (struct resample *r); void (*update_rate) (struct resample *r, double rate); uint32_t (*in_len) (struct resample *r, uint32_t out_len); @@ -49,6 +77,32 @@ struct resample { #define resample_phase(r) (r)->phase(r) int resample_native_init(struct resample *r); +int resample_native_init_config(struct resample *r, struct resample_config *conf); int resample_peaks_init(struct resample *r); +static const struct resample_window_info { + uint32_t window; + const char *label; + const char *description; +} resample_window_info[] = { + [RESAMPLE_WINDOW_DEFAULT] = { RESAMPLE_WINDOW_DEFAULT, + "default", "Default window", }, + [RESAMPLE_WINDOW_EXP] = { RESAMPLE_WINDOW_EXP, + "exponential", "Exponential window", }, + [RESAMPLE_WINDOW_BLACKMAN] = { RESAMPLE_WINDOW_BLACKMAN, + "blackman", "Blackman window", }, + [RESAMPLE_WINDOW_KAISER] = { RESAMPLE_WINDOW_KAISER, + "kaiser", "Kaiser window", }, +}; + +static inline uint32_t resample_window_from_label(const char *label) +{ + SPA_FOR_EACH_ELEMENT_VAR(resample_window_info, i) { + if (spa_streq(i->label, label)) + return i->window; + } + return RESAMPLE_WINDOW_EXP; +} + + #endif /* RESAMPLE_H */ diff --git a/spa/plugins/audioconvert/spa-resample.c b/spa/plugins/audioconvert/spa-resample.c index ec752c654..4c9ae21ea 100644 --- a/spa/plugins/audioconvert/spa-resample.c +++ b/spa/plugins/audioconvert/spa-resample.c @@ -29,7 +29,10 @@ struct data { bool verbose; int rate; int format; + uint32_t window; int quality; + uint32_t n_taps; + double params[4]; int cpu_flags; const char *iname; @@ -43,14 +46,17 @@ struct data { #define STR_FMTS "(s8|s16|s32|f32|f64)" -#define OPTIONS "hvr:f:q:c:" +#define OPTIONS "hvr:f:q:c:w:p:t:" static const struct option long_options[] = { { "help", no_argument, NULL, 'h'}, { "verbose", no_argument, NULL, 'v'}, { "rate", required_argument, NULL, 'r' }, { "format", required_argument, NULL, 'f' }, + { "window", required_argument, NULL, 'w' }, { "quality", required_argument, NULL, 'q' }, + { "param", required_argument, NULL, 'p' }, + { "taps", required_argument, NULL, 't' }, { "cpuflags", required_argument, NULL, 'c' }, { NULL, 0, NULL, 0 } @@ -70,7 +76,10 @@ 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" " -c --cpuflags CPU flags (default 0)\n" "\n", STR_FMTS, DEFAULT_QUALITY); @@ -207,6 +216,9 @@ 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]; if ((res = resample_native_init(&r)) < 0) { fprintf(stderr, "can't init converter: %s\n", spa_strerror(res)); return res; @@ -320,6 +332,15 @@ int main(int argc, char *argv[]) case 'c': data.cpu_flags = strtol(optarg, NULL, 0); break; + case 'w': + data.window = resample_window_from_label(optarg); + break; + case 'p': + data.params[0] = atof(optarg); + break; + case 't': + data.n_taps = atoi(optarg); + break; default: fprintf(stderr, "error: unknown option '%c'\n", c); goto error_usage;