mirror of
https://gitlab.freedesktop.org/pipewire/pipewire.git
synced 2025-11-22 06:59:59 -05:00
resample: make window configurable
Add kaiser window
This commit is contained in:
parent
18ff08243b
commit
bfd26c98e3
5 changed files with 422 additions and 66 deletions
|
|
@ -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;
|
||||||
|
|
|
||||||
147
spa/plugins/audioconvert/dbesi0.c
Normal file
147
spa/plugins/audioconvert/dbesi0.c
Normal 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;
|
||||||
|
}
|
||||||
|
|
@ -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 double window_cosh(double x, double n_taps)
|
static inline void blackman_config(struct resample *r)
|
||||||
{
|
{
|
||||||
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;
|
||||||
|
|
|
||||||
|
|
@ -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 */
|
||||||
|
|
|
||||||
|
|
@ -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;
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue