audio-resampler: add cubic interpolation

This commit is contained in:
Wim Taymans 2016-02-10 17:28:24 +01:00
parent 58dcd0587d
commit e9fc039bb1
2 changed files with 183 additions and 50 deletions

View file

@ -69,6 +69,26 @@ inner_product_gfloat_linear_1_sse (gfloat * o, const gfloat * a,
_mm_store_ss (o, sum); _mm_store_ss (o, sum);
} }
static inline void
inner_product_gfloat_cubic_1_sse (gfloat * o, const gfloat * a,
const gfloat * b, gint len, const gfloat * icoeff, gint oversample)
{
gint i = 0;
__m128 sum = _mm_setzero_ps ();
__m128 f = _mm_loadu_ps(icoeff);
for (; i < len; i += 2) {
sum = _mm_add_ps (sum, _mm_mul_ps (_mm_load1_ps (a + i + 0),
_mm_loadu_ps (b + (i + 0) * oversample)));
sum = _mm_add_ps (sum, _mm_mul_ps (_mm_load1_ps (a + i + 1),
_mm_loadu_ps (b + (i + 1) * oversample)));
}
sum = _mm_mul_ps (sum, f);
sum = _mm_add_ps (sum, _mm_movehl_ps (sum, sum));
sum = _mm_add_ss (sum, _mm_shuffle_ps (sum, sum, 0x55));
_mm_store_ss (o, sum);
}
static inline void static inline void
inner_product_gfloat_none_2_sse (gfloat * o, const gfloat * a, inner_product_gfloat_none_2_sse (gfloat * o, const gfloat * a,
const gfloat * b, gint len, const gfloat * icoeff, gint oversample) const gfloat * b, gint len, const gfloat * icoeff, gint oversample)
@ -100,6 +120,7 @@ inner_product_gfloat_none_2_sse (gfloat * o, const gfloat * a,
MAKE_RESAMPLE_FUNC (gfloat, none, 1, sse); MAKE_RESAMPLE_FUNC (gfloat, none, 1, sse);
MAKE_RESAMPLE_FUNC (gfloat, none, 2, sse); MAKE_RESAMPLE_FUNC (gfloat, none, 2, sse);
MAKE_RESAMPLE_FUNC (gfloat, linear, 1, sse); MAKE_RESAMPLE_FUNC (gfloat, linear, 1, sse);
MAKE_RESAMPLE_FUNC (gfloat, cubic, 1, sse);
#endif #endif
#if defined (HAVE_EMMINTRIN_H) && defined(__SSE2__) #if defined (HAVE_EMMINTRIN_H) && defined(__SSE2__)
@ -276,6 +297,7 @@ audio_resampler_check_x86 (const gchar *option)
resample_gfloat_none_1 = resample_gfloat_none_1_sse; resample_gfloat_none_1 = resample_gfloat_none_1_sse;
resample_gfloat_none_2 = resample_gfloat_none_2_sse; resample_gfloat_none_2 = resample_gfloat_none_2_sse;
resample_gfloat_linear_1 = resample_gfloat_linear_1_sse; resample_gfloat_linear_1 = resample_gfloat_linear_1_sse;
resample_gfloat_cubic_1 = resample_gfloat_cubic_1_sse;
#endif #endif
} else if (!strcmp (option, "sse2")) { } else if (!strcmp (option, "sse2")) {
#if defined (HAVE_EMMINTRIN_H) && defined(__SSE2__) #if defined (HAVE_EMMINTRIN_H) && defined(__SSE2__)
@ -287,6 +309,7 @@ audio_resampler_check_x86 (const gchar *option)
resample_gint16_none_2 = resample_gint16_none_2_sse2; resample_gint16_none_2 = resample_gint16_none_2_sse2;
resample_gdouble_none_2 = resample_gdouble_none_2_sse2; resample_gdouble_none_2 = resample_gdouble_none_2_sse2;
resample_gfloat_linear_1 = resample_gfloat_linear_1_sse; resample_gfloat_linear_1 = resample_gfloat_linear_1_sse;
resample_gfloat_cubic_1 = resample_gfloat_cubic_1_sse;
#endif #endif
} else if (!strcmp (option, "sse41")) { } else if (!strcmp (option, "sse41")) {
#if defined (HAVE_SMMINTRIN_H) && defined(__SSE4_1__) #if defined (HAVE_SMMINTRIN_H) && defined(__SSE4_1__)

View file

@ -157,7 +157,7 @@ static const BlackmanQualityMap blackman_qualities[] = {
#define DEFAULT_OPT_CUBIC_C 0.0 #define DEFAULT_OPT_CUBIC_C 0.0
#define DEFAULT_OPT_FILTER_MODE GST_AUDIO_RESAMPLER_FILTER_MODE_AUTO #define DEFAULT_OPT_FILTER_MODE GST_AUDIO_RESAMPLER_FILTER_MODE_AUTO
#define DEFAULT_OPT_FILTER_MODE_THRESHOLD 1048576 #define DEFAULT_OPT_FILTER_MODE_THRESHOLD 1048576
#define DEFAULT_OPT_FILTER_INTERPOLATION GST_AUDIO_RESAMPLER_FILTER_INTERPOLATION_LINEAR #define DEFAULT_OPT_FILTER_INTERPOLATION GST_AUDIO_RESAMPLER_FILTER_INTERPOLATION_CUBIC
#define DEFAULT_OPT_FILTER_OVERSAMPLE 8 #define DEFAULT_OPT_FILTER_OVERSAMPLE 8
#define DEFAULT_OPT_MAX_PHASE_ERROR 0.1 #define DEFAULT_OPT_MAX_PHASE_ERROR 0.1
@ -432,16 +432,16 @@ static inline void \
make_coeff_##type##_linear (gint frac, gint out_rate, type *icoeff) \ make_coeff_##type##_linear (gint frac, gint out_rate, type *icoeff) \
{ \ { \
type x = (type)frac / out_rate; \ type x = (type)frac / out_rate; \
icoeff[0] = icoeff[2] = 1.0 - x; \ icoeff[0] = icoeff[2] = x; \
icoeff[1] = icoeff[3] = x; \ icoeff[1] = icoeff[3] = 1.0 - x; \
} }
#define MAKE_COEFF_LINEAR_INT_FUNC(type,type2,prec) \ #define MAKE_COEFF_LINEAR_INT_FUNC(type,type2,prec) \
static inline void \ static inline void \
make_coeff_##type##_linear (gint frac, gint out_rate, type *icoeff) \ make_coeff_##type##_linear (gint frac, gint out_rate, type *icoeff) \
{ \ { \
type x = ((type2)frac << prec) / out_rate; \ type x = ((type2)frac << prec) / out_rate; \
icoeff[0] = icoeff[2] = (1 << prec) - x; \ icoeff[0] = icoeff[2] = x; \
icoeff[1] = icoeff[3] = x; \ icoeff[1] = icoeff[3] = (1 << prec) - x; \
} }
MAKE_COEFF_LINEAR_INT_FUNC (gint16, gint32, PRECISION_S16); MAKE_COEFF_LINEAR_INT_FUNC (gint16, gint32, PRECISION_S16);
@ -449,21 +449,52 @@ MAKE_COEFF_LINEAR_INT_FUNC (gint32, gint64, PRECISION_S32);
MAKE_COEFF_LINEAR_FLOAT_FUNC (gfloat); MAKE_COEFF_LINEAR_FLOAT_FUNC (gfloat);
MAKE_COEFF_LINEAR_FLOAT_FUNC (gdouble); MAKE_COEFF_LINEAR_FLOAT_FUNC (gdouble);
#define GET_TAPS_LINEAR_FUNC(type) \ #define MAKE_COEFF_CUBIC_FLOAT_FUNC(type) \
static inline void \
make_coeff_##type##_cubic (gint frac, gint out_rate, type *icoeff) \
{ \
type x = (type) frac / out_rate, x2 = x * x, x3 = x2 * x; \
icoeff[0] = 0.16667f * (x3 - x); \
icoeff[1] = x + 0.5f * (x2 - x3); \
icoeff[3] = -0.33333f * x + 0.5f * x2 - 0.16667f * x3; \
icoeff[2] = 1. - icoeff[0] - icoeff[1] - icoeff[3]; \
}
#define MAKE_COEFF_CUBIC_INT_FUNC(type,type2,prec) \
static inline void \
make_coeff_##type##_cubic (gint frac, gint out_rate, type *icoeff) \
{ \
type x = ((type2) frac << prec) / out_rate; \
type one = 1 << prec; \
type x2 = ((type2) x * (type2) x) >> prec; \
type x3 = ((type2) x2 * (type2) x) >> prec; \
icoeff[0] = (((type2) (x3 - x) << prec) / 6) >> prec; \
icoeff[1] = x + ((x2 - x3) >> 1); \
icoeff[3] = -((((type2) x << prec) / 3) >> prec) + \
(x2 >> 1) - ((((type2) x3 << prec) / 6) >> prec); \
icoeff[2] = one - icoeff[0] - icoeff[1] - icoeff[3]; \
}
MAKE_COEFF_CUBIC_INT_FUNC (gint16, gint32, PRECISION_S16);
MAKE_COEFF_CUBIC_INT_FUNC (gint32, gint64, PRECISION_S32);
MAKE_COEFF_CUBIC_FLOAT_FUNC (gfloat);
MAKE_COEFF_CUBIC_FLOAT_FUNC (gdouble);
#define GET_TAPS_INTERPOLATE_FUNC(type,inter) \
static inline gpointer \ static inline gpointer \
get_taps_##type##_linear (GstAudioResampler * resampler, \ get_taps_##type##_##inter (GstAudioResampler * resampler, \
gint *samp_index, gint *samp_phase, type icoeff[4]) \ gint *samp_index, gint *samp_phase, type icoeff[4]) \
{ \ { \
gpointer res; \ gpointer res; \
gint out_rate = resampler->out_rate; \ gint out_rate = resampler->out_rate; \
gint offset, frac, pos; \ gint offset, frac, pos; \
gint oversample = resampler->oversample; \
\ \
pos = ((out_rate - 1) - *samp_phase) * resampler->oversample; \ pos = *samp_phase * oversample; \
offset = pos / out_rate; \ offset = (oversample - 1) - (pos / out_rate); \
frac = pos % out_rate; \ frac = pos % out_rate; \
\ \
res = (type *)resampler->coeff + offset; \ res = (type *)resampler->coeff + offset; \
make_coeff_##type##_linear (frac, out_rate, icoeff); \ make_coeff_##type##_##inter (frac, out_rate, icoeff); \
\ \
*samp_index += resampler->samp_inc; \ *samp_index += resampler->samp_inc; \
*samp_phase += resampler->samp_frac; \ *samp_phase += resampler->samp_frac; \
@ -474,10 +505,15 @@ get_taps_##type##_linear (GstAudioResampler * resampler, \
return res; \ return res; \
} }
GET_TAPS_LINEAR_FUNC (gint16); GET_TAPS_INTERPOLATE_FUNC (gint16, linear);
GET_TAPS_LINEAR_FUNC (gint32); GET_TAPS_INTERPOLATE_FUNC (gint32, linear);
GET_TAPS_LINEAR_FUNC (gfloat); GET_TAPS_INTERPOLATE_FUNC (gfloat, linear);
GET_TAPS_LINEAR_FUNC (gdouble); GET_TAPS_INTERPOLATE_FUNC (gdouble, linear);
GET_TAPS_INTERPOLATE_FUNC (gint16, cubic);
GET_TAPS_INTERPOLATE_FUNC (gint32, cubic);
GET_TAPS_INTERPOLATE_FUNC (gfloat, cubic);
GET_TAPS_INTERPOLATE_FUNC (gdouble, cubic);
#define INNER_PRODUCT_INT_NONE_FUNC(type,type2,prec,limit) \ #define INNER_PRODUCT_INT_NONE_FUNC(type,type2,prec,limit) \
static inline void \ static inline void \
@ -503,20 +539,46 @@ inner_product_##type##_linear_1_c (type * o, const type * a, \
const type * b, gint len, const type *ic, gint oversample) \ const type * b, gint len, const type *ic, gint oversample) \
{ \ { \
gint i; \ gint i; \
type2 res, res1 = 0, res2 = 0; \ type2 res[2] = { 0, 0 }; \
\ \
for (i = 0; i < len; i++) { \ for (i = 0; i < len; i++) { \
res1 += (type2) a[i] * (type2) b[i * oversample]; \ res[0] += (type2) a[i] * (type2) b[i * oversample + 0]; \
res2 += (type2) a[i] * (type2) b[i * oversample + 1]; \ res[1] += (type2) a[i] * (type2) b[i * oversample + 1]; \
} \ } \
res = (res1 >> (prec)) * ic[0] + (res2 >> (prec)) * ic[1]; \ res[0] = (res[0] >> (prec)) * ic[0] + \
res = (res + (1 << ((prec) - 1))) >> (prec); \ (res[1] >> (prec)) * ic[1]; \
*o = CLAMP (res, -(limit), (limit) - 1); \ res[0] = (res[0] + (1 << ((prec) - 1))) >> (prec); \
*o = CLAMP (res[0], -(limit), (limit) - 1); \
} }
INNER_PRODUCT_INT_LINEAR_FUNC (gint16, gint32, PRECISION_S16, 1L << 15); INNER_PRODUCT_INT_LINEAR_FUNC (gint16, gint32, PRECISION_S16, 1L << 15);
INNER_PRODUCT_INT_LINEAR_FUNC (gint32, gint64, PRECISION_S32, 1L << 31); INNER_PRODUCT_INT_LINEAR_FUNC (gint32, gint64, PRECISION_S32, 1L << 31);
#define INNER_PRODUCT_INT_CUBIC_FUNC(type,type2,prec,limit) \
static inline void \
inner_product_##type##_cubic_1_c (type * o, const type * a, \
const type * b, gint len, const type *ic, gint oversample) \
{ \
gint i; \
type2 res[4] = { 0, 0, 0, 0 }; \
\
for (i = 0; i < len; i++) { \
res[0] += (type2) a[i] * (type2) b[i * oversample + 0]; \
res[1] += (type2) a[i] * (type2) b[i * oversample + 1]; \
res[2] += (type2) a[i] * (type2) b[i * oversample + 2]; \
res[3] += (type2) a[i] * (type2) b[i * oversample + 3]; \
} \
res[0] = (res[0] >> (prec)) * ic[0] + \
(res[1] >> (prec)) * ic[1] + \
(res[2] >> (prec)) * ic[2] + \
(res[3] >> (prec)) * ic[3]; \
res[0] = (res[0] + (1 << ((prec) - 1))) >> (prec); \
*o = CLAMP (res[0], -(limit), (limit) - 1); \
}
INNER_PRODUCT_INT_CUBIC_FUNC (gint16, gint32, PRECISION_S16, 1L << 15);
INNER_PRODUCT_INT_CUBIC_FUNC (gint32, gint64, PRECISION_S32, 1L << 31);
#define INNER_PRODUCT_FLOAT_NONE_FUNC(type) \ #define INNER_PRODUCT_FLOAT_NONE_FUNC(type) \
static inline void \ static inline void \
inner_product_##type##_none_1_c (type * o, const type * a, \ inner_product_##type##_none_1_c (type * o, const type * a, \
@ -540,17 +602,37 @@ inner_product_##type##_linear_1_c (type * o, const type * a, \
const type * b, gint len, const type *ic, gint oversample) \ const type * b, gint len, const type *ic, gint oversample) \
{ \ { \
gint i; \ gint i; \
type res1 = 0.0, res2 = 0.0; \ type res[2] = { 0.0, 0.0 }; \
\ \
for (i = 0; i < len; i++) { \ for (i = 0; i < len; i++) { \
res1 += a[i] * b[i * oversample]; \ res[0] += a[i] * b[i * oversample + 0]; \
res2 += a[i] * b[i * oversample + 1]; \ res[1] += a[i] * b[i * oversample + 1]; \
} \ } \
*o = res1 * ic[0] + res2 * ic[1]; \ *o = res[0] * ic[0] + res[1] * ic[1]; \
} }
INNER_PRODUCT_FLOAT_LINEAR_FUNC (gfloat); INNER_PRODUCT_FLOAT_LINEAR_FUNC (gfloat);
INNER_PRODUCT_FLOAT_LINEAR_FUNC (gdouble); INNER_PRODUCT_FLOAT_LINEAR_FUNC (gdouble);
#define INNER_PRODUCT_FLOAT_CUBIC_FUNC(type) \
static inline void \
inner_product_##type##_cubic_1_c (type * o, const type * a, \
const type * b, gint len, const type *ic, gint oversample) \
{ \
gint i; \
type res[4] = { 0.0, 0.0, 0.0, 0.0 }; \
\
for (i = 0; i < len; i++) { \
res[0] += a[i] * b[i * oversample + 0]; \
res[1] += a[i] * b[i * oversample + 1]; \
res[2] += a[i] * b[i * oversample + 2]; \
res[3] += a[i] * b[i * oversample + 3]; \
} \
*o = res[0] * ic[0] + res[1] * ic[1] + \
res[2] * ic[2] + res[3] * ic[3]; \
}
INNER_PRODUCT_FLOAT_CUBIC_FUNC (gfloat);
INNER_PRODUCT_FLOAT_CUBIC_FUNC (gdouble);
#define MAKE_RESAMPLE_FUNC(type,inter,channels,arch) \ #define MAKE_RESAMPLE_FUNC(type,inter,channels,arch) \
static void \ static void \
resample_ ##type## _ ##inter## _ ##channels## _ ##arch (GstAudioResampler * resampler, \ resample_ ##type## _ ##inter## _ ##channels## _ ##arch (GstAudioResampler * resampler, \
@ -601,6 +683,11 @@ MAKE_RESAMPLE_FUNC (gint32, linear, 1, c);
MAKE_RESAMPLE_FUNC (gfloat, linear, 1, c); MAKE_RESAMPLE_FUNC (gfloat, linear, 1, c);
MAKE_RESAMPLE_FUNC (gdouble, linear, 1, c); MAKE_RESAMPLE_FUNC (gdouble, linear, 1, c);
MAKE_RESAMPLE_FUNC (gint16, cubic, 1, c);
MAKE_RESAMPLE_FUNC (gint32, cubic, 1, c);
MAKE_RESAMPLE_FUNC (gfloat, cubic, 1, c);
MAKE_RESAMPLE_FUNC (gdouble, cubic, 1, c);
static ResampleFunc resample_funcs[] = { static ResampleFunc resample_funcs[] = {
resample_gint16_none_1_c, resample_gint16_none_1_c,
resample_gint32_none_1_c, resample_gint32_none_1_c,
@ -619,6 +706,15 @@ static ResampleFunc resample_funcs[] = {
NULL, NULL,
NULL, NULL,
NULL, NULL,
resample_gint16_cubic_1_c,
resample_gint32_cubic_1_c,
resample_gfloat_cubic_1_c,
resample_gdouble_cubic_1_c,
NULL,
NULL,
NULL,
NULL,
}; };
#define resample_gint16_none_1 resample_funcs[0] #define resample_gint16_none_1 resample_funcs[0]
@ -635,6 +731,11 @@ static ResampleFunc resample_funcs[] = {
#define resample_gfloat_linear_1 resample_funcs[10] #define resample_gfloat_linear_1 resample_funcs[10]
#define resample_gdouble_linear_1 resample_funcs[11] #define resample_gdouble_linear_1 resample_funcs[11]
#define resample_gint16_cubic_1 resample_funcs[16]
#define resample_gint32_cubic_1 resample_funcs[17]
#define resample_gfloat_cubic_1 resample_funcs[18]
#define resample_gdouble_cubic_1 resample_funcs[19]
#if defined HAVE_ORC && !defined DISABLE_ORC #if defined HAVE_ORC && !defined DISABLE_ORC
# if defined (__i386__) || defined (__x86_64__) # if defined (__i386__) || defined (__x86_64__)
# define CHECK_X86 # define CHECK_X86
@ -772,12 +873,12 @@ calculate_kaiser_params (GstAudioResampler * resampler)
resampler->n_taps, resampler->cutoff); resampler->n_taps, resampler->cutoff);
} }
static gboolean static void
alloc_coeff_mem (GstAudioResampler * resampler, gint bps, gint n_taps, alloc_coeff_mem (GstAudioResampler * resampler, gint bps, gint n_taps,
gint n_phases) gint n_phases)
{ {
if (resampler->alloc_taps >= n_taps && resampler->alloc_phases >= n_phases) if (resampler->alloc_taps >= n_taps && resampler->alloc_phases >= n_phases)
return FALSE; return;
resampler->tmpcoeff = resampler->tmpcoeff =
g_realloc_n (resampler->tmpcoeff, n_taps, sizeof (gdouble)); g_realloc_n (resampler->tmpcoeff, n_taps, sizeof (gdouble));
@ -788,8 +889,6 @@ alloc_coeff_mem (GstAudioResampler * resampler, gint bps, gint n_taps,
resampler->coeff = MEM_ALIGN (resampler->coeffmem, ALIGN); resampler->coeff = MEM_ALIGN (resampler->coeffmem, ALIGN);
resampler->alloc_taps = n_taps; resampler->alloc_taps = n_taps;
resampler->alloc_phases = n_phases; resampler->alloc_phases = n_phases;
return TRUE;
} }
static void static void
@ -884,7 +983,9 @@ resampler_calculate_taps (GstAudioResampler * resampler)
} }
if (interpolate) { if (interpolate) {
gint otaps = oversample * n_taps + 1; gint otaps;
gpointer coeff;
gdouble x, weight, *tmpcoeff;
GstAudioResamplerFilterInterpolation filter_interpolation = GstAudioResamplerFilterInterpolation filter_interpolation =
GET_OPT_FILTER_INTERPOLATION (resampler->options); GET_OPT_FILTER_INTERPOLATION (resampler->options);
@ -894,29 +995,38 @@ resampler_calculate_taps (GstAudioResampler * resampler)
else else
resampler->filter_interpolation = filter_interpolation; resampler->filter_interpolation = filter_interpolation;
if (alloc_coeff_mem (resampler, bps, otaps, 1)) { otaps = oversample * n_taps;
gpointer coeff = (gint8 *) resampler->coeff; switch (resampler->filter_interpolation) {
gdouble *tmpcoeff = resampler->tmpcoeff; default:
gdouble x, weight; case GST_AUDIO_RESAMPLER_FILTER_INTERPOLATION_LINEAR:
otaps += 1;
break;
case GST_AUDIO_RESAMPLER_FILTER_INTERPOLATION_CUBIC:
otaps += 3;
break;
}
x = 1.0 - n_taps / 2; alloc_coeff_mem (resampler, bps, otaps, 1);
weight = fill_taps (resampler, tmpcoeff, x, otaps, oversample);
switch (resampler->format) { coeff = resampler->coeff;
case GST_AUDIO_FORMAT_S16: tmpcoeff = resampler->tmpcoeff;
convert_taps_gint16 (tmpcoeff, coeff, weight / oversample, otaps); x = 1.0 - n_taps / 2;
break; weight = fill_taps (resampler, tmpcoeff, x, otaps, oversample);
case GST_AUDIO_FORMAT_S32:
convert_taps_gint32 (tmpcoeff, coeff, weight / oversample, otaps); switch (resampler->format) {
break; case GST_AUDIO_FORMAT_S16:
case GST_AUDIO_FORMAT_F32: convert_taps_gint16 (tmpcoeff, coeff, weight / oversample, otaps);
convert_taps_gfloat (tmpcoeff, coeff, weight / oversample, otaps); break;
break; case GST_AUDIO_FORMAT_S32:
default: convert_taps_gint32 (tmpcoeff, coeff, weight / oversample, otaps);
case GST_AUDIO_FORMAT_F64: break;
convert_taps_gdouble (tmpcoeff, coeff, weight / oversample, otaps); case GST_AUDIO_FORMAT_F32:
break; convert_taps_gfloat (tmpcoeff, coeff, weight / oversample, otaps);
} break;
default:
case GST_AUDIO_FORMAT_F64:
convert_taps_gdouble (tmpcoeff, coeff, weight / oversample, otaps);
break;
} }
} else { } else {
resampler->filter_interpolation = resampler->filter_interpolation =