audio-resampler: add linear interpolation method
authorWim Taymans <wtaymans@redhat.com>
Wed, 10 Feb 2016 11:04:12 +0000 (12:04 +0100)
committerWim Taymans <wtaymans@redhat.com>
Mon, 28 Mar 2016 11:25:51 +0000 (13:25 +0200)
Make more functions into macros.
Add linear interpolation of filter coefficients.

gst-libs/gst/audio/audio-resampler-x86.h
gst-libs/gst/audio/audio-resampler.c
gst-libs/gst/audio/audio-resampler.h

index c660aa0..9f96685 100644 (file)
@@ -21,7 +21,7 @@
 #include <xmmintrin.h>
 
 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 <emmintrin.h>
 
 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 <smmintrin.h>
 
 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
   }
 }
index 9f38b1e..c5fde62 100644 (file)
 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);
 }
 
 /**
index 736e711..74a0e6f 100644 (file)
@@ -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"