resample: make window configurable

Add kaiser window
This commit is contained in:
Wim Taymans 2025-11-14 18:15:38 +01:00
parent 18ff08243b
commit bfd26c98e3
5 changed files with 422 additions and 66 deletions

View file

@ -100,6 +100,7 @@ struct props {
unsigned int mix_disabled:1; unsigned int mix_disabled:1;
unsigned int resample_disabled:1; unsigned int resample_disabled:1;
unsigned int resample_quality; unsigned int resample_quality;
uint32_t resample_window;
double rate; double rate;
char wav_path[512]; char wav_path[512];
unsigned int lock_volumes:1; unsigned int lock_volumes:1;
@ -122,6 +123,7 @@ static void props_reset(struct props *props)
props->mix_disabled = false; props->mix_disabled = false;
props->resample_disabled = false; props->resample_disabled = false;
props->resample_quality = RESAMPLE_DEFAULT_QUALITY; props->resample_quality = RESAMPLE_DEFAULT_QUALITY;
props->resample_window = RESAMPLE_WINDOW_DEFAULT;
props->rate = 1.0; props->rate = 1.0;
spa_zero(props->wav_path); spa_zero(props->wav_path);
props->lock_volumes = false; 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); this->props.resample_quality = atoi(s);
else if (spa_streq(k, "resample.disable")) else if (spa_streq(k, "resample.disable"))
this->props.resample_disabled = spa_atob(s); 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")) else if (spa_streq(k, "dither.noise"))
spa_atou32(s, &this->dir[1].conv.noise_bits, 0); spa_atou32(s, &this->dir[1].conv.noise_bits, 0);
else if (spa_streq(k, "dither.method")) 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.o_rate = out->format.info.raw.rate;
this->resample.log = this->log; this->resample.log = this->log;
this->resample.quality = this->props.resample_quality; 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->resample.cpu_flags = this->cpu_flags;
this->rate_adjust = this->props.rate != 1.0; this->rate_adjust = this->props.rate != 1.0;

View file

@ -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 <math.h>
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;
}

View file

@ -15,70 +15,189 @@ SPA_LOG_TOPIC_DEFINE(resample_log_topic, "spa.resample");
struct quality { struct quality {
uint32_t n_taps; 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[] = { struct window_info {
{ 8, 0.53, }, uint32_t window;
{ 16, 0.67, }, void (*func) (struct resample *r, double *w, double t, uint32_t n_taps);
{ 24, 0.75, }, uint32_t n_qualities;
{ 32, 0.80, }, const struct quality *qualities;
{ 48, 0.85, }, /* default */ void (*config) (struct resample *r);
{ 64, 0.88, }, };
{ 80, 0.895, }, struct window_info window_info[];
{ 96, 0.910, },
{ 128, 0.936, }, static const struct quality blackman_qualities[] = {
{ 144, 0.945, }, { 8, 0.53, 1.0, { 0.16, }},
{ 160, 0.950, }, { 16, 0.67, 1.0, { 0.20, }},
{ 192, 0.960, }, { 24, 0.75, 1.0, { 0.16, }},
{ 256, 0.970, }, { 32, 0.80, 1.0, { 0.16, }},
{ 896, 0.990, }, { 48, 0.85, 1.0, { 0.16, }}, /* default */
{ 1024, 0.995, }, { 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;
x *= M_PI; uint32_t i, n_taps12 = n_taps/2;
return sin(x) / x; 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) +
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); (alpha / 2.0) * cos(2.0 * x);
return r; }
} }
static inline void blackman_config(struct resample *r)
static inline double window_cosh(double x, double n_taps)
{ {
double r; const struct quality *q = &window_info[r->config.window].qualities[r->quality];
double A = 16.97789; if (r->config.blackman_params.alpha == 0.0)
double x2; r->config.blackman_params.alpha = q->params[0];
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 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 int build_filter(float *taps, uint32_t stride, uint32_t n_taps, uint32_t n_phases, double cutoff) 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 * cutoff) / x;
}
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; uint32_t i, j, n_taps12 = n_taps/2;
double window[n_taps12+1];
for (i = 0; i <= n_phases; i++) { for (i = 0; i <= n_phases; i++) {
double t = (double) i / (double) n_phases; 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) { for (j = 0; j < n_taps12; j++, t += 1.0) {
/* exploit symmetry in filter taps */ /* exploit symmetry in filter taps */
taps[(n_phases - i) * stride + n_taps12 + j] = taps[(n_phases - i) * stride + n_taps12 + j] =
taps[i * stride + (n_taps12 - j - 1)] = (float) taps[i * stride + (n_taps12 - j - 1)] = (float)
(cutoff * sinc(t * cutoff) * window(t, n_taps)); (sinc(t, cutoff) * window[j]);
} }
} }
return 0; return 0;
@ -352,11 +471,15 @@ int resample_native_init(struct resample *r)
{ {
struct native_data *d; struct native_data *d;
const struct quality *q; const struct quality *q;
double scale; double scale, cutoff_factor;
uint32_t c, n_taps, n_phases, filter_size, in_rate, out_rate, gcd, filter_stride; uint32_t i, n_taps, n_phases, filter_size, in_rate, out_rate, gcd, filter_stride;
uint32_t history_stride, history_size, oversample; 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->free = impl_native_free;
r->update_rate = impl_native_update_rate; r->update_rate = impl_native_update_rate;
r->in_len = impl_native_in_len; r->in_len = impl_native_in_len;
@ -366,17 +489,22 @@ int resample_native_init(struct resample *r)
r->delay = impl_native_delay; r->delay = impl_native_delay;
r->phase = impl_native_phase; 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); gcd = calc_gcd(r->i_rate, r->o_rate);
in_rate = r->i_rate / gcd; in_rate = r->i_rate / gcd;
out_rate = r->o_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 */ /* 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); n_taps = SPA_MIN(n_taps, 1u << 18);
/* try to get at least 256 phases so that interpolation is /* try to get at least 256 phases so that interpolation is
@ -400,7 +528,7 @@ int resample_native_init(struct resample *r)
return -errno; return -errno;
r->data = d; r->data = d;
d->n_taps = n_taps; c->n_taps = d->n_taps = n_taps;
d->n_phases = n_phases; d->n_phases = n_phases;
d->in_rate = UINT32_TO_FIXP(in_rate); d->in_rate = UINT32_TO_FIXP(in_rate);
d->out_rate = out_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->history = SPA_PTROFF(d->hist_mem, history_size, float*);
d->filter_stride = filter_stride / sizeof(float); d->filter_stride = filter_stride / sizeof(float);
d->filter_stride_os = d->filter_stride * oversample; d->filter_stride_os = d->filter_stride * oversample;
for (c = 0; c < r->channels; c++) for (i = 0; i < r->channels; i++)
d->history[c] = SPA_PTROFF(d->hist_mem, c * history_stride, float); d->history[i] = SPA_PTROFF(d->hist_mem, i * history_stride, float);
#ifndef RESAMPLE_DISABLE_PRECOMP #ifndef RESAMPLE_DISABLE_PRECOMP
/* See if we have precomputed coefficients */ /* See if we have precomputed coefficients */
for (c = 0; precomp_coeffs[c].filter; c++) { for (i = 0; precomp_coeffs[i].filter; i++) {
if (precomp_coeffs[c].in_rate == r->i_rate && if (precomp_coeffs[i].in_rate == r->i_rate &&
precomp_coeffs[c].out_rate == r->o_rate && precomp_coeffs[i].out_rate == r->o_rate &&
precomp_coeffs[c].quality == r->quality) precomp_coeffs[i].quality == r->quality)
break; break;
} }
if (precomp_coeffs[c].filter) { if (precomp_coeffs[i].filter) {
spa_log_debug(r->log, "using precomputed filter for %u->%u(%u)", spa_log_debug(r->log, "using precomputed filter for %u->%u(%u)",
r->i_rate, r->o_rate, r->quality); 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 { } else {
#endif #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 #ifndef RESAMPLE_DISABLE_PRECOMP
} }
#endif #endif
@ -440,8 +568,8 @@ int resample_native_init(struct resample *r)
return -ENOTSUP; 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", 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, r->i_rate, r->o_rate, gcd, n_taps, n_phases, 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);
r->cpu_flags = d->info->cpu_flags; r->cpu_flags = d->info->cpu_flags;

View file

@ -10,6 +10,32 @@
#define RESAMPLE_DEFAULT_QUALITY 4 #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 resample {
struct spa_log *log; struct spa_log *log;
#define RESAMPLE_OPTION_PREFILL (1<<0) #define RESAMPLE_OPTION_PREFILL (1<<0)
@ -23,6 +49,8 @@ struct resample {
double rate; double rate;
int quality; int quality;
struct resample_config config;
void (*free) (struct resample *r); void (*free) (struct resample *r);
void (*update_rate) (struct resample *r, double rate); void (*update_rate) (struct resample *r, double rate);
uint32_t (*in_len) (struct resample *r, uint32_t out_len); uint32_t (*in_len) (struct resample *r, uint32_t out_len);
@ -49,6 +77,32 @@ struct resample {
#define resample_phase(r) (r)->phase(r) #define resample_phase(r) (r)->phase(r)
int resample_native_init(struct resample *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); 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 */ #endif /* RESAMPLE_H */

View file

@ -29,7 +29,10 @@ struct data {
bool verbose; bool verbose;
int rate; int rate;
int format; int format;
uint32_t window;
int quality; int quality;
uint32_t n_taps;
double params[4];
int cpu_flags; int cpu_flags;
const char *iname; const char *iname;
@ -43,14 +46,17 @@ struct data {
#define STR_FMTS "(s8|s16|s32|f32|f64)" #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[] = { static const struct option long_options[] = {
{ "help", no_argument, NULL, 'h'}, { "help", no_argument, NULL, 'h'},
{ "verbose", no_argument, NULL, 'v'}, { "verbose", no_argument, NULL, 'v'},
{ "rate", required_argument, NULL, 'r' }, { "rate", required_argument, NULL, 'r' },
{ "format", required_argument, NULL, 'f' }, { "format", required_argument, NULL, 'f' },
{ "window", required_argument, NULL, 'w' },
{ "quality", required_argument, NULL, 'q' }, { "quality", required_argument, NULL, 'q' },
{ "param", required_argument, NULL, 'p' },
{ "taps", required_argument, NULL, 't' },
{ "cpuflags", required_argument, NULL, 'c' }, { "cpuflags", required_argument, NULL, 'c' },
{ NULL, 0, NULL, 0 } { NULL, 0, NULL, 0 }
@ -70,7 +76,10 @@ 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"
" -t --taps Resampler taps\n"
" -c --cpuflags CPU flags (default 0)\n" " -c --cpuflags CPU flags (default 0)\n"
"\n", "\n",
STR_FMTS, DEFAULT_QUALITY); STR_FMTS, DEFAULT_QUALITY);
@ -207,6 +216,9 @@ 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.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;
@ -320,6 +332,15 @@ int main(int argc, char *argv[])
case 'c': case 'c':
data.cpu_flags = strtol(optarg, NULL, 0); data.cpu_flags = strtol(optarg, NULL, 0);
break; 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: default:
fprintf(stderr, "error: unknown option '%c'\n", c); fprintf(stderr, "error: unknown option '%c'\n", c);
goto error_usage; goto error_usage;