plugins: port some plugins to the new memory API
[platform/upstream/gstreamer.git] / gst / audioresample / gstaudioresample.c
index 5276220..277f1a6 100644 (file)
@@ -1,6 +1,7 @@
 /* GStreamer
  * Copyright (C) 1999 Erik Walthinsen <omega@cse.ogi.edu>
  * Copyright (C) 2003,2004 David A. Schleef <ds@schleef.org>
+ * Copyright (C) 2007-2008 Sebastian Dröge <sebastian.droege@collabora.co.uk>
  *
  * This library is free software; you can redistribute it and/or
  * modify it under the terms of the GNU Library General Public
  * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
  * Boston, MA 02111-1307, USA.
  */
-/* Element-Checklist-Version: 5 */
 
 /**
  * SECTION:element-audioresample
  *
- * Audioresample resamples raw audio buffers to different sample rates using
+ * audioresample resamples raw audio buffers to different sample rates using
  * a configurable windowing function to enhance quality.
  *
  * <refsect2>
  * <title>Example launch line</title>
  * |[
  * gst-launch -v filesrc location=sine.ogg ! oggdemux ! vorbisdec ! audioconvert ! audioresample ! audio/x-raw-int, rate=8000 ! alsasink
- * ]| Decode an Ogg/Vorbis downsample to 8Khz and play sound through alsa. 
+ * ]| Decode an Ogg/Vorbis downsample to 8Khz and play sound through alsa.
  * To create the Ogg/Vorbis file refer to the documentation of vorbisenc.
  * </refsect2>
- *
- * Last reviewed on 2006-03-02 (0.10.4)
+ */
+
+/* TODO:
+ *  - Enable SSE/ARM optimizations and select at runtime
  */
 
 #ifdef HAVE_CONFIG_H
 #include <string.h>
 #include <math.h>
 
-/*#define DEBUG_ENABLED */
 #include "gstaudioresample.h"
+#include <gst/gstutils.h>
 #include <gst/audio/audio.h>
 #include <gst/base/gstbasetransform.h>
 
-GST_DEBUG_CATEGORY_STATIC (audioresample_debug);
-#define GST_CAT_DEFAULT audioresample_debug
-
-/* elementfactory information */
-static const GstElementDetails gst_audioresample_details =
-GST_ELEMENT_DETAILS ("Audio scaler",
-    "Filter/Converter/Audio",
-    "Resample audio",
-    "David Schleef <ds@schleef.org>");
+#ifndef DISABLE_ORC
+#include <orc/orc.h>
+#include <orc-test/orctest.h>
+#include <orc-test/orcprofile.h>
+#endif
 
-#define DEFAULT_FILTERLEN       16
+GST_DEBUG_CATEGORY (audio_resample_debug);
+#define GST_CAT_DEFAULT audio_resample_debug
 
 enum
 {
   PROP_0,
-  PROP_FILTERLEN
+  PROP_QUALITY,
+  PROP_FILTER_LENGTH
 };
 
 #define SUPPORTED_CAPS \
 GST_STATIC_CAPS ( \
-    "audio/x-raw-int, " \
-      "rate = (int) [ 1, MAX ], " \
+    "audio/x-raw-float, " \
+      "rate = (int) [ 1, MAX ], "      \
       "channels = (int) [ 1, MAX ], " \
       "endianness = (int) BYTE_ORDER, " \
-      "width = (int) 16, " \
-      "depth = (int) 16, " \
-      "signed = (boolean) true;" \
+      "width = (int) { 32, 64 }; " \
     "audio/x-raw-int, " \
       "rate = (int) [ 1, MAX ], " \
       "channels = (int) [ 1, MAX ], " \
       "endianness = (int) BYTE_ORDER, " \
       "width = (int) 32, " \
       "depth = (int) 32, " \
-      "signed = (boolean) true;" \
-    "audio/x-raw-float, " \
-      "rate = (int) [ 1, MAX ], "      \
+      "signed = (boolean) true; " \
+    "audio/x-raw-int, " \
+      "rate = (int) [ 1, MAX ], " \
       "channels = (int) [ 1, MAX ], " \
       "endianness = (int) BYTE_ORDER, " \
-      "width = (int) 32; " \
-    "audio/x-raw-float, " \
-      "rate = (int) [ 1, MAX ], "      \
+      "width = (int) 24, " \
+      "depth = (int) 24, " \
+      "signed = (boolean) true; " \
+    "audio/x-raw-int, " \
+      "rate = (int) [ 1, MAX ], " \
       "channels = (int) [ 1, MAX ], " \
       "endianness = (int) BYTE_ORDER, " \
-      "width = (int) 64" \
+      "width = (int) 16, " \
+      "depth = (int) 16, " \
+      "signed = (boolean) true; " \
+    "audio/x-raw-int, " \
+      "rate = (int) [ 1, MAX ], " \
+      "channels = (int) [ 1, MAX ], " \
+      "endianness = (int) BYTE_ORDER, " \
+      "width = (int) 8, " \
+      "depth = (int) 8, " \
+      "signed = (boolean) true" \
 )
 
-static GstStaticPadTemplate gst_audioresample_sink_template =
+/* If TRUE integer arithmetic resampling is faster and will be used if appropiate */
+#if defined AUDIORESAMPLE_FORMAT_INT
+static gboolean gst_audio_resample_use_int = TRUE;
+#elif defined AUDIORESAMPLE_FORMAT_FLOAT
+static gboolean gst_audio_resample_use_int = FALSE;
+#else
+static gboolean gst_audio_resample_use_int = FALSE;
+#endif
+
+static GstStaticPadTemplate gst_audio_resample_sink_template =
 GST_STATIC_PAD_TEMPLATE ("sink",
     GST_PAD_SINK, GST_PAD_ALWAYS, SUPPORTED_CAPS);
 
-static GstStaticPadTemplate gst_audioresample_src_template =
+static GstStaticPadTemplate gst_audio_resample_src_template =
 GST_STATIC_PAD_TEMPLATE ("src",
     GST_PAD_SRC, GST_PAD_ALWAYS, SUPPORTED_CAPS);
 
-static void gst_audioresample_set_property (GObject * object,
+static void gst_audio_resample_set_property (GObject * object,
     guint prop_id, const GValue * value, GParamSpec * pspec);
-static void gst_audioresample_get_property (GObject * object,
+static void gst_audio_resample_get_property (GObject * object,
     guint prop_id, GValue * value, GParamSpec * pspec);
 
 /* vmethods */
-static gboolean audioresample_get_unit_size (GstBaseTransform * base,
-    GstCaps * caps, guint * size);
-static GstCaps *audioresample_transform_caps (GstBaseTransform * base,
+static gboolean gst_audio_resample_get_unit_size (GstBaseTransform * base,
+    GstCaps * caps, gsize * size);
+static GstCaps *gst_audio_resample_transform_caps (GstBaseTransform * base,
     GstPadDirection direction, GstCaps * caps);
-static void audioresample_fixate_caps (GstBaseTransform * base,
+static void gst_audio_resample_fixate_caps (GstBaseTransform * base,
     GstPadDirection direction, GstCaps * caps, GstCaps * othercaps);
-static gboolean audioresample_transform_size (GstBaseTransform * trans,
-    GstPadDirection direction, GstCaps * incaps, guint insize,
-    GstCaps * outcaps, guint * outsize);
-static gboolean audioresample_set_caps (GstBaseTransform * base,
+static gboolean gst_audio_resample_transform_size (GstBaseTransform * trans,
+    GstPadDirection direction, GstCaps * incaps, gsize insize,
+    GstCaps * outcaps, gsize * outsize);
+static gboolean gst_audio_resample_set_caps (GstBaseTransform * base,
     GstCaps * incaps, GstCaps * outcaps);
-static GstFlowReturn audioresample_pushthrough (GstAudioresample *
-    audioresample);
-static GstFlowReturn audioresample_transform (GstBaseTransform * base,
+static GstFlowReturn gst_audio_resample_transform (GstBaseTransform * base,
     GstBuffer * inbuf, GstBuffer * outbuf);
-static gboolean audioresample_event (GstBaseTransform * base, GstEvent * event);
-static gboolean audioresample_start (GstBaseTransform * base);
-static gboolean audioresample_stop (GstBaseTransform * base);
-
-static gboolean audioresample_query (GstPad * pad, GstQuery * query);
-static const GstQueryType *audioresample_query_type (GstPad * pad);
+static gboolean gst_audio_resample_event (GstBaseTransform * base,
+    GstEvent * event);
+static gboolean gst_audio_resample_start (GstBaseTransform * base);
+static gboolean gst_audio_resample_stop (GstBaseTransform * base);
+static gboolean gst_audio_resample_query (GstPad * pad, GstQuery * query);
+static const GstQueryType *gst_audio_resample_query_type (GstPad * pad);
 
-#define DEBUG_INIT(bla) \
-  GST_DEBUG_CATEGORY_INIT (audioresample_debug, "audioresample", 0, "audio resampling element");
-
-GST_BOILERPLATE_FULL (GstAudioresample, gst_audioresample, GstBaseTransform,
-    GST_TYPE_BASE_TRANSFORM, DEBUG_INIT);
+GST_BOILERPLATE (GstAudioResample, gst_audio_resample, GstBaseTransform,
+    GST_TYPE_BASE_TRANSFORM);
 
 static void
-gst_audioresample_base_init (gpointer g_class)
+gst_audio_resample_base_init (gpointer g_class)
 {
   GstElementClass *gstelement_class = GST_ELEMENT_CLASS (g_class);
 
   gst_element_class_add_pad_template (gstelement_class,
-      gst_static_pad_template_get (&gst_audioresample_src_template));
+      gst_static_pad_template_get (&gst_audio_resample_src_template));
   gst_element_class_add_pad_template (gstelement_class,
-      gst_static_pad_template_get (&gst_audioresample_sink_template));
+      gst_static_pad_template_get (&gst_audio_resample_sink_template));
 
-  gst_element_class_set_details (gstelement_class, &gst_audioresample_details);
+  gst_element_class_set_details_simple (gstelement_class, "Audio resampler",
+      "Filter/Converter/Audio", "Resamples audio",
+      "Sebastian Dröge <sebastian.droege@collabora.co.uk>");
 }
 
 static void
-gst_audioresample_class_init (GstAudioresampleClass * klass)
+gst_audio_resample_class_init (GstAudioResampleClass * klass)
 {
-  GObjectClass *gobject_class;
-
-  gobject_class = (GObjectClass *) klass;
+  GObjectClass *gobject_class = (GObjectClass *) klass;
 
-  gobject_class->set_property = gst_audioresample_set_property;
-  gobject_class->get_property = gst_audioresample_get_property;
+  gobject_class->set_property = gst_audio_resample_set_property;
+  gobject_class->get_property = gst_audio_resample_get_property;
 
-  g_object_class_install_property (gobject_class, PROP_FILTERLEN,
-      g_param_spec_int ("filter-length", "filter length",
-          "Length of the resample filter", 0, G_MAXINT, DEFAULT_FILTERLEN,
+  g_object_class_install_property (gobject_class, PROP_QUALITY,
+      g_param_spec_int ("quality", "Quality", "Resample quality with 0 being "
+          "the lowest and 10 being the best",
+          SPEEX_RESAMPLER_QUALITY_MIN, SPEEX_RESAMPLER_QUALITY_MAX,
+          SPEEX_RESAMPLER_QUALITY_DEFAULT,
           G_PARAM_READWRITE | G_PARAM_CONSTRUCT | G_PARAM_STATIC_STRINGS));
 
+  /* FIXME 0.11: Remove this property, it's just for compatibility
+   * with old audioresample
+   */
+  /**
+   * GstAudioResample:filter-length:
+   *
+   * Length of the resample filter
+   *
+   * Deprectated: Use #GstAudioResample:quality property instead
+   */
+  g_object_class_install_property (gobject_class, PROP_FILTER_LENGTH,
+      g_param_spec_int ("filter-length", "Filter length",
+          "Length of the resample filter", 0, G_MAXINT, 64,
+          G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
+
   GST_BASE_TRANSFORM_CLASS (klass)->start =
-      GST_DEBUG_FUNCPTR (audioresample_start);
+      GST_DEBUG_FUNCPTR (gst_audio_resample_start);
   GST_BASE_TRANSFORM_CLASS (klass)->stop =
-      GST_DEBUG_FUNCPTR (audioresample_stop);
+      GST_DEBUG_FUNCPTR (gst_audio_resample_stop);
   GST_BASE_TRANSFORM_CLASS (klass)->transform_size =
-      GST_DEBUG_FUNCPTR (audioresample_transform_size);
+      GST_DEBUG_FUNCPTR (gst_audio_resample_transform_size);
   GST_BASE_TRANSFORM_CLASS (klass)->get_unit_size =
-      GST_DEBUG_FUNCPTR (audioresample_get_unit_size);
+      GST_DEBUG_FUNCPTR (gst_audio_resample_get_unit_size);
   GST_BASE_TRANSFORM_CLASS (klass)->transform_caps =
-      GST_DEBUG_FUNCPTR (audioresample_transform_caps);
+      GST_DEBUG_FUNCPTR (gst_audio_resample_transform_caps);
   GST_BASE_TRANSFORM_CLASS (klass)->fixate_caps =
-      GST_DEBUG_FUNCPTR (audioresample_fixate_caps);
+      GST_DEBUG_FUNCPTR (gst_audio_resample_fixate_caps);
   GST_BASE_TRANSFORM_CLASS (klass)->set_caps =
-      GST_DEBUG_FUNCPTR (audioresample_set_caps);
+      GST_DEBUG_FUNCPTR (gst_audio_resample_set_caps);
   GST_BASE_TRANSFORM_CLASS (klass)->transform =
-      GST_DEBUG_FUNCPTR (audioresample_transform);
+      GST_DEBUG_FUNCPTR (gst_audio_resample_transform);
   GST_BASE_TRANSFORM_CLASS (klass)->event =
-      GST_DEBUG_FUNCPTR (audioresample_event);
+      GST_DEBUG_FUNCPTR (gst_audio_resample_event);
 
   GST_BASE_TRANSFORM_CLASS (klass)->passthrough_on_same_caps = TRUE;
 }
 
 static void
-gst_audioresample_init (GstAudioresample * audioresample,
-    GstAudioresampleClass * klass)
+gst_audio_resample_init (GstAudioResample * resample,
+    GstAudioResampleClass * klass)
 {
-  GstBaseTransform *trans;
-
-  trans = GST_BASE_TRANSFORM (audioresample);
-
-  /* buffer alloc passthrough is too impossible. FIXME, it
-   * is trivial in the passthrough case. */
-  gst_pad_set_bufferalloc_function (trans->sinkpad, NULL);
+  GstBaseTransform *trans = GST_BASE_TRANSFORM (resample);
 
-  audioresample->filter_length = DEFAULT_FILTERLEN;
+  resample->quality = SPEEX_RESAMPLER_QUALITY_DEFAULT;
 
-  audioresample->need_discont = FALSE;
-
-  gst_pad_set_query_function (trans->srcpad, audioresample_query);
-  gst_pad_set_query_type_function (trans->srcpad, audioresample_query_type);
+  gst_base_transform_set_gap_aware (trans, TRUE);
+  gst_pad_set_query_function (trans->srcpad, gst_audio_resample_query);
+  gst_pad_set_query_type_function (trans->srcpad,
+      gst_audio_resample_query_type);
 }
 
 /* vmethods */
 static gboolean
-audioresample_start (GstBaseTransform * base)
+gst_audio_resample_start (GstBaseTransform * base)
 {
-  GstAudioresample *audioresample = GST_AUDIORESAMPLE (base);
+  GstAudioResample *resample = GST_AUDIO_RESAMPLE (base);
+
+  resample->need_discont = TRUE;
 
-  audioresample->resample = resample_new ();
-  audioresample->ts_offset = -1;
-  audioresample->offset = -1;
-  audioresample->next_ts = -1;
+  resample->num_gap_samples = 0;
+  resample->num_nongap_samples = 0;
+  resample->t0 = GST_CLOCK_TIME_NONE;
+  resample->in_offset0 = GST_BUFFER_OFFSET_NONE;
+  resample->out_offset0 = GST_BUFFER_OFFSET_NONE;
+  resample->samples_in = 0;
+  resample->samples_out = 0;
 
-  resample_set_filter_length (audioresample->resample,
-      audioresample->filter_length);
+  resample->tmp_in = NULL;
+  resample->tmp_in_size = 0;
+  resample->tmp_out = NULL;
+  resample->tmp_out_size = 0;
 
   return TRUE;
 }
 
 static gboolean
-audioresample_stop (GstBaseTransform * base)
+gst_audio_resample_stop (GstBaseTransform * base)
 {
-  GstAudioresample *audioresample = GST_AUDIORESAMPLE (base);
+  GstAudioResample *resample = GST_AUDIO_RESAMPLE (base);
 
-  if (audioresample->resample) {
-    resample_free (audioresample->resample);
-    audioresample->resample = NULL;
+  if (resample->state) {
+    resample->funcs->destroy (resample->state);
+    resample->state = NULL;
   }
 
-  gst_caps_replace (&audioresample->sinkcaps, NULL);
-  gst_caps_replace (&audioresample->srccaps, NULL);
+  resample->funcs = NULL;
+
+  g_free (resample->tmp_in);
+  resample->tmp_in = NULL;
+  resample->tmp_in_size = 0;
+
+  g_free (resample->tmp_out);
+  resample->tmp_out = NULL;
+  resample->tmp_out_size = 0;
+
+  gst_caps_replace (&resample->sinkcaps, NULL);
+  gst_caps_replace (&resample->srccaps, NULL);
 
   return TRUE;
 }
 
 static gboolean
-audioresample_get_unit_size (GstBaseTransform * base, GstCaps * caps,
-    guint * size)
+gst_audio_resample_get_unit_size (GstBaseTransform * base, GstCaps * caps,
+    gsize * size)
 {
   gint width, channels;
   GstStructure *structure;
   gboolean ret;
 
-  g_assert (size);
+  g_return_val_if_fail (size != NULL, FALSE);
 
   /* this works for both float and int */
   structure = gst_caps_get_structure (caps, 0);
   ret = gst_structure_get_int (structure, "width", &width);
   ret &= gst_structure_get_int (structure, "channels", &channels);
-  g_return_val_if_fail (ret, FALSE);
 
-  *size = width * channels / 8;
+  if (G_UNLIKELY (!ret))
+    return FALSE;
+
+  *size = (width / 8) * channels;
 
   return TRUE;
 }
 
 static GstCaps *
-audioresample_transform_caps (GstBaseTransform * base,
+gst_audio_resample_transform_caps (GstBaseTransform * base,
     GstPadDirection direction, GstCaps * caps)
 {
+  const GValue *val;
+  GstStructure *s;
   GstCaps *res;
-  GstStructure *structure;
 
-  /* transform caps gives one single caps so we can just replace
-   * the rate property with our range. */
+  /* transform single caps into input_caps + input_caps with the rate
+   * field set to our supported range. This ensures that upstream knows
+   * about downstream's prefered rate(s) and can negotiate accordingly. */
   res = gst_caps_copy (caps);
-  structure = gst_caps_get_structure (res, 0);
-  gst_structure_set (structure, "rate", GST_TYPE_INT_RANGE, 1, G_MAXINT, NULL);
+
+  /* first, however, check if the caps contain a range for the rate field, in
+   * which case that side isn't going to care much about the exact sample rate
+   * chosen and we should just assume things will get fixated to something sane
+   * and we may just as well offer our full range instead of the range in the
+   * caps. If the rate is not an int range value, it's likely to express a
+   * real preference or limitation and we should maintain that structure as
+   * preference by putting it first into the transformed caps, and only add
+   * our full rate range as second option  */
+  s = gst_caps_get_structure (res, 0);
+  val = gst_structure_get_value (s, "rate");
+  if (val == NULL || GST_VALUE_HOLDS_INT_RANGE (val)) {
+    /* overwrite existing range, or add field if it doesn't exist yet */
+    gst_structure_set (s, "rate", GST_TYPE_INT_RANGE, 1, G_MAXINT, NULL);
+  } else {
+    /* append caps with full range to existing caps with non-range rate field */
+    s = gst_structure_copy (s);
+    gst_structure_set (s, "rate", GST_TYPE_INT_RANGE, 1, G_MAXINT, NULL);
+    gst_caps_append_structure (res, s);
+  }
 
   return res;
 }
 
 /* Fixate rate to the allowed rate that has the smallest difference */
 static void
-audioresample_fixate_caps (GstBaseTransform * base,
+gst_audio_resample_fixate_caps (GstBaseTransform * base,
     GstPadDirection direction, GstCaps * caps, GstCaps * othercaps)
 {
   GstStructure *s;
   gint rate;
 
   s = gst_caps_get_structure (caps, 0);
-  if (!gst_structure_get_int (s, "rate", &rate))
+  if (G_UNLIKELY (!gst_structure_get_int (s, "rate", &rate)))
     return;
 
   s = gst_caps_get_structure (othercaps, 0);
   gst_structure_fixate_field_nearest_int (s, "rate", rate);
 }
 
+static const SpeexResampleFuncs *
+gst_audio_resample_get_funcs (gint width, gboolean fp)
+{
+  const SpeexResampleFuncs *funcs = NULL;
+
+  if (gst_audio_resample_use_int && (width == 8 || width == 16) && !fp)
+    funcs = &int_funcs;
+  else if ((!gst_audio_resample_use_int && (width == 8 || width == 16) && !fp)
+      || (width == 32 && fp))
+    funcs = &float_funcs;
+  else if ((width == 64 && fp) || ((width == 32 || width == 24) && !fp))
+    funcs = &double_funcs;
+  else
+    g_assert_not_reached ();
+
+  return funcs;
+}
+
+static SpeexResamplerState *
+gst_audio_resample_init_state (GstAudioResample * resample, gint width,
+    gint channels, gint inrate, gint outrate, gint quality, gboolean fp)
+{
+  SpeexResamplerState *ret = NULL;
+  gint err = RESAMPLER_ERR_SUCCESS;
+  const SpeexResampleFuncs *funcs = gst_audio_resample_get_funcs (width, fp);
+
+  ret = funcs->init (channels, inrate, outrate, quality, &err);
+
+  if (G_UNLIKELY (err != RESAMPLER_ERR_SUCCESS)) {
+    GST_ERROR_OBJECT (resample, "Failed to create resampler state: %s",
+        funcs->strerror (err));
+    return NULL;
+  }
+
+  funcs->skip_zeros (ret);
+
+  return ret;
+}
+
+static gboolean
+gst_audio_resample_update_state (GstAudioResample * resample, gint width,
+    gint channels, gint inrate, gint outrate, gint quality, gboolean fp)
+{
+  gboolean ret = TRUE;
+  gboolean updated_latency = FALSE;
+
+  updated_latency = (resample->inrate != inrate
+      || quality != resample->quality) && resample->state != NULL;
+
+  if (resample->state == NULL) {
+    ret = TRUE;
+  } else if (resample->channels != channels || fp != resample->fp
+      || width != resample->width) {
+    resample->funcs->destroy (resample->state);
+    resample->state =
+        gst_audio_resample_init_state (resample, width, channels, inrate,
+        outrate, quality, fp);
+
+    resample->funcs = gst_audio_resample_get_funcs (width, fp);
+    ret = (resample->state != NULL);
+  } else if (resample->inrate != inrate || resample->outrate != outrate) {
+    gint err = RESAMPLER_ERR_SUCCESS;
+
+    err = resample->funcs->set_rate (resample->state, inrate, outrate);
+
+    if (G_UNLIKELY (err != RESAMPLER_ERR_SUCCESS))
+      GST_ERROR_OBJECT (resample, "Failed to update rate: %s",
+          resample->funcs->strerror (err));
+
+    ret = (err == RESAMPLER_ERR_SUCCESS);
+  } else if (quality != resample->quality) {
+    gint err = RESAMPLER_ERR_SUCCESS;
+
+    err = resample->funcs->set_quality (resample->state, quality);
+
+    if (G_UNLIKELY (err != RESAMPLER_ERR_SUCCESS))
+      GST_ERROR_OBJECT (resample, "Failed to update quality: %s",
+          resample->funcs->strerror (err));
+
+    ret = (err == RESAMPLER_ERR_SUCCESS);
+  }
+
+  resample->width = width;
+  resample->channels = channels;
+  resample->fp = fp;
+  resample->quality = quality;
+  resample->inrate = inrate;
+  resample->outrate = outrate;
+
+  if (updated_latency)
+    gst_element_post_message (GST_ELEMENT (resample),
+        gst_message_new_latency (GST_OBJECT (resample)));
+
+  return ret;
+}
+
+static void
+gst_audio_resample_reset_state (GstAudioResample * resample)
+{
+  if (resample->state)
+    resample->funcs->reset_mem (resample->state);
+}
+
 static gboolean
-resample_set_state_from_caps (ResampleState * state, GstCaps * incaps,
-    GstCaps * outcaps, gint * channels, gint * inrate, gint * outrate)
+gst_audio_resample_parse_caps (GstCaps * incaps,
+    GstCaps * outcaps, gint * width, gint * channels, gint * inrate,
+    gint * outrate, gboolean * fp)
 {
   GstStructure *structure;
   gboolean ret;
-  gint myinrate, myoutrate;
-  int mychannels;
-  gint width, depth;
-  ResampleFormat format;
+  gint mywidth, myinrate, myoutrate, mychannels;
+  gboolean myfp;
 
   GST_DEBUG ("incaps %" GST_PTR_FORMAT ", outcaps %"
       GST_PTR_FORMAT, incaps, outcaps);
 
   structure = gst_caps_get_structure (incaps, 0);
 
-  /* get width */
-  ret = gst_structure_get_int (structure, "width", &width);
-  if (!ret)
-    goto no_width;
-
-  /* figure out the format */
-  if (g_str_equal (gst_structure_get_name (structure), "audio/x-raw-float")) {
-    if (width == 32)
-      format = RESAMPLE_FORMAT_F32;
-    else if (width == 64)
-      format = RESAMPLE_FORMAT_F64;
-    else
-      goto wrong_depth;
-  } else {
-    /* for int, depth and width must be the same */
-    ret = gst_structure_get_int (structure, "depth", &depth);
-    if (!ret || width != depth)
-      goto not_equal;
-
-    if (width == 16)
-      format = RESAMPLE_FORMAT_S16;
-    else if (width == 32)
-      format = RESAMPLE_FORMAT_S32;
-    else
-      goto wrong_depth;
-  }
+  if (g_str_equal (gst_structure_get_name (structure), "audio/x-raw-float"))
+    myfp = TRUE;
+  else
+    myfp = FALSE;
+
   ret = gst_structure_get_int (structure, "rate", &myinrate);
   ret &= gst_structure_get_int (structure, "channels", &mychannels);
-  if (!ret)
+  ret &= gst_structure_get_int (structure, "width", &mywidth);
+  if (G_UNLIKELY (!ret))
     goto no_in_rate_channels;
 
   structure = gst_caps_get_structure (outcaps, 0);
   ret = gst_structure_get_int (structure, "rate", &myoutrate);
-  if (!ret)
+  if (G_UNLIKELY (!ret))
     goto no_out_rate;
 
   if (channels)
@@ -350,30 +495,14 @@ resample_set_state_from_caps (ResampleState * state, GstCaps * incaps,
     *inrate = myinrate;
   if (outrate)
     *outrate = myoutrate;
-
-  resample_set_format (state, format);
-  resample_set_n_channels (state, mychannels);
-  resample_set_input_rate (state, myinrate);
-  resample_set_output_rate (state, myoutrate);
+  if (width)
+    *width = mywidth;
+  if (fp)
+    *fp = myfp;
 
   return TRUE;
 
   /* ERRORS */
-no_width:
-  {
-    GST_DEBUG ("failed to get width from caps");
-    return FALSE;
-  }
-not_equal:
-  {
-    GST_DEBUG ("width %d and depth %d must be the same", width, depth);
-    return FALSE;
-  }
-wrong_depth:
-  {
-    GST_DEBUG ("unknown depth %d found", depth);
-    return FALSE;
-  }
 no_in_rate_channels:
   {
     GST_DEBUG ("could not get input rate and channels");
@@ -386,349 +515,765 @@ no_out_rate:
   }
 }
 
+static gint
+_gcd (gint a, gint b)
+{
+  while (b != 0) {
+    int temp = a;
+
+    a = b;
+    b = temp % b;
+  }
+
+  return ABS (a);
+}
+
 static gboolean
-audioresample_transform_size (GstBaseTransform * base,
-    GstPadDirection direction, GstCaps * caps, guint size, GstCaps * othercaps,
-    guint * othersize)
-{
-  GstAudioresample *audioresample = GST_AUDIORESAMPLE (base);
-  ResampleState *state;
-  GstCaps *srccaps, *sinkcaps;
-  gboolean use_internal = FALSE;        /* whether we use the internal state */
+gst_audio_resample_transform_size (GstBaseTransform * base,
+    GstPadDirection direction, GstCaps * caps, gsize size, GstCaps * othercaps,
+    gsize * othersize)
+{
   gboolean ret = TRUE;
+  guint32 ratio_den, ratio_num;
+  gint inrate, outrate, gcd;
+  gint bytes_per_samp, channels;
 
   GST_LOG_OBJECT (base, "asked to transform size %d in direction %s",
       size, direction == GST_PAD_SINK ? "SINK" : "SRC");
-  if (direction == GST_PAD_SINK) {
-    sinkcaps = caps;
-    srccaps = othercaps;
-  } else {
-    sinkcaps = othercaps;
-    srccaps = caps;
-  }
 
-  /* if the caps are the ones that _set_caps got called with; we can use
-   * our own state; otherwise we'll have to create a state */
-  if (gst_caps_is_equal (sinkcaps, audioresample->sinkcaps) &&
-      gst_caps_is_equal (srccaps, audioresample->srccaps)) {
-    use_internal = TRUE;
-    state = audioresample->resample;
-  } else {
-    GST_DEBUG_OBJECT (audioresample,
-        "caps are not the set caps, creating state");
-    state = resample_new ();
-    resample_set_filter_length (state, audioresample->filter_length);
-    resample_set_state_from_caps (state, sinkcaps, srccaps, NULL, NULL, NULL);
+  /* Get sample width -> bytes_per_samp, channels, inrate, outrate */
+  ret =
+      gst_audio_resample_parse_caps (caps, othercaps, &bytes_per_samp,
+      &channels, &inrate, &outrate, NULL);
+  if (G_UNLIKELY (!ret)) {
+    GST_ERROR_OBJECT (base, "Wrong caps");
+    return FALSE;
   }
+  /* Number of samples in either buffer is size / (width*channels) ->
+   * calculate the factor */
+  bytes_per_samp = bytes_per_samp * channels / 8;
+  /* Convert source buffer size to samples */
+  size /= bytes_per_samp;
+
+  /* Simplify the conversion ratio factors */
+  gcd = _gcd (inrate, outrate);
+  ratio_num = inrate / gcd;
+  ratio_den = outrate / gcd;
 
   if (direction == GST_PAD_SINK) {
-    /* asked to convert size of an incoming buffer */
-    *othersize = resample_get_output_size_for_input (state, size);
+    /* asked to convert size of an incoming buffer. Round up the output size */
+    *othersize = gst_util_uint64_scale_int_ceil (size, ratio_den, ratio_num);
+    *othersize *= bytes_per_samp;
   } else {
-    /* asked to convert size of an outgoing buffer */
-    *othersize = resample_get_input_size_for_output (state, size);
+    /* asked to convert size of an outgoing buffer. Round down the input size */
+    *othersize = gst_util_uint64_scale_int (size, ratio_num, ratio_den);
+    *othersize *= bytes_per_samp;
   }
-  g_assert (*othersize % state->sample_size == 0);
 
-  /* we make room for one extra sample, given that the resampling filter
-   * can output an extra one for non-integral i_rate/o_rate */
-  GST_LOG_OBJECT (base, "transformed size %d to %d", size, *othersize);
-
-  if (!use_internal) {
-    resample_free (state);
-  }
+  GST_LOG_OBJECT (base, "transformed size %d to %d", size * bytes_per_samp,
+      *othersize);
 
   return ret;
 }
 
 static gboolean
-audioresample_set_caps (GstBaseTransform * base, GstCaps * incaps,
+gst_audio_resample_set_caps (GstBaseTransform * base, GstCaps * incaps,
     GstCaps * outcaps)
 {
   gboolean ret;
-  gint inrate, outrate;
-  int channels;
-  GstAudioresample *audioresample = GST_AUDIORESAMPLE (base);
+  gint width = 0, inrate = 0, outrate = 0, channels = 0;
+  gboolean fp;
+  GstAudioResample *resample = GST_AUDIO_RESAMPLE (base);
 
-  GST_DEBUG_OBJECT (base, "incaps %" GST_PTR_FORMAT ", outcaps %"
+  GST_LOG ("incaps %" GST_PTR_FORMAT ", outcaps %"
       GST_PTR_FORMAT, incaps, outcaps);
 
-  ret = resample_set_state_from_caps (audioresample->resample, incaps, outcaps,
-      &channels, &inrate, &outrate);
+  ret = gst_audio_resample_parse_caps (incaps, outcaps,
+      &width, &channels, &inrate, &outrate, &fp);
 
-  g_return_val_if_fail (ret, FALSE);
+  if (G_UNLIKELY (!ret))
+    return FALSE;
 
-  audioresample->channels = channels;
-  GST_DEBUG_OBJECT (audioresample, "set channels to %d", channels);
-  audioresample->i_rate = inrate;
-  GST_DEBUG_OBJECT (audioresample, "set i_rate to %d", inrate);
-  audioresample->o_rate = outrate;
-  GST_DEBUG_OBJECT (audioresample, "set o_rate to %d", outrate);
+  ret =
+      gst_audio_resample_update_state (resample, width, channels, inrate,
+      outrate, resample->quality, fp);
+
+  if (G_UNLIKELY (!ret))
+    return FALSE;
 
   /* save caps so we can short-circuit in the size_transform if the caps
    * are the same */
-  gst_caps_replace (&audioresample->sinkcaps, incaps);
-  gst_caps_replace (&audioresample->srccaps, outcaps);
+  gst_caps_replace (&resample->sinkcaps, incaps);
+  gst_caps_replace (&resample->srccaps, outcaps);
 
   return TRUE;
 }
 
-static gboolean
-audioresample_event (GstBaseTransform * base, GstEvent * event)
+#define GST_MAXINT24 (8388607)
+#define GST_MININT24 (-8388608)
+
+#if (G_BYTE_ORDER == G_LITTLE_ENDIAN)
+#define GST_READ_UINT24 GST_READ_UINT24_LE
+#define GST_WRITE_UINT24 GST_WRITE_UINT24_LE
+#else
+#define GST_READ_UINT24 GST_READ_UINT24_BE
+#define GST_WRITE_UINT24 GST_WRITE_UINT24_BE
+#endif
+
+static void
+gst_audio_resample_convert_buffer (GstAudioResample * resample,
+    const guint8 * in, guint8 * out, guint len, gboolean inverse)
 {
-  GstAudioresample *audioresample;
+  len *= resample->channels;
+
+  if (inverse) {
+    if (gst_audio_resample_use_int && resample->width == 8 && !resample->fp) {
+      gint8 *o = (gint8 *) out;
+      gint16 *i = (gint16 *) in;
+      gint32 tmp;
+
+      while (len) {
+        tmp = *i + (G_MAXINT8 >> 1);
+        *o = CLAMP (tmp >> 8, G_MININT8, G_MAXINT8);
+        o++;
+        i++;
+        len--;
+      }
+    } else if (!gst_audio_resample_use_int && resample->width == 8
+        && !resample->fp) {
+      gint8 *o = (gint8 *) out;
+      gfloat *i = (gfloat *) in;
+      gfloat tmp;
+
+      while (len) {
+        tmp = *i;
+        *o = (gint8) CLAMP (tmp * G_MAXINT8 + 0.5, G_MININT8, G_MAXINT8);
+        o++;
+        i++;
+        len--;
+      }
+    } else if (!gst_audio_resample_use_int && resample->width == 16
+        && !resample->fp) {
+      gint16 *o = (gint16 *) out;
+      gfloat *i = (gfloat *) in;
+      gfloat tmp;
+
+      while (len) {
+        tmp = *i;
+        *o = (gint16) CLAMP (tmp * G_MAXINT16 + 0.5, G_MININT16, G_MAXINT16);
+        o++;
+        i++;
+        len--;
+      }
+    } else if (resample->width == 24 && !resample->fp) {
+      guint8 *o = (guint8 *) out;
+      gdouble *i = (gdouble *) in;
+      gdouble tmp;
+
+      while (len) {
+        tmp = *i;
+        GST_WRITE_UINT24 (o, (gint32) CLAMP (tmp * GST_MAXINT24 + 0.5,
+                GST_MININT24, GST_MAXINT24));
+        o += 3;
+        i++;
+        len--;
+      }
+    } else if (resample->width == 32 && !resample->fp) {
+      gint32 *o = (gint32 *) out;
+      gdouble *i = (gdouble *) in;
+      gdouble tmp;
+
+      while (len) {
+        tmp = *i;
+        *o = (gint32) CLAMP (tmp * G_MAXINT32 + 0.5, G_MININT32, G_MAXINT32);
+        o++;
+        i++;
+        len--;
+      }
+    } else {
+      g_assert_not_reached ();
+    }
+  } else {
+    if (gst_audio_resample_use_int && resample->width == 8 && !resample->fp) {
+      gint8 *i = (gint8 *) in;
+      gint16 *o = (gint16 *) out;
+      gint32 tmp;
+
+      while (len) {
+        tmp = *i;
+        *o = tmp << 8;
+        o++;
+        i++;
+        len--;
+      }
+    } else if (!gst_audio_resample_use_int && resample->width == 8
+        && !resample->fp) {
+      gint8 *i = (gint8 *) in;
+      gfloat *o = (gfloat *) out;
+      gfloat tmp;
+
+      while (len) {
+        tmp = *i;
+        *o = tmp / G_MAXINT8;
+        o++;
+        i++;
+        len--;
+      }
+    } else if (!gst_audio_resample_use_int && resample->width == 16
+        && !resample->fp) {
+      gint16 *i = (gint16 *) in;
+      gfloat *o = (gfloat *) out;
+      gfloat tmp;
+
+      while (len) {
+        tmp = *i;
+        *o = tmp / G_MAXINT16;
+        o++;
+        i++;
+        len--;
+      }
+    } else if (resample->width == 24 && !resample->fp) {
+      guint8 *i = (guint8 *) in;
+      gdouble *o = (gdouble *) out;
+      gdouble tmp;
+      guint32 tmp2;
+
+      while (len) {
+        tmp2 = GST_READ_UINT24 (i);
+        if (tmp2 & 0x00800000)
+          tmp2 |= 0xff000000;
+        tmp = (gint32) tmp2;
+        *o = tmp / GST_MAXINT24;
+        o++;
+        i += 3;
+        len--;
+      }
+    } else if (resample->width == 32 && !resample->fp) {
+      gint32 *i = (gint32 *) in;
+      gdouble *o = (gdouble *) out;
+      gdouble tmp;
+
+      while (len) {
+        tmp = *i;
+        *o = tmp / G_MAXINT32;
+        o++;
+        i++;
+        len--;
+      }
+    } else {
+      g_assert_not_reached ();
+    }
+  }
+}
 
-  audioresample = GST_AUDIORESAMPLE (base);
+static guint8 *
+gst_audio_resample_workspace_realloc (guint8 ** workspace, guint * size,
+    guint new_size)
+{
+  guint8 *new;
+  if (new_size <= *size)
+    /* no need to resize */
+    return *workspace;
+  new = g_realloc (*workspace, new_size);
+  if (!new)
+    /* failure (re)allocating memeory */
+    return NULL;
+  /* success */
+  *workspace = new;
+  *size = new_size;
+  return *workspace;
+}
+
+/* Push history_len zeros into the filter, but discard the output. */
+static void
+gst_audio_resample_dump_drain (GstAudioResample * resample, guint history_len)
+{
+  gint outsize;
+  guint in_len, in_processed;
+  guint out_len, out_processed;
+  guint num, den;
+  gpointer buf;
+
+  g_assert (resample->state != NULL);
+
+  resample->funcs->get_ratio (resample->state, &num, &den);
+
+  in_len = in_processed = history_len;
+  out_processed = out_len =
+      gst_util_uint64_scale_int_ceil (history_len, den, num);
+  outsize = out_len * resample->channels * (resample->funcs->width / 8);
+
+  if (out_len == 0)
+    return;
+
+  buf = g_malloc (outsize);
+  resample->funcs->process (resample->state, NULL, &in_processed, buf,
+      &out_processed);
+  g_free (buf);
+
+  g_assert (in_len == in_processed);
+}
+
+static void
+gst_audio_resample_push_drain (GstAudioResample * resample, guint history_len)
+{
+  GstBuffer *outbuf;
+  GstFlowReturn res;
+  gint outsize;
+  guint in_len, in_processed;
+  guint out_len, out_processed;
+  gint err;
+  guint num, den;
+  guint8 *data;
+
+  g_assert (resample->state != NULL);
+
+  /* Don't drain samples if we were reset. */
+  if (!GST_CLOCK_TIME_IS_VALID (resample->t0))
+    return;
+
+  resample->funcs->get_ratio (resample->state, &num, &den);
+
+  in_len = in_processed = history_len;
+  out_len = out_processed =
+      gst_util_uint64_scale_int_ceil (history_len, den, num);
+  outsize = out_len * resample->channels * (resample->width / 8);
+
+  if (out_len == 0)
+    return;
+
+  res =
+      gst_pad_alloc_buffer_and_set_caps (GST_BASE_TRANSFORM_SRC_PAD (resample),
+      GST_BUFFER_OFFSET_NONE, outsize,
+      GST_PAD_CAPS (GST_BASE_TRANSFORM_SRC_PAD (resample)), &outbuf);
+  if (G_UNLIKELY (res != GST_FLOW_OK)) {
+    GST_WARNING_OBJECT (resample, "failed allocating buffer of %d bytes",
+        outsize);
+    return;
+  }
+
+  data = gst_buffer_map (outbuf, NULL, NULL, GST_MAP_WRITE);
+
+  if (resample->funcs->width != resample->width) {
+    /* need to convert data format;  allocate workspace */
+    if (!gst_audio_resample_workspace_realloc (&resample->tmp_out,
+            &resample->tmp_out_size, (resample->funcs->width / 8) * out_len *
+            resample->channels)) {
+      GST_ERROR_OBJECT (resample, "failed to allocate workspace");
+      return;
+    }
+
+    /* process */
+    err = resample->funcs->process (resample->state, NULL, &in_processed,
+        resample->tmp_out, &out_processed);
+
+    /* convert output format */
+    gst_audio_resample_convert_buffer (resample, resample->tmp_out,
+        data, out_processed, TRUE);
+  } else {
+    /* don't need to convert data format;  process */
+    err = resample->funcs->process (resample->state, NULL, &in_processed,
+        data, &out_processed);
+  }
+
+  /* If we wrote more than allocated something is really wrong now
+   * and we should better abort immediately */
+  g_assert (out_len >= out_processed);
+
+  outsize = out_processed * resample->channels * (resample->width / 8);
+  gst_buffer_unmap (outbuf, data, outsize);
+
+  if (G_UNLIKELY (err != RESAMPLER_ERR_SUCCESS)) {
+    GST_WARNING_OBJECT (resample, "Failed to process drain: %s",
+        resample->funcs->strerror (err));
+    gst_buffer_unref (outbuf);
+    return;
+  }
+
+  /* time */
+  if (GST_CLOCK_TIME_IS_VALID (resample->t0)) {
+    GST_BUFFER_TIMESTAMP (outbuf) = resample->t0 +
+        gst_util_uint64_scale_int_round (resample->samples_out, GST_SECOND,
+        resample->outrate);
+    GST_BUFFER_DURATION (outbuf) = resample->t0 +
+        gst_util_uint64_scale_int_round (resample->samples_out + out_processed,
+        GST_SECOND, resample->outrate) - GST_BUFFER_TIMESTAMP (outbuf);
+  } else {
+    GST_BUFFER_TIMESTAMP (outbuf) = GST_CLOCK_TIME_NONE;
+    GST_BUFFER_DURATION (outbuf) = GST_CLOCK_TIME_NONE;
+  }
+  /* offset */
+  if (resample->out_offset0 != GST_BUFFER_OFFSET_NONE) {
+    GST_BUFFER_OFFSET (outbuf) = resample->out_offset0 + resample->samples_out;
+    GST_BUFFER_OFFSET_END (outbuf) = GST_BUFFER_OFFSET (outbuf) + out_processed;
+  } else {
+    GST_BUFFER_OFFSET (outbuf) = GST_BUFFER_OFFSET_NONE;
+    GST_BUFFER_OFFSET_END (outbuf) = GST_BUFFER_OFFSET_NONE;
+  }
+  /* move along */
+  resample->samples_out += out_processed;
+  resample->samples_in += history_len;
+
+  if (G_UNLIKELY (out_processed == 0 && in_len * den > num)) {
+    GST_WARNING_OBJECT (resample, "Failed to get drain, dropping buffer");
+    gst_buffer_unref (outbuf);
+    return;
+  }
+
+  GST_LOG_OBJECT (resample,
+      "Pushing drain buffer of %u bytes with timestamp %" GST_TIME_FORMAT
+      " duration %" GST_TIME_FORMAT " offset %" G_GUINT64_FORMAT " offset_end %"
+      G_GUINT64_FORMAT, outsize,
+      GST_TIME_ARGS (GST_BUFFER_TIMESTAMP (outbuf)),
+      GST_TIME_ARGS (GST_BUFFER_DURATION (outbuf)), GST_BUFFER_OFFSET (outbuf),
+      GST_BUFFER_OFFSET_END (outbuf));
+
+  res = gst_pad_push (GST_BASE_TRANSFORM_SRC_PAD (resample), outbuf);
+
+  if (G_UNLIKELY (res != GST_FLOW_OK))
+    GST_WARNING_OBJECT (resample, "Failed to push drain: %s",
+        gst_flow_get_name (res));
+
+  return;
+}
+
+static gboolean
+gst_audio_resample_event (GstBaseTransform * base, GstEvent * event)
+{
+  GstAudioResample *resample = GST_AUDIO_RESAMPLE (base);
 
   switch (GST_EVENT_TYPE (event)) {
-    case GST_EVENT_FLUSH_START:
-      break;
     case GST_EVENT_FLUSH_STOP:
-      resample_input_flush (audioresample->resample);
-      audioresample->ts_offset = -1;
-      audioresample->next_ts = -1;
-      audioresample->offset = -1;
+      gst_audio_resample_reset_state (resample);
+      if (resample->state)
+        resample->funcs->skip_zeros (resample->state);
+      resample->num_gap_samples = 0;
+      resample->num_nongap_samples = 0;
+      resample->t0 = GST_CLOCK_TIME_NONE;
+      resample->in_offset0 = GST_BUFFER_OFFSET_NONE;
+      resample->out_offset0 = GST_BUFFER_OFFSET_NONE;
+      resample->samples_in = 0;
+      resample->samples_out = 0;
+      resample->need_discont = TRUE;
       break;
     case GST_EVENT_NEWSEGMENT:
-      resample_input_pushthrough (audioresample->resample);
-      audioresample_pushthrough (audioresample);
-      audioresample->ts_offset = -1;
-      audioresample->next_ts = -1;
-      audioresample->offset = -1;
+      if (resample->state) {
+        guint latency = resample->funcs->get_input_latency (resample->state);
+        gst_audio_resample_push_drain (resample, latency);
+      }
+      gst_audio_resample_reset_state (resample);
+      if (resample->state)
+        resample->funcs->skip_zeros (resample->state);
+      resample->num_gap_samples = 0;
+      resample->num_nongap_samples = 0;
+      resample->t0 = GST_CLOCK_TIME_NONE;
+      resample->in_offset0 = GST_BUFFER_OFFSET_NONE;
+      resample->out_offset0 = GST_BUFFER_OFFSET_NONE;
+      resample->samples_in = 0;
+      resample->samples_out = 0;
+      resample->need_discont = TRUE;
       break;
     case GST_EVENT_EOS:
-      resample_input_eos (audioresample->resample);
-      audioresample_pushthrough (audioresample);
+      if (resample->state) {
+        guint latency = resample->funcs->get_input_latency (resample->state);
+        gst_audio_resample_push_drain (resample, latency);
+      }
+      gst_audio_resample_reset_state (resample);
       break;
     default:
       break;
   }
+
   return parent_class->event (base, event);
 }
 
-static GstFlowReturn
-audioresample_do_output (GstAudioresample * audioresample, GstBuffer * outbuf)
-{
-  int outsize;
-  int outsamples;
-  ResampleState *r;
-
-  r = audioresample->resample;
-
-  outsize = resample_get_output_size (r);
-  GST_LOG_OBJECT (audioresample, "audioresample can give me %d bytes", outsize);
-
-  /* protect against mem corruption */
-  if (outsize > GST_BUFFER_SIZE (outbuf)) {
-    GST_WARNING_OBJECT (audioresample,
-        "overriding audioresample's outsize %d with outbuffer's size %d",
-        outsize, GST_BUFFER_SIZE (outbuf));
-    outsize = GST_BUFFER_SIZE (outbuf);
-  }
-  /* catch possibly wrong size differences */
-  if (GST_BUFFER_SIZE (outbuf) - outsize > r->sample_size) {
-    GST_WARNING_OBJECT (audioresample,
-        "audioresample's outsize %d too far from outbuffer's size %d",
-        outsize, GST_BUFFER_SIZE (outbuf));
-  }
-
-  outsize = resample_get_output_data (r, GST_BUFFER_DATA (outbuf), outsize);
-  outsamples = outsize / r->sample_size;
-  GST_LOG_OBJECT (audioresample, "resample gave me %d bytes or %d samples",
-      outsize, outsamples);
-
-  GST_BUFFER_OFFSET (outbuf) = audioresample->offset;
-  GST_BUFFER_TIMESTAMP (outbuf) = audioresample->next_ts;
-
-  if (audioresample->ts_offset != -1) {
-    audioresample->offset += outsamples;
-    audioresample->ts_offset += outsamples;
-    audioresample->next_ts =
-        gst_util_uint64_scale_int (audioresample->ts_offset, GST_SECOND,
-        audioresample->o_rate);
-    GST_BUFFER_OFFSET_END (outbuf) = audioresample->offset;
-
-    /* we calculate DURATION as the difference between "next" timestamp
-     * and current timestamp so we ensure a contiguous stream, instead of
-     * having rounding errors. */
-    GST_BUFFER_DURATION (outbuf) = audioresample->next_ts -
-        GST_BUFFER_TIMESTAMP (outbuf);
-  } else {
-    /* no valid offset know, we can still sortof calculate the duration though */
-    GST_BUFFER_DURATION (outbuf) =
-        gst_util_uint64_scale_int (outsamples, GST_SECOND,
-        audioresample->o_rate);
-  }
-
-  /* check for possible mem corruption */
-  if (outsize > GST_BUFFER_SIZE (outbuf)) {
-    /* this is an error that when it happens, would need fixing in the
-     * resample library; we told it we wanted only GST_BUFFER_SIZE (outbuf),
-     * and it gave us more ! */
-    GST_WARNING_OBJECT (audioresample,
-        "audioresample, you memory corrupting bastard. "
-        "you gave me outsize %d while my buffer was size %d",
-        outsize, GST_BUFFER_SIZE (outbuf));
-    return GST_FLOW_ERROR;
-  }
-  /* catch possibly wrong size differences */
-  if (GST_BUFFER_SIZE (outbuf) - outsize > r->sample_size) {
-    GST_WARNING_OBJECT (audioresample,
-        "audioresample's written outsize %d too far from outbuffer's size %d",
-        outsize, GST_BUFFER_SIZE (outbuf));
-  }
-  GST_BUFFER_SIZE (outbuf) = outsize;
-
-  if (G_UNLIKELY (audioresample->need_discont)) {
-    GST_DEBUG_OBJECT (audioresample,
-        "marking this buffer with the DISCONT flag");
-    GST_BUFFER_FLAG_SET (outbuf, GST_BUFFER_FLAG_DISCONT);
-    audioresample->need_discont = FALSE;
-  }
-
-  GST_LOG_OBJECT (audioresample, "transformed to buffer of %d bytes, ts %"
-      GST_TIME_FORMAT ", duration %" GST_TIME_FORMAT ", offset %"
-      G_GINT64_FORMAT ", offset_end %" G_GINT64_FORMAT,
-      outsize, GST_TIME_ARGS (GST_BUFFER_TIMESTAMP (outbuf)),
-      GST_TIME_ARGS (GST_BUFFER_DURATION (outbuf)),
-      GST_BUFFER_OFFSET (outbuf), GST_BUFFER_OFFSET_END (outbuf));
+static gboolean
+gst_audio_resample_check_discont (GstAudioResample * resample, GstBuffer * buf)
+{
+  guint64 offset;
+  guint64 delta;
 
+  /* is the incoming buffer a discontinuity? */
+  if (G_UNLIKELY (GST_BUFFER_IS_DISCONT (buf)))
+    return TRUE;
 
-  return GST_FLOW_OK;
-}
+  /* no valid timestamps or offsets to compare --> no discontinuity */
+  if (G_UNLIKELY (!(GST_BUFFER_TIMESTAMP_IS_VALID (buf) &&
+              GST_CLOCK_TIME_IS_VALID (resample->t0))))
+    return FALSE;
 
-static gboolean
-audioresample_check_discont (GstAudioresample * audioresample,
-    GstClockTime timestamp)
-{
-  if (timestamp != GST_CLOCK_TIME_NONE &&
-      audioresample->prev_ts != GST_CLOCK_TIME_NONE &&
-      audioresample->prev_duration != GST_CLOCK_TIME_NONE &&
-      timestamp != audioresample->prev_ts + audioresample->prev_duration) {
-    /* Potentially a discontinuous buffer. However, it turns out that many
-     * elements generate imperfect streams due to rounding errors, so we permit
-     * a small error (up to one sample) without triggering a filter 
-     * flush/restart (if triggered incorrectly, this will be audible) */
-    GstClockTimeDiff diff = timestamp -
-        (audioresample->prev_ts + audioresample->prev_duration);
-
-    if (ABS (diff) > GST_SECOND / audioresample->i_rate) {
-      GST_WARNING_OBJECT (audioresample,
-          "encountered timestamp discontinuity of %" G_GINT64_FORMAT, diff);
-      return TRUE;
-    }
-  }
+  /* convert the inbound timestamp to an offset. */
+  offset =
+      gst_util_uint64_scale_int_round (GST_BUFFER_TIMESTAMP (buf) -
+      resample->t0, resample->inrate, GST_SECOND);
+
+  /* many elements generate imperfect streams due to rounding errors, so we
+   * permit a small error (up to one sample) without triggering a filter
+   * flush/restart (if triggered incorrectly, this will be audible) */
+  /* allow even up to more samples, since sink is not so strict anyway,
+   * so give that one a chance to handle this as configured */
+  delta = ABS ((gint64) (offset - resample->samples_in));
+  if (delta <= (resample->inrate >> 5))
+    return FALSE;
 
-  return FALSE;
+  GST_WARNING_OBJECT (resample,
+      "encountered timestamp discontinuity of %" G_GUINT64_FORMAT " samples = %"
+      GST_TIME_FORMAT, delta,
+      GST_TIME_ARGS (gst_util_uint64_scale_int_round (delta, GST_SECOND,
+              resample->inrate)));
+  return TRUE;
 }
 
 static GstFlowReturn
-audioresample_transform (GstBaseTransform * base, GstBuffer * inbuf,
+gst_audio_resample_process (GstAudioResample * resample, GstBuffer * inbuf,
     GstBuffer * outbuf)
 {
-  GstAudioresample *audioresample;
-  ResampleState *r;
-  guchar *data, *datacopy;
-  gulong size;
-  GstClockTime timestamp;
+  gsize in_size, out_size;
+  guint8 *in_data, *out_data;
+  guint32 in_len, in_processed;
+  guint32 out_len, out_processed;
+  guint filt_len = resample->funcs->get_filt_len (resample->state);
+
+  in_data = gst_buffer_map (inbuf, &in_size, NULL, GST_MAP_READ);
+  out_data = gst_buffer_map (outbuf, &out_size, NULL, GST_MAP_WRITE);
+
+  in_len = in_size / resample->channels;
+  out_len = out_size / resample->channels;
+
+  in_len /= (resample->width / 8);
+  out_len /= (resample->width / 8);
+
+  in_processed = in_len;
+  out_processed = out_len;
+
+  if (GST_BUFFER_FLAG_IS_SET (inbuf, GST_BUFFER_FLAG_GAP)) {
+    resample->num_nongap_samples = 0;
+    if (resample->num_gap_samples < filt_len) {
+      guint zeros_to_push;
+      if (in_len >= filt_len - resample->num_gap_samples)
+        zeros_to_push = filt_len - resample->num_gap_samples;
+      else
+        zeros_to_push = in_len;
+
+      gst_audio_resample_push_drain (resample, zeros_to_push);
+      in_len -= zeros_to_push;
+      resample->num_gap_samples += zeros_to_push;
+    }
 
-  audioresample = GST_AUDIORESAMPLE (base);
-  r = audioresample->resample;
+    {
+      guint num, den;
+      resample->funcs->get_ratio (resample->state, &num, &den);
+      if (resample->samples_in + in_len >= filt_len / 2)
+        out_processed =
+            gst_util_uint64_scale_int_ceil (resample->samples_in + in_len -
+            filt_len / 2, den, num) - resample->samples_out;
+      else
+        out_processed = 0;
+
+      memset (out_data, 0, out_size);
+      GST_BUFFER_FLAG_SET (outbuf, GST_BUFFER_FLAG_GAP);
+      resample->num_gap_samples += in_len;
+      in_processed = in_len;
+    }
+  } else {                      /* not a gap */
 
-  data = GST_BUFFER_DATA (inbuf);
-  size = GST_BUFFER_SIZE (inbuf);
-  timestamp = GST_BUFFER_TIMESTAMP (inbuf);
+    gint err;
 
-  GST_LOG_OBJECT (audioresample, "transforming buffer of %ld bytes, ts %"
-      GST_TIME_FORMAT ", duration %" GST_TIME_FORMAT ", offset %"
-      G_GINT64_FORMAT ", offset_end %" G_GINT64_FORMAT,
-      size, GST_TIME_ARGS (timestamp),
-      GST_TIME_ARGS (GST_BUFFER_DURATION (inbuf)),
-      GST_BUFFER_OFFSET (inbuf), GST_BUFFER_OFFSET_END (inbuf));
+    if (resample->num_gap_samples > filt_len) {
+      /* push in enough zeros to restore the filter to the right offset */
+      guint num, den;
+      resample->funcs->get_ratio (resample->state, &num, &den);
+      gst_audio_resample_dump_drain (resample,
+          (resample->num_gap_samples - filt_len) % num);
+    }
+    resample->num_gap_samples = 0;
+    if (resample->num_nongap_samples < filt_len) {
+      resample->num_nongap_samples += in_len;
+      if (resample->num_nongap_samples > filt_len)
+        resample->num_nongap_samples = filt_len;
+    }
+
+    if (resample->funcs->width != resample->width) {
+      /* need to convert data format for processing;  ensure we have enough
+       * workspace available */
+      if (!gst_audio_resample_workspace_realloc (&resample->tmp_in,
+              &resample->tmp_in_size, in_len * resample->channels *
+              (resample->funcs->width / 8)) ||
+          !gst_audio_resample_workspace_realloc (&resample->tmp_out,
+              &resample->tmp_out_size, out_len * resample->channels *
+              (resample->funcs->width / 8))) {
+        GST_ERROR_OBJECT (resample, "failed to allocate workspace");
+        gst_buffer_unmap (inbuf, in_data, in_size);
+        gst_buffer_unmap (outbuf, out_data, out_size);
+        return GST_FLOW_ERROR;
+      }
+
+      /* convert input */
+      gst_audio_resample_convert_buffer (resample, in_data,
+          resample->tmp_in, in_len, FALSE);
+
+      /* process */
+      err = resample->funcs->process (resample->state,
+          resample->tmp_in, &in_processed, resample->tmp_out, &out_processed);
+
+      /* convert output */
+      gst_audio_resample_convert_buffer (resample, resample->tmp_out,
+          out_data, out_processed, TRUE);
+    } else {
+      /* no format conversion required;  process */
+      err = resample->funcs->process (resample->state,
+          in_data, &in_processed, out_data, &out_processed);
+    }
 
-  /* check for timestamp discontinuities and flush/reset if needed */
-  if (G_UNLIKELY (audioresample_check_discont (audioresample, timestamp))) {
-    /* Flush internal samples */
-    audioresample_pushthrough (audioresample);
-    /* Inform downstream element about discontinuity */
-    audioresample->need_discont = TRUE;
-    /* We want to recalculate the offset */
-    audioresample->ts_offset = -1;
-  }
-
-  if (audioresample->ts_offset == -1) {
-    /* if we don't know the initial offset yet, calculate it based on the 
-     * input timestamp. */
-    if (GST_CLOCK_TIME_IS_VALID (timestamp)) {
-      GstClockTime stime;
-
-      /* offset used to calculate the timestamps. We use the sample offset for
-       * this to make it more accurate. We want the first buffer to have the
-       * same timestamp as the incoming timestamp. */
-      audioresample->next_ts = timestamp;
-      audioresample->ts_offset =
-          gst_util_uint64_scale_int (timestamp, r->o_rate, GST_SECOND);
-      /* offset used to set as the buffer offset, this offset is always
-       * relative to the stream time, note that timestamp is not... */
-      stime = (timestamp - base->segment.start) + base->segment.time;
-      audioresample->offset =
-          gst_util_uint64_scale_int (stime, r->o_rate, GST_SECOND);
+    if (G_UNLIKELY (err != RESAMPLER_ERR_SUCCESS)) {
+      GST_ERROR_OBJECT (resample, "Failed to convert data: %s",
+          resample->funcs->strerror (err));
+      gst_buffer_unmap (inbuf, in_data, in_size);
+      gst_buffer_unmap (outbuf, out_data, out_size);
+      return GST_FLOW_ERROR;
     }
   }
-  audioresample->prev_ts = timestamp;
-  audioresample->prev_duration = GST_BUFFER_DURATION (inbuf);
 
-  /* need to memdup, resample takes ownership. */
-  datacopy = g_memdup (data, size);
-  resample_add_input_data (r, datacopy, size, g_free, datacopy);
+  /* If we wrote more than allocated something is really wrong now and we
+   * should better abort immediately */
+  g_assert (out_len >= out_processed);
+
+  if (G_UNLIKELY (in_len != in_processed)) {
+    GST_WARNING_OBJECT (resample, "converted %d of %d input samples",
+        in_processed, in_len);
+  }
+
+  /* time */
+  if (GST_CLOCK_TIME_IS_VALID (resample->t0)) {
+    GST_BUFFER_TIMESTAMP (outbuf) = resample->t0 +
+        gst_util_uint64_scale_int_round (resample->samples_out, GST_SECOND,
+        resample->outrate);
+    GST_BUFFER_DURATION (outbuf) = resample->t0 +
+        gst_util_uint64_scale_int_round (resample->samples_out + out_processed,
+        GST_SECOND, resample->outrate) - GST_BUFFER_TIMESTAMP (outbuf);
+  } else {
+    GST_BUFFER_TIMESTAMP (outbuf) = GST_CLOCK_TIME_NONE;
+    GST_BUFFER_DURATION (outbuf) = GST_CLOCK_TIME_NONE;
+  }
+  /* offset */
+  if (resample->out_offset0 != GST_BUFFER_OFFSET_NONE) {
+    GST_BUFFER_OFFSET (outbuf) = resample->out_offset0 + resample->samples_out;
+    GST_BUFFER_OFFSET_END (outbuf) = GST_BUFFER_OFFSET (outbuf) + out_processed;
+  } else {
+    GST_BUFFER_OFFSET (outbuf) = GST_BUFFER_OFFSET_NONE;
+    GST_BUFFER_OFFSET_END (outbuf) = GST_BUFFER_OFFSET_NONE;
+  }
+  /* move along */
+  resample->samples_out += out_processed;
+  resample->samples_in += in_len;
+
+  out_size = out_processed * resample->channels * (resample->width / 8);
+  gst_buffer_unmap (inbuf, in_data, in_size);
+  gst_buffer_unmap (outbuf, out_data, out_size);
+
+  GST_LOG_OBJECT (resample,
+      "Converted to buffer of %" G_GUINT32_FORMAT
+      " samples (%u bytes) with timestamp %" GST_TIME_FORMAT ", duration %"
+      GST_TIME_FORMAT ", offset %" G_GUINT64_FORMAT ", offset_end %"
+      G_GUINT64_FORMAT, out_processed, out_size,
+      GST_TIME_ARGS (GST_BUFFER_TIMESTAMP (outbuf)),
+      GST_TIME_ARGS (GST_BUFFER_DURATION (outbuf)),
+      GST_BUFFER_OFFSET (outbuf), GST_BUFFER_OFFSET_END (outbuf));
 
-  return audioresample_do_output (audioresample, outbuf);
+  if (out_processed == 0) {
+    GST_DEBUG_OBJECT (resample, "buffer dropped");
+    return GST_BASE_TRANSFORM_FLOW_DROPPED;
+  }
+  return GST_FLOW_OK;
 }
 
-/* push remaining data in the buffers out */
 static GstFlowReturn
-audioresample_pushthrough (GstAudioresample * audioresample)
+gst_audio_resample_transform (GstBaseTransform * base, GstBuffer * inbuf,
+    GstBuffer * outbuf)
 {
-  int outsize;
-  ResampleState *r;
-  GstBuffer *outbuf;
-  GstFlowReturn res = GST_FLOW_OK;
-  GstBaseTransform *trans;
+  GstAudioResample *resample = GST_AUDIO_RESAMPLE (base);
+  GstFlowReturn ret;
+
+  if (resample->state == NULL) {
+    if (G_UNLIKELY (!(resample->state =
+                gst_audio_resample_init_state (resample, resample->width,
+                    resample->channels, resample->inrate, resample->outrate,
+                    resample->quality, resample->fp))))
+      return GST_FLOW_ERROR;
+
+    resample->funcs =
+        gst_audio_resample_get_funcs (resample->width, resample->fp);
+  }
 
-  r = audioresample->resample;
+  GST_LOG_OBJECT (resample, "transforming buffer of %ld bytes, ts %"
+      GST_TIME_FORMAT ", duration %" GST_TIME_FORMAT ", offset %"
+      G_GINT64_FORMAT ", offset_end %" G_GINT64_FORMAT,
+      gst_buffer_get_size (inbuf), GST_TIME_ARGS (GST_BUFFER_TIMESTAMP (inbuf)),
+      GST_TIME_ARGS (GST_BUFFER_DURATION (inbuf)),
+      GST_BUFFER_OFFSET (inbuf), GST_BUFFER_OFFSET_END (inbuf));
 
-  outsize = resample_get_output_size (r);
-  if (outsize == 0) {
-    GST_DEBUG_OBJECT (audioresample, "no internal buffers needing flush");
-    goto done;
+  /* check for timestamp discontinuities;  flush/reset if needed, and set
+   * flag to resync timestamp and offset counters and send event
+   * downstream */
+  if (G_UNLIKELY (gst_audio_resample_check_discont (resample, inbuf))) {
+    gst_audio_resample_reset_state (resample);
+    resample->need_discont = TRUE;
   }
 
-  trans = GST_BASE_TRANSFORM (audioresample);
-
-  res = gst_pad_alloc_buffer (trans->srcpad, GST_BUFFER_OFFSET_NONE, outsize,
-      GST_PAD_CAPS (trans->srcpad), &outbuf);
-  if (G_UNLIKELY (res != GST_FLOW_OK)) {
-    GST_WARNING_OBJECT (audioresample, "failed allocating buffer of %d bytes",
-        outsize);
-    goto done;
+  /* handle discontinuity */
+  if (G_UNLIKELY (resample->need_discont)) {
+    resample->funcs->skip_zeros (resample->state);
+    resample->num_gap_samples = 0;
+    resample->num_nongap_samples = 0;
+    /* reset */
+    resample->samples_in = 0;
+    resample->samples_out = 0;
+    GST_DEBUG_OBJECT (resample, "found discontinuity; resyncing");
+    /* resync the timestamp and offset counters if possible */
+    if (GST_BUFFER_TIMESTAMP_IS_VALID (inbuf)) {
+      resample->t0 = GST_BUFFER_TIMESTAMP (inbuf);
+    } else {
+      GST_DEBUG_OBJECT (resample, "... but new timestamp is invalid");
+      resample->t0 = GST_CLOCK_TIME_NONE;
+    }
+    if (GST_BUFFER_OFFSET_IS_VALID (inbuf)) {
+      resample->in_offset0 = GST_BUFFER_OFFSET (inbuf);
+      resample->out_offset0 =
+          gst_util_uint64_scale_int_round (resample->in_offset0,
+          resample->outrate, resample->inrate);
+    } else {
+      GST_DEBUG_OBJECT (resample, "... but new offset is invalid");
+      resample->in_offset0 = GST_BUFFER_OFFSET_NONE;
+      resample->out_offset0 = GST_BUFFER_OFFSET_NONE;
+    }
+    /* set DISCONT flag on output buffer */
+    GST_DEBUG_OBJECT (resample, "marking this buffer with the DISCONT flag");
+    GST_BUFFER_FLAG_SET (outbuf, GST_BUFFER_FLAG_DISCONT);
+    resample->need_discont = FALSE;
   }
 
-  res = audioresample_do_output (audioresample, outbuf);
-  if (G_UNLIKELY (res != GST_FLOW_OK))
-    goto done;
+  ret = gst_audio_resample_process (resample, inbuf, outbuf);
+  if (G_UNLIKELY (ret != GST_FLOW_OK))
+    return ret;
 
-  res = gst_pad_push (trans->srcpad, outbuf);
+  GST_DEBUG_OBJECT (resample, "input = samples [%" G_GUINT64_FORMAT ", %"
+      G_GUINT64_FORMAT ") = [%" G_GUINT64_FORMAT ", %" G_GUINT64_FORMAT
+      ") ns;  output = samples [%" G_GUINT64_FORMAT ", %" G_GUINT64_FORMAT
+      ") = [%" G_GUINT64_FORMAT ", %" G_GUINT64_FORMAT ") ns",
+      GST_BUFFER_OFFSET (inbuf), GST_BUFFER_OFFSET_END (inbuf),
+      GST_BUFFER_TIMESTAMP (inbuf), GST_BUFFER_TIMESTAMP (inbuf) +
+      GST_BUFFER_DURATION (inbuf), GST_BUFFER_OFFSET (outbuf),
+      GST_BUFFER_OFFSET_END (outbuf), GST_BUFFER_TIMESTAMP (outbuf),
+      GST_BUFFER_TIMESTAMP (outbuf) + GST_BUFFER_DURATION (outbuf));
 
-done:
-  return res;
+  return GST_FLOW_OK;
 }
 
 static gboolean
-audioresample_query (GstPad * pad, GstQuery * query)
+gst_audio_resample_query (GstPad * pad, GstQuery * query)
 {
-  GstAudioresample *audioresample =
-      GST_AUDIORESAMPLE (gst_pad_get_parent (pad));
-  GstBaseTransform *trans = GST_BASE_TRANSFORM (audioresample);
+  GstAudioResample *resample = GST_AUDIO_RESAMPLE (gst_pad_get_parent (pad));
+  GstBaseTransform *trans = GST_BASE_TRANSFORM (resample);
   gboolean res = TRUE;
 
   switch (GST_QUERY_TYPE (query)) {
@@ -738,34 +1283,41 @@ audioresample_query (GstPad * pad, GstQuery * query)
       gboolean live;
       guint64 latency;
       GstPad *peer;
-      gint rate = audioresample->i_rate;
-      gint resampler_latency = audioresample->filter_length / 2;
+      gint rate = resample->inrate;
+      gint resampler_latency;
+
+      if (resample->state)
+        resampler_latency =
+            resample->funcs->get_input_latency (resample->state);
+      else
+        resampler_latency = 0;
 
       if (gst_base_transform_is_passthrough (trans))
         resampler_latency = 0;
 
-      if ((peer = gst_pad_get_peer (trans->sinkpad))) {
+      if ((peer = gst_pad_get_peer (GST_BASE_TRANSFORM_SINK_PAD (trans)))) {
         if ((res = gst_pad_query (peer, query))) {
           gst_query_parse_latency (query, &live, &min, &max);
 
-          GST_DEBUG ("Peer latency: min %"
+          GST_DEBUG_OBJECT (resample, "Peer latency: min %"
               GST_TIME_FORMAT " max %" GST_TIME_FORMAT,
               GST_TIME_ARGS (min), GST_TIME_ARGS (max));
 
           /* add our own latency */
           if (rate != 0 && resampler_latency != 0)
-            latency =
-                gst_util_uint64_scale (resampler_latency, GST_SECOND, rate);
+            latency = gst_util_uint64_scale_round (resampler_latency,
+                GST_SECOND, rate);
           else
             latency = 0;
 
-          GST_DEBUG ("Our latency: %" GST_TIME_FORMAT, GST_TIME_ARGS (latency));
+          GST_DEBUG_OBJECT (resample, "Our latency: %" GST_TIME_FORMAT,
+              GST_TIME_ARGS (latency));
 
           min += latency;
-          if (max != GST_CLOCK_TIME_NONE)
+          if (GST_CLOCK_TIME_IS_VALID (max))
             max += latency;
 
-          GST_DEBUG ("Calculated total latency : min %"
+          GST_DEBUG_OBJECT (resample, "Calculated total latency : min %"
               GST_TIME_FORMAT " max %" GST_TIME_FORMAT,
               GST_TIME_ARGS (min), GST_TIME_ARGS (max));
 
@@ -779,12 +1331,12 @@ audioresample_query (GstPad * pad, GstQuery * query)
       res = gst_pad_query_default (pad, query);
       break;
   }
-  gst_object_unref (audioresample);
+  gst_object_unref (resample);
   return res;
 }
 
 static const GstQueryType *
-audioresample_query_type (GstPad * pad)
+gst_audio_resample_query_type (GstPad * pad)
 {
   static const GstQueryType types[] = {
     GST_QUERY_LATENCY,
@@ -795,25 +1347,59 @@ audioresample_query_type (GstPad * pad)
 }
 
 static void
-gst_audioresample_set_property (GObject * object, guint prop_id,
+gst_audio_resample_set_property (GObject * object, guint prop_id,
     const GValue * value, GParamSpec * pspec)
 {
-  GstAudioresample *audioresample;
+  GstAudioResample *resample;
 
-  audioresample = GST_AUDIORESAMPLE (object);
+  resample = GST_AUDIO_RESAMPLE (object);
 
   switch (prop_id) {
-    case PROP_FILTERLEN:
-      audioresample->filter_length = g_value_get_int (value);
-      GST_DEBUG_OBJECT (GST_ELEMENT (audioresample), "new filter length %d",
-          audioresample->filter_length);
-      if (audioresample->resample) {
-        resample_set_filter_length (audioresample->resample,
-            audioresample->filter_length);
-        gst_element_post_message (GST_ELEMENT (audioresample),
-            gst_message_new_latency (GST_OBJECT (audioresample)));
-      }
+    case PROP_QUALITY:
+      GST_BASE_TRANSFORM_LOCK (resample);
+      resample->quality = g_value_get_int (value);
+      GST_DEBUG_OBJECT (resample, "new quality %d", resample->quality);
+
+      gst_audio_resample_update_state (resample, resample->width,
+          resample->channels, resample->inrate, resample->outrate,
+          resample->quality, resample->fp);
+      GST_BASE_TRANSFORM_UNLOCK (resample);
+      break;
+    case PROP_FILTER_LENGTH:{
+      gint filter_length = g_value_get_int (value);
+
+      GST_BASE_TRANSFORM_LOCK (resample);
+      if (filter_length <= 8)
+        resample->quality = 0;
+      else if (filter_length <= 16)
+        resample->quality = 1;
+      else if (filter_length <= 32)
+        resample->quality = 2;
+      else if (filter_length <= 48)
+        resample->quality = 3;
+      else if (filter_length <= 64)
+        resample->quality = 4;
+      else if (filter_length <= 80)
+        resample->quality = 5;
+      else if (filter_length <= 96)
+        resample->quality = 6;
+      else if (filter_length <= 128)
+        resample->quality = 7;
+      else if (filter_length <= 160)
+        resample->quality = 8;
+      else if (filter_length <= 192)
+        resample->quality = 9;
+      else
+        resample->quality = 10;
+
+      GST_DEBUG_OBJECT (resample, "new quality %d", resample->quality);
+
+      gst_audio_resample_update_state (resample, resample->width,
+          resample->channels, resample->inrate, resample->outrate,
+          resample->quality, resample->fp);
+      GST_BASE_TRANSFORM_UNLOCK (resample);
       break;
+    }
     default:
       G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
       break;
@@ -821,16 +1407,53 @@ gst_audioresample_set_property (GObject * object, guint prop_id,
 }
 
 static void
-gst_audioresample_get_property (GObject * object, guint prop_id,
+gst_audio_resample_get_property (GObject * object, guint prop_id,
     GValue * value, GParamSpec * pspec)
 {
-  GstAudioresample *audioresample;
+  GstAudioResample *resample;
 
-  audioresample = GST_AUDIORESAMPLE (object);
+  resample = GST_AUDIO_RESAMPLE (object);
 
   switch (prop_id) {
-    case PROP_FILTERLEN:
-      g_value_set_int (value, audioresample->filter_length);
+    case PROP_QUALITY:
+      g_value_set_int (value, resample->quality);
+      break;
+    case PROP_FILTER_LENGTH:
+      switch (resample->quality) {
+        case 0:
+          g_value_set_int (value, 8);
+          break;
+        case 1:
+          g_value_set_int (value, 16);
+          break;
+        case 2:
+          g_value_set_int (value, 32);
+          break;
+        case 3:
+          g_value_set_int (value, 48);
+          break;
+        case 4:
+          g_value_set_int (value, 64);
+          break;
+        case 5:
+          g_value_set_int (value, 80);
+          break;
+        case 6:
+          g_value_set_int (value, 96);
+          break;
+        case 7:
+          g_value_set_int (value, 128);
+          break;
+        case 8:
+          g_value_set_int (value, 160);
+          break;
+        case 9:
+          g_value_set_int (value, 192);
+          break;
+        case 10:
+          g_value_set_int (value, 256);
+          break;
+      }
       break;
     default:
       G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
@@ -838,14 +1461,141 @@ gst_audioresample_get_property (GObject * object, guint prop_id,
   }
 }
 
+/* FIXME: should have a benchmark fallback for the case where orc is disabled */
+#if defined(AUDIORESAMPLE_FORMAT_AUTO) && !defined(DISABLE_ORC)
+
+#define BENCHMARK_SIZE 512
+
+static gboolean
+_benchmark_int_float (SpeexResamplerState * st)
+{
+  gint16 in[BENCHMARK_SIZE] = { 0, }, out[BENCHMARK_SIZE / 2];
+  gfloat in_tmp[BENCHMARK_SIZE], out_tmp[BENCHMARK_SIZE / 2];
+  gint i;
+  guint32 inlen = BENCHMARK_SIZE, outlen = BENCHMARK_SIZE / 2;
+
+  for (i = 0; i < BENCHMARK_SIZE; i++) {
+    gfloat tmp = in[i];
+    in_tmp[i] = tmp / G_MAXINT16;
+  }
+
+  resample_float_resampler_process_interleaved_float (st,
+      (const guint8 *) in_tmp, &inlen, (guint8 *) out_tmp, &outlen);
+
+  if (outlen == 0) {
+    GST_ERROR ("Failed to use float resampler");
+    return FALSE;
+  }
+
+  for (i = 0; i < outlen; i++) {
+    gfloat tmp = out_tmp[i];
+    out[i] = CLAMP (tmp * G_MAXINT16 + 0.5, G_MININT16, G_MAXINT16);
+  }
+
+  return TRUE;
+}
+
+static gboolean
+_benchmark_int_int (SpeexResamplerState * st)
+{
+  gint16 in[BENCHMARK_SIZE] = { 0, }, out[BENCHMARK_SIZE / 2];
+  guint32 inlen = BENCHMARK_SIZE, outlen = BENCHMARK_SIZE / 2;
+
+  resample_int_resampler_process_interleaved_int (st, (const guint8 *) in,
+      &inlen, (guint8 *) out, &outlen);
+
+  if (outlen == 0) {
+    GST_ERROR ("Failed to use int resampler");
+    return FALSE;
+  }
+
+  return TRUE;
+}
+
+static gboolean
+_benchmark_integer_resampling (void)
+{
+  OrcProfile a, b;
+  gdouble av, bv;
+  SpeexResamplerState *sta, *stb;
+  int i;
+
+  orc_profile_init (&a);
+  orc_profile_init (&b);
+
+  sta = resample_float_resampler_init (1, 48000, 24000, 4, NULL);
+  if (sta == NULL) {
+    GST_ERROR ("Failed to create float resampler state");
+    return FALSE;
+  }
+
+  stb = resample_int_resampler_init (1, 48000, 24000, 4, NULL);
+  if (stb == NULL) {
+    resample_float_resampler_destroy (sta);
+    GST_ERROR ("Failed to create int resampler state");
+    return FALSE;
+  }
+
+  /* Benchmark */
+  for (i = 0; i < 10; i++) {
+    orc_profile_start (&a);
+    if (!_benchmark_int_float (sta))
+      goto error;
+    orc_profile_stop (&a);
+  }
+
+  /* Benchmark */
+  for (i = 0; i < 10; i++) {
+    orc_profile_start (&b);
+    if (!_benchmark_int_int (stb))
+      goto error;
+    orc_profile_stop (&b);
+  }
+
+  /* Handle results */
+  orc_profile_get_ave_std (&a, &av, NULL);
+  orc_profile_get_ave_std (&b, &bv, NULL);
+
+  /* Remember benchmark result in global variable */
+  gst_audio_resample_use_int = (av > bv);
+  resample_float_resampler_destroy (sta);
+  resample_int_resampler_destroy (stb);
+
+  if (av > bv)
+    GST_INFO ("Using integer resampler if appropiate: %lf < %lf", bv, av);
+  else
+    GST_INFO ("Using float resampler for everything: %lf <= %lf", av, bv);
+
+  return TRUE;
+
+error:
+  resample_float_resampler_destroy (sta);
+  resample_int_resampler_destroy (stb);
+
+  return FALSE;
+}
+#endif /* defined(AUDIORESAMPLE_FORMAT_AUTO) && !defined(DISABLE_ORC) */
 
 static gboolean
 plugin_init (GstPlugin * plugin)
 {
-  resample_init ();
+  GST_DEBUG_CATEGORY_INIT (audio_resample_debug, "audioresample", 0,
+      "audio resampling element");
+
+#if defined(AUDIORESAMPLE_FORMAT_AUTO) && !defined(DISABLE_ORC)
+  if (!_benchmark_integer_resampling ())
+    return FALSE;
+#else
+  GST_WARNING ("Orc disabled, can't benchmark int vs. float resampler");
+  {
+    GST_DEBUG_CATEGORY_STATIC (GST_CAT_PERFORMANCE);
+    GST_DEBUG_CATEGORY_GET (GST_CAT_PERFORMANCE, "GST_PERFORMANCE");
+    GST_CAT_WARNING (GST_CAT_PERFORMANCE, "orc disabled, no benchmarking done");
+  }
+#endif
 
   if (!gst_element_register (plugin, "audioresample", GST_RANK_PRIMARY,
-          GST_TYPE_AUDIORESAMPLE)) {
+          GST_TYPE_AUDIO_RESAMPLE)) {
     return FALSE;
   }