and make it handle more than one channel.
authorTim-Philipp Müller <tim@centricular.net>
Thu, 10 Feb 2005 15:06:13 +0000 (15:06 +0000)
committerTim-Philipp Müller <tim@centricular.net>
Thu, 10 Feb 2005 15:06:13 +0000 (15:06 +0000)
Original commit message from CVS:
Fix speed element and make it chain-based (fixes #156467), and make it handle more than one channel.

ChangeLog
gst/speed/Makefile.am
gst/speed/demo-mp3.c
gst/speed/filter.func [deleted file]
gst/speed/gstspeed.c
gst/speed/gstspeed.h

index e9b2f5a..c801394 100644 (file)
--- a/ChangeLog
+++ b/ChangeLog
@@ -1,3 +1,16 @@
+2005-02-10  Tim-Philipp Müller  <tim at centricular dot net>
+
+       * gst/speed/Makefile.am:
+       * gst/speed/demo-mp3.c: (main):
+       * gst/speed/filter.func:
+       * gst/speed/gstspeed.c: (speed_link), (speed_parse_caps),
+       (speed_class_init), (speed_init), (speed_chain_int16),
+       (speed_chain_float32), (speed_chain), (speed_set_property),
+       (speed_get_property), (speed_change_state):
+       * gst/speed/gstspeed.h:
+         Fix speed element and make it chain-based (fixes #156467),
+         and make it handle more than one channel.
+
 2005-02-10  Jan Schmidt  <thaytan@mad.scientist.com>
 
        * ext/dts/gstdtsdec.c: (gst_dtsdec_init), (gst_dtsdec_channels),
index 7062fa9..9f86662 100644 (file)
@@ -6,7 +6,7 @@ libgstspeed_la_CFLAGS = $(GST_CFLAGS)
 libgstspeed_la_LIBADD =
 libgstspeed_la_LDFLAGS = $(GST_PLUGIN_LDFLAGS)
 
-noinst_HEADERS = gstspeed.h filter.func
+noinst_HEADERS = gstspeed.h
 
 if HAVE_GTK
 noinst_PROGRAMS = demo-mp3
index 3b55194..c61f15e 100644 (file)
@@ -37,7 +37,7 @@ int
 main (int argc, char **argv)
 {
   GtkWidget *window, *vbox, *hscale, *button;
-  GstElement *filesrc, *mad, *stereo2mono, *speed, *audiosink, *pipeline;
+  GstElement *filesrc, *mad, *audioconvert, *speed, *audiosink, *pipeline;
 
   gst_init (&argc, &argv);
   gtk_init (&argc, &argv);
@@ -65,18 +65,17 @@ main (int argc, char **argv)
 
   filesrc = gst_element_factory_make ("filesrc", "filesrc");
   mad = gst_element_factory_make ("mad", "mad");
-  stereo2mono = gst_element_factory_make ("stereo2mono", "stereo2mono");
+  audioconvert = gst_element_factory_make ("audioconvert", "audioconvert0");
   speed = gst_element_factory_make ("speed", "speed");
   audiosink = gst_element_factory_make (DEFAULT_AUDIOSINK, "audiosink");
-  g_object_set (audiosink, "fragment", 0x00180008, NULL);
 
   gtk_signal_connect (GTK_OBJECT (gtk_range_get_adjustment (GTK_RANGE
               (hscale))), "value_changed", G_CALLBACK (set_speed), speed);
 
   pipeline = gst_pipeline_new ("app");
-  gst_bin_add_many (GST_BIN (pipeline), filesrc, mad, stereo2mono, speed,
+  gst_bin_add_many (GST_BIN (pipeline), filesrc, mad, audioconvert, speed,
       audiosink, NULL);
-  gst_element_link_many (filesrc, mad, stereo2mono, speed, audiosink, NULL);
+  gst_element_link_many (filesrc, mad, audioconvert, speed, audiosink, NULL);
   g_object_set (G_OBJECT (filesrc), "location", argv[1], NULL);
 
   gst_element_set_state (pipeline, GST_STATE_PLAYING);
diff --git a/gst/speed/filter.func b/gst/speed/filter.func
deleted file mode 100644 (file)
index 6802b20..0000000
+++ /dev/null
@@ -1,60 +0,0 @@
-/* -*- Mode: c; c-basic-offset: 2 -*- */ 
-    _FORMAT *in_data, *out_data;
-    static gint64 offset = 0, timestamp = 0;
-
-    /* get a buffer here so that we can have something to interpolate
-     * against for the first few samples if speed < 0.5 */
-    in_data = (_FORMAT*) GST_BUFFER_DATA(in);
-    nin = GST_BUFFER_SIZE(in)/sizeof(_FORMAT);
-    lower = in_data[0];
-    i_float = 0.5 * (speed - 1.0);
-    i = i_float + 1.0; /* ciel(i_float) for ints */
-    
-    do {
-      speed = filter->speed; /* update this, it might have changed */
-      
-      out = gst_buffer_new();
-      GST_BUFFER_DATA(out) = (gchar*) g_new(_FORMAT,SPEED_BUFSIZE/sizeof(_FORMAT));
-      GST_BUFFER_SIZE(out) = SPEED_BUFSIZE;
-      out_data = (_FORMAT*) GST_BUFFER_DATA(out);
-      nout = GST_BUFFER_SIZE(out) / sizeof(_FORMAT);
-      GST_BUFFER_TIMESTAMP (out) = timestamp;
-      offset += nout;
-      timestamp = offset * GST_SECOND / filter->rate;
-      
-      for (j=0; j<nout; j++) {
-        /* index of upper bounds of interpolation for
-         * new sample, got it by trial&error on the chalkboard */
-        i_float += speed;
-        i = i_float + 1.0; /* ciel(i_float) for ints */
-        
-        while (i >= nin) {
-          i = i % nin;
-          i_float = i_float - nin;
-          lower = in_data[nin-1];
-          gst_buffer_unref(in);
-          in = GST_BUFFER (gst_pad_pull (filter->sinkpad));
-          
-          while (GST_IS_EVENT (in)) {
-            gst_pad_event_default (filter->srcpad, GST_EVENT (in));
-            in = GST_BUFFER (gst_pad_pull (filter->sinkpad));
-          }
-          
-          in_data = (_FORMAT*) GST_BUFFER_DATA(in);
-          nin = GST_BUFFER_SIZE(in) / sizeof(_FORMAT);
-        } 
-        
-        if (i>0)
-          lower = in_data[i-1];
-        
-        interp = i_float - floor(i_float);
-        
-        out_data[j] = lower*(1-interp) + in_data[i]*interp;
-        
-        lower = in_data[i];
-      }
-      
-      gst_pad_push(filter->srcpad, GST_DATA (out));
-
-      gst_element_yield (element);
-    } while (TRUE);
index b685bb3..79b39f8 100644 (file)
 
 #include "gstspeed.h"
 
-/* buffer size to make if no bufferpool is available, must be divisible by
- * sizeof(gfloat) */
-#define SPEED_BUFSIZE 4096
-/* number of buffers to allocate per chunk in sink buffer pool */
-#define SPEED_NUMBUF 6
-
 /* elementfactory information */
 static GstElementDetails speed_details = GST_ELEMENT_DETAILS ("Speed",
     "Filter/Effect/Audio",
     "Set speed/pitch on audio/raw streams (resampler)",
-    "Andy Wingo <apwingo@eos.ncsu.edu>");
-
+    "Andy Wingo <apwingo@eos.ncsu.edu>, "
+    "Tim-Philipp Müller <tim@centricular.net>");
 
-/* Filter signals and args */
-enum
-{
-  /* FILL ME */
-  LAST_SIGNAL
-};
 
 enum
 {
@@ -55,20 +43,35 @@ enum
   ARG_SPEED
 };
 
+/* assumption here: sizeof (gfloat) = 4 */
+#define GST_SPEED_AUDIO_CAPS \
+    "audio/x-raw-float, " \
+    "rate = (int) [ 1, MAX ], " \
+    "channels = (int) [ 1, MAX ], " \
+    "endianness = (int) BYTE_ORDER, " \
+    "width = (int) 32, " \
+    "buffer-frames = (int) 0; " \
+    \
+    "audio/x-raw-int, " \
+    "rate = (int) [ 1, MAX ], " \
+    "channels = (int) [ 1, MAX ], " \
+    "endianness = (int) BYTE_ORDER, " \
+    "width = (int) 16, " \
+    "depth = (int) 16, " \
+    "signed = (boolean) true"
+
 static GstStaticPadTemplate gst_speed_sink_template =
-    GST_STATIC_PAD_TEMPLATE ("sink",
+GST_STATIC_PAD_TEMPLATE ("sink",
     GST_PAD_SINK,
     GST_PAD_ALWAYS,
-    GST_STATIC_CAPS (GST_AUDIO_INT_PAD_TEMPLATE_CAPS "; "
-        GST_AUDIO_FLOAT_STANDARD_PAD_TEMPLATE_CAPS)
+    GST_STATIC_CAPS (GST_SPEED_AUDIO_CAPS)
     );
 
 static GstStaticPadTemplate gst_speed_src_template =
-    GST_STATIC_PAD_TEMPLATE ("src",
+GST_STATIC_PAD_TEMPLATE ("src",
     GST_PAD_SRC,
     GST_PAD_ALWAYS,
-    GST_STATIC_CAPS (GST_AUDIO_INT_PAD_TEMPLATE_CAPS "; "
-        GST_AUDIO_FLOAT_STANDARD_PAD_TEMPLATE_CAPS)
+    GST_STATIC_CAPS (GST_SPEED_AUDIO_CAPS)
     );
 
 static void speed_base_init (gpointer g_class);
@@ -82,11 +85,11 @@ static void speed_get_property (GObject * object, guint prop_id, GValue * value,
 
 static gboolean speed_parse_caps (GstSpeed * filter, const GstCaps * caps);
 
-static void speed_loop (GstElement * element);
+static void speed_chain (GstPad * pad, GstData * data);
 
-static GstElementClass *parent_class = NULL;
+static GstElementStateReturn speed_change_state (GstElement * element);
 
-/*static guint gst_filter_signals[LAST_SIGNAL] = { 0 }; */
+static GstElementClass *parent_class;   /* NULL */
 
 static GstPadLinkReturn
 speed_link (GstPad * pad, const GstCaps * caps)
@@ -97,7 +100,7 @@ speed_link (GstPad * pad, const GstCaps * caps)
   filter = GST_SPEED (gst_pad_get_parent (pad));
   g_return_val_if_fail (filter != NULL, GST_PAD_LINK_REFUSED);
   g_return_val_if_fail (GST_IS_SPEED (filter), GST_PAD_LINK_REFUSED);
-  otherpad = (pad == filter->srcpad ? filter->sinkpad : filter->srcpad);
+  otherpad = (pad == filter->srcpad) ? filter->sinkpad : filter->srcpad;
 
   if (!speed_parse_caps (filter, caps))
     return GST_PAD_LINK_REFUSED;
@@ -117,29 +120,31 @@ speed_parse_caps (GstSpeed * filter, const GstCaps * caps)
 
   structure = gst_caps_get_structure (caps, 0);
 
+  mimetype = gst_structure_get_name (structure);
+  if (strcmp (mimetype, "audio/x-raw-float") == 0)
+    filter->format = GST_SPEED_FORMAT_FLOAT;
+  else if (strcmp (mimetype, "audio/x-raw-int") == 0)
+    filter->format = GST_SPEED_FORMAT_INT;
+  else
+    return FALSE;
+
   ret = gst_structure_get_int (structure, "rate", &filter->rate);
   ret &= gst_structure_get_int (structure, "channels", &filter->channels);
   ret &= gst_structure_get_int (structure, "width", &filter->width);
-  ret &= gst_structure_get_int (structure, "endianness", &filter->endianness);
 
   filter->buffer_frames = 0;
   gst_structure_get_int (structure, "buffer-frames", &filter->buffer_frames);
 
-  mimetype = gst_structure_get_name (structure);
-
-  if (strcmp (mimetype, "audio/x-raw-int") == 0) {
-    filter->format = GST_SPEED_FORMAT_INT;
-    ret &= gst_structure_get_int (structure, "depth", &filter->depth);
-    ret &= gst_structure_get_boolean (structure, "signed", &filter->is_signed);
-  } else if (strcmp (mimetype, "audio/x-raw-float") == 0) {
-    filter->format = GST_SPEED_FORMAT_FLOAT;
+  if (filter->format == GST_SPEED_FORMAT_FLOAT) {
+    filter->sample_size = filter->channels * filter->width / 8;
   } else {
-    return FALSE;
+    /* our caps only allow width == depth for now */
+    filter->sample_size = filter->channels * filter->width / 8;
   }
+
   return ret;
 }
 
-
 GType
 gst_speed_get_type (void)
 {
@@ -180,11 +185,13 @@ static void
 speed_class_init (GstSpeedClass * klass)
 {
   GObjectClass *gobject_class = (GObjectClass *) klass;
+  GstElementClass *gstelement_class = (GstElementClass *) klass;
 
   gobject_class->set_property = speed_set_property;
   gobject_class->get_property = speed_get_property;
+  gstelement_class->change_state = speed_change_state;
 
-  parent_class = g_type_class_ref (GST_TYPE_ELEMENT);
+  parent_class = g_type_class_peek_parent (klass);
 
   g_object_class_install_property (G_OBJECT_CLASS (klass), ARG_SPEED,
       g_param_spec_float ("speed", "speed", "speed",
@@ -198,73 +205,165 @@ speed_init (GstSpeed * filter)
       gst_pad_new_from_template (gst_static_pad_template_get
       (&gst_speed_sink_template), "sink");
   gst_pad_set_link_function (filter->sinkpad, speed_link);
+  gst_pad_set_chain_function (filter->sinkpad, speed_chain);
+  gst_pad_set_getcaps_function (filter->sinkpad, gst_pad_proxy_getcaps);
   gst_element_add_pad (GST_ELEMENT (filter), filter->sinkpad);
 
   filter->srcpad =
       gst_pad_new_from_template (gst_static_pad_template_get
       (&gst_speed_src_template), "src");
   gst_pad_set_link_function (filter->srcpad, speed_link);
+  gst_pad_set_getcaps_function (filter->srcpad, gst_pad_proxy_getcaps);
   gst_element_add_pad (GST_ELEMENT (filter), filter->srcpad);
 
-  gst_element_set_loop_function (GST_ELEMENT (filter), speed_loop);
+  filter->offset = 0;
+  filter->timestamp = 0;
+  filter->sample_size = 0;
 }
 
-static void
-speed_loop (GstElement * element)
+static inline guint
+speed_chain_int16 (GstSpeed * filter, GstBuffer * in_buf, GstBuffer * out_buf,
+    guint c, guint in_samples)
 {
-  GstSpeed *filter = GST_SPEED (element);
-  GstBuffer *in, *out;
-  guint i, j, nin, nout;
-  gfloat interp, speed, lower, i_float;
+  gint16 *in_data, *out_data;
+  gfloat interp, lower, i_float;
+  guint i, j;
 
-  g_return_if_fail (filter != NULL);
-  g_return_if_fail (GST_IS_SPEED (filter));
+  in_data = ((gint16 *) GST_BUFFER_DATA (in_buf)) + c;
+  out_data = ((gint16 *) GST_BUFFER_DATA (out_buf)) + c;
 
-  i = j = 0;
-  speed = filter->speed;
+  lower = in_data[0];
+  i_float = 0.5 * (filter->speed - 1.0);
+  i = (guint) ceil (i_float);
+  j = 0;
 
-  in = GST_BUFFER (gst_pad_pull (filter->sinkpad));
+  while (i < in_samples) {
+    interp = i_float - floor (i_float);
 
-  if (GST_IS_EVENT (in)) {
-    gst_pad_event_default (filter->sinkpad, GST_EVENT (in));
-    return;
+    out_data[j * filter->channels] =
+        lower * (1 - interp) + in_data[i * filter->channels] * interp;
+
+    lower = in_data[i * filter->channels];
+
+    i_float += filter->speed;
+    i = (guint) ceil (i_float);
+
+    ++j;
   }
 
-  while (GST_IS_EVENT (in)) {
-    gst_pad_event_default (filter->srcpad, GST_EVENT (in));
-    in = GST_BUFFER (gst_pad_pull (filter->sinkpad));
+  return j;
+}
+
+static inline guint
+speed_chain_float32 (GstSpeed * filter, GstBuffer * in_buf, GstBuffer * out_buf,
+    guint c, guint in_samples)
+{
+  gfloat *in_data, *out_data;
+  gfloat interp, lower, i_float;
+  guint i, j;
+
+  in_data = ((gfloat *) GST_BUFFER_DATA (in_buf)) + c;
+  out_data = ((gfloat *) GST_BUFFER_DATA (out_buf)) + c;
+
+  lower = in_data[0];
+  i_float = 0.5 * (filter->speed - 1.0);
+  i = (guint) ceil (i_float);
+  j = 0;
+
+  while (i < in_samples) {
+    interp = i_float - floor (i_float);
+
+    out_data[j * filter->channels] =
+        lower * (1 - interp) + in_data[i * filter->channels] * interp;
+
+    lower = in_data[i * filter->channels];
+
+    i_float += filter->speed;
+    i = (guint) ceil (i_float);
+
+    ++j;
   }
 
-  /* this is a bit nasty, but hey, it's what you've got to do to keep the same
-   * algorithm and multiple data types in c. */
-  if (filter->format == GST_SPEED_FORMAT_FLOAT) {
-#define _FORMAT gfloat
-#include "filter.func"
-#undef _FORMAT
-  } else if (filter->format == GST_SPEED_FORMAT_INT && filter->width == 16) {
-#define _FORMAT gint16
-#include "filter.func"
-#undef _FORMAT
-  } else if (filter->format == GST_SPEED_FORMAT_INT && filter->width == 8) {
-#define _FORMAT gint8
-#include "filter.func"
-#undef _FORMAT
-  } else {
-    GST_ELEMENT_ERROR (filter, CORE, NEGOTIATION, (NULL),
-        ("format wasn't negotiated before chain function"));
-    gst_element_yield (element);
+  return j;
+}
+
+static void
+speed_chain (GstPad * pad, GstData * data)
+{
+  GstBuffer *in_buf, *out_buf;
+  GstSpeed *filter;
+  guint c, in_samples, out_samples, out_size;
+
+  g_return_if_fail (pad != NULL);
+  g_return_if_fail (GST_IS_PAD (pad));
+  g_return_if_fail (data != NULL);
+
+  filter = GST_SPEED (GST_OBJECT_PARENT (pad));
+  g_return_if_fail (GST_IS_SPEED (filter));
+
+  if (GST_IS_EVENT (data)) {
+    switch (GST_EVENT_TYPE (GST_EVENT (data))) {
+      case GST_EVENT_DISCONTINUOUS:
+      {
+        gint64 timestamp, offset;
+
+        if (gst_event_discont_get_value (GST_EVENT (data), GST_FORMAT_BYTES,
+                &timestamp)
+            && gst_event_discont_get_value (GST_EVENT (data), GST_FORMAT_BYTES,
+                &offset)) {
+          filter->offset = offset;
+          filter->timestamp = timestamp;
+        }
+        break;
+      }
+      default:
+        break;
+    }
+    gst_pad_event_default (pad, GST_EVENT (data));
+    return;
   }
+
+  in_buf = GST_BUFFER (data);
+
+  out_size = ceil ((gfloat) GST_BUFFER_SIZE (in_buf) / filter->speed);
+  out_buf = gst_pad_alloc_buffer (filter->srcpad, -1, out_size);
+
+  in_samples = GST_BUFFER_SIZE (in_buf) / filter->sample_size;
+
+  out_samples = 0;
+
+  for (c = 0; c < filter->channels; ++c) {
+    if (filter->format == GST_SPEED_FORMAT_INT) {
+      out_samples = speed_chain_int16 (filter, in_buf, out_buf, c, in_samples);
+    } else {
+      out_samples =
+          speed_chain_float32 (filter, in_buf, out_buf, c, in_samples);
+    }
+  }
+
+  GST_BUFFER_SIZE (out_buf) = out_samples * filter->sample_size;
+
+  GST_BUFFER_OFFSET (out_buf) = filter->offset;
+  GST_BUFFER_TIMESTAMP (out_buf) = filter->timestamp;
+
+  filter->offset += GST_BUFFER_SIZE (out_buf) / filter->sample_size;
+  filter->timestamp = filter->offset * GST_SECOND / filter->rate;
+
+  GST_BUFFER_DURATION (out_buf) =
+      filter->timestamp - GST_BUFFER_TIMESTAMP (out_buf);
+
+  gst_pad_push (filter->srcpad, GST_DATA (out_buf));
+
+  gst_buffer_unref (in_buf);
 }
 
 static void
 speed_set_property (GObject * object, guint prop_id, const GValue * value,
     GParamSpec * pspec)
 {
-  GstSpeed *filter;
+  GstSpeed *filter = (GstSpeed *) object;
 
-  /* it's not null if we got it, but it might not be ours */
   g_return_if_fail (GST_IS_SPEED (object));
-  filter = GST_SPEED (object);
 
   switch (prop_id) {
     case ARG_SPEED:
@@ -279,11 +378,9 @@ static void
 speed_get_property (GObject * object, guint prop_id, GValue * value,
     GParamSpec * pspec)
 {
-  GstSpeed *filter;
+  GstSpeed *filter = (GstSpeed *) object;
 
-  /* it's not null if we got it, but it might not be ours */
   g_return_if_fail (GST_IS_SPEED (object));
-  filter = GST_SPEED (object);
 
   switch (prop_id) {
     case ARG_SPEED:
@@ -295,6 +392,29 @@ speed_get_property (GObject * object, guint prop_id, GValue * value,
   }
 }
 
+static GstElementStateReturn
+speed_change_state (GstElement * element)
+{
+  GstSpeed *speed = GST_SPEED (element);
+
+  switch (GST_STATE_TRANSITION (element)) {
+    case GST_STATE_PAUSED_TO_READY:
+      break;
+    case GST_STATE_READY_TO_PAUSED:
+      speed->offset = 0;
+      speed->timestamp = 0;
+      speed->sample_size = 0;
+      break;
+    default:
+      break;
+  }
+
+  if (parent_class->change_state)
+    return parent_class->change_state (element);
+
+  return GST_STATE_SUCCESS;
+}
+
 static gboolean
 plugin_init (GstPlugin * plugin)
 {
index 2bbb0e6..7c2ba2a 100644 (file)
@@ -47,23 +47,23 @@ enum _GstSpeedFormat {
 };
 
 struct _GstSpeed {
-  GstElement element;
+  GstElement     element;
 
-  GstPad *sinkpad, *srcpad;
+  GstPad        *sinkpad;
+  GstPad        *srcpad;
 
-  gfloat speed;
+  gfloat         speed;
 
-  /* valid for both int and float */
+  gint64         offset;
+  gint64         timestamp;
+
+  guint          rate;
+  guint          channels;
+  guint          width;
+  guint          buffer_frames;
+
+  guint          sample_size;
   GstSpeedFormat format;
-  guint rate;
-  guint channels;
-  guint width;
-  guint endianness;
-  guint buffer_frames;
-
-  /* valid only for format==GST_SPEED_FORMAT_INT */
-  guint depth;
-  gboolean is_signed;
 };
 
 struct _GstSpeedClass {