From e02af5c534c90ba086f43ea76bc9063d338c556b Mon Sep 17 00:00:00 2001 From: Wim Taymans Date: Wed, 10 Feb 2016 12:04:12 +0100 Subject: [PATCH] audio-resampler: add linear interpolation method Make more functions into macros. Add linear interpolation of filter coefficients. --- gst-libs/gst/audio/audio-resampler-x86.h | 83 ++-- gst-libs/gst/audio/audio-resampler.c | 633 ++++++++++++++++++++++--------- gst-libs/gst/audio/audio-resampler.h | 33 +- 3 files changed, 540 insertions(+), 209 deletions(-) diff --git a/gst-libs/gst/audio/audio-resampler-x86.h b/gst-libs/gst/audio/audio-resampler-x86.h index c660aa0..9f96685 100644 --- a/gst-libs/gst/audio/audio-resampler-x86.h +++ b/gst-libs/gst/audio/audio-resampler-x86.h @@ -21,7 +21,7 @@ #include static inline void -inner_product_gfloat_1_sse (gfloat * o, const gfloat * a, const gfloat * b, gint len) +inner_product_gfloat_none_1_sse (gfloat * o, const gfloat * a, const gfloat * b, gint len, gpointer icoeff, gint oversample) { gint i = 0; __m128 sum = _mm_setzero_ps (); @@ -40,7 +40,35 @@ inner_product_gfloat_1_sse (gfloat * o, const gfloat * a, const gfloat * b, gint } static inline void -inner_product_gfloat_2_sse (gfloat * o, const gfloat * a, const gfloat * b, gint len) +inner_product_gfloat_linear_1_sse (gfloat * o, const gfloat * a, const gfloat * b, gint len, gpointer icoeff, gint oversample) +{ + gint i = 0; + __m128 sum = _mm_setzero_ps (), t, b0; + __m128 f = _mm_loadu_ps(icoeff); + + for (; i < len; i += 4) { + t = _mm_loadu_ps (a + i); + + b0 = _mm_loadh_pi (b0, (__m64 *) (b + (i+0)*oversample)); + b0 = _mm_loadl_pi (b0, (__m64 *) (b + (i+1)*oversample)); + + sum = + _mm_add_ps (sum, _mm_mul_ps (_mm_unpacklo_ps (t, t), b0)); + + b0 = _mm_loadh_pi (b0, (__m64 *) (b + (i+2)*oversample)); + b0 = _mm_loadl_pi (b0, (__m64 *) (b + (i+3)*oversample)); + + sum = + _mm_add_ps (sum, _mm_mul_ps (_mm_unpackhi_ps (t, t), b0)); + } + 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 +inner_product_gfloat_none_2_sse (gfloat * o, const gfloat * a, const gfloat * b, gint len, gpointer icoeff, gint oversample) { gint i = 0; __m128 sum = _mm_setzero_ps (), t; @@ -66,15 +94,16 @@ inner_product_gfloat_2_sse (gfloat * o, const gfloat * a, const gfloat * b, gint *(gint64*)o = _mm_cvtsi128_si64 ((__m128i)sum); } -MAKE_RESAMPLE_FUNC (gfloat, 1, sse); -MAKE_RESAMPLE_FUNC (gfloat, 2, sse); +MAKE_RESAMPLE_FUNC (gfloat, none, 1, sse); +MAKE_RESAMPLE_FUNC (gfloat, none, 2, sse); +MAKE_RESAMPLE_FUNC (gfloat, linear, 1, sse); #endif #if defined (HAVE_EMMINTRIN_H) && defined(__SSE2__) #include static inline void -inner_product_gint16_1_sse2 (gint16 * o, const gint16 * a, const gint16 * b, gint len) +inner_product_gint16_none_1_sse2 (gint16 * o, const gint16 * a, const gint16 * b, gint len, gpointer icoeff, gint oversample) { gint i = 0; __m128i sum, ta, tb; @@ -101,8 +130,8 @@ inner_product_gint16_1_sse2 (gint16 * o, const gint16 * a, const gint16 * b, gin } static inline void -inner_product_gdouble_1_sse2 (gdouble * o, const gdouble * a, const gdouble * b, - gint len) +inner_product_gdouble_none_1_sse2 (gdouble * o, const gdouble * a, const gdouble * b, + gint len, gpointer icoeff, gint oversample) { gint i = 0; __m128d sum = _mm_setzero_pd (); @@ -126,7 +155,7 @@ inner_product_gdouble_1_sse2 (gdouble * o, const gdouble * a, const gdouble * b, } static inline void -inner_product_gint16_2_sse2 (gint16 * o, const gint16 * a, const gint16 * b, gint len) +inner_product_gint16_none_2_sse2 (gint16 * o, const gint16 * a, const gint16 * b, gint len, gpointer icoeff, gint oversample) { gint i = 0; __m128i sum, ta, tb, t1; @@ -157,8 +186,8 @@ inner_product_gint16_2_sse2 (gint16 * o, const gint16 * a, const gint16 * b, gin } static inline void -inner_product_gdouble_2_sse2 (gdouble * o, const gdouble * a, const gdouble * b, - gint len) +inner_product_gdouble_none_2_sse2 (gdouble * o, const gdouble * a, const gdouble * b, + gint len, gpointer icoeff, gint oversample) { gint i = 0; __m128d sum = _mm_setzero_pd (), t; @@ -183,18 +212,18 @@ inner_product_gdouble_2_sse2 (gdouble * o, const gdouble * a, const gdouble * b, _mm_store_pd (o, sum); } -MAKE_RESAMPLE_FUNC (gint16, 1, sse2); -MAKE_RESAMPLE_FUNC (gdouble, 1, sse2); -MAKE_RESAMPLE_FUNC (gint16, 2, sse2); -MAKE_RESAMPLE_FUNC (gdouble, 2, sse2); +MAKE_RESAMPLE_FUNC (gint16, none, 1, sse2); +MAKE_RESAMPLE_FUNC (gdouble, none, 1, sse2); +MAKE_RESAMPLE_FUNC (gint16, none, 2, sse2); +MAKE_RESAMPLE_FUNC (gdouble, none, 2, sse2); #endif #if defined (HAVE_SMMINTRIN_H) && defined(__SSE4_1__) #include static inline void -inner_product_gint32_1_sse41 (gint32 * o, const gint32 * a, const gint32 * b, - gint len) +inner_product_gint32_none_1_sse41 (gint32 * o, const gint32 * a, const gint32 * b, + gint len, gpointer icoeff, gint oversample) { gint i = 0; __m128i sum, ta, tb; @@ -230,7 +259,7 @@ inner_product_gint32_1_sse41 (gint32 * o, const gint32 * a, const gint32 * b, *o = CLAMP (res, -(1L << 31), (1L << 31) - 1); } -MAKE_RESAMPLE_FUNC (gint32, 1, sse41); +MAKE_RESAMPLE_FUNC (gint32, none, 1, sse41); #endif static void @@ -239,23 +268,25 @@ audio_resampler_check_x86 (const gchar *option) if (!strcmp (option, "sse")) { #if defined (HAVE_XMMINTRIN_H) && defined(__SSE__) GST_DEBUG ("enable SSE optimisations"); - resample_gfloat_1 = resample_gfloat_1_sse; - resample_gfloat_2 = resample_gfloat_2_sse; + resample_gfloat_none_1 = resample_gfloat_none_1_sse; + resample_gfloat_none_2 = resample_gfloat_none_2_sse; + resample_gfloat_linear_1 = resample_gfloat_linear_1_sse; #endif } else if (!strcmp (option, "sse2")) { #if defined (HAVE_EMMINTRIN_H) && defined(__SSE2__) GST_DEBUG ("enable SSE2 optimisations"); - resample_gint16_1 = resample_gint16_1_sse2; - resample_gfloat_1 = resample_gfloat_1_sse; - resample_gfloat_2 = resample_gfloat_2_sse; - resample_gdouble_1 = resample_gdouble_1_sse2; - resample_gint16_2 = resample_gint16_2_sse2; - resample_gdouble_2 = resample_gdouble_2_sse2; + resample_gint16_none_1 = resample_gint16_none_1_sse2; + resample_gfloat_none_1 = resample_gfloat_none_1_sse; + resample_gfloat_linear_1 = resample_gfloat_linear_1_sse; + resample_gfloat_none_2 = resample_gfloat_none_2_sse; + resample_gdouble_none_1 = resample_gdouble_none_1_sse2; + resample_gint16_none_2 = resample_gint16_none_2_sse2; + resample_gdouble_none_2 = resample_gdouble_none_2_sse2; #endif } else if (!strcmp (option, "sse41")) { #if defined (HAVE_SMMINTRIN_H) && defined(__SSE4_1__) GST_DEBUG ("enable SSE41 optimisations"); - resample_gint32_1 = resample_gint32_1_sse41; + resample_gint32_none_1 = resample_gint32_none_1_sse41; #endif } } diff --git a/gst-libs/gst/audio/audio-resampler.c b/gst-libs/gst/audio/audio-resampler.c index 9f38b1e..c5fde62 100644 --- a/gst-libs/gst/audio/audio-resampler.c +++ b/gst-libs/gst/audio/audio-resampler.c @@ -34,10 +34,6 @@ typedef struct _Tap { gpointer taps; - - gint sample_inc; - gint next_phase; - gint size; } Tap; typedef void (*MakeTapsFunc) (GstAudioResampler * resampler, Tap * t, gint j); @@ -67,10 +63,17 @@ struct _GstAudioResampler /* for cubic */ gdouble b, c; + GstAudioResamplerFilterMode filter_mode; + guint filter_threshold; + GstAudioResamplerFilterInterpolation filter_interpolation; + gint oversample; + guint n_taps; Tap *taps; gpointer coeff; gpointer coeffmem; + guint alloc_taps; + guint alloc_phases; gsize cstride; gpointer tmpcoeff; @@ -103,6 +106,10 @@ GST_DEBUG_CATEGORY_STATIC (audio_resampler_debug); * */ +static const gint oversample_qualities[] = { + 4, 4, 4, 8, 8, 16, 16, 16, 16, 32, 32 +}; + typedef struct { gdouble cutoff; @@ -148,7 +155,11 @@ static const BlackmanQualityMap blackman_qualities[] = { #define DEFAULT_QUALITY GST_AUDIO_RESAMPLER_QUALITY_DEFAULT #define DEFAULT_OPT_CUBIC_B 1.0 #define DEFAULT_OPT_CUBIC_C 0.0 -#define DEFAULT_OPT_MAX_PHASE_ERROR 0.05 +#define DEFAULT_OPT_FILTER_MODE GST_AUDIO_RESAMPLER_FILTER_MODE_AUTO +#define DEFAULT_OPT_FILTER_MODE_THRESHOLD 1048576 +#define DEFAULT_OPT_FILTER_INTERPOLATION GST_AUDIO_RESAMPLER_FILTER_INTERPOLATION_LINEAR +#define DEFAULT_OPT_FILTER_OVERSAMPLE 8 +#define DEFAULT_OPT_MAX_PHASE_ERROR 0.1 static gdouble get_opt_double (GstStructure * options, const gchar * name, gdouble def) @@ -168,6 +179,16 @@ get_opt_int (GstStructure * options, const gchar * name, gint def) return res; } +static gint +get_opt_enum (GstStructure * options, const gchar * name, GType type, gint def) +{ + gint res; + if (!options || !gst_structure_get_enum (options, name, type, &res)) + res = def; + return res; +} + + #define GET_OPT_CUTOFF(options,def) get_opt_double(options, \ GST_AUDIO_RESAMPLER_OPT_CUTOFF,def) #define GET_OPT_DOWN_CUTOFF_FACTOR(options,def) get_opt_double(options, \ @@ -182,6 +203,16 @@ get_opt_int (GstStructure * options, const gchar * name, gint def) GST_AUDIO_RESAMPLER_OPT_CUBIC_C, DEFAULT_OPT_CUBIC_C) #define GET_OPT_N_TAPS(options,def) get_opt_int(options, \ GST_AUDIO_RESAMPLER_OPT_N_TAPS, def) +#define GET_OPT_FILTER_MODE(options) get_opt_enum(options, \ + GST_AUDIO_RESAMPLER_OPT_FILTER_MODE, GST_TYPE_AUDIO_RESAMPLER_FILTER_MODE, \ + DEFAULT_OPT_FILTER_MODE) +#define GET_OPT_FILTER_MODE_THRESHOLD(options) get_opt_int(options, \ + GST_AUDIO_RESAMPLER_OPT_FILTER_MODE_THRESHOLD, DEFAULT_OPT_FILTER_MODE_THRESHOLD) +#define GET_OPT_FILTER_INTERPOLATION(options) get_opt_enum(options, \ + GST_AUDIO_RESAMPLER_OPT_FILTER_INTERPOLATION, GST_TYPE_AUDIO_RESAMPLER_FILTER_INTERPOLATION, \ + DEFAULT_OPT_FILTER_INTERPOLATION) +#define GET_OPT_FILTER_OVERSAMPLE(options) get_opt_int(options, \ + GST_AUDIO_RESAMPLER_OPT_FILTER_OVERSAMPLE, DEFAULT_OPT_FILTER_OVERSAMPLE) #define GET_OPT_MAX_PHASE_ERROR(options) get_opt_double(options, \ GST_AUDIO_RESAMPLER_OPT_MAX_PHASE_ERROR, DEFAULT_OPT_MAX_PHASE_ERROR) @@ -189,7 +220,7 @@ get_opt_int (GstStructure * options, const gchar * name, gint def) #define bessel dbesi0 static inline gdouble -get_nearest_tap (GstAudioResampler * resampler, gdouble x) +get_nearest_tap (gdouble x) { gdouble a = fabs (x); @@ -200,11 +231,11 @@ get_nearest_tap (GstAudioResampler * resampler, gdouble x) } static inline gdouble -get_linear_tap (GstAudioResampler * resampler, gdouble x) +get_linear_tap (gdouble x, gint n_taps) { gdouble a; - a = fabs (x) / resampler->n_taps; + a = fabs (x) / n_taps; if (a < 1.0) return 1.0 - a; @@ -213,17 +244,14 @@ get_linear_tap (GstAudioResampler * resampler, gdouble x) } static inline gdouble -get_cubic_tap (GstAudioResampler * resampler, gdouble x) +get_cubic_tap (gdouble x, gint n_taps, gdouble b, gdouble c) { - gdouble a, a2, a3, b, c; + gdouble a, a2, a3; - a = fabs (x * 4.0) / resampler->n_taps; + a = fabs (x * 4.0) / n_taps; a2 = a * a; a3 = a2 * a; - b = resampler->b; - c = resampler->c; - if (a <= 1.0) return ((12.0 - 9.0 * b - 6.0 * c) * a3 + (-18.0 + 12.0 * b + 6.0 * c) * a2 + (6.0 - 2.0 * b)) / 6.0; @@ -236,33 +264,84 @@ get_cubic_tap (GstAudioResampler * resampler, gdouble x) } static inline gdouble -get_blackman_nuttall_tap (GstAudioResampler * resampler, gdouble x) +get_blackman_nuttall_tap (gdouble x, gint n_taps, gdouble Fc) { - gdouble s, y, w, Fc = resampler->cutoff; + gdouble s, y, w; y = G_PI * x; s = (y == 0.0 ? Fc : sin (y * Fc) / y); - w = 2.0 * y / resampler->n_taps + G_PI; + w = 2.0 * y / n_taps + G_PI; return s * (0.3635819 - 0.4891775 * cos (w) + 0.1365995 * cos (2 * w) - 0.0106411 * cos (3 * w)); } static inline gdouble -get_kaiser_tap (GstAudioResampler * resampler, gdouble x) +get_kaiser_tap (gdouble x, gint n_taps, gdouble Fc, gdouble beta) { - gdouble s, y, w, Fc = resampler->cutoff; + gdouble s, y, w; y = G_PI * x; s = (y == 0.0 ? Fc : sin (y * Fc) / y); - w = 2.0 * x / resampler->n_taps; - return s * bessel (resampler->kaiser_beta * sqrt (MAX (1 - w * w, 0))); + w = 2.0 * x / n_taps; + return s * bessel (beta * sqrt (MAX (1 - w * w, 0))); } -#define CONVERT_TAPS(type, precision) \ -G_STMT_START { \ - type *taps = res = (type *) ((gint8*)resampler->coeff + j * resampler->cstride); \ +#define PRECISION_S16 14 +#define PRECISION_S32 30 + +static inline gdouble +fill_taps (GstAudioResampler * resampler, + gdouble * tmpcoeff, gdouble x, gint n_taps, gint oversample) +{ + gdouble weight = 0.0; + gint i; + + switch (resampler->method) { + case GST_AUDIO_RESAMPLER_METHOD_NEAREST: + for (i = 0; i < n_taps; i++) + weight += tmpcoeff[i] = get_nearest_tap (x + i / (double) oversample); + break; + + case GST_AUDIO_RESAMPLER_METHOD_LINEAR: + for (i = 0; i < n_taps; i++) + weight += tmpcoeff[i] = + get_linear_tap (x + i / (double) oversample, resampler->n_taps); + break; + + case GST_AUDIO_RESAMPLER_METHOD_CUBIC: + for (i = 0; i < n_taps; i++) + weight += tmpcoeff[i] = + get_cubic_tap (x + i / (double) oversample, resampler->n_taps, + resampler->b, resampler->c); + break; + + case GST_AUDIO_RESAMPLER_METHOD_BLACKMAN_NUTTALL: + for (i = 0; i < n_taps; i++) + weight += tmpcoeff[i] = + get_blackman_nuttall_tap (x + i / (double) oversample, + resampler->n_taps, resampler->cutoff); + break; + + case GST_AUDIO_RESAMPLER_METHOD_KAISER: + for (i = 0; i < n_taps; i++) + weight += tmpcoeff[i] = + get_kaiser_tap (x + i / (double) oversample, resampler->n_taps, + resampler->cutoff, resampler->kaiser_beta); + break; + + default: + break; + } + return weight; +} + +#define MAKE_CONVERT_TAPS_INT_FUNC(type, precision) \ +static inline void \ +convert_taps_##type (gdouble *tmpcoeff, type *taps, \ + gdouble weight, gint n_taps) \ +{ \ gdouble multiplier = (1 << precision); \ gint i, j; \ gdouble offset, l_offset, h_offset; \ @@ -294,93 +373,115 @@ G_STMT_START { \ } \ if (!exact) \ GST_WARNING ("can't find exact taps"); \ -} G_STMT_END - -#define PRECISION_S16 15 -#define PRECISION_S32 30 - -static gpointer -make_taps (GstAudioResampler * resampler, Tap * t, gint j) -{ - gpointer res; - gint n_taps = resampler->n_taps; - gdouble x, weight = 0.0; - gdouble *tmpcoeff = resampler->tmpcoeff; - gint tap_offs = n_taps / 2; - gint in_rate = resampler->in_rate; - gint out_rate = resampler->out_rate; - gint l; - - x = ((double) (1.0 - tap_offs) - (double) j / out_rate); - - switch (resampler->method) { - case GST_AUDIO_RESAMPLER_METHOD_NEAREST: - for (l = 0; l < n_taps; l++, x += 1.0) - weight += tmpcoeff[l] = get_nearest_tap (resampler, x); - break; +} - case GST_AUDIO_RESAMPLER_METHOD_LINEAR: - for (l = 0; l < n_taps; l++, x += 1.0) - weight += tmpcoeff[l] = get_linear_tap (resampler, x); - break; +#define MAKE_CONVERT_TAPS_FLOAT_FUNC(type) \ +static inline void \ +convert_taps_##type (gdouble *tmpcoeff, type *taps, \ + gdouble weight, gint n_taps) \ +{ \ + gint i; \ + for (i = 0; i < n_taps; i++) \ + taps[i] = tmpcoeff[i] / weight; \ +} - case GST_AUDIO_RESAMPLER_METHOD_CUBIC: - for (l = 0; l < n_taps; l++, x += 1.0) - weight += tmpcoeff[l] = get_cubic_tap (resampler, x); - break; +MAKE_CONVERT_TAPS_INT_FUNC (gint16, PRECISION_S16); +MAKE_CONVERT_TAPS_INT_FUNC (gint32, PRECISION_S32); +MAKE_CONVERT_TAPS_FLOAT_FUNC (gfloat); +MAKE_CONVERT_TAPS_FLOAT_FUNC (gdouble); - case GST_AUDIO_RESAMPLER_METHOD_BLACKMAN_NUTTALL: - for (l = 0; l < n_taps; l++, x += 1.0) - weight += tmpcoeff[l] = get_blackman_nuttall_tap (resampler, x); - break; +#define GET_TAPS_NONE_FUNC(type) \ +static inline gpointer \ +get_taps_##type##_none (GstAudioResampler * resampler, \ + gint *samp_index, gint *samp_phase, type icoeff[4]) \ +{ \ + Tap *t = &resampler->taps[*samp_phase]; \ + gpointer res; \ + gdouble x, weight; \ + gint out_rate = resampler->out_rate; \ + gdouble *tmpcoeff = resampler->tmpcoeff; \ + gint n_taps = resampler->n_taps; \ + \ + if (G_LIKELY (t->taps)) { \ + res = t->taps; \ + } else { \ + res = (gint8 *) resampler->coeff + *samp_phase * resampler->cstride; \ + \ + x = 1.0 - n_taps / 2 - (double) *samp_phase / out_rate; \ + weight = fill_taps (resampler, tmpcoeff, x, n_taps, 1); \ + convert_taps_##type (tmpcoeff, res, weight, n_taps); \ + \ + t->taps = res; \ + } \ + *samp_index += resampler->samp_inc; \ + *samp_phase += resampler->samp_frac; \ + if (*samp_phase >= out_rate) { \ + *samp_phase -= out_rate; \ + (*samp_index)++; \ + } \ + return res; \ +} - case GST_AUDIO_RESAMPLER_METHOD_KAISER: - for (l = 0; l < n_taps; l++, x += 1.0) - weight += tmpcoeff[l] = get_kaiser_tap (resampler, x); - break; +GET_TAPS_NONE_FUNC (gint16); +GET_TAPS_NONE_FUNC (gint32); +GET_TAPS_NONE_FUNC (gfloat); +GET_TAPS_NONE_FUNC (gdouble); - default: - break; - } +#define MAKE_COEFF_LINEAR_FLOAT_FUNC(type) \ +static inline void \ +make_coeff_##type##_linear (gint frac, gint out_rate, type *icoeff) \ +{ \ + type x = (type)frac / out_rate; \ + icoeff[0] = icoeff[2] = 1.0 - x; \ + icoeff[1] = icoeff[3] = x; \ +} +#define MAKE_COEFF_LINEAR_INT_FUNC(type,type2,prec) \ +static inline void \ +make_coeff_##type##_linear (gint frac, gint out_rate, type *icoeff) \ +{ \ + type x = ((type2)frac << prec) / out_rate; \ + icoeff[0] = icoeff[2] = (1 << prec) - x; \ + icoeff[1] = icoeff[3] = x; \ +} - switch (resampler->format) { - case GST_AUDIO_FORMAT_F64: - { - gdouble *taps = res = - (gdouble *) ((gint8 *) resampler->coeff + j * resampler->cstride); - for (l = 0; l < n_taps; l++) - taps[l] = tmpcoeff[l] / weight; - break; - } - case GST_AUDIO_FORMAT_F32: - { - gfloat *taps = res = - (gfloat *) ((gint8 *) resampler->coeff + j * resampler->cstride); - for (l = 0; l < n_taps; l++) - taps[l] = tmpcoeff[l] / weight; - break; - } - case GST_AUDIO_FORMAT_S32: - CONVERT_TAPS (gint32, PRECISION_S32); - break; - case GST_AUDIO_FORMAT_S16: - CONVERT_TAPS (gint16, PRECISION_S16); - break; - default: - g_assert_not_reached (); - break; - } - if (t) { - t->taps = res; - t->sample_inc = (j + in_rate) / out_rate; - t->next_phase = (j + in_rate) % out_rate; - } - return res; +MAKE_COEFF_LINEAR_INT_FUNC (gint16, gint32, PRECISION_S16); +MAKE_COEFF_LINEAR_INT_FUNC (gint32, gint64, PRECISION_S32); +MAKE_COEFF_LINEAR_FLOAT_FUNC (gfloat); +MAKE_COEFF_LINEAR_FLOAT_FUNC (gdouble); + +#define GET_TAPS_LINEAR_FUNC(type) \ +static inline gpointer \ +get_taps_##type##_linear (GstAudioResampler * resampler, \ + gint *samp_index, gint *samp_phase, type icoeff[4]) \ +{ \ + gpointer res; \ + gint out_rate = resampler->out_rate; \ + gint offset, frac, pos; \ + \ + pos = ((out_rate - 1) - *samp_phase) * resampler->oversample; \ + offset = pos / out_rate; \ + frac = pos % out_rate; \ + \ + res = (type *)resampler->coeff + offset; \ + make_coeff_##type##_linear (frac, out_rate, icoeff); \ + \ + *samp_index += resampler->samp_inc; \ + *samp_phase += resampler->samp_frac; \ + if (*samp_phase >= out_rate) { \ + *samp_phase -= out_rate; \ + (*samp_index)++; \ + } \ + return res; \ } +GET_TAPS_LINEAR_FUNC (gint16); +GET_TAPS_LINEAR_FUNC (gint32); +GET_TAPS_LINEAR_FUNC (gfloat); +GET_TAPS_LINEAR_FUNC (gdouble); + static inline void -inner_product_gint16_1_c (gint16 * o, const gint16 * a, const gint16 * b, - gint len) +inner_product_gint16_none_1_c (gint16 * o, const gint16 * a, + const gint16 * b, gint len, gpointer icoeff, gint oversample) { gint i; gint32 res = 0; @@ -393,8 +494,25 @@ inner_product_gint16_1_c (gint16 * o, const gint16 * a, const gint16 * b, } static inline void -inner_product_gint32_1_c (gint32 * o, const gint32 * a, const gint32 * b, - gint len) +inner_product_gint16_linear_1_c (gint16 * o, const gint16 * a, const gint16 * b, + gint len, gpointer icoeff, gint oversample) +{ + gint i; + gint32 res, res1 = 0, res2 = 0; + gint16 *ic = icoeff; + + for (i = 0; i < len; i++) { + res1 += (gint32) a[i] * (gint32) b[i * oversample]; + res2 += (gint32) a[i] * (gint32) b[i * oversample + 1]; + } + res = (res1 >> PRECISION_S16) * ic[0] + (res2 >> PRECISION_S16) * ic[1]; + res = (res + (1 << (PRECISION_S16 - 1))) >> PRECISION_S16; + *o = CLAMP (res, -(1L << 15), (1L << 15) - 1); +} + +static inline void +inner_product_gint32_none_1_c (gint32 * o, const gint32 * a, const gint32 * b, + gint len, gpointer icoeff, gint oversample) { gint i; gint64 res = 0; @@ -407,8 +525,8 @@ inner_product_gint32_1_c (gint32 * o, const gint32 * a, const gint32 * b, } static inline void -inner_product_gfloat_1_c (gfloat * o, const gfloat * a, const gfloat * b, - gint len) +inner_product_gfloat_none_1_c (gfloat * o, const gfloat * a, const gfloat * b, + gint len, gpointer icoeff, gint oversample) { gint i; gfloat res = 0.0; @@ -420,8 +538,24 @@ inner_product_gfloat_1_c (gfloat * o, const gfloat * a, const gfloat * b, } static inline void -inner_product_gdouble_1_c (gdouble * o, const gdouble * a, const gdouble * b, - gint len) +inner_product_gfloat_linear_1_c (gfloat * o, const gfloat * a, const gfloat * b, + gint len, gpointer icoeff, gint oversample) +{ + gint i; + gfloat res, res1 = 0.0, res2 = 0.0, *ic = icoeff; + + for (i = 0; i < len; i++) { + res1 += a[i] * b[i * oversample]; + res2 += a[i] * b[i * oversample + 1]; + } + res = res1 * ic[0] + res2 * ic[1]; + + *o = res; +} + +static inline void +inner_product_gdouble_none_1_c (gdouble * o, const gdouble * a, + const gdouble * b, gint len, gpointer icoeff, gint oversample) { gint i; gdouble res = 0.0; @@ -432,9 +566,26 @@ inner_product_gdouble_1_c (gdouble * o, const gdouble * a, const gdouble * b, *o = res; } -#define MAKE_RESAMPLE_FUNC(type,channels,arch) \ +static inline void +inner_product_gdouble_linear_1_c (gdouble * o, const gdouble * a, + const gdouble * b, gint len, gpointer icoeff, gint oversample) +{ + gint i; + gdouble res, res1 = 0.0, res2 = 0.0, *ic = icoeff; + + for (i = 0; i < len; i++) { + res1 += a[i] * b[i * oversample]; + res2 += a[i] * b[i * oversample + 1]; + } + res = res1 * ic[0] + res2 * ic[1]; + + *o = res; +} + + +#define MAKE_RESAMPLE_FUNC(type,inter,channels,arch) \ static void \ -resample_ ##type## _ ##channels## _ ##arch (GstAudioResampler * resampler, \ +resample_ ##type## _ ##inter## _ ##channels## _ ##arch (GstAudioResampler * resampler, \ gpointer in[], gsize in_len, gpointer out[], gsize out_len, \ gsize * consumed) \ { \ @@ -442,6 +593,7 @@ resample_ ##type## _ ##channels## _ ##arch (GstAudioResampler * resampler, gint n_taps = resampler->n_taps; \ gint blocks = resampler->blocks; \ gint ostride = resampler->ostride; \ + gint oversample = resampler->oversample; \ gint samp_index = 0; \ gint samp_phase = 0; \ \ @@ -453,18 +605,14 @@ resample_ ##type## _ ##channels## _ ##arch (GstAudioResampler * resampler, samp_phase = resampler->samp_phase; \ \ for (di = 0; di < out_len; di++) { \ - Tap *t = &resampler->taps[samp_phase]; \ - type *ipp = &ip[samp_index * channels]; \ - gpointer taps; \ + type *ipp, icoeff[4], *taps; \ \ - if (G_UNLIKELY ((taps = t->taps) == NULL)) \ - taps = make_taps (resampler, t, samp_phase); \ + ipp = &ip[samp_index * channels]; \ \ - inner_product_ ##type## _##channels##_##arch (op, ipp, taps, n_taps); \ - op += ostride; \ + taps = get_taps_ ##type##_##inter (resampler, &samp_index, &samp_phase, icoeff); \ \ - samp_phase = t->next_phase; \ - samp_index += t->sample_inc; \ + inner_product_ ##type##_##inter##_##channels##_##arch (op, ipp, taps, n_taps, &icoeff,oversample); \ + op += ostride; \ } \ memmove (ip, &ip[samp_index * channels], \ (in_len - samp_index) * sizeof(type) * channels); \ @@ -475,30 +623,47 @@ resample_ ##type## _ ##channels## _ ##arch (GstAudioResampler * resampler, resampler->samp_phase = samp_phase; \ } -MAKE_RESAMPLE_FUNC (gint16, 1, c); -MAKE_RESAMPLE_FUNC (gint32, 1, c); -MAKE_RESAMPLE_FUNC (gfloat, 1, c); -MAKE_RESAMPLE_FUNC (gdouble, 1, c); +MAKE_RESAMPLE_FUNC (gint16, none, 1, c); +MAKE_RESAMPLE_FUNC (gint32, none, 1, c); +MAKE_RESAMPLE_FUNC (gfloat, none, 1, c); +MAKE_RESAMPLE_FUNC (gdouble, none, 1, c); + +MAKE_RESAMPLE_FUNC (gint16, linear, 1, c); +MAKE_RESAMPLE_FUNC (gfloat, linear, 1, c); static ResampleFunc resample_funcs[] = { - resample_gint16_1_c, - resample_gint32_1_c, - resample_gfloat_1_c, - resample_gdouble_1_c, + resample_gint16_none_1_c, + resample_gint32_none_1_c, + resample_gfloat_none_1_c, + resample_gdouble_none_1_c, + NULL, + NULL, + NULL, + NULL, + + resample_gint16_linear_1_c, + NULL, + resample_gfloat_linear_1_c, + NULL, NULL, NULL, NULL, NULL, }; -#define resample_gint16_1 resample_funcs[0] -#define resample_gint32_1 resample_funcs[1] -#define resample_gfloat_1 resample_funcs[2] -#define resample_gdouble_1 resample_funcs[3] -#define resample_gint16_2 resample_funcs[4] -#define resample_gint32_2 resample_funcs[5] -#define resample_gfloat_2 resample_funcs[6] -#define resample_gdouble_2 resample_funcs[7] +#define resample_gint16_none_1 resample_funcs[0] +#define resample_gint32_none_1 resample_funcs[1] +#define resample_gfloat_none_1 resample_funcs[2] +#define resample_gdouble_none_1 resample_funcs[3] +#define resample_gint16_none_2 resample_funcs[4] +#define resample_gint32_none_2 resample_funcs[5] +#define resample_gfloat_none_2 resample_funcs[6] +#define resample_gdouble_none_2 resample_funcs[7] + +#define resample_gint16_linear_1 resample_funcs[8] +#define resample_gint32_linear_1 resample_funcs[9] +#define resample_gfloat_linear_1 resample_funcs[10] +#define resample_gdouble_linear_1 resample_funcs[11] #if defined HAVE_ORC && !defined DISABLE_ORC # if defined (__i386__) || defined (__x86_64__) @@ -571,6 +736,13 @@ MAKE_DEINTERLEAVE_FUNC (gfloat); MAKE_DEINTERLEAVE_FUNC (gint32); MAKE_DEINTERLEAVE_FUNC (gint16); +static DeinterleaveFunc deinterleave_funcs[] = { + deinterleave_gint16, + deinterleave_gint32, + deinterleave_gfloat, + deinterleave_gdouble +}; + static void deinterleave_copy (GstAudioResampler * resampler, gpointer sbuf[], gpointer in[], gsize in_frames) @@ -630,15 +802,34 @@ calculate_kaiser_params (GstAudioResampler * resampler) resampler->n_taps, resampler->cutoff); } +static gboolean +alloc_coeff_mem (GstAudioResampler * resampler, gint bps, gint n_taps, + gint n_phases) +{ + if (resampler->alloc_taps >= n_taps && resampler->alloc_phases >= n_phases) + return FALSE; + + resampler->tmpcoeff = + g_realloc_n (resampler->tmpcoeff, n_taps, sizeof (gdouble)); + + resampler->cstride = GST_ROUND_UP_32 (bps * (n_taps + TAPS_OVERREAD)); + g_free (resampler->coeffmem); + resampler->coeffmem = g_malloc0 (n_phases * resampler->cstride + ALIGN - 1); + resampler->coeff = MEM_ALIGN (resampler->coeffmem, ALIGN); + resampler->alloc_taps = n_taps; + resampler->alloc_phases = n_phases; + + return TRUE; +} + static void resampler_calculate_taps (GstAudioResampler * resampler) { gint bps; - gint j; gint n_taps; gint out_rate; - gint in_rate; - gboolean non_interleaved; + gint in_rate, index, oversample; + gboolean non_interleaved, interpolate; DeinterleaveFunc deinterleave; ResampleFunc resample, resample_2; @@ -669,11 +860,25 @@ resampler_calculate_taps (GstAudioResampler * resampler) in_rate = resampler->in_rate; out_rate = resampler->out_rate; + oversample = GET_OPT_FILTER_OVERSAMPLE (resampler->options); + if (out_rate < in_rate) { + gint mult = 2; + resampler->cutoff = resampler->cutoff * out_rate / in_rate; resampler->n_taps = gst_util_uint64_scale_int (resampler->n_taps, in_rate, out_rate); + + while (oversample > 1) { + if (mult * out_rate >= in_rate) + break; + + mult *= 2; + oversample >>= 1; + } } + resampler->oversample = oversample; + /* only round up for bigger taps, the small taps are used for nearest, * linear and cubic and we want to use less taps for those. */ if (resampler->n_taps > 4) @@ -682,84 +887,146 @@ resampler_calculate_taps (GstAudioResampler * resampler) n_taps = resampler->n_taps; bps = resampler->bps; - GST_LOG ("using n_taps %d cutoff %f", n_taps, resampler->cutoff); + GST_LOG ("using n_taps %d cutoff %f, oversample %d", n_taps, + resampler->cutoff, oversample); - resampler->taps = g_realloc_n (resampler->taps, out_rate, sizeof (Tap)); + resampler->filter_mode = GET_OPT_FILTER_MODE (resampler->options); + resampler->filter_threshold = + GET_OPT_FILTER_MODE_THRESHOLD (resampler->options); - resampler->cstride = GST_ROUND_UP_32 (bps * (n_taps + TAPS_OVERREAD)); - g_free (resampler->coeffmem); - resampler->coeffmem = g_malloc0 (out_rate * resampler->cstride + ALIGN - 1); - resampler->coeff = MEM_ALIGN (resampler->coeffmem, ALIGN); + switch (resampler->filter_mode) { + case GST_AUDIO_RESAMPLER_FILTER_MODE_INTERPOLATED: + interpolate = TRUE; + break; + case GST_AUDIO_RESAMPLER_FILTER_MODE_FULL: + interpolate = FALSE; + break; + default: + case GST_AUDIO_RESAMPLER_FILTER_MODE_AUTO: + if (out_rate <= oversample) { + /* don't interpolate if we need to calculate at least the same amount + * of filter coefficients than the full table case */ + interpolate = FALSE; + } else { + interpolate = TRUE; + } + break; + } - resampler->tmpcoeff = - g_realloc_n (resampler->tmpcoeff, n_taps, sizeof (gdouble)); + if (interpolate) { + gint otaps = oversample * n_taps + 1; + GstAudioResamplerFilterInterpolation filter_interpolation = + GET_OPT_FILTER_INTERPOLATION (resampler->options); + + /* if we're asked to intepolate but no interpolation was given, */ + if (filter_interpolation == GST_AUDIO_RESAMPLER_FILTER_INTERPOLATION_NONE) + resampler->filter_interpolation = DEFAULT_OPT_FILTER_INTERPOLATION; + else + resampler->filter_interpolation = filter_interpolation; + + if (alloc_coeff_mem (resampler, bps, otaps, 1)) { + gpointer coeff = (gint8 *) resampler->coeff; + gdouble *tmpcoeff = resampler->tmpcoeff; + gdouble x, weight; + + x = 1.0 - n_taps / 2; + weight = fill_taps (resampler, tmpcoeff, x, otaps, oversample); + + switch (resampler->format) { + case GST_AUDIO_FORMAT_S16: + convert_taps_gint16 (tmpcoeff, coeff, weight / oversample, otaps); + break; + case GST_AUDIO_FORMAT_S32: + convert_taps_gint32 (tmpcoeff, coeff, weight / oversample, otaps); + break; + case GST_AUDIO_FORMAT_F32: + 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 { + resampler->filter_interpolation = + GST_AUDIO_RESAMPLER_FILTER_INTERPOLATION_NONE; + resampler->taps = g_realloc_n (resampler->taps, out_rate, sizeof (Tap)); + memset (resampler->taps, 0, sizeof (Tap) * out_rate); + alloc_coeff_mem (resampler, bps, n_taps, out_rate); + } resampler->samp_inc = in_rate / out_rate; resampler->samp_frac = in_rate % out_rate; - for (j = 0; j < out_rate; j++) { - Tap *t = &resampler->taps[j]; - t->taps = NULL; - } - non_interleaved = (resampler->flags & GST_AUDIO_RESAMPLER_FLAG_NON_INTERLEAVED); resampler->ostride = non_interleaved ? 1 : resampler->channels; - /* we resample each channel separately */ - resampler->blocks = resampler->channels; - resampler->inc = 1; - switch (resampler->format) { case GST_AUDIO_FORMAT_S16: - resample = resample_gint16_1; - resample_2 = resample_gint16_2; - deinterleave = deinterleave_gint16; + index = 0; break; case GST_AUDIO_FORMAT_S32: - resample = resample_gint32_1; - resample_2 = resample_gint32_2; - deinterleave = deinterleave_gint32; + index = 1; break; case GST_AUDIO_FORMAT_F32: - resample = resample_gfloat_1; - resample_2 = resample_gfloat_2; - deinterleave = deinterleave_gfloat; + index = 2; break; case GST_AUDIO_FORMAT_F64: - resample = resample_gdouble_1; - resample_2 = resample_gdouble_2; - deinterleave = deinterleave_gdouble; + index = 3; break; default: g_assert_not_reached (); break; } + deinterleave = deinterleave_funcs[index]; + + switch (resampler->filter_interpolation) { + default: + case GST_AUDIO_RESAMPLER_FILTER_INTERPOLATION_NONE: + break; + case GST_AUDIO_RESAMPLER_FILTER_INTERPOLATION_LINEAR: + index += 8; + break; + case GST_AUDIO_RESAMPLER_FILTER_INTERPOLATION_CUBIC: + index += 16; + break; + } + resample = resample_funcs[index]; + resample_2 = resample_funcs[index + 4]; + if (!non_interleaved && resampler->channels == 2 && n_taps >= 4 && resample_2) { + /* we resample 2 channels in parallel */ resampler->resample = resample_2; resampler->deinterleave = deinterleave_copy; resampler->blocks = 1; resampler->inc = resampler->channels;; } else { + /* we resample each channel separately */ resampler->resample = resample; resampler->deinterleave = deinterleave; + resampler->blocks = resampler->channels; + resampler->inc = 1; } } -#define PRINT_TAPS(type,print) \ -G_STMT_START { \ - type sum = 0.0, *taps; \ - \ - if ((taps = t->taps) == NULL) \ - taps = make_taps (resampler, t, i); \ - \ - for (j = 0; j < n_taps; j++) { \ - type tap = taps[j]; \ - fprintf (stderr, "\t%" print " ", tap); \ - sum += tap; \ - } \ - fprintf (stderr, "\t: sum %" print "\n", sum);\ +#define PRINT_TAPS(type,print) \ +G_STMT_START { \ + type sum = 0.0, *taps; \ + type icoeff[4]; \ + gint samp_index = 0, samp_phase = i; \ + \ + taps = get_taps_##type##_none (resampler, &samp_index,\ + &samp_phase, icoeff); \ + \ + for (j = 0; j < n_taps; j++) { \ + type tap = taps[j]; \ + fprintf (stderr, "\t%" print " ", tap); \ + sum += tap; \ + } \ + fprintf (stderr, "\t: sum %" print "\n", sum); \ } G_STMT_END static void @@ -778,9 +1045,8 @@ resampler_dump (GstAudioResampler * resampler) for (i = 0; i < out_rate; i++) { gint j; - Tap *t = &resampler->taps[i]; - fprintf (stderr, "%u: %d %d\t ", i, t->sample_inc, t->next_phase); + //fprintf (stderr, "%u: %d %d\t ", i, t->sample_inc, t->next_phase); switch (resampler->format) { case GST_AUDIO_FORMAT_F64: PRINT_TAPS (gdouble, "f"); @@ -861,6 +1127,9 @@ gst_audio_resampler_options_set_quality (GstAudioResamplerMethod method, break; } } + gst_structure_set (options, + GST_AUDIO_RESAMPLER_OPT_FILTER_OVERSAMPLE, G_TYPE_INT, + oversample_qualities[quality], NULL); } /** diff --git a/gst-libs/gst/audio/audio-resampler.h b/gst-libs/gst/audio/audio-resampler.h index 736e711..74a0e6f 100644 --- a/gst-libs/gst/audio/audio-resampler.h +++ b/gst-libs/gst/audio/audio-resampler.h @@ -113,11 +113,42 @@ typedef enum { #define GST_AUDIO_RESAMPLER_OPT_FILTER_MODE_THRESHOLD "GstAudioResampler.filter-mode-threshold" /** + * GstAudioResamplerFilterInterpolation: + * @GST_AUDIO_RESAMPLER_FILTER_INTERPOLATION_NONE: no interpolation + * @GST_AUDIO_RESAMPLER_FILTER_INTERPOLATION_LINEAR: linear interpolation of the + * filter coeficients. + * @GST_AUDIO_RESAMPLER_FILTER_INTERPOLATION_CUBIC: cubic interpolation of the + * filter coeficients. + * + * The different filter interpolation methods. + */ +typedef enum { + GST_AUDIO_RESAMPLER_FILTER_INTERPOLATION_NONE = (0), + GST_AUDIO_RESAMPLER_FILTER_INTERPOLATION_LINEAR, + GST_AUDIO_RESAMPLER_FILTER_INTERPOLATION_CUBIC, +} GstAudioResamplerFilterInterpolation; +/** + * GST_AUDIO_RESAMPLER_OPT_FILTER_INTERPOLATION: + * + * GST_TYPE_AUDIO_RESAMPLER_INTERPOLATION: how the filter coeficients should be + * interpolated. + * GST_AUDIO_RESAMPLER_FILTER_INTERPOLATION_LINEAR is default. + */ +#define GST_AUDIO_RESAMPLER_OPT_FILTER_INTERPOLATION "GstAudioResampler.filter-interpolation" +/** + * GST_AUDIO_RESAMPLER_OPT_FILTER_OVERSAMPLE + * + * G_TYPE_UINT, oversampling to use when interpolating filters + * 8 is the default. + */ +#define GST_AUDIO_RESAMPLER_OPT_FILTER_OVERSAMPLE "GstAudioResampler.filter-oversample" + +/** * GST_AUDIO_RESAMPLER_OPT_MAX_PHASE_ERROR: * * G_TYPE_DOUBLE: The maximum allowed phase error when switching sample * rates. - * 0.02 is the default. + * 0.05 is the default. */ #define GST_AUDIO_RESAMPLER_OPT_MAX_PHASE_ERROR "GstAudioResampler.max-phase-error" -- 2.7.4