From 524ea147cc86289304b85bb33a8dcba43c78b818 Mon Sep 17 00:00:00 2001 From: Wim Taymans Date: Mon, 15 Feb 2016 18:06:19 +0100 Subject: [PATCH] audio-resampler: improve filter construction Remove some unused variables from the inner product functions. Make filter coefficients by interpolating if required. Rename some fields. Try hard to not recalculate filters when just chaging the rate. Add more proprties to audioresample. --- gst-libs/gst/audio/audio-converter.c | 7 +- gst-libs/gst/audio/audio-resampler-x86.h | 75 ++- gst-libs/gst/audio/audio-resampler.c | 702 ++++++++++++++--------- gst/audioresample/gstaudioresample.c | 42 +- gst/audioresample/gstaudioresample.h | 1 + 5 files changed, 533 insertions(+), 294 deletions(-) diff --git a/gst-libs/gst/audio/audio-converter.c b/gst-libs/gst/audio/audio-converter.c index e2f6c82a54..e6d9961750 100644 --- a/gst-libs/gst/audio/audio-converter.c +++ b/gst-libs/gst/audio/audio-converter.c @@ -337,15 +337,14 @@ gst_audio_converter_update_config (GstAudioConverter * convert, convert->in.rate = in_rate; convert->out.rate = out_rate; + if (convert->resampler) + gst_audio_resampler_update (convert->resampler, in_rate, out_rate, config); + if (config) { gst_structure_foreach (config, copy_config, convert); gst_structure_free (config); } - if (convert->resampler) - gst_audio_resampler_update (convert->resampler, in_rate, out_rate, - convert->config); - return TRUE; } diff --git a/gst-libs/gst/audio/audio-resampler-x86.h b/gst-libs/gst/audio/audio-resampler-x86.h index 11e44f1006..b5033ef6c4 100644 --- a/gst-libs/gst/audio/audio-resampler-x86.h +++ b/gst-libs/gst/audio/audio-resampler-x86.h @@ -22,7 +22,7 @@ static inline void inner_product_gfloat_none_1_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 i = 0; __m128 sum = _mm_setzero_ps (); @@ -42,12 +42,13 @@ inner_product_gfloat_none_1_sse (gfloat * o, const gfloat * a, static inline void inner_product_gfloat_linear_1_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 i = 0; - __m128 sum = _mm_setzero_ps (), t; + __m128 sum, t; __m128 f = _mm_loadu_ps(icoeff); + sum = _mm_setzero_ps (); for (; i < len; i += 4) { t = _mm_loadu_ps (a + i); sum = _mm_add_ps (sum, _mm_mul_ps (_mm_unpacklo_ps (t, t), @@ -63,7 +64,7 @@ inner_product_gfloat_linear_1_sse (gfloat * o, const gfloat * a, static inline void inner_product_gfloat_cubic_1_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 i = 0; __m128 sum = _mm_setzero_ps (); @@ -83,7 +84,7 @@ inner_product_gfloat_cubic_1_sse (gfloat * o, const gfloat * a, static inline void 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 i = 0; __m128 sum = _mm_setzero_ps (), t; @@ -121,7 +122,7 @@ MAKE_RESAMPLE_FUNC (gfloat, none, 2, sse); static inline void inner_product_gint16_none_1_sse2 (gint16 * o, const gint16 * a, - const gint16 * b, gint len, const gint16 * icoeff, gint oversample) + const gint16 * b, gint len, const gint16 * icoeff) { gint i = 0; __m128i sum, ta, tb; @@ -149,7 +150,7 @@ inner_product_gint16_none_1_sse2 (gint16 * o, const gint16 * a, static inline void inner_product_gint16_linear_1_sse2 (gint16 * o, const gint16 * a, - const gint16 * b, gint len, const gint16 * icoeff, gint oversample) + const gint16 * b, gint len, const gint16 * icoeff) { gint i = 0; __m128i sum, t, ta, tb; @@ -193,7 +194,7 @@ inner_product_gint16_linear_1_sse2 (gint16 * o, const gint16 * a, static inline void inner_product_gint16_cubic_1_sse2 (gint16 * o, const gint16 * a, - const gint16 * b, gint len, const gint16 * icoeff, gint oversample) + const gint16 * b, gint len, const gint16 * icoeff) { gint i = 0; __m128i sum, ta, tb; @@ -230,7 +231,7 @@ inner_product_gint16_cubic_1_sse2 (gint16 * o, const gint16 * a, static inline void inner_product_gdouble_none_1_sse2 (gdouble * o, const gdouble * a, - const gdouble * b, gint len, const gdouble * icoeff, gint oversample) + const gdouble * b, gint len, const gdouble * icoeff) { gint i = 0; __m128d sum = _mm_setzero_pd (); @@ -255,7 +256,7 @@ inner_product_gdouble_none_1_sse2 (gdouble * o, const gdouble * a, static inline void inner_product_gdouble_linear_1_sse2 (gdouble * o, const gdouble * a, - const gdouble * b, gint len, const gdouble * icoeff, gint oversample) + const gdouble * b, gint len, const gdouble * icoeff) { gint i = 0; __m128d sum = _mm_setzero_pd (); @@ -274,7 +275,7 @@ inner_product_gdouble_linear_1_sse2 (gdouble * o, const gdouble * a, static inline void inner_product_gdouble_cubic_1_sse2 (gdouble * o, const gdouble * a, - const gdouble * b, gint len, const gdouble * icoeff, gint oversample) + const gdouble * b, gint len, const gdouble * icoeff) { gint i = 0; __m128d sum1 = _mm_setzero_pd (), t; @@ -300,7 +301,7 @@ inner_product_gdouble_cubic_1_sse2 (gdouble * o, const gdouble * a, static inline void inner_product_gint16_none_2_sse2 (gint16 * o, const gint16 * a, - const gint16 * b, gint len, const gint16 * icoeff, gint oversample) + const gint16 * b, gint len, const gint16 * icoeff) { gint i = 0; __m128i sum, ta, tb, t1; @@ -332,7 +333,7 @@ inner_product_gint16_none_2_sse2 (gint16 * o, const gint16 * a, static inline void inner_product_gdouble_none_2_sse2 (gdouble * o, const gdouble * a, - const gdouble * b, gint len, const gdouble * icoeff, gint oversample) + const gdouble * b, gint len, const gdouble * icoeff) { gint i = 0; __m128d sum = _mm_setzero_pd (), t; @@ -368,6 +369,45 @@ MAKE_RESAMPLE_FUNC (gdouble, cubic, 1, sse2); MAKE_RESAMPLE_FUNC (gint16, none, 2, sse2); MAKE_RESAMPLE_FUNC (gdouble, none, 2, sse2); +static void +interpolate_gdouble_linear_sse2 (gdouble * o, const gdouble * a, + gint len, const gdouble * icoeff) +{ + gint i = 0; + __m128d f = _mm_loadu_pd (icoeff), t1, t2; + + for (; i < len; i += 2) { + t1 = _mm_mul_pd (_mm_load_pd (a + 2*i + 0), f); + t1 = _mm_add_sd (t1, _mm_unpackhi_pd (t1, t1)); + t2 = _mm_mul_pd (_mm_load_pd (a + 2*i + 2), f); + t2 = _mm_add_sd (t2, _mm_unpackhi_pd (t2, t2)); + + _mm_store_pd (o + i, _mm_unpacklo_pd (t1, t2)); + } +} + +static void +interpolate_gdouble_cubic_sse2 (gdouble * o, const gdouble * a, + gint len, const gdouble * icoeff) +{ + gint i = 0; + __m128d t1, t2; + __m128d f1 = _mm_loadu_pd (icoeff); + __m128d f2 = _mm_loadu_pd (icoeff+2); + + for (; i < len; i += 2) { + t1 = _mm_add_pd (_mm_mul_pd (_mm_load_pd (a + 4*i + 0), f1), + _mm_mul_pd (_mm_load_pd (a + 4*i + 2), f2)); + t1 = _mm_add_sd (t1, _mm_unpackhi_pd (t1, t1)); + + t2 = _mm_add_pd (_mm_mul_pd (_mm_load_pd (a + 4*i + 4), f1), + _mm_mul_pd (_mm_load_pd (a + 4*i + 6), f2)); + t2 = _mm_add_sd (t2, _mm_unpackhi_pd (t2, t2)); + + _mm_store_pd (o + i, _mm_unpacklo_pd (t1, t2)); + } +} + #endif #if defined (HAVE_SMMINTRIN_H) && defined(__SSE4_1__) @@ -375,7 +415,7 @@ MAKE_RESAMPLE_FUNC (gdouble, none, 2, sse2); static inline void inner_product_gint32_none_1_sse41 (gint32 * o, const gint32 * a, - const gint32 * b, gint len, const gint32 * icoeff, gint oversample) + const gint32 * b, gint len, const gint32 * icoeff) { gint i = 0; __m128i sum, ta, tb; @@ -413,7 +453,7 @@ inner_product_gint32_none_1_sse41 (gint32 * o, const gint32 * a, static inline void inner_product_gint32_linear_1_sse41 (gint32 * o, const gint32 * a, - const gint32 * b, gint len, const gint32 * icoeff, gint oversample) + const gint32 * b, gint len, const gint32 * icoeff) { gint i = 0; gint64 res; @@ -457,7 +497,7 @@ inner_product_gint32_linear_1_sse41 (gint32 * o, const gint32 * a, static inline void inner_product_gint32_cubic_1_sse41 (gint32 * o, const gint32 * a, - const gint32 * b, gint len, const gint32 * icoeff, gint oversample) + const gint32 * b, gint len, const gint32 * icoeff) { gint i = 0; gint64 res; @@ -537,6 +577,9 @@ audio_resampler_check_x86 (const gchar *option) resample_gint16_none_2 = resample_gint16_none_2_sse2; resample_gfloat_none_2 = resample_gfloat_none_2_sse; resample_gdouble_none_2 = resample_gdouble_none_2_sse2; + + interpolate_gdouble_linear = interpolate_gdouble_linear_sse2; + interpolate_gdouble_cubic = interpolate_gdouble_cubic_sse2; #else GST_DEBUG ("SSE2 optimisations not enabled"); #endif diff --git a/gst-libs/gst/audio/audio-resampler.c b/gst-libs/gst/audio/audio-resampler.c index 8fb04f8dfe..51f8dfc4fa 100644 --- a/gst-libs/gst/audio/audio-resampler.c +++ b/gst-libs/gst/audio/audio-resampler.c @@ -31,12 +31,6 @@ #include "audio-resampler.h" -typedef struct _Tap -{ - gpointer taps; -} Tap; - -typedef void (*MakeTapsFunc) (GstAudioResampler * resampler, Tap * t, gint j); typedef void (*ResampleFunc) (GstAudioResampler * resampler, gpointer in[], gsize in_len, gpointer out[], gsize out_len, gsize * consumed); typedef void (*DeinterleaveFunc) (GstAudioResampler * resampler, @@ -55,27 +49,37 @@ struct _GstAudioResampler gint channels; gint in_rate; gint out_rate; + gint bps; gint ostride; + GstAudioResamplerFilterMode filter_mode; + guint filter_threshold; + GstAudioResamplerFilterInterpolation filter_interpolation; + gdouble cutoff; gdouble kaiser_beta; /* for cubic */ gdouble b, c; - GstAudioResamplerFilterMode filter_mode; - guint filter_threshold; - GstAudioResamplerFilterInterpolation filter_interpolation; - gint oversample; + /* temp taps */ + gpointer tmp_taps; + /* oversampled main filter table */ + gint oversample; guint n_taps; - Tap *taps; - gpointer coeff; - gpointer coeffmem; + gpointer taps; + gpointer taps_mem; + gsize taps_stride; + gint n_phases; guint alloc_taps; guint alloc_phases; - gsize cstride; - gpointer tmpcoeff; + + /* cached taps */ + gpointer *cached_phases; + gpointer cached_taps; + gpointer cached_taps_mem; + gsize cached_taps_stride; DeinterleaveFunc deinterleave; ResampleFunc resample; @@ -152,6 +156,7 @@ static const BlackmanQualityMap blackman_qualities[] = { {160, 0.960,} }; +#define DEFAULT_RESAMPLER_METHOD GST_AUDIO_RESAMPLER_METHOD_KAISER #define DEFAULT_QUALITY GST_AUDIO_RESAMPLER_QUALITY_DEFAULT #define DEFAULT_OPT_CUBIC_B 1.0 #define DEFAULT_OPT_CUBIC_C 0.0 @@ -220,47 +225,45 @@ get_opt_enum (GstStructure * options, const gchar * name, GType type, gint def) #define bessel dbesi0 static inline gdouble -get_nearest_tap (gdouble x) +get_nearest_tap (gdouble x, gint n_taps) { - gdouble a = fabs (x); + gdouble a = fabs (x), res;; if (a < 0.5) - return 1.0; + res = 1.0; else - return 0.0; + res = 0.0; + + return res; } static inline gdouble get_linear_tap (gdouble x, gint n_taps) { - gdouble a; - - a = fabs (x) / n_taps; - - if (a < 1.0) - return 1.0 - a; - else - return 0.0; + gdouble res = GST_ROUND_UP_2 (n_taps) / 2 - fabs (x); + return res; } static inline gdouble get_cubic_tap (gdouble x, gint n_taps, gdouble b, gdouble c) { - gdouble a, a2, a3; + gdouble res, a, a2, a3; a = fabs (x * 4.0) / n_taps; a2 = a * a; a3 = a2 * a; if (a <= 1.0) - return ((12.0 - 9.0 * b - 6.0 * c) * a3 + + res = ((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; else if (a <= 2.0) - return ((-b - 6.0 * c) * a3 + + res = ((-b - 6.0 * c) * a3 + (6.0 * b + 30.0 * c) * a2 + (-12.0 * b - 48.0 * c) * a + (8.0 * b + 24.0 * c)) / 6.0; else - return 0.0; + res = 0.0; + + return res; } static inline gdouble @@ -292,8 +295,8 @@ get_kaiser_tap (gdouble x, gint n_taps, gdouble Fc, gdouble beta) #define PRECISION_S32 31 static inline gdouble -fill_taps (GstAudioResampler * resampler, - gdouble * tmpcoeff, gdouble x, gint n_taps, gint oversample) +make_taps (GstAudioResampler * resampler, + gdouble * tmp_taps, gdouble x, gint n_taps, gint oversample) { gdouble weight = 0.0; gint i; @@ -301,32 +304,30 @@ fill_taps (GstAudioResampler * resampler, 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); + weight += tmp_taps[i] = get_nearest_tap (x + i, resampler->n_taps); 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); + weight += tmp_taps[i] = get_linear_tap (x + i, 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, + weight += tmp_taps[i] = get_cubic_tap (x + i, 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] = + weight += tmp_taps[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] = + weight += tmp_taps[i] = get_kaiser_tap (x + i / (double) oversample, resampler->n_taps, resampler->cutoff, resampler->kaiser_beta); break; @@ -339,7 +340,7 @@ fill_taps (GstAudioResampler * resampler, #define MAKE_CONVERT_TAPS_INT_FUNC(type, precision) \ static inline void \ -convert_taps_##type (gdouble *tmpcoeff, type *taps, \ +convert_taps_##type (gdouble *tmp_taps, type *taps, \ gdouble weight, gint n_taps) \ { \ gint64 one = (1L << precision) - 1; \ @@ -355,7 +356,7 @@ convert_taps_##type (gdouble *tmpcoeff, type *taps, \ for (i = 0; i < 32; i++) { \ gint64 sum = 0; \ for (j = 0; j < n_taps; j++) \ - sum += floor (offset + tmpcoeff[j] * multiplier / weight); \ + sum += floor (offset + tmp_taps[j] * multiplier / weight); \ if (sum == one) { \ exact = TRUE; \ break; \ @@ -373,19 +374,19 @@ convert_taps_##type (gdouble *tmpcoeff, type *taps, \ } \ } \ for (j = 0; j < n_taps; j++) \ - taps[j] = floor (offset + tmpcoeff[j] * multiplier / weight); \ + taps[j] = floor (offset + tmp_taps[j] * multiplier / weight); \ if (!exact) \ GST_WARNING ("can't find exact taps"); \ } #define MAKE_CONVERT_TAPS_FLOAT_FUNC(type) \ static inline void \ -convert_taps_##type (gdouble *tmpcoeff, type *taps, \ +convert_taps_##type (gdouble *tmp_taps, type *taps, \ gdouble weight, gint n_taps) \ { \ gint i; \ for (i = 0; i < n_taps; i++) \ - taps[i] = tmpcoeff[i] / weight; \ + taps[i] = tmp_taps[i] / weight; \ } MAKE_CONVERT_TAPS_INT_FUNC (gint16, PRECISION_S16); @@ -395,16 +396,16 @@ MAKE_CONVERT_TAPS_FLOAT_FUNC (gdouble); #define MAKE_EXTRACT_TAPS_FUNC(type) \ static inline void \ -extract_taps_##type (GstAudioResampler * resampler, type *tmpcoeff, \ +extract_taps_##type (GstAudioResampler * resampler, type *tmp_taps, \ gint n_taps, gint oversample, gint mult) \ { \ gint i, j, k; \ for (i = 0; i < oversample; i++) { \ - type *coeff = (type *) ((gint8*)resampler->coeff + \ - i * resampler->cstride); \ + type *taps = (type *) ((gint8*)resampler->taps + \ + i * resampler->taps_stride); \ for (j = 0; j < n_taps; j++) { \ for (k = 0; k < mult; k++) { \ - *coeff++ = tmpcoeff[i + j*oversample + k]; \ + *taps++ = tmp_taps[i + j*oversample + k]; \ } \ } \ } \ @@ -414,42 +415,38 @@ MAKE_EXTRACT_TAPS_FUNC (gint32); MAKE_EXTRACT_TAPS_FUNC (gfloat); MAKE_EXTRACT_TAPS_FUNC (gdouble); -#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; \ - gint out_rate = resampler->out_rate; \ - \ - if (G_LIKELY (t->taps)) { \ - res = t->taps; \ - } else { \ - gdouble x, weight; \ - gdouble *tmpcoeff = resampler->tmpcoeff; \ - gint n_taps = resampler->n_taps; \ - \ - 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; \ +typedef void (*InterpolateFunc) (gdouble * o, const gdouble * a, gint len, + const gdouble * icoeff); + +static void +interpolate_gdouble_linear_c (gdouble * o, const gdouble * a, gint len, + const gdouble * ic) +{ + gint i; + + for (i = 0; i < len; i++) + o[i] = (a[2 * i + 0] - a[2 * i + 1]) * ic[0] + a[2 * i + 1]; } -GET_TAPS_NONE_FUNC (gint16); -GET_TAPS_NONE_FUNC (gint32); -GET_TAPS_NONE_FUNC (gfloat); -GET_TAPS_NONE_FUNC (gdouble); + +static void +interpolate_gdouble_cubic_c (gdouble * o, const gdouble * a, gint len, + const gdouble * ic) +{ + gint i; + + for (i = 0; i < len; i++) + o[i] = + a[4 * i + 0] * ic[0] + a[4 * i + 1] * ic[1] + a[4 * i + 2] * ic[2] + + a[4 * i + 3] * ic[3]; +} + +static InterpolateFunc interpolate_funcs[] = { + interpolate_gdouble_linear_c, + interpolate_gdouble_cubic_c +}; + +#define interpolate_gdouble_linear interpolate_funcs[0] +#define interpolate_gdouble_cubic interpolate_funcs[1] #define MAKE_COEFF_LINEAR_INT_FUNC(type,prec) \ static inline void \ @@ -457,7 +454,7 @@ make_coeff_##type##_linear (gint frac, gint out_rate, type *icoeff) \ { \ type x = ((gint64)frac << prec) / out_rate; \ icoeff[0] = icoeff[2] = x; \ - icoeff[1] = icoeff[3] = (1L << prec) - 1 - x; \ + icoeff[1] = icoeff[3] = (1L << prec) -1 - x; \ } #define MAKE_COEFF_LINEAR_FLOAT_FUNC(type) \ static inline void \ @@ -501,6 +498,84 @@ MAKE_COEFF_CUBIC_INT_FUNC (gint32, gint64, PRECISION_S32); MAKE_COEFF_CUBIC_FLOAT_FUNC (gfloat); MAKE_COEFF_CUBIC_FLOAT_FUNC (gdouble); +static inline gdouble +fill_taps (GstAudioResampler * resampler, + gdouble * tmp_taps, gint phase, gint n_phases, gint n_taps) +{ + gdouble res; + + if (resampler->filter_interpolation == + GST_AUDIO_RESAMPLER_FILTER_INTERPOLATION_NONE) { + gdouble x = 1.0 - n_taps / 2 - (gdouble) phase / n_phases; + res = make_taps (resampler, tmp_taps, x, n_taps, 1); + } else { + gint out_rate = resampler->out_rate; + gint offset, pos, frac; + gint oversample = resampler->oversample; + gint taps_stride = resampler->taps_stride; + gdouble ic[4], *taps; + + pos = phase * oversample; + offset = (oversample - 1) - (pos / out_rate); + frac = pos % out_rate; + + taps = (gdouble *) ((gint8 *) resampler->taps + offset * taps_stride); + + switch (resampler->filter_interpolation) { + case GST_AUDIO_RESAMPLER_FILTER_INTERPOLATION_LINEAR: + make_coeff_gdouble_linear (frac, out_rate, ic); + interpolate_gdouble_linear (tmp_taps, taps, n_taps, ic); + break; + case GST_AUDIO_RESAMPLER_FILTER_INTERPOLATION_CUBIC: + make_coeff_gdouble_cubic (frac, out_rate, ic); + interpolate_gdouble_cubic (tmp_taps, taps, n_taps, ic); + break; + default: + break; + } + res = 1.0; + } + return res; +} + +#define GET_TAPS_NONE_FUNC(type) \ +static inline gpointer \ +get_taps_##type##_none (GstAudioResampler * resampler, \ + gint *samp_index, gint *samp_phase, type icoeff[4]) \ +{ \ + gpointer res; \ + gint out_rate = resampler->out_rate; \ + gint n_phases = resampler->n_phases; \ + gint phase = (n_phases == out_rate ? *samp_phase : \ + ((gint64)*samp_phase * n_phases) / out_rate); \ + \ + res = resampler->cached_phases[phase]; \ + if (G_UNLIKELY (res == NULL)) { \ + gdouble weight; \ + gdouble *tmp_taps = resampler->tmp_taps; \ + gint n_taps = resampler->n_taps; \ + \ + res = (gint8 *) resampler->cached_taps + \ + phase * resampler->cached_taps_stride; \ + \ + weight = fill_taps (resampler, tmp_taps, phase, n_phases, n_taps); \ + convert_taps_##type (tmp_taps, res, weight, n_taps); \ + \ + resampler->cached_phases[phase] = res; \ + } \ + *samp_index += resampler->samp_inc; \ + *samp_phase += resampler->samp_frac; \ + if (*samp_phase >= out_rate) { \ + *samp_phase -= out_rate; \ + *samp_index += 1; \ + } \ + return res; \ +} +GET_TAPS_NONE_FUNC (gint16); +GET_TAPS_NONE_FUNC (gint32); +GET_TAPS_NONE_FUNC (gfloat); +GET_TAPS_NONE_FUNC (gdouble); + #define GET_TAPS_INTERPOLATE_FUNC(type,inter) \ static inline gpointer \ get_taps_##type##_##inter (GstAudioResampler * resampler, \ @@ -510,20 +585,20 @@ get_taps_##type##_##inter (GstAudioResampler * resampler, \ gint out_rate = resampler->out_rate; \ gint offset, frac, pos; \ gint oversample = resampler->oversample; \ - gint cstride = resampler->cstride; \ + gint taps_stride = resampler->taps_stride; \ \ pos = *samp_phase * oversample; \ offset = (oversample - 1) - (pos / out_rate); \ frac = pos % out_rate; \ \ - res = (gint8 *) resampler->coeff + offset * cstride; \ + res = (gint8 *) resampler->taps + offset * taps_stride; \ make_coeff_##type##_##inter (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)++; \ + *samp_index += 1; \ } \ return res; \ } @@ -541,7 +616,7 @@ GET_TAPS_INTERPOLATE_FUNC (gdouble, cubic); #define INNER_PRODUCT_INT_NONE_FUNC(type,type2,prec,limit) \ static inline void \ inner_product_##type##_none_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 i; \ type2 res = 0; \ @@ -559,7 +634,7 @@ INNER_PRODUCT_INT_NONE_FUNC (gint32, gint64, PRECISION_S32, 1L << 31); #define INNER_PRODUCT_INT_LINEAR_FUNC(type,type2,prec,limit) \ static inline void \ 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 i; \ type2 res[2] = { 0, 0 }; \ @@ -577,10 +652,10 @@ inner_product_##type##_linear_1_c (type * o, const type * a, \ INNER_PRODUCT_INT_LINEAR_FUNC (gint16, gint32, PRECISION_S16, 1L << 15); INNER_PRODUCT_INT_LINEAR_FUNC (gint32, gint64, PRECISION_S32, 1L << 31); -#define INNER_PRODUCT_INT_CUBIC_FUNC(type,type2,prec,limit) \ +#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) \ +inner_product_##type##_cubic_1_c (type * o, const type * a, \ + const type * b, gint len, const type *ic) \ { \ gint i; \ type2 res[4] = { 0, 0, 0, 0 }; \ @@ -605,7 +680,7 @@ INNER_PRODUCT_INT_CUBIC_FUNC (gint32, gint64, PRECISION_S32, 1L << 31); #define INNER_PRODUCT_FLOAT_NONE_FUNC(type) \ static inline void \ inner_product_##type##_none_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 i; \ type res = 0.0; \ @@ -622,7 +697,7 @@ INNER_PRODUCT_FLOAT_NONE_FUNC (gdouble); #define INNER_PRODUCT_FLOAT_LINEAR_FUNC(type) \ static inline void \ 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 i; \ type res[2] = { 0.0, 0.0 }; \ @@ -639,7 +714,7 @@ 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) \ + const type * b, gint len, const type *ic) \ { \ gint i; \ type res[4] = { 0.0, 0.0, 0.0, 0.0 }; \ @@ -666,7 +741,6 @@ resample_ ##type## _ ##inter## _ ##channels## _ ##arch (GstAudioResampler * resa 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; \ \ @@ -685,11 +759,12 @@ resample_ ##type## _ ##inter## _ ##channels## _ ##arch (GstAudioResampler * resa taps = get_taps_ ##type##_##inter \ (resampler, &samp_index, &samp_phase, icoeff); \ inner_product_ ##type##_##inter##_##channels##_##arch \ - (op, ipp, taps, n_taps, icoeff, oversample); \ + (op, ipp, taps, n_taps, icoeff); \ op += ostride; \ } \ - memmove (ip, &ip[samp_index * channels], \ - (in_len - samp_index) * sizeof(type) * channels); \ + if (in_len > samp_index) \ + memmove (ip, &ip[samp_index * channels], \ + (in_len - samp_index) * sizeof(type) * channels); \ } \ *consumed = samp_index - resampler->samp_index; \ \ @@ -898,38 +973,142 @@ calculate_kaiser_params (GstAudioResampler * resampler) } static void -alloc_coeff_mem (GstAudioResampler * resampler, gint bps, gint n_taps, +alloc_taps_mem (GstAudioResampler * resampler, gint bps, gint n_taps, gint n_phases, gint n_mult) { if (resampler->alloc_taps >= n_taps && resampler->alloc_phases >= n_phases) return; - resampler->tmpcoeff = - g_realloc_n (resampler->tmpcoeff, n_taps, sizeof (gdouble)); + GST_DEBUG ("allocate n_taps %d n_phases %d n_mult %d", n_taps, n_phases, + n_mult); - resampler->cstride = + resampler->tmp_taps = + g_realloc_n (resampler->tmp_taps, n_taps, sizeof (gdouble)); + + resampler->taps_stride = GST_ROUND_UP_32 (bps * (n_mult * 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); + + g_free (resampler->taps_mem); + resampler->taps_mem = + g_malloc0 (n_phases * resampler->taps_stride + ALIGN - 1); + resampler->taps = MEM_ALIGN ((gint8 *) resampler->taps_mem, ALIGN); resampler->alloc_taps = n_taps; resampler->alloc_phases = n_phases; } +static void +alloc_cache_mem (GstAudioResampler * resampler, gint bps, gint n_taps, + gint n_phases) +{ + gsize phases_size; + + resampler->tmp_taps = + g_realloc_n (resampler->tmp_taps, n_taps, sizeof (gdouble)); + + resampler->cached_taps_stride = + GST_ROUND_UP_32 (bps * (n_taps + TAPS_OVERREAD)); + + phases_size = sizeof (gpointer) * n_phases; + + g_free (resampler->cached_taps_mem); + resampler->cached_taps_mem = + g_malloc0 (phases_size + n_phases * resampler->cached_taps_stride + + ALIGN - 1); + resampler->cached_taps = + MEM_ALIGN ((gint8 *) resampler->cached_taps_mem + phases_size, ALIGN); + resampler->cached_phases = resampler->cached_taps_mem; +} + +static void +setup_functions (GstAudioResampler * resampler) +{ + gboolean non_interleaved; + gint n_taps, index; + DeinterleaveFunc deinterleave; + ResampleFunc resample, resample_2; + + n_taps = resampler->n_taps; + non_interleaved = + (resampler->flags & GST_AUDIO_RESAMPLER_FLAG_NON_INTERLEAVED); + + resampler->ostride = non_interleaved ? 1 : resampler->channels; + + switch (resampler->format) { + case GST_AUDIO_FORMAT_S16: + GST_DEBUG ("using S16 functions"); + index = 0; + break; + case GST_AUDIO_FORMAT_S32: + GST_DEBUG ("using S32 functions"); + index = 1; + break; + case GST_AUDIO_FORMAT_F32: + GST_DEBUG ("using F32 functions"); + index = 2; + break; + case GST_AUDIO_FORMAT_F64: + GST_DEBUG ("using F64 functions"); + index = 3; + break; + default: + g_assert_not_reached (); + break; + } + deinterleave = deinterleave_funcs[index]; + + switch (resampler->filter_mode) { + default: + case GST_AUDIO_RESAMPLER_FILTER_MODE_FULL: + GST_DEBUG ("using full filter function"); + break; + case GST_AUDIO_RESAMPLER_FILTER_MODE_INTERPOLATED: + switch (resampler->filter_interpolation) { + case GST_AUDIO_RESAMPLER_FILTER_INTERPOLATION_LINEAR: + GST_DEBUG ("using linear interpolation filter function"); + index += 8; + break; + case GST_AUDIO_RESAMPLER_FILTER_INTERPOLATION_CUBIC: + GST_DEBUG ("using cubic interpolation filter function"); + index += 16; + break; + default: + break; + } + 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;; + GST_DEBUG ("resample 2 channels at a time"); + } else { + /* we resample each channel separately */ + resampler->resample = resample; + resampler->deinterleave = deinterleave; + resampler->blocks = resampler->channels; + resampler->inc = 1; + GST_DEBUG ("resample 1 channel at a time"); + } +} + static void resampler_calculate_taps (GstAudioResampler * resampler) { gint bps; - gint n_taps; - gint out_rate; - gint in_rate, index, oversample; - gboolean non_interleaved, interpolate; - DeinterleaveFunc deinterleave; - ResampleFunc resample, resample_2; + gint n_taps, oversample; + gint in_rate, out_rate; + gboolean scale = TRUE, sinc_table = FALSE; + GstAudioResamplerFilterInterpolation filter_interpolation; switch (resampler->method) { case GST_AUDIO_RESAMPLER_METHOD_NEAREST: resampler->n_taps = 2; + scale = FALSE; break; case GST_AUDIO_RESAMPLER_METHOD_LINEAR: resampler->n_taps = GET_OPT_N_TAPS (resampler->options, 2); @@ -944,25 +1123,45 @@ resampler_calculate_taps (GstAudioResampler * resampler) const BlackmanQualityMap *q = &blackman_qualities[DEFAULT_QUALITY]; resampler->n_taps = GET_OPT_N_TAPS (resampler->options, q->n_taps); resampler->cutoff = GET_OPT_CUTOFF (resampler->options, q->cutoff); + sinc_table = TRUE; break; } case GST_AUDIO_RESAMPLER_METHOD_KAISER: calculate_kaiser_params (resampler); + sinc_table = TRUE; break; } 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; - + if (out_rate < in_rate && scale) { resampler->cutoff = resampler->cutoff * out_rate / in_rate; resampler->n_taps = gst_util_uint64_scale_int (resampler->n_taps, in_rate, out_rate); + } + if (sinc_table) { + resampler->n_taps = GST_ROUND_UP_8 (resampler->n_taps); + resampler->filter_mode = GET_OPT_FILTER_MODE (resampler->options); + resampler->filter_threshold = + GET_OPT_FILTER_MODE_THRESHOLD (resampler->options); + filter_interpolation = GET_OPT_FILTER_INTERPOLATION (resampler->options); + + /* interpolated table but no interpolation given, assume default */ + if (resampler->filter_mode != GST_AUDIO_RESAMPLER_FILTER_MODE_FULL && + filter_interpolation == GST_AUDIO_RESAMPLER_FILTER_INTERPOLATION_NONE) + filter_interpolation = DEFAULT_OPT_FILTER_INTERPOLATION; + } else { + resampler->filter_mode = GST_AUDIO_RESAMPLER_FILTER_MODE_FULL; + filter_interpolation = GST_AUDIO_RESAMPLER_FILTER_INTERPOLATION_NONE; + } + + /* calculate oversampling for interpolated filter */ + if (filter_interpolation != GST_AUDIO_RESAMPLER_FILTER_INTERPOLATION_NONE) { + gint mult = 2; + + oversample = GET_OPT_FILTER_OVERSAMPLE (resampler->options); while (oversample > 1) { if (mult * out_rate >= in_rate) break; @@ -970,154 +1169,96 @@ resampler_calculate_taps (GstAudioResampler * resampler) mult *= 2; oversample >>= 1; } + + switch (filter_interpolation) { + case GST_AUDIO_RESAMPLER_FILTER_INTERPOLATION_LINEAR: + oversample <<= 5; + break; + default: + break; + } + } else { + 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) - resampler->n_taps = GST_ROUND_UP_8 (resampler->n_taps); + resampler->filter_interpolation = filter_interpolation; n_taps = resampler->n_taps; bps = resampler->bps; - GST_LOG ("using n_taps %d cutoff %f, oversample %d", n_taps, - resampler->cutoff, oversample); + GST_LOG ("using n_taps %d cutoff %f oversample %d", n_taps, resampler->cutoff, + oversample); - resampler->filter_mode = GET_OPT_FILTER_MODE (resampler->options); - resampler->filter_threshold = - GET_OPT_FILTER_MODE_THRESHOLD (resampler->options); - - 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; + if (resampler->filter_mode == 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 */ + resampler->filter_mode = GST_AUDIO_RESAMPLER_FILTER_MODE_FULL; + GST_DEBUG ("automatically selected full filter, %d <= %d", out_rate, + oversample); + } else { + GST_DEBUG ("automatically selected interpolated filter"); + resampler->filter_mode = GST_AUDIO_RESAMPLER_FILTER_MODE_INTERPOLATED; + } } - if (interpolate) { - gint otaps, mult; - gpointer coeff; - gdouble x, weight, *tmpcoeff; - GstAudioResamplerFilterInterpolation filter_interpolation = - GET_OPT_FILTER_INTERPOLATION (resampler->options); + if (resampler->filter_mode == GST_AUDIO_RESAMPLER_FILTER_MODE_FULL) { + GST_DEBUG ("setting up filter cache"); + resampler->n_phases = out_rate; + alloc_cache_mem (resampler, bps, n_taps, out_rate); + } - /* 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 (resampler->filter_interpolation != + GST_AUDIO_RESAMPLER_FILTER_INTERPOLATION_NONE) { + gint otaps, isize; + gdouble x, weight, *tmp_taps; + GstAudioFormat format; + gpointer taps; switch (resampler->filter_interpolation) { default: case GST_AUDIO_RESAMPLER_FILTER_INTERPOLATION_LINEAR: - mult = 2; + isize = 2; break; case GST_AUDIO_RESAMPLER_FILTER_INTERPOLATION_CUBIC: - mult = 4; + isize = 4; break; } - otaps = oversample * n_taps + mult - 1; + otaps = oversample * n_taps + isize - 1; - alloc_coeff_mem (resampler, bps, otaps, oversample, mult); + if (resampler->filter_mode == GST_AUDIO_RESAMPLER_FILTER_MODE_FULL) { + format = GST_AUDIO_FORMAT_F64; + bps = sizeof (gdouble); + } else + format = resampler->format; - coeff = tmpcoeff = resampler->tmpcoeff; + alloc_taps_mem (resampler, bps, otaps, oversample, isize); + + taps = tmp_taps = resampler->tmp_taps; x = 1.0 - n_taps / 2; - weight = fill_taps (resampler, tmpcoeff, x, otaps, oversample); + weight = make_taps (resampler, tmp_taps, x, otaps, oversample); - switch (resampler->format) { + switch (format) { case GST_AUDIO_FORMAT_S16: - convert_taps_gint16 (tmpcoeff, coeff, weight / oversample, otaps); - extract_taps_gint16 (resampler, coeff, n_taps, oversample, mult); + convert_taps_gint16 (tmp_taps, taps, weight / oversample, otaps); + extract_taps_gint16 (resampler, taps, n_taps, oversample, isize); break; case GST_AUDIO_FORMAT_S32: - convert_taps_gint32 (tmpcoeff, coeff, weight / oversample, otaps); - extract_taps_gint32 (resampler, coeff, n_taps, oversample, mult); + convert_taps_gint32 (tmp_taps, taps, weight / oversample, otaps); + extract_taps_gint32 (resampler, taps, n_taps, oversample, isize); break; case GST_AUDIO_FORMAT_F32: - convert_taps_gfloat (tmpcoeff, coeff, weight / oversample, otaps); - extract_taps_gfloat (resampler, coeff, n_taps, oversample, mult); + convert_taps_gfloat (tmp_taps, taps, weight / oversample, otaps); + extract_taps_gfloat (resampler, taps, n_taps, oversample, isize); break; default: case GST_AUDIO_FORMAT_F64: - convert_taps_gdouble (tmpcoeff, coeff, weight / oversample, otaps); - extract_taps_gdouble (resampler, coeff, n_taps, oversample, mult); + convert_taps_gdouble (tmp_taps, taps, weight / oversample, otaps); + extract_taps_gdouble (resampler, taps, n_taps, oversample, isize); 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, 1); - } - - resampler->samp_inc = in_rate / out_rate; - resampler->samp_frac = in_rate % out_rate; - - non_interleaved = - (resampler->flags & GST_AUDIO_RESAMPLER_FLAG_NON_INTERLEAVED); - - resampler->ostride = non_interleaved ? 1 : resampler->channels; - - switch (resampler->format) { - case GST_AUDIO_FORMAT_S16: - index = 0; - break; - case GST_AUDIO_FORMAT_S32: - index = 1; - break; - case GST_AUDIO_FORMAT_F32: - index = 2; - break; - case GST_AUDIO_FORMAT_F64: - 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; } + setup_functions (resampler); } #define PRINT_TAPS(type,print) \ @@ -1261,6 +1402,7 @@ gst_audio_resampler_new (GstAudioResamplerMethod method, { GstAudioResampler *resampler; const GstAudioFormatInfo *info; + GstStructure *def_options = NULL; g_return_val_if_fail (channels > 0, FALSE); g_return_val_if_fail (in_rate > 0, FALSE); @@ -1281,12 +1423,22 @@ gst_audio_resampler_new (GstAudioResamplerMethod method, GST_DEBUG ("method %d, bps %d, channels %d", method, resampler->bps, resampler->channels); + if (options == NULL) { + options = def_options = + gst_structure_new_empty ("GstAudioResampler.options"); + gst_audio_resampler_options_set_quality (DEFAULT_RESAMPLER_METHOD, + GST_AUDIO_RESAMPLER_QUALITY_DEFAULT, in_rate, out_rate, options); + } + gst_audio_resampler_update (resampler, in_rate, out_rate, options); /* half of the filter is filled with 0 */ resampler->samp_index = 0; resampler->samples_avail = resampler->n_taps / 2 - 1; + if (def_options) + gst_structure_free (def_options); + return resampler; } @@ -1380,11 +1532,12 @@ gst_audio_resampler_update (GstAudioResampler * resampler, if (out_rate <= 0) out_rate = resampler->out_rate; - if (resampler->out_rate > 0) + if (resampler->out_rate > 0) { + GST_INFO ("old phase %d/%d", resampler->samp_phase, resampler->out_rate); samp_phase = gst_util_uint64_scale_int (resampler->samp_phase, out_rate, resampler->out_rate); - else + } else samp_phase = 0; gcd = gst_util_greatest_common_divisor (in_rate, out_rate); @@ -1392,6 +1545,7 @@ gst_audio_resampler_update (GstAudioResampler * resampler, max_error = GET_OPT_MAX_PHASE_ERROR (resampler->options); if (max_error < 1.0e-8) { + GST_INFO ("using exact phase divider"); gcd = gst_util_greatest_common_divisor (gcd, samp_phase); } else { while (gcd > 1) { @@ -1412,55 +1566,61 @@ gst_audio_resampler_update (GstAudioResampler * resampler, } } - GST_INFO ("phase %d, out_rate %d, in_rate %d, gcd %d", samp_phase, out_rate, + GST_INFO ("phase %d out_rate %d, in_rate %d, gcd %d", samp_phase, out_rate, in_rate, gcd); - resampler->samp_phase = samp_phase / gcd; - resampler->in_rate = in_rate / gcd; - resampler->out_rate = out_rate / gcd; + resampler->samp_phase = samp_phase /= gcd; + resampler->in_rate = in_rate /= gcd; + resampler->out_rate = out_rate /= gcd; + + GST_INFO ("new phase %d/%d", resampler->samp_phase, resampler->out_rate); + + resampler->samp_inc = in_rate / out_rate; + resampler->samp_frac = in_rate % out_rate; if (options) { + GST_INFO ("have new options, reconfigure filter"); + if (resampler->options) gst_structure_free (resampler->options); resampler->options = gst_structure_copy (options); - } - old_n_taps = resampler->n_taps; + old_n_taps = resampler->n_taps; - resampler_calculate_taps (resampler); - resampler_dump (resampler); + resampler_calculate_taps (resampler); + resampler_dump (resampler); - GST_DEBUG ("rate %u->%u, taps %d->%d", resampler->in_rate, - resampler->out_rate, old_n_taps, resampler->n_taps); + if (old_n_taps > 0 && old_n_taps != resampler->n_taps) { + gpointer *sbuf; + gint i, bpf, bytes, soff, doff, diff; - if (old_n_taps > 0) { - gpointer *sbuf; - gint i, bpf, bytes, soff, doff, diff; + sbuf = get_sample_bufs (resampler, resampler->n_taps); - sbuf = get_sample_bufs (resampler, resampler->n_taps); + bpf = resampler->bps * resampler->inc; + bytes = resampler->samples_avail * bpf; + soff = doff = resampler->samp_index * bpf; - bpf = resampler->bps * resampler->inc; - bytes = resampler->samples_avail * bpf; - soff = doff = resampler->samp_index * bpf; + diff = ((gint) resampler->n_taps - old_n_taps) / 2; - diff = ((gint) resampler->n_taps - old_n_taps) / 2; + GST_DEBUG ("taps %d->%d, %d", old_n_taps, resampler->n_taps, diff); - if (diff < 0) { - /* diff < 0, decrease taps, adjust source */ - soff += -diff * bpf; - bytes -= -diff * bpf; - } else { - /* diff > 0, increase taps, adjust dest */ - doff += diff * bpf; + if (diff < 0) { + /* diff < 0, decrease taps, adjust source */ + soff += -diff * bpf; + bytes -= -diff * bpf; + } else { + /* diff > 0, increase taps, adjust dest */ + doff += diff * bpf; + } + + /* now shrink or enlarge the history buffer, when we enlarge we + * just leave the old samples in there. FIXME, probably do something better + * like mirror or fill with zeroes. */ + for (i = 0; i < resampler->blocks; i++) + memmove ((gint8 *) sbuf[i] + doff, (gint8 *) sbuf[i] + soff, bytes); + + resampler->samples_avail += diff; } - - /* now shrink or enlarge the history buffer, when we enlarge we - * just leave the old samples in there. FIXME, probably do something better - * like mirror or fill with zeroes. */ - for (i = 0; i < resampler->blocks; i++) - memmove ((gint8 *) sbuf[i] + doff, (gint8 *) sbuf[i] + soff, bytes); - - resampler->samples_avail += diff; } return TRUE; } @@ -1478,9 +1638,9 @@ gst_audio_resampler_free (GstAudioResampler * resampler) { g_return_if_fail (resampler != NULL); - g_free (resampler->taps); - g_free (resampler->coeffmem); - g_free (resampler->tmpcoeff); + g_free (resampler->cached_taps_mem); + g_free (resampler->taps_mem); + g_free (resampler->tmp_taps); g_free (resampler->samples); g_free (resampler->sbuf); if (resampler->options) diff --git a/gst/audioresample/gstaudioresample.c b/gst/audioresample/gstaudioresample.c index 0f15ac59fc..3e0a3f2b4c 100644 --- a/gst/audioresample/gstaudioresample.c +++ b/gst/audioresample/gstaudioresample.c @@ -74,15 +74,19 @@ GST_DEBUG_CATEGORY_STATIC (GST_CAT_PERFORMANCE); #undef USE_SPEEX #define DEFAULT_QUALITY GST_AUDIO_RESAMPLER_QUALITY_DEFAULT +#define DEFAULT_RESAMPLE_METHOD GST_AUDIO_RESAMPLER_METHOD_KAISER #define DEFAULT_SINC_FILTER_MODE GST_AUDIO_RESAMPLER_FILTER_MODE_AUTO #define DEFAULT_SINC_FILTER_AUTO_THRESHOLD (1*1048576) +#define DEFAULT_SINC_FILTER_INTERPOLATION GST_AUDIO_RESAMPLER_FILTER_INTERPOLATION_CUBIC enum { PROP_0, PROP_QUALITY, + PROP_RESAMPLE_METHOD, PROP_SINC_FILTER_MODE, - PROP_SINC_FILTER_AUTO_THRESHOLD + PROP_SINC_FILTER_AUTO_THRESHOLD, + PROP_SINC_FILTER_INTERPOLATION }; #define SUPPORTED_CAPS \ @@ -150,6 +154,11 @@ gst_audio_resample_class_init (GstAudioResampleClass * klass) DEFAULT_QUALITY, G_PARAM_READWRITE | G_PARAM_CONSTRUCT | G_PARAM_STATIC_STRINGS)); + g_object_class_install_property (gobject_class, PROP_RESAMPLE_METHOD, + g_param_spec_enum ("resample-method", "Resample method to use", + "What resample method to use", + GST_TYPE_AUDIO_RESAMPLER_METHOD, + DEFAULT_RESAMPLE_METHOD, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS)); g_object_class_install_property (gobject_class, PROP_SINC_FILTER_MODE, g_param_spec_enum ("sinc-filter-mode", "Sinc filter table mode", "What sinc filter table mode to use", @@ -164,6 +173,14 @@ gst_audio_resample_class_init (GstAudioResampleClass * klass) "Memory usage threshold to use if sinc filter mode is AUTO, given in bytes", 0, G_MAXUINT, DEFAULT_SINC_FILTER_AUTO_THRESHOLD, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS)); + g_object_class_install_property (gobject_class, + PROP_SINC_FILTER_INTERPOLATION, + g_param_spec_enum ("sinc-filter-interpolation", + "Sinc filter interpolation", + "How to interpolate the sinc filter table", + GST_TYPE_AUDIO_RESAMPLER_FILTER_INTERPOLATION, + DEFAULT_SINC_FILTER_INTERPOLATION, + G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS)); gst_element_class_add_static_pad_template (gstelement_class, &gst_audio_resample_src_template); @@ -205,10 +222,11 @@ gst_audio_resample_init (GstAudioResample * resample) { GstBaseTransform *trans = GST_BASE_TRANSFORM (resample); - resample->method = GST_AUDIO_RESAMPLER_METHOD_KAISER; + resample->method = DEFAULT_RESAMPLE_METHOD; resample->quality = DEFAULT_QUALITY; resample->sinc_filter_mode = DEFAULT_SINC_FILTER_MODE; resample->sinc_filter_auto_threshold = DEFAULT_SINC_FILTER_AUTO_THRESHOLD; + resample->sinc_filter_interpolation = DEFAULT_SINC_FILTER_INTERPOLATION; gst_base_transform_set_gap_aware (trans, TRUE); gst_pad_set_query_function (trans->srcpad, gst_audio_resample_query); @@ -357,7 +375,10 @@ make_options (GstAudioResample * resample, GstAudioInfo * in, resample->method, GST_AUDIO_RESAMPLER_OPT_FILTER_MODE, GST_TYPE_AUDIO_RESAMPLER_FILTER_MODE, resample->sinc_filter_mode, GST_AUDIO_RESAMPLER_OPT_FILTER_MODE_THRESHOLD, - G_TYPE_UINT, resample->sinc_filter_auto_threshold, NULL); + G_TYPE_UINT, resample->sinc_filter_auto_threshold, + GST_AUDIO_RESAMPLER_OPT_FILTER_INTERPOLATION, + GST_TYPE_AUDIO_RESAMPLER_FILTER_INTERPOLATION, + resample->sinc_filter_interpolation, NULL); return options; } @@ -999,6 +1020,10 @@ gst_audio_resample_set_property (GObject * object, guint prop_id, GST_DEBUG_OBJECT (resample, "new quality %d", resample->quality); gst_audio_resample_update_state (resample, NULL, NULL); break; + case PROP_RESAMPLE_METHOD: + resample->method = g_value_get_enum (value); + gst_audio_resample_update_state (resample, NULL, NULL); + break; case PROP_SINC_FILTER_MODE: /* FIXME locking! */ resample->sinc_filter_mode = g_value_get_enum (value); @@ -1009,6 +1034,11 @@ gst_audio_resample_set_property (GObject * object, guint prop_id, resample->sinc_filter_auto_threshold = g_value_get_uint (value); gst_audio_resample_update_state (resample, NULL, NULL); break; + case PROP_SINC_FILTER_INTERPOLATION: + /* FIXME locking! */ + resample->sinc_filter_interpolation = g_value_get_enum (value); + gst_audio_resample_update_state (resample, NULL, NULL); + break; default: G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); break; @@ -1027,12 +1057,18 @@ gst_audio_resample_get_property (GObject * object, guint prop_id, case PROP_QUALITY: g_value_set_int (value, resample->quality); break; + case PROP_RESAMPLE_METHOD: + g_value_set_enum (value, resample->method); + break; case PROP_SINC_FILTER_MODE: g_value_set_enum (value, resample->sinc_filter_mode); break; case PROP_SINC_FILTER_AUTO_THRESHOLD: g_value_set_uint (value, resample->sinc_filter_auto_threshold); break; + case PROP_SINC_FILTER_INTERPOLATION: + g_value_set_enum (value, resample->sinc_filter_interpolation); + break; default: G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); break; diff --git a/gst/audioresample/gstaudioresample.h b/gst/audioresample/gstaudioresample.h index d7f552b550..6092493ea9 100644 --- a/gst/audioresample/gstaudioresample.h +++ b/gst/audioresample/gstaudioresample.h @@ -67,6 +67,7 @@ struct _GstAudioResample { gint quality; GstAudioResamplerFilterMode sinc_filter_mode; guint32 sinc_filter_auto_threshold; + GstAudioResamplerFilterInterpolation sinc_filter_interpolation; /* state */ GstAudioInfo in;