Signed-off-by: falkTX <falktx@falktx.com>pull/1807/head
@@ -116,70 +116,70 @@ int CResampler::process (void) | |||
while (out_count) | |||
{ | |||
if (nr) | |||
{ | |||
if (inp_count == 0) break; | |||
if (nr) | |||
{ | |||
if (inp_count == 0) break; | |||
n = (4 - nr) * _nchan; | |||
if (inp_data) | |||
{ | |||
if (inp_data) | |||
{ | |||
for (c = 0; c < _nchan; c++) pb [n + c] = inp_data [c]; | |||
inp_data += _nchan; | |||
nz = 0; | |||
} | |||
else | |||
{ | |||
inp_data += _nchan; | |||
nz = 0; | |||
} | |||
else | |||
{ | |||
for (c = 0; c < _nchan; c++) pb [n + c] = 0; | |||
if (nz < 4) nz++; | |||
} | |||
nr--; | |||
inp_count--; | |||
} | |||
else | |||
{ | |||
n = _nchan; | |||
if (out_data) | |||
{ | |||
if (nz < 4) | |||
{ | |||
a = ph; | |||
b = 1 - a; | |||
d = a * b / 2; | |||
m0 = -d * b; | |||
m1 = b + (3 * b - 1) * d; | |||
m2 = a + (3 * a - 1) * d; | |||
m3 = -d * a; | |||
for (c = 0; c < n; c++) | |||
{ | |||
*out_data++ = m0 * pb [0] | |||
if (nz < 4) nz++; | |||
} | |||
nr--; | |||
inp_count--; | |||
} | |||
else | |||
{ | |||
n = _nchan; | |||
if (out_data) | |||
{ | |||
if (nz < 4) | |||
{ | |||
a = ph; | |||
b = 1 - a; | |||
d = a * b / 2; | |||
m0 = -d * b; | |||
m1 = b + (3 * b - 1) * d; | |||
m2 = a + (3 * a - 1) * d; | |||
m3 = -d * a; | |||
for (c = 0; c < n; c++) | |||
{ | |||
*out_data++ = m0 * pb [0] | |||
+ m1 * pb [n] | |||
+ m2 * pb [2 * n] | |||
+ m3 * pb [3 * n]; | |||
pb++; | |||
} | |||
pb -= n; | |||
} | |||
else | |||
{ | |||
for (c = 0; c < n; c++) *out_data++ = 0; | |||
} | |||
} | |||
out_count--; | |||
ph += _pstep; | |||
if (ph >= 1.0) | |||
{ | |||
nr = (unsigned int) floor (ph); | |||
ph -= nr; | |||
in += nr; | |||
pb += nr * _nchan; | |||
if (in >= _inmax) | |||
{ | |||
memcpy (_buff, pb, (4 - nr) * _nchan * sizeof (float)); | |||
in = 0; | |||
pb = _buff; | |||
} | |||
} | |||
} | |||
+ m3 * pb [3 * n]; | |||
pb++; | |||
} | |||
pb -= n; | |||
} | |||
else | |||
{ | |||
for (c = 0; c < n; c++) *out_data++ = 0; | |||
} | |||
} | |||
out_count--; | |||
ph += _pstep; | |||
if (ph >= 1.0) | |||
{ | |||
nr = (unsigned int) floor (ph); | |||
ph -= nr; | |||
in += nr; | |||
pb += nr * _nchan; | |||
if (in >= _inmax) | |||
{ | |||
memcpy (_buff, pb, (4 - nr) * _nchan * sizeof (float)); | |||
in = 0; | |||
pb = _buff; | |||
} | |||
} | |||
} | |||
} | |||
_index = in; | |||
@@ -1,7 +1,7 @@ | |||
// ---------------------------------------------------------------------------- | |||
// | |||
// Copyright (C) 2006-2012 Fons Adriaensen <fons@linuxaudio.org> | |||
// | |||
// Copyright (C) 2006-2023 Fons Adriaensen <fons@linuxaudio.org> | |||
// | |||
// This program is free software; you can redistribute it and/or modify | |||
// it under the terms of the GNU General Public License as published by | |||
// the Free Software Foundation; either version 3 of the License, or | |||
@@ -24,6 +24,13 @@ | |||
#include <math.h> | |||
#include "resampler-table.h" | |||
#undef ENABLE_VEC4 | |||
#if defined(__SSE2_MATH__) || defined(__ARM_NEON) || defined(__ARM_NEON__) | |||
# define ENABLE_VEC4 | |||
#endif | |||
static double sinc (double x) | |||
{ | |||
x = fabs (x); | |||
@@ -42,7 +49,6 @@ static double wind (double x) | |||
} | |||
Resampler_table *Resampler_table::_list = 0; | |||
Resampler_mutex Resampler_table::_mutex; | |||
@@ -54,11 +60,16 @@ Resampler_table::Resampler_table (double fr, unsigned int hl, unsigned int np) : | |||
_hl (hl), | |||
_np (np) | |||
{ | |||
unsigned int i, j; | |||
unsigned int i, j, n; | |||
double t; | |||
float *p; | |||
_ctab = new float [hl * (np + 1)]; | |||
n = hl * (np + 1); | |||
#ifdef ENABLE_VEC4 | |||
posix_memalign ((void **) &_ctab, 16, n * sizeof (float)); | |||
#else | |||
_ctab = new float [n]; | |||
#endif | |||
p = _ctab; | |||
for (j = 0; j <= np; j++) | |||
{ | |||
@@ -75,7 +86,11 @@ Resampler_table::Resampler_table (double fr, unsigned int hl, unsigned int np) : | |||
Resampler_table::~Resampler_table (void) | |||
{ | |||
#ifdef ENABLE_VEC4 | |||
free (_ctab); | |||
#else | |||
delete[] _ctab; | |||
#endif | |||
} | |||
@@ -1,6 +1,6 @@ | |||
// ---------------------------------------------------------------------------- | |||
// | |||
// Copyright (C) 2006-2012 Fons Adriaensen <fons@linuxaudio.org> | |||
// Copyright (C) 2006-2023 Fons Adriaensen <fons@linuxaudio.org> | |||
// | |||
// This program is free software; you can redistribute it and/or modify | |||
// it under the terms of the GNU General Public License as published by | |||
@@ -1,7 +1,7 @@ | |||
// ---------------------------------------------------------------------------- | |||
// | |||
// Copyright (C) 2006-2012 Fons Adriaensen <fons@linuxaudio.org> | |||
// | |||
// Copyright (C) 2006-2023 Fons Adriaensen <fons@linuxaudio.org> | |||
// | |||
// This program is free software; you can redistribute it and/or modify | |||
// it under the terms of the GNU General Public License as published by | |||
// the Free Software Foundation; either version 3 of the License, or | |||
@@ -22,6 +22,16 @@ | |||
#include <stdio.h> | |||
#include <string.h> | |||
#include <math.h> | |||
#undef ENABLE_VEC4 | |||
#if defined(__SSE2_MATH__) | |||
# define ENABLE_VEC4 | |||
# include <xmmintrin.h> | |||
#elif defined(__ARM_NEON) || defined(__ARM_NEON__) | |||
# define ENABLE_VEC4 | |||
# include <arm_neon.h> | |||
#endif | |||
#include "resampler.h" | |||
@@ -31,20 +41,20 @@ static unsigned int gcd (unsigned int a, unsigned int b) | |||
if (b == 0) return a; | |||
while (1) | |||
{ | |||
if (a > b) | |||
{ | |||
a = a % b; | |||
if (a == 0) return b; | |||
if (a == 1) return 1; | |||
} | |||
else | |||
{ | |||
b = b % a; | |||
if (b == 0) return a; | |||
if (b == 1) return 1; | |||
} | |||
} | |||
return 1; | |||
if (a > b) | |||
{ | |||
a = a % b; | |||
if (a == 0) return b; | |||
if (a == 1) return 1; | |||
} | |||
else | |||
{ | |||
b = b % a; | |||
if (b == 0) return a; | |||
if (b == 1) return 1; | |||
} | |||
} | |||
return 1; | |||
} | |||
@@ -63,66 +73,82 @@ Resampler::~Resampler (void) | |||
} | |||
int Resampler::setup (unsigned int fs_inp, | |||
unsigned int fs_out, | |||
unsigned int nchan, | |||
unsigned int hlen) | |||
bool Resampler::setup (unsigned int fs_inp, | |||
unsigned int fs_out, | |||
unsigned int nchan, | |||
unsigned int hlen) | |||
{ | |||
if ((hlen < 8) || (hlen > 96)) return 1; | |||
return setup (fs_inp, fs_out, nchan, hlen, 1.0 - 2.6 / hlen); | |||
} | |||
int Resampler::setup (unsigned int fs_inp, | |||
unsigned int fs_out, | |||
unsigned int nchan, | |||
unsigned int hlen, | |||
double frel) | |||
bool Resampler::setup (unsigned int fs_inp, | |||
unsigned int fs_out, | |||
unsigned int nchan, | |||
unsigned int hlen, | |||
double frel) | |||
{ | |||
unsigned int g, h, k, n, s; | |||
unsigned int np, dp, mi, hl, n; | |||
double r; | |||
float *B = 0; | |||
Resampler_table *T = 0; | |||
k = s = 0; | |||
if (fs_inp && fs_out && nchan) | |||
if (!nchan || (hlen < 8) || (hlen > 96)) | |||
{ | |||
r = (double) fs_out / (double) fs_inp; | |||
g = gcd (fs_out, fs_inp); | |||
n = fs_out / g; | |||
s = fs_inp / g; | |||
if ((16 * r >= 1) && (n <= 1000)) | |||
{ | |||
h = hlen; | |||
k = 250; | |||
if (r < 1) | |||
{ | |||
frel *= r; | |||
h = (unsigned int)(ceil (h / r)); | |||
k = (unsigned int)(ceil (k / r)); | |||
} | |||
T = Resampler_table::create (frel, h, n); | |||
B = new float [nchan * (2 * h - 1 + k)]; | |||
} | |||
clear (); | |||
return false; | |||
} | |||
r = (double) fs_out / (double) fs_inp; | |||
n = gcd (fs_out, fs_inp); | |||
np = fs_out / n; | |||
dp = fs_inp / n; | |||
if ((64 * r < 1.0) || (np > 1000)) | |||
{ | |||
clear (); | |||
return false; | |||
} | |||
hl = hlen; | |||
mi = 32; | |||
if (r < 1.0) | |||
{ | |||
frel *= r; | |||
hl = (unsigned int)(ceil (hl / r)); | |||
mi = (unsigned int)(ceil (mi / r)); | |||
} | |||
#ifdef ENABLE_VEC4 | |||
hl = (hl + 3) & ~3; | |||
#endif | |||
T = Resampler_table::create (frel, hl, np); | |||
clear (); | |||
if (T) | |||
{ | |||
_table = T; | |||
_buff = B; | |||
n = nchan * (2 * hl + mi); | |||
#ifdef ENABLE_VEC4 | |||
posix_memalign ((void **)(&_buff), 16, n * sizeof (float)); | |||
memset (_buff, 0, n * sizeof (float)); | |||
#else | |||
_buff = new float [n]; | |||
#endif | |||
_nchan = nchan; | |||
_inmax = k; | |||
_pstep = s; | |||
_inmax = mi; | |||
_pstep = dp; | |||
return reset (); | |||
} | |||
return 1; | |||
else return false; | |||
} | |||
void Resampler::clear (void) | |||
{ | |||
Resampler_table::destroy (_table); | |||
#ifdef ENABLE_VEC4 | |||
free (_buff); | |||
#else | |||
delete[] _buff; | |||
#endif | |||
_buff = 0; | |||
_table = 0; | |||
_nchan = 0; | |||
@@ -139,7 +165,7 @@ double Resampler::inpdist (void) const noexcept | |||
} | |||
unsigned int Resampler::inpsize (void) const noexcept | |||
int Resampler::inpsize (void) const noexcept | |||
{ | |||
if (!_table) return 0; | |||
return 2 * _table->_hl; | |||
@@ -152,102 +178,157 @@ bool Resampler::reset (void) noexcept | |||
inp_count = 0; | |||
out_count = 0; | |||
inp_data = nullptr; | |||
out_data = nullptr; | |||
inp_data = 0; | |||
out_data = 0; | |||
_index = 0; | |||
_nread = 0; | |||
_nzero = 0; | |||
_phase = 0; | |||
_nread = 2 * _table->_hl; | |||
return true; | |||
if (_table) | |||
{ | |||
_nread = 2 * _table->_hl; | |||
return true; | |||
} | |||
return false; | |||
} | |||
bool Resampler::process (void) | |||
{ | |||
unsigned int hl, ph, np, dp, in, nr, nz, i, n, c; | |||
float *p1, *p2; | |||
unsigned int hl, np, ph, dp, in, nr, nz, di, i, j, n; | |||
float *c1, *c2, *p1, *p2, *q1, *q2; | |||
if (!_table) return false; | |||
hl = _table->_hl; | |||
np = _table->_np; | |||
dp = _pstep; | |||
in = _index; | |||
nr = _nread; | |||
ph = _phase; | |||
nz = _nzero; | |||
n = (2 * hl - nr) * _nchan; | |||
p1 = _buff + in * _nchan; | |||
p2 = p1 + n; | |||
ph = _phase; | |||
p1 = _buff + in; | |||
p2 = p1 + 2 * hl - nr; | |||
di = 2 * hl + _inmax; | |||
while (out_count) | |||
{ | |||
if (nr) | |||
while (nr && inp_count) | |||
{ | |||
if (inp_count == 0) break; | |||
if (inp_data) | |||
{ | |||
for (c = 0; c < _nchan; c++) p2 [c] = inp_data [c]; | |||
for (j = 0; j < _nchan; j++) p2 [j * di] = inp_data [j]; | |||
inp_data += _nchan; | |||
nz = 0; | |||
nz = 0; | |||
} | |||
else | |||
{ | |||
for (c = 0; c < _nchan; c++) p2 [c] = 0; | |||
for (j = 0; j < _nchan; j++) p2 [j * di] = 0; | |||
if (nz < 2 * hl) nz++; | |||
} | |||
p2++; | |||
nr--; | |||
p2 += _nchan; | |||
inp_count--; | |||
} | |||
else | |||
if (nr) break; | |||
if (out_data) | |||
{ | |||
if (out_data) | |||
if (nz < 2 * hl) | |||
{ | |||
if (nz < 2 * hl) | |||
c1 = _table->_ctab + hl * ph; | |||
c2 = _table->_ctab + hl * (np - ph); | |||
#if defined(__SSE2_MATH__) | |||
__m128 C1, C2, Q1, Q2, S; | |||
for (j = 0; j < _nchan; j++) | |||
{ | |||
float *c1 = _table->_ctab + hl * ph; | |||
float *c2 = _table->_ctab + hl * (np - ph); | |||
for (c = 0; c < _nchan; c++) | |||
q1 = p1 + j * di; | |||
q2 = p2 + j * di; | |||
S = _mm_setzero_ps (); | |||
for (i = 0; i < hl; i += 4) | |||
{ | |||
float *q1 = p1 + c; | |||
float *q2 = p2 + c; | |||
float s = 1e-20f; | |||
for (i = 0; i < hl; i++) | |||
{ | |||
q2 -= _nchan; | |||
s += *q1 * c1 [i] + *q2 * c2 [i]; | |||
q1 += _nchan; | |||
} | |||
*out_data++ = s - 1e-20f; | |||
C1 = _mm_load_ps (c1 + i); | |||
Q1 = _mm_loadu_ps (q1); | |||
q2 -= 4; | |||
S = _mm_add_ps (S, _mm_mul_ps (C1, Q1)); | |||
C2 = _mm_loadr_ps (c2 + i); | |||
Q2 = _mm_loadu_ps (q2); | |||
q1 += 4; | |||
S = _mm_add_ps (S, _mm_mul_ps (C2, Q2)); | |||
} | |||
*out_data++ = S [0] + S [1] + S [2] + S [3]; | |||
} | |||
else | |||
#elif defined(__ARM_NEON) || defined(__ARM_NEON__) | |||
// ARM64 version by Nicolas Belin <nbelin@baylibre.com> | |||
float32x4_t *C1 = (float32x4_t *)c1; | |||
float32x4_t *C2 = (float32x4_t *)c2; | |||
float32x4_t S, T; | |||
for (j = 0; j < _nchan; j++) | |||
{ | |||
for (c = 0; c < _nchan; c++) *out_data++ = 0; | |||
q1 = p1 + j * di; | |||
q2 = p2 + j * di - 4; | |||
T = vrev64q_f32 (vld1q_f32 (q2)); | |||
S = vmulq_f32 (vextq_f32 (T, T, 2), C2 [0]); | |||
S = vmlaq_f32 (S, vld1q_f32(q1), C1 [0]); | |||
for (i = 1; i < (hl>>2); i++) | |||
{ | |||
q2 -= 4; | |||
q1 += 4; | |||
T = vrev64q_f32 (vld1q_f32 (q2)); | |||
S = vmlaq_f32 (S, vextq_f32 (T, T, 2), C2 [i]); | |||
S = vmlaq_f32 (S, vld1q_f32 (q1), C1 [i]); | |||
} | |||
*out_data++ = vaddvq_f32(S); | |||
} | |||
#else | |||
float s; | |||
for (j = 0; j < _nchan; j++) | |||
{ | |||
q1 = p1 + j * di; | |||
q2 = p2 + j * di; | |||
s = 1e-30f; | |||
for (i = 0; i < hl; i++) | |||
{ | |||
q2--; | |||
s += *q1 * c1 [i] + *q2 * c2 [i]; | |||
q1++; | |||
} | |||
*out_data++ = s - 1e-30f; | |||
} | |||
#endif | |||
} | |||
out_count--; | |||
else | |||
{ | |||
for (j = 0; j < _nchan; j++) *out_data++ = 0; | |||
} | |||
} | |||
out_count--; | |||
ph += dp; | |||
if (ph >= np) | |||
ph += dp; | |||
if (ph >= np) | |||
{ | |||
nr = ph / np; | |||
ph -= nr * np; | |||
in += nr; | |||
p1 += nr; | |||
if (in >= _inmax) | |||
{ | |||
nr = ph / np; | |||
ph -= nr * np; | |||
in += nr; | |||
p1 += nr * _nchan;; | |||
if (in >= _inmax) | |||
n = 2 * hl - nr; | |||
p2 = _buff; | |||
for (j = 0; j < _nchan; j++) | |||
{ | |||
n = (2 * hl - nr) * _nchan; | |||
memcpy (_buff, p1, n * sizeof (float)); | |||
in = 0; | |||
p1 = _buff; | |||
p2 = p1 + n; | |||
memmove (p2 + j * di, p1 + j * di, n * sizeof (float)); | |||
} | |||
in = 0; | |||
p1 = _buff; | |||
p2 = p1 + n; | |||
} | |||
} | |||
} | |||
_index = in; | |||
_nread = nr; | |||
_phase = ph; | |||
@@ -1,6 +1,6 @@ | |||
// ---------------------------------------------------------------------------- | |||
// | |||
// Copyright (C) 2006-2012 Fons Adriaensen <fons@linuxaudio.org> | |||
// Copyright (C) 2006-2023 Fons Adriaensen <fons@linuxaudio.org> | |||
// | |||
// This program is free software; you can redistribute it and/or modify | |||
// it under the terms of the GNU General Public License as published by | |||
@@ -32,31 +32,31 @@ public: | |||
Resampler (void) noexcept; | |||
~Resampler (void); | |||
int setup (unsigned int fs_inp, | |||
bool setup (unsigned int fs_inp, | |||
unsigned int fs_out, | |||
unsigned int nchan, | |||
unsigned int hlen); | |||
int setup (unsigned int fs_inp, | |||
bool setup (unsigned int fs_inp, | |||
unsigned int fs_out, | |||
unsigned int nchan, | |||
unsigned int hlen, | |||
double frel); | |||
void clear (void); | |||
bool reset (void) noexcept; | |||
unsigned int nchan (void) const noexcept { return _nchan; } | |||
unsigned int filtlen (void) const noexcept { return inpsize (); } // Deprecated | |||
unsigned int inpsize (void) const noexcept; | |||
double inpdist (void) const noexcept; | |||
bool process (void); | |||
void clear (void); | |||
bool reset (void) noexcept; | |||
int nchan (void) const noexcept { return _nchan; } | |||
int filtlen (void) const noexcept { return inpsize (); } // Deprecated | |||
int inpsize (void) const noexcept; | |||
double inpdist (void) const noexcept; | |||
bool process (void); | |||
unsigned int inp_count; | |||
unsigned int out_count; | |||
float *inp_data; | |||
float *out_data; | |||
void *inp_list; | |||
void *out_list; | |||
float **inp_list; | |||
float **out_list; | |||
private: | |||
@@ -69,6 +69,7 @@ private: | |||
unsigned int _phase; | |||
unsigned int _pstep; | |||
float *_buff; | |||
void *_dummy [8]; | |||
}; | |||
@@ -1,7 +1,7 @@ | |||
// ---------------------------------------------------------------------------- | |||
// | |||
// Copyright (C) 2006-2013 Fons Adriaensen <fons@linuxaudio.org> | |||
// | |||
// Copyright (C) 2006-2023 Fons Adriaensen <fons@linuxaudio.org> | |||
// | |||
// This program is free software; you can redistribute it and/or modify | |||
// it under the terms of the GNU General Public License as published by | |||
// the Free Software Foundation; either version 3 of the License, or | |||
@@ -22,10 +22,20 @@ | |||
#include <stdio.h> | |||
#include <string.h> | |||
#include <math.h> | |||
#undef ENABLE_VEC4 | |||
#if defined(__SSE2_MATH__) | |||
# define ENABLE_VEC4 | |||
# include <xmmintrin.h> | |||
#elif defined(__ARM_NEON) || defined(__ARM_NEON__) | |||
# define ENABLE_VEC4 | |||
# include <arm_neon.h> | |||
#endif | |||
#include "vresampler.h" | |||
VResampler::VResampler (void) : | |||
VResampler::VResampler (void) noexcept : | |||
_table (0), | |||
_nchan (0), | |||
_buff (0), | |||
@@ -42,62 +52,81 @@ VResampler::~VResampler (void) | |||
} | |||
int VResampler::setup (double ratio, | |||
unsigned int nchan, | |||
unsigned int hlen) | |||
bool VResampler::setup (double ratio, | |||
unsigned int nchan, | |||
unsigned int hlen) | |||
{ | |||
if ((hlen < 8) || (hlen > 96) || (16 * ratio < 1) || (ratio > 256)) return 1; | |||
return setup (ratio, nchan, hlen, 1.0 - 2.6 / hlen); | |||
} | |||
int VResampler::setup (double ratio, | |||
unsigned int nchan, | |||
unsigned int hlen, | |||
double frel) | |||
bool VResampler::setup (double ratio, | |||
unsigned int nchan, | |||
unsigned int hlen, | |||
double frel) | |||
{ | |||
unsigned int h, k, n; | |||
double s; | |||
unsigned int hl, mi, n; | |||
double dp; | |||
Resampler_table *T = 0; | |||
if (! nchan) return 1; | |||
n = NPHASE; | |||
s = n / ratio; | |||
h = hlen; | |||
k = 250; | |||
if (ratio < 1) | |||
if (!nchan || (hlen < 8) || (hlen > 96) || (64 * ratio < 1) || (ratio > 256)) | |||
{ | |||
clear (); | |||
return false; | |||
} | |||
dp = NPHASE / ratio; | |||
hl = hlen; | |||
mi = 32; | |||
if (ratio < 1.0) | |||
{ | |||
frel *= ratio; | |||
h = (unsigned int)(ceil (h / ratio)); | |||
k = (unsigned int)(ceil (k / ratio)); | |||
hl = (unsigned int)(ceil (hl / ratio)); | |||
mi = (unsigned int)(ceil (mi / ratio)); | |||
} | |||
T = Resampler_table::create (frel, h, n); | |||
#ifdef ENABLE_VEC4 | |||
hl = (hl + 3) & ~3; | |||
#endif | |||
T = Resampler_table::create (frel, hl, NPHASE); | |||
clear (); | |||
if (T) | |||
{ | |||
_table = T; | |||
_buff = new float [nchan * (2 * h - 1 + k)]; | |||
_c1 = new float [2 * h]; | |||
_c2 = new float [2 * h]; | |||
_nchan = nchan; | |||
_inmax = k; | |||
_ratio = ratio; | |||
_pstep = s; | |||
_qstep = s; | |||
_wstep = 1; | |||
return reset (); | |||
_table = T; | |||
n = nchan * (2 * hl + mi); | |||
#ifdef ENABLE_VEC4 | |||
posix_memalign ((void **)(&_buff), 16, n * sizeof (float)); | |||
posix_memalign ((void **)(&_c1), 16, hl * sizeof (float)); | |||
posix_memalign ((void **)(&_c2), 16, hl * sizeof (float)); | |||
#else | |||
_buff = new float [n]; | |||
_c1 = new float [hl]; | |||
_c2 = new float [hl]; | |||
#endif | |||
_nchan = nchan; | |||
_ratio = ratio; | |||
_inmax = mi; | |||
_pstep = dp; | |||
_qstep = dp; | |||
_wstep = 1; | |||
return reset (); | |||
} | |||
else return 1; | |||
else return false; | |||
} | |||
void VResampler::clear (void) | |||
{ | |||
Resampler_table::destroy (_table); | |||
#ifdef ENABLE_VEC4 | |||
free (_buff); | |||
free (_c1); | |||
free (_c2); | |||
#else | |||
delete[] _buff; | |||
delete[] _c1; | |||
delete[] _c2; | |||
_buff = 0; | |||
#endif | |||
_buff = 0; | |||
_c1 = 0; | |||
_c2 = 0; | |||
_table = 0; | |||
@@ -133,44 +162,49 @@ void VResampler::set_rratio (double r) | |||
} | |||
double VResampler::inpdist (void) const | |||
double VResampler::inpdist (void) const noexcept | |||
{ | |||
if (!_table) return 0; | |||
return (int)(_table->_hl + 1 - _nread) - _phase / _table->_np; | |||
} | |||
int VResampler::inpsize (void) const | |||
int VResampler::inpsize (void) const noexcept | |||
{ | |||
if (!_table) return 0; | |||
return 2 * _table->_hl; | |||
} | |||
int VResampler::reset (void) | |||
bool VResampler::reset (void) noexcept | |||
{ | |||
if (!_table) return 1; | |||
if (!_table) return false; | |||
inp_count = 0; | |||
out_count = 0; | |||
inp_data = 0; | |||
out_data = 0; | |||
_index = 0; | |||
_phase = 0; | |||
_nread = 2 * _table->_hl; | |||
_nread = 0; | |||
_nzero = 0; | |||
return 0; | |||
_phase = 0; | |||
if (_table) | |||
{ | |||
_nread = 2 * _table->_hl; | |||
return true; | |||
} | |||
return false; | |||
} | |||
int VResampler::process (void) | |||
bool VResampler::process (void) | |||
{ | |||
unsigned int k, np, in, nr, n, c; | |||
int i, hl, nz; | |||
double ph, dp, dd; | |||
int nr, np, hl, nz, di, i, n; | |||
unsigned int in, j; | |||
double ph, dp, dd; | |||
float a, b, *p1, *p2, *q1, *q2; | |||
if (!_table) return 1; | |||
if (!_table) return false; | |||
hl = _table->_hl; | |||
np = _table->_np; | |||
@@ -179,94 +213,169 @@ int VResampler::process (void) | |||
nz = _nzero; | |||
ph = _phase; | |||
dp = _pstep; | |||
n = (2 * hl - nr) * _nchan; | |||
p1 = _buff + in * _nchan; | |||
p2 = p1 + n; | |||
p1 = _buff + in; | |||
p2 = p1 + 2 * hl - nr; | |||
di = 2 * hl + _inmax; | |||
while (out_count) | |||
{ | |||
if (nr) | |||
{ | |||
if (inp_count == 0) break; | |||
if (inp_data) | |||
{ | |||
for (c = 0; c < _nchan; c++) p2 [c] = inp_data [c]; | |||
inp_data += _nchan; | |||
nz = 0; | |||
} | |||
else | |||
{ | |||
for (c = 0; c < _nchan; c++) p2 [c] = 0; | |||
if (nz < 2 * hl) nz++; | |||
} | |||
nr--; | |||
p2 += _nchan; | |||
inp_count--; | |||
} | |||
else | |||
{ | |||
if (out_data) | |||
{ | |||
if (nz < 2 * hl) | |||
{ | |||
k = (unsigned int) ph; | |||
b = (float)(ph - k); | |||
a = 1.0f - b; | |||
q1 = _table->_ctab + hl * k; | |||
q2 = _table->_ctab + hl * (np - k); | |||
for (i = 0; i < hl; i++) | |||
{ | |||
_c1 [i] = a * q1 [i] + b * q1 [i + hl]; | |||
_c2 [i] = a * q2 [i] + b * q2 [i - hl]; | |||
} | |||
for (c = 0; c < _nchan; c++) | |||
{ | |||
q1 = p1 + c; | |||
q2 = p2 + c; | |||
a = 1e-25f; | |||
for (i = 0; i < hl; i++) | |||
{ | |||
q2 -= _nchan; | |||
a += *q1 * _c1 [i] + *q2 * _c2 [i]; | |||
q1 += _nchan; | |||
} | |||
*out_data++ = a - 1e-25f; | |||
} | |||
} | |||
else | |||
{ | |||
for (c = 0; c < _nchan; c++) *out_data++ = 0; | |||
} | |||
} | |||
out_count--; | |||
dd = _qstep - dp; | |||
if (fabs (dd) < 1e-30) dp = _qstep; | |||
else dp += _wstep * dd; | |||
ph += dp; | |||
if (ph >= np) | |||
{ | |||
nr = (unsigned int) floor( ph / np); | |||
ph -= nr * np;; | |||
in += nr; | |||
p1 += nr * _nchan;; | |||
if (in >= _inmax) | |||
{ | |||
n = (2 * hl - nr) * _nchan; | |||
memcpy (_buff, p1, n * sizeof (float)); | |||
in = 0; | |||
p1 = _buff; | |||
p2 = p1 + n; | |||
} | |||
} | |||
} | |||
while (nr && inp_count) | |||
{ | |||
if (inp_data) | |||
{ | |||
for (j = 0; j < _nchan; j++) p2 [j * di] = inp_data [j]; | |||
inp_data += _nchan; | |||
nz = 0; | |||
} | |||
else | |||
{ | |||
for (j = 0; j < _nchan; j++) p2 [j * di] = 0; | |||
if (nz < 2 * hl) nz++; | |||
} | |||
p2++; | |||
nr--; | |||
inp_count--; | |||
} | |||
if (nr) break; | |||
if (out_data) | |||
{ | |||
if (nz < 2 * hl) | |||
{ | |||
n = (unsigned int) ph; | |||
b = (float)(ph - n); | |||
a = 1.0f - b; | |||
q1 = _table->_ctab + hl * n; | |||
q2 = _table->_ctab + hl * (np - n); | |||
#if defined(__SSE2_MATH__) | |||
__m128 C1, C2, Q1, Q2, S; | |||
C1 = _mm_load1_ps (&a); | |||
C2 = _mm_load1_ps (&b); | |||
for (i = 0; i < hl; i += 4) | |||
{ | |||
Q1 = _mm_load_ps (q1 + i); | |||
Q2 = _mm_load_ps (q1 + i + hl); | |||
S = _mm_add_ps (_mm_mul_ps (Q1, C1), _mm_mul_ps (Q2, C2)); | |||
_mm_store_ps (_c1 + i, S); | |||
Q1 = _mm_load_ps (q2 + i); | |||
Q2 = _mm_load_ps (q2 + i - hl); | |||
S = _mm_add_ps (_mm_mul_ps (Q1, C1), _mm_mul_ps (Q2, C2)); | |||
_mm_store_ps (_c2 + i, S); | |||
} | |||
for (j = 0; j < _nchan; j++) | |||
{ | |||
q1 = p1 + j * di; | |||
q2 = p2 + j * di; | |||
S = _mm_setzero_ps (); | |||
for (i = 0; i < hl; i += 4) | |||
{ | |||
C1 = _mm_load_ps (_c1 + i); | |||
Q1 = _mm_loadu_ps (q1); | |||
q2 -= 4; | |||
S = _mm_add_ps (S, _mm_mul_ps (C1, Q1)); | |||
C2 = _mm_loadr_ps (_c2 + i); | |||
Q2 = _mm_loadu_ps (q2); | |||
q1 += 4; | |||
S = _mm_add_ps (S, _mm_mul_ps (C2, Q2)); | |||
} | |||
*out_data++ = S [0] + S [1] + S [2] + S [3]; | |||
} | |||
#elif defined(__ARM_NEON) || defined(__ARM_NEON__) | |||
// ARM64 version by Nicolas Belin <nbelin@baylibre.com> | |||
float32x4_t *C1 = (float32x4_t *)_c1; | |||
float32x4_t *C2 = (float32x4_t *)_c2; | |||
float32x4_t S, T; | |||
for (i = 0; i < (hl>>2); i++) | |||
{ | |||
T = vmulq_n_f32 (vld1q_f32 (q1 + hl), b); | |||
C1 [i] = vmlaq_n_f32 (T, vld1q_f32 (q1), a); | |||
T = vmulq_n_f32 (vld1q_f32 (q2 - hl), b); | |||
C2 [i] = vmlaq_n_f32 (T, vld1q_f32 (q2), a); | |||
q2 += 4; | |||
q1 += 4; | |||
} | |||
for (j = 0; j < _nchan; j++) | |||
{ | |||
q1 = p1 + j * di; | |||
q2 = p2 + j * di - 4; | |||
T = vrev64q_f32 (vld1q_f32 (q2)); | |||
S = vmulq_f32 (vextq_f32 (T, T, 2), C2 [0]); | |||
S = vmlaq_f32 (S, vld1q_f32 (q1), C1 [0]); | |||
for (i = 1; i < (hl>>2); i++) | |||
{ | |||
q2 -= 4; | |||
q1 += 4; | |||
T = vrev64q_f32 (vld1q_f32 (q2)); | |||
S = vmlaq_f32 (S, vextq_f32 (T, T, 2), C2 [i]); | |||
S = vmlaq_f32 (S, vld1q_f32 (q1), C1 [i]); | |||
} | |||
*out_data++ = vaddvq_f32 (S); | |||
} | |||
#else | |||
float s; | |||
for (i = 0; i < hl; i++) | |||
{ | |||
_c1 [i] = a * q1 [i] + b * q1 [i + hl]; | |||
_c2 [i] = a * q2 [i] + b * q2 [i - hl]; | |||
} | |||
for (j = 0; j < _nchan; j++) | |||
{ | |||
q1 = p1 + j * di; | |||
q2 = p2 + j * di; | |||
s = 1e-30f; | |||
for (i = 0; i < hl; i++) | |||
{ | |||
q2--; | |||
s += *q1 * _c1 [i] + *q2 * _c2 [i]; | |||
q1++; | |||
} | |||
*out_data++ = s - 1e-30f; | |||
} | |||
#endif | |||
} | |||
else | |||
{ | |||
for (j = 0; j < _nchan; j++) *out_data++ = 0; | |||
} | |||
} | |||
out_count--; | |||
dd = _qstep - dp; | |||
if (fabs (dd) < 1e-20) dp = _qstep; | |||
else dp += _wstep * dd; | |||
ph += dp; | |||
if (ph >= np) | |||
{ | |||
nr = (unsigned int) floor (ph / np); | |||
ph -= nr * np;; | |||
in += nr; | |||
p1 += nr; | |||
if (in >= _inmax) | |||
{ | |||
n = 2 * hl - nr; | |||
p2 = _buff; | |||
for (j = 0; j < _nchan; j++) | |||
{ | |||
memmove (p2 + j * di, p1 + j * di, n * sizeof (float)); | |||
} | |||
in = 0; | |||
p1 = _buff; | |||
p2 = p1 + n; | |||
} | |||
} | |||
} | |||
_index = in; | |||
_nread = nr; | |||
_phase = ph; | |||
_pstep = dp; | |||
_nzero = nz; | |||
return 0; | |||
return true; | |||
} | |||
@@ -1,6 +1,6 @@ | |||
// ---------------------------------------------------------------------------- | |||
// | |||
// Copyright (C) 2006-2012 Fons Adriaensen <fons@linuxaudio.org> | |||
// Copyright (C) 2006-2023 Fons Adriaensen <fons@linuxaudio.org> | |||
// | |||
// This program is free software; you can redistribute it and/or modify | |||
// it under the terms of the GNU General Public License as published by | |||
@@ -29,28 +29,28 @@ class VResampler | |||
{ | |||
public: | |||
VResampler (void); | |||
VResampler (void) noexcept; | |||
~VResampler (void); | |||
int setup (double ratio, | |||
bool setup (double ratio, | |||
unsigned int nchan, | |||
unsigned int hlen); | |||
int setup (double ratio, | |||
bool setup (double ratio, | |||
unsigned int nchan, | |||
unsigned int hlen, | |||
double frel); | |||
void clear (void); | |||
int reset (void); | |||
int nchan (void) const { return _nchan; } | |||
int inpsize (void) const; | |||
double inpdist (void) const; | |||
int process (void); | |||
bool reset (void) noexcept; | |||
int nchan (void) const noexcept { return _nchan; } | |||
int inpsize (void) const noexcept; | |||
double inpdist (void) const noexcept; | |||
bool process (void); | |||
void set_phase (double p); | |||
void set_rrfilt (double t); | |||
void set_rratio (double r); | |||
void set_rratio (double r); | |||
unsigned int inp_count; | |||
unsigned int out_count; | |||
@@ -61,7 +61,7 @@ public: | |||
private: | |||
enum { NPHASE = 256 }; | |||
enum { NPHASE = 120 }; | |||
Resampler_table *_table; | |||
unsigned int _nchan; | |||
@@ -77,6 +77,7 @@ private: | |||
float *_buff; | |||
float *_c1; | |||
float *_c2; | |||
void *_dummy [8]; | |||
}; | |||