actually recurse into sndfile if we are able big ladspa cleanups, mainly to comply...
authorAndy Wingo <wingo@pobox.com>
Wed, 16 Jul 2003 16:08:13 +0000 (16:08 +0000)
committerAndy Wingo <wingo@pobox.com>
Wed, 16 Jul 2003 16:08:13 +0000 (16:08 +0000)
Original commit message from CVS:
* actually recurse into sndfile if we are able
* big ladspa cleanups, mainly to comply with the buffer-frames caps property, but also general
cleanups
- the samplerate prop is gone, if you want to set it explicitly (as in for get-based plugins)
you need to use a filtered connection, just like with buffer-frames
* big float2int and int2float changes for buffer-frames compatibility - I think it's quite a bit
simpler
* make the ossclock general, add it to gstaudio, and use it in sndfile as well

i need to update mimetypes, but that's coming soon. there are some other plugins that don't
support buffer-frames, i guess i need to get around to fixing them as well.

12 files changed:
common
ext/Makefile.am
ext/ladspa/gstladspa.c
ext/ladspa/gstladspa.h
ext/sndfile/gstsf.c
ext/sndfile/gstsf.h
gst-libs/ext/ffmpeg/Tag
gst-libs/gst/audio/Makefile.am
gst-libs/gst/audio/audio.c
gst-libs/gst/audio/audio.h
gst-libs/gst/audio/audioclock.c [new file with mode: 0644]
gst-libs/gst/audio/audioclock.h [new file with mode: 0644]

diff --git a/common b/common
index 74856703c922315f64b9314547966bd4963db968..063ebfd201ca87f316bf478d19440a585ce0af2c 160000 (submodule)
--- a/common
+++ b/common
@@ -1 +1 @@
-Subproject commit 74856703c922315f64b9314547966bd4963db968
+Subproject commit 063ebfd201ca87f316bf478d19440a585ce0af2c
index 843263250b0cdf565f7ce16c4bb0dc0f621e5016..558196485b6cc75c8609fd1be29836d54f6306c3 100644 (file)
@@ -273,7 +273,7 @@ SUBDIRS=$(A52DEC_DIR) $(AALIB_DIR) $(ALSA_DIR) \
        $(MAD_DIR) $(MATROSKA_DIR) $(MIKMOD_DIR) \
        $(MPEG2DEC_DIR) $(PANGO_DIR) $(RAW1394_DIR) \
        $(SDL_DIR) $(SHOUT_DIR) $(SIDPLAY_DIR) \
-       $(SMOOTHWAVE_DIR) $(SWFDEC_DIR) $(TARKIN_DIR) \
+       $(SMOOTHWAVE_DIR) $(SNDFILE_DIR) $(SWFDEC_DIR) $(TARKIN_DIR) \
        $(VORBIS_DIR) $(XVID_DIR) $(SNAPSHOT_DIR)
 
 DIST_SUBDIRS=\
index ddaa7970d1e5996c2cd692149349d85627151478..f4b9684a693c318a86b4fb281963da6fdd2e6e1f 100644 (file)
 #include "utils.h"      /* ladspa sdk utility functions */
 
 
-GST_PAD_TEMPLATE_FACTORY (ladspa_sink_factory,
-  "sink",
+/* takes ownership of the name */
+static GstPadTemplate*
+ladspa_sink_factory (gchar *name)
+{
+  return GST_PAD_TEMPLATE_NEW (
+  name,
   GST_PAD_SINK,
-  GST_PAD_REQUEST,
+  GST_PAD_ALWAYS,
   GST_CAPS_NEW (
     "ladspa_sink",
     "audio/x-raw-float",
-    "width",      GST_PROPS_INT (32),
-    "endianness", GST_PROPS_INT (G_BYTE_ORDER),
-    "rate",       GST_PROPS_INT_RANGE (4000, 96000),
-    "intercept",  GST_PROPS_FLOAT(0.0),
-    "slope",      GST_PROPS_FLOAT(1.0),
-    "channels",   GST_PROPS_INT (1)
-  )
-);
-
-GST_PAD_TEMPLATE_FACTORY (ladspa_src_factory,
-  "src",
+    "width",           GST_PROPS_INT (32),
+    "endianness",      GST_PROPS_INT (G_BYTE_ORDER),
+    "rate",            GST_PROPS_INT_RANGE (4000, 96000),
+    "buffer-frames",   GST_PROPS_INT_RANGE (1, G_MAXINT),
+    "channels",                GST_PROPS_INT (1)
+    )
+  );
+}
+
+/* takes ownership of the name */
+static GstPadTemplate*
+ladspa_src_factory (gchar *name)
+{
+  return GST_PAD_TEMPLATE_NEW (
+  name,
   GST_PAD_SRC,
-  GST_PAD_REQUEST,
+  GST_PAD_ALWAYS,
   GST_CAPS_NEW (
     "ladspa_src",
     "audio/x-raw-float",
-    "width",      GST_PROPS_INT (32),
-    "endianness", GST_PROPS_INT (G_BYTE_ORDER),
-    "rate",       GST_PROPS_INT_RANGE (4000, 96000),
-    "intercept",  GST_PROPS_FLOAT (0.0),
-    "slope",      GST_PROPS_FLOAT (1.0),
-    "channels",   GST_PROPS_INT (1)
-  )
-);
-
-static GstPadTemplate *srctempl, *sinktempl;
-
-enum {
-  ARG_0,
-  ARG_SAMPLERATE,
-  ARG_BUFFERSIZE,
-  ARG_LAST,
-};
+    "width",           GST_PROPS_INT (32),
+    "endianness",      GST_PROPS_INT (G_BYTE_ORDER),
+    "rate",            GST_PROPS_INT_RANGE (4000, 96000),
+    "buffer-frames",   GST_PROPS_INT_RANGE (1, G_MAXINT),
+    "channels",                GST_PROPS_INT (1)
+    )
+  );
+}
 
 static void                    gst_ladspa_class_init           (GstLADSPAClass *klass);
 static void                    gst_ladspa_init                 (GstLADSPA *ladspa);
 
-static void                    gst_ladspa_update_int(const GValue *value, gpointer data);
-static GstPadLinkReturn        gst_ladspa_connect              (GstPad *pad, GstCaps *caps);
-static GstPadLinkReturn        gst_ladspa_connect_get          (GstPad *pad, GstCaps *caps);
+static void                    gst_ladspa_update_int           (const GValue *value, gpointer data);
+static GstPadLinkReturn                gst_ladspa_link                 (GstPad *pad, GstCaps *caps);
 static void                    gst_ladspa_force_src_caps       (GstLADSPA *ladspa, GstPad *pad);
 
 static void                    gst_ladspa_set_property         (GObject *object, guint prop_id, const GValue *value, GParamSpec *pspec);
@@ -89,26 +87,15 @@ static void                 gst_ladspa_chain                (GstPad *pad,GstBuffer *buf);
 static GstBuffer *             gst_ladspa_get                  (GstPad *pad);
 
 static GstElementClass *parent_class = NULL;
-/* static guint gst_ladspa_signals[LAST_SIGNAL] = { 0 }; */
 
 static GstPlugin *ladspa_plugin;
 static GHashTable *ladspa_descriptors;
 
-static GstBufferPool*
-gst_ladspa_get_bufferpool (GstPad *pad)
-{
-  gint i;
-  GstBufferPool *bp;
-  GstLADSPA *ladspa = (GstLADSPA *) gst_pad_get_parent (pad);
-  GstLADSPAClass *oclass = (GstLADSPAClass *) (G_OBJECT_GET_CLASS (ladspa));
-
-  if (oclass->numsrcpads > 0)
-    for (i=0;i<oclass->numsrcpads;i++)
-      if ((bp = gst_pad_get_bufferpool(ladspa->srcpads[i])) != NULL)
-        return bp;
-
-  return NULL;
-}
+GST_DEBUG_CATEGORY_STATIC (ladspa_debug);
+#define DEBUG(...) \
+    GST_CAT_LEVEL_LOG (ladspa_debug, GST_LEVEL_DEBUG, NULL, __VA_ARGS__)
+#define DEBUG_OBJ(obj,...) \
+    GST_CAT_LEVEL_LOG (ladspa_debug, GST_LEVEL_DEBUG, obj, __VA_ARGS__)
 
 static void
 gst_ladspa_class_init (GstLADSPAClass *klass)
@@ -141,23 +128,20 @@ gst_ladspa_class_init (GstLADSPAClass *klass)
   klass->numcontrols = 0;
 
   /* walk through the ports, count the input, output and control ports */
-  for (i=0;i<desc->PortCount;i++) {
-    if (LADSPA_IS_PORT_AUDIO(desc->PortDescriptors[i]) && 
-        LADSPA_IS_PORT_INPUT(desc->PortDescriptors[i])){
-      klass->numsinkpads++;
-    }
-      
-    if (LADSPA_IS_PORT_AUDIO(desc->PortDescriptors[i]) && 
-        LADSPA_IS_PORT_OUTPUT(desc->PortDescriptors[i])){
-      klass->numsrcpads++;
-    }
-      
-    if (LADSPA_IS_PORT_CONTROL(desc->PortDescriptors[i]) && 
-        LADSPA_IS_PORT_INPUT(desc->PortDescriptors[i])){
+  for (i=0; i<desc->PortCount; i++) {
+    if (LADSPA_IS_PORT_AUDIO(desc->PortDescriptors[i]))
+      if (LADSPA_IS_PORT_INPUT(desc->PortDescriptors[i]))
+        klass->numsinkpads++;
+      else
+        klass->numsrcpads++;
+    else if (LADSPA_IS_PORT_INPUT(desc->PortDescriptors[i]))
       klass->numcontrols++;
-    }
   }
 
+  DEBUG ("ladspa element class: init %s with %d sink, %d src, %d control\n",
+         g_type_name (G_TYPE_FROM_CLASS (klass)),
+         klass->numsinkpads, klass->numsrcpads, klass->numcontrols);
+
   klass->srcpad_portnums = g_new0(gint,klass->numsrcpads);
   klass->sinkpad_portnums = g_new0(gint,klass->numsinkpads);
   klass->control_portnums = g_new0(gint,klass->numcontrols);
@@ -167,39 +151,17 @@ gst_ladspa_class_init (GstLADSPAClass *klass)
 
   /* walk through the ports, note the portnums for srcpads, sinkpads and control
      params */
-  for (i=0;i<desc->PortCount;i++) {
-    if (LADSPA_IS_PORT_AUDIO(desc->PortDescriptors[i]) && 
-        LADSPA_IS_PORT_INPUT(desc->PortDescriptors[i])){
-      GST_DEBUG ("input port %d", i);
-      klass->sinkpad_portnums[sinkcount++] = i;
-    }
-      
-    if (LADSPA_IS_PORT_AUDIO(desc->PortDescriptors[i]) && 
-        LADSPA_IS_PORT_OUTPUT(desc->PortDescriptors[i])){
-      GST_DEBUG ("output port %d", i);
-      klass->srcpad_portnums[srccount++] = i;
-    }
-      
-    if (LADSPA_IS_PORT_CONTROL(desc->PortDescriptors[i]) && 
-        LADSPA_IS_PORT_INPUT(desc->PortDescriptors[i])){
-      GST_DEBUG ("control port %d", i);
+  for (i=0; i<desc->PortCount; i++) {
+    if (LADSPA_IS_PORT_AUDIO(desc->PortDescriptors[i]))
+      if (LADSPA_IS_PORT_INPUT(desc->PortDescriptors[i]))
+        klass->sinkpad_portnums[sinkcount++] = i;
+      else
+        klass->srcpad_portnums[srccount++] = i;
+    else if (LADSPA_IS_PORT_INPUT(desc->PortDescriptors[i]))
       klass->control_portnums[controlcount++] = i;
-    }
   }
 
-  /* no sink pads - we'll use get mode and add params for samplerate and
-     buffersize */
-  if (klass->numsinkpads == 0 && klass->numsrcpads > 0){
-    g_object_class_install_property(G_OBJECT_CLASS(klass), ARG_SAMPLERATE,
-      g_param_spec_int("samplerate","samplerate","samplerate",
-                      0,G_MAXINT,44100,G_PARAM_READWRITE));
-    g_object_class_install_property(G_OBJECT_CLASS(klass), ARG_BUFFERSIZE,
-      g_param_spec_int("buffersize","buffersize","buffersize",
-                      0,G_MAXINT,64,G_PARAM_READWRITE));
-
-  }
-  
-  /* now build the contorl info from the control ports */
+  /* now build the control info from the control ports */
   klass->control_info = g_new0(ladspa_control_info,klass->numcontrols);
     
   for (i=0;i<klass->numcontrols;i++) {
@@ -235,13 +197,16 @@ gst_ladspa_class_init (GstLADSPAClass *klass)
       klass->control_info[i].upper = TRUE;
       klass->control_info[i].upperbound =
         desc->PortRangeHints[current_portnum].UpperBound;
-      if (LADSPA_IS_HINT_SAMPLE_RATE(hintdesc))
+      if (LADSPA_IS_HINT_SAMPLE_RATE(hintdesc)) {
         klass->control_info[i].samplerate = TRUE;
+        klass->control_info[i].upperbound *= 44100; /* FIXME? */
+      }
     } else {
       if (argtype==G_TYPE_INT) klass->control_info[i].upperbound = (gfloat)G_MAXINT;
       if (argtype==G_TYPE_FLOAT) klass->control_info[i].upperbound = G_MAXFLOAT;
     }
 
+    /* use the lowerbound as the default value */
     klass->control_info[i].def = klass->control_info[i].lowerbound;
 
 #ifdef LADSPA_IS_HINT_HAS_DEFAULT
@@ -284,7 +249,8 @@ gst_ladspa_class_init (GstLADSPAClass *klass)
 #endif /* LADSPA_IS_HINT_HAS_DEFAULT */
 
     klass->control_info[i].def = CLAMP(klass->control_info[i].def,
-      klass->control_info[i].lowerbound, klass->control_info[i].upperbound);
+                                       klass->control_info[i].lowerbound,
+                                       klass->control_info[i].upperbound);
     
     if (LADSPA_IS_PORT_INPUT(desc->PortDescriptors[current_portnum])) {
       argperms = G_PARAM_READWRITE;
@@ -324,7 +290,7 @@ gst_ladspa_class_init (GstLADSPAClass *klass)
     
     klass->control_info[i].param_name = argname;
     
-    GST_DEBUG ("adding arg %s from %s",argname, klass->control_info[i].name);
+    DEBUG ("adding arg %s from %s", argname, klass->control_info[i].name);
     
     if (argtype==G_TYPE_BOOLEAN){
       paramspec = g_param_spec_boolean(argname,argname,argname, FALSE, argperms);
@@ -343,19 +309,21 @@ gst_ladspa_class_init (GstLADSPAClass *klass)
         klass->control_info[i].def, argperms);
     }
     
-    g_object_class_install_property(G_OBJECT_CLASS(klass), i+ARG_LAST, paramspec);
+    /* properties have an offset of 1 */
+    g_object_class_install_property(G_OBJECT_CLASS(klass), i+1, paramspec);
   }
 }
 
 static void
 gst_ladspa_init (GstLADSPA *ladspa)
 {
-  GstLADSPAClass *oclass = (GstLADSPAClass*)(G_OBJECT_GET_CLASS(ladspa));
+  GstLADSPAClass *oclass;
   ladspa_control_info cinfo;
-  
+  GList *l;
   LADSPA_Descriptor *desc;
-  gint i,sinkcount,srccount,controlcount;
+  gint i,sinkcount,srccount;
 
+  oclass = (GstLADSPAClass*)G_OBJECT_GET_CLASS (ladspa);
   desc = oclass->descriptor;
   ladspa->descriptor = oclass->descriptor;
   
@@ -365,39 +333,33 @@ gst_ladspa_init (GstLADSPA *ladspa)
   ladspa->controls = g_new(gfloat,oclass->numcontrols);
   ladspa->dpman = gst_dpman_new ("ladspa_dpman", GST_ELEMENT(ladspa));
   
-  /* walk through the ports and add all the pads */
+  /* set up pads */
   sinkcount = 0;
   srccount = 0;
-  controlcount = 0;
-  for (i=0;i<desc->PortCount;i++) {
-    
-    if (LADSPA_IS_PORT_AUDIO(desc->PortDescriptors[i])){
-      gchar *canon_port_name = g_strdup((gchar *)desc->PortNames[i]);
-      g_strcanon (canon_port_name, G_CSET_A_2_Z G_CSET_a_2_z G_CSET_DIGITS "-", '-');
-      if (LADSPA_IS_PORT_INPUT(desc->PortDescriptors[i])) {
-        ladspa->sinkpads[sinkcount] = gst_pad_new_from_template (sinktempl, canon_port_name);
-        gst_element_add_pad(GST_ELEMENT(ladspa),ladspa->sinkpads[sinkcount]);
-        sinkcount++;
-      }
-      if (LADSPA_IS_PORT_OUTPUT(desc->PortDescriptors[i])) {
-        ladspa->srcpads[srccount] = gst_pad_new_from_template (srctempl, canon_port_name);
-        gst_element_add_pad(GST_ELEMENT(ladspa),ladspa->srcpads[srccount]);
-        srccount++;
-      }
-    }
-    if (LADSPA_IS_PORT_CONTROL(desc->PortDescriptors[i]) &&
-        LADSPA_IS_PORT_INPUT(desc->PortDescriptors[i])) {
-      cinfo = oclass->control_info[controlcount];
-      /* use the lowerbound as the default value if it exists */
-      ladspa->controls[controlcount]=cinfo.def;
+  for (l=GST_ELEMENT_CLASS (oclass)->padtemplates; l; l=l->next) {
+    GstPad *pad = gst_pad_new_from_template (GST_PAD_TEMPLATE (l->data),
+                                             GST_PAD_TEMPLATE_NAME_TEMPLATE (l->data));
+    gst_pad_set_link_function (pad, gst_ladspa_link);
+    gst_element_add_pad ((GstElement*)ladspa, pad);
+
+    if (GST_PAD_DIRECTION (pad) == GST_PAD_SINK)
+      ladspa->sinkpads[sinkcount++] = pad;
+    else
+      ladspa->srcpads[srccount++] = pad;
+  }
+  
+  /* set up dparams */
+  for (i=0; i<oclass->numcontrols; i++) {
+    if (LADSPA_IS_PORT_INPUT(desc->PortDescriptors[i])) {
+      cinfo = oclass->control_info[i];
+      ladspa->controls[i]=cinfo.def;
       
-      /* set up dparams for this instance */
       if (cinfo.toggled){
         gst_dpman_add_required_dparam_callback (
           ladspa->dpman, 
           g_param_spec_int(cinfo.param_name, cinfo.name, cinfo.name,
-                           0, 1, (gint)(ladspa->controls[controlcount]), G_PARAM_READWRITE),
-          "int", gst_ladspa_update_int, &(ladspa->controls[controlcount])
+                           0, 1, (gint)(ladspa->controls[i]), G_PARAM_READWRITE),
+          "int", gst_ladspa_update_int, &(ladspa->controls[i])
         );
       }
       else if (cinfo.integer){
@@ -405,8 +367,8 @@ gst_ladspa_init (GstLADSPA *ladspa)
           ladspa->dpman, 
           g_param_spec_int(cinfo.param_name, cinfo.name, cinfo.name,
                            (gint)cinfo.lowerbound, (gint)cinfo.upperbound,
-                           (gint)ladspa->controls[controlcount], G_PARAM_READWRITE),
-          "int", gst_ladspa_update_int, &(ladspa->controls[controlcount])
+                           (gint)ladspa->controls[i], G_PARAM_READWRITE),
+          "int", gst_ladspa_update_int, &(ladspa->controls[i])
         );
       }
       else if (cinfo.samplerate){
@@ -414,8 +376,8 @@ gst_ladspa_init (GstLADSPA *ladspa)
           ladspa->dpman, 
           g_param_spec_float(cinfo.param_name, cinfo.name, cinfo.name,
                            cinfo.lowerbound, cinfo.upperbound,
-                           ladspa->controls[controlcount], G_PARAM_READWRITE),
-          "hertz-rate-bound", &(ladspa->controls[controlcount])
+                           ladspa->controls[i], G_PARAM_READWRITE),
+          "hertz-rate-bound", &(ladspa->controls[i])
         );
       }
       else {
@@ -423,53 +385,41 @@ gst_ladspa_init (GstLADSPA *ladspa)
           ladspa->dpman, 
           g_param_spec_float(cinfo.param_name, cinfo.name, cinfo.name,
                            cinfo.lowerbound, cinfo.upperbound,
-                           ladspa->controls[controlcount], G_PARAM_READWRITE),
-          "float", &(ladspa->controls[controlcount])
+                           ladspa->controls[i], G_PARAM_READWRITE),
+          "float", &(ladspa->controls[i])
         );
       }
-
-      controlcount++;
     }
   }
 
   /* nonzero default needed to instantiate() some plugins */
   ladspa->samplerate = 44100;
 
-  ladspa->buffersize = 64;
-  ladspa->numbuffers = 16;
+  ladspa->buffer_frames = 0; /* should be set with caps */
   ladspa->activated = FALSE;
   ladspa->bufpool = NULL;
   ladspa->inplace_broken = LADSPA_IS_INPLACE_BROKEN(ladspa->descriptor->Properties);
 
   if (sinkcount==0 && srccount == 1) {
     /* get mode (no sink pads) */
-    GST_DEBUG ("mono get mode with 1 src pad");
-
-    ladspa->newcaps = TRUE;
+    DEBUG_OBJ (ladspa, "mono get mode with 1 src pad");
 
-    gst_pad_set_link_function (ladspa->srcpads[0], gst_ladspa_connect_get);
     gst_pad_set_get_function (ladspa->srcpads[0], gst_ladspa_get);
   } else if (sinkcount==1){
     /* with one sink we can use the chain function */
-    GST_DEBUG ("chain mode");
+    DEBUG_OBJ (ladspa, "chain mode");
 
-    gst_pad_set_link_function (ladspa->sinkpads[0], gst_ladspa_connect);
     gst_pad_set_chain_function (ladspa->sinkpads[0], gst_ladspa_chain);
-    gst_pad_set_bufferpool_function (ladspa->sinkpads[0], gst_ladspa_get_bufferpool);
   } else if (sinkcount > 1){
     /* more than one sink pad needs loop mode */
-    GST_DEBUG ("loop mode with %d sink pads and %d src pads", sinkcount, srccount);
+    DEBUG_OBJ (ladspa, "loop mode with %d sink pads and %d src pads", sinkcount, srccount);
 
-    for (i=0;i<sinkcount;i++) {
-      gst_pad_set_link_function (ladspa->sinkpads[i], gst_ladspa_connect);
-      gst_pad_set_bufferpool_function (ladspa->sinkpads[i], gst_ladspa_get_bufferpool);
-    }
     gst_element_set_loop_function (GST_ELEMENT (ladspa), gst_ladspa_loop);
-  } 
-  else if (sinkcount==0 && srccount == 0){
-    /* for some reason these plugins exist - we'll just ignore them */
+  } else if (sinkcount==0 && srccount == 0) {
+    /* for example, a plugin with only control inputs and output -- just ignore
+     * it for now */
   } else {
-    GST_DEBUG ("%d sink pads, %d src pads not yet supported", sinkcount, srccount);
+    g_warning ("%d sink pads, %d src pads not yet supported", sinkcount, srccount);
   }
 
   gst_ladspa_instantiate (ladspa);
@@ -483,169 +433,128 @@ gst_ladspa_update_int(const GValue *value, gpointer data)
 }
 
 static GstPadLinkReturn
-gst_ladspa_connect (GstPad *pad, GstCaps *caps)
+gst_ladspa_link (GstPad *pad, GstCaps *caps)
 {
-  GstLADSPA *ladspa = (GstLADSPA *) GST_PAD_PARENT (pad);
-  GstLADSPAClass *oclass = (GstLADSPAClass *) (G_OBJECT_GET_CLASS (ladspa));
-  guint i;
+  GstElement *element = (GstElement*)GST_PAD_PARENT (pad);
+  GstLADSPA *ladspa = (GstLADSPA*)element;
+  const GList *l = NULL;
   gint rate;
 
-  g_return_val_if_fail (caps != NULL, GST_PAD_LINK_DELAYED);
-  g_return_val_if_fail (pad  != NULL, GST_PAD_LINK_DELAYED);
-
-  if (gst_caps_get_int (caps, "rate", &rate)){
+  if (GST_CAPS_IS_FIXED (caps)) {
+    /* if this fails in some other plugin, the graph is left in an inconsistent
+       state */
+    for (l=gst_element_get_pad_list (element); l; l=l->next)
+      if (pad != (GstPad*)l->data)
+        if (gst_pad_try_set_caps ((GstPad*)l->data, caps) <= 0)
+          return GST_PAD_LINK_REFUSED;
+    
+    /* we assume that the ladspa plugin can handle any sample rate, so this
+       check gets put last */
+    gst_caps_get_int (caps, "rate", &rate);
     /* have to instantiate ladspa plugin when samplerate changes (groan) */
-    if (ladspa->samplerate != rate){
+    if (ladspa->samplerate != rate) {
       ladspa->samplerate = rate;
-  
       if (! gst_ladspa_instantiate(ladspa))
         return GST_PAD_LINK_REFUSED;
     }
-  }
-
-  /* if the caps are fixed, we are going to try to set all srcpads using this
-     one caps object. if any of the pads barfs, we'll refuse the connection. i'm
-     not sure if this is correct. */
-  if (GST_CAPS_IS_FIXED (caps)) {
-    for (i=0;i<oclass->numsrcpads;i++) {
-      if (gst_pad_try_set_caps (ladspa->srcpads[i], caps) <= 0)
-        return GST_PAD_LINK_REFUSED;
-    }
+    
+    gst_caps_get_int (caps, "buffer-frames", &ladspa->buffer_frames);
+    
+    if (ladspa->bufpool)
+      gst_buffer_pool_unref (ladspa->bufpool);
+    ladspa->bufpool = gst_buffer_pool_get_default (ladspa->buffer_frames * sizeof(gfloat),
+                                                   3);
+    
+    return GST_PAD_LINK_OK;
   }
   
-  return GST_PAD_LINK_OK;
+  return GST_PAD_LINK_DELAYED;
 }
 
-static GstPadLinkReturn 
-gst_ladspa_connect_get (GstPad *pad, GstCaps *caps) 
+static void
+gst_ladspa_force_src_caps(GstLADSPA *ladspa, GstPad *pad)
 {
-  GstLADSPA *ladspa = (GstLADSPA*)GST_OBJECT_PARENT (pad);
-  gint rate;
-  g_return_val_if_fail (caps != NULL, GST_PAD_LINK_DELAYED);
-  g_return_val_if_fail (pad  != NULL, GST_PAD_LINK_DELAYED);
-  
-  if (gst_caps_get_int (caps, "rate", &rate)){
-    if (ladspa->samplerate != rate) {
-      ladspa->samplerate = rate;
-      if (! gst_ladspa_instantiate(ladspa))
-        return GST_PAD_LINK_REFUSED;
-    }
+  if (!ladspa->buffer_frames) {
+    ladspa->buffer_frames = 256; /* 5 ms at 44100 kHz (just a default...) */
+    g_return_if_fail (ladspa->bufpool == NULL);
+    ladspa->bufpool = gst_buffer_pool_get_default (ladspa->buffer_frames * sizeof(gfloat),
+                                                   3);
   }
 
-  return GST_PAD_LINK_OK;
-}
+  DEBUG_OBJ (ladspa, "forcing caps with rate=%d, buffer-frames=%d",
+             ladspa->samplerate, ladspa->buffer_frames);
 
-static void
-gst_ladspa_force_src_caps(GstLADSPA *ladspa, GstPad *pad)
-{
-  GST_DEBUG ("forcing caps with rate %d", ladspa->samplerate);
   gst_pad_try_set_caps (pad, gst_caps_new (
     "ladspa_src_caps",
     "audio/x-raw-float",
     gst_props_new (
-      "width",      GST_PROPS_INT (32),
-      "endianness", GST_PROPS_INT (G_BYTE_ORDER),
-      "intercept",  GST_PROPS_FLOAT(0.0),
-      "slope",      GST_PROPS_FLOAT(1.0),
-      "rate",       GST_PROPS_INT (ladspa->samplerate),
-      "channels",   GST_PROPS_INT (1),
+      "width",         GST_PROPS_INT (32),
+      "endianness",    GST_PROPS_INT (G_BYTE_ORDER),
+      "buffer-frames", GST_PROPS_INT (ladspa->buffer_frames),
+      "rate",          GST_PROPS_INT (ladspa->samplerate),
+      "channels",      GST_PROPS_INT (1),
       NULL
     )
   ));
-  ladspa->newcaps=FALSE;
 }
 
 static void
 gst_ladspa_set_property (GObject *object, guint prop_id, const GValue *value, GParamSpec *pspec)
 {
   GstLADSPA *ladspa = (GstLADSPA*)object;
-  gint cid = prop_id - ARG_LAST;
   GstLADSPAClass *oclass;
   ladspa_control_info *control_info;
-  gfloat val=0.0;
     
-  /* these are only registered in get mode */
-  switch (prop_id) {
-    case ARG_SAMPLERATE:
-      ladspa->samplerate = g_value_get_int (value);
-      ladspa->newcaps=TRUE;
-      gst_ladspa_instantiate (ladspa);
-      break;
-    case ARG_BUFFERSIZE:
-      ladspa->buffersize = g_value_get_int (value);
-      break;
-  }
-  
-  /* is it a ladspa plugin arg? */
-  if (cid < 0) return;
-
   oclass = (GstLADSPAClass*)(G_OBJECT_GET_CLASS (object));
 
-  /* verify it exists and is a control (not a port) */
-  g_return_if_fail(cid < oclass->numcontrols);
+  /* remember, properties have an offset of 1 */
+  prop_id--;
+
+  /* verify it exists */
+  g_return_if_fail (prop_id < oclass->numcontrols);
   
-  control_info = &(oclass->control_info[cid]);
+  control_info = &(oclass->control_info[prop_id]);
   g_return_if_fail (control_info->name != NULL);
 
   /* check to see if it's writable */
   g_return_if_fail (control_info->writable);
 
   /* now see what type it is */
-  if (control_info->toggled) {
-    if (g_value_get_boolean (value))
-      ladspa->controls[cid] = 1.0;
-    else
-      ladspa->controls[cid] = 0.0;
-  } else if (control_info->integer) {
-    val = (gfloat)g_value_get_int (value);
-    ladspa->controls[cid] = val;
-  } else {
-    val = g_value_get_float (value);
-    ladspa->controls[cid] = val;
-  }    
-
-  GST_DEBUG ("set arg %s to %f", control_info->name, ladspa->controls[cid]);
+  if (control_info->toggled)
+    ladspa->controls[prop_id] = g_value_get_boolean (value) ? 1.f : 0.f;
+  else if (control_info->integer)
+    ladspa->controls[prop_id] = g_value_get_int (value);
+  else
+    ladspa->controls[prop_id] = g_value_get_float (value);
+
+  DEBUG_OBJ (object, "set arg %s to %f", control_info->name, ladspa->controls[prop_id]);
 }
 
 static void
 gst_ladspa_get_property (GObject *object, guint prop_id, GValue *value, GParamSpec *pspec)
 {
   GstLADSPA *ladspa = (GstLADSPA*)object;
-  gint cid = prop_id - ARG_LAST;
   GstLADSPAClass *oclass = (GstLADSPAClass*)(G_OBJECT_GET_CLASS (object));
   ladspa_control_info *control_info;
 
-  /* these are only registered in get mode */
-  switch (prop_id){
-    case ARG_SAMPLERATE:
-      g_value_set_int (value, ladspa->samplerate);
-      break;
-    case ARG_BUFFERSIZE:
-      g_value_set_int (value, ladspa->buffersize);
-      break;
-  }
-    
-  if (cid < 0) return;
+  /* remember, properties have an offset of 1 */
+  prop_id--;
 
-  /* verify it exists and is a control (not a port) */
-  if (cid >= oclass->numcontrols) return;
-  control_info = &(oclass->control_info[cid]);
-  if (control_info->name == NULL) return;
+  /* verify it exists */
+  g_return_if_fail (prop_id < oclass->numcontrols);
 
-  GST_DEBUG ("got arg %s as %f", control_info->name, ladspa->controls[cid]);
+  control_info = &(oclass->control_info[prop_id]);
+  g_return_if_fail (control_info->name != NULL);
 
   /* now see what type it is */
-  if (control_info->toggled) {
-    if (ladspa->controls[cid] == 1.0)
-      g_value_set_boolean (value, TRUE);
-    else
-      g_value_set_boolean (value, FALSE);
-  } else if (control_info->integer) {
-    g_value_set_int (value, (gint)ladspa->controls[cid]);
-  } else {
-    g_value_set_float (value, ladspa->controls[cid]);
-  }
+  if (control_info->toggled)
+    g_value_set_boolean (value, ladspa->controls[prop_id] == 1.0);
+  else if (control_info->integer)
+    g_value_set_int (value, (gint)ladspa->controls[prop_id]);
+  else
+    g_value_set_float (value, ladspa->controls[prop_id]);
+
+  DEBUG_OBJ (object, "got arg %s as %f", control_info->name, ladspa->controls[prop_id]);
 }
 
 static gboolean
@@ -666,24 +575,21 @@ gst_ladspa_instantiate (GstLADSPA *ladspa)
   }
         
   /* instantiate the plugin */ 
-  GST_DEBUG ("instantiating the plugin");
+  DEBUG_OBJ (ladspa, "instantiating the plugin at %d Hz", ladspa->samplerate);
   
   ladspa->handle = desc->instantiate(desc,ladspa->samplerate);
   g_return_val_if_fail (ladspa->handle != NULL, FALSE);
 
-  /* walk through the ports and add all the arguments */
-  for (i=0;i<oclass->numcontrols;i++) {
-    /* connect the argument to the plugin */
-    GST_DEBUG ("added control port %d", oclass->control_portnums[i]);
+  /* connect the control ports */
+  for (i=0;i<oclass->numcontrols;i++)
     desc->connect_port(ladspa->handle,
                        oclass->control_portnums[i],
                        &(ladspa->controls[i]));
-  }
 
   /* reactivate if it was activated before the reinstantiation */
-  if (was_activated){
+  if (was_activated)
     gst_ladspa_activate(ladspa);
-  }
+
   return TRUE;
 }
 
@@ -694,7 +600,6 @@ gst_ladspa_change_state (GstElement *element)
   GstLADSPA *ladspa = (GstLADSPA*)element;
   desc = ladspa->descriptor;
 
-  GST_DEBUG ("changing state");
   switch (GST_STATE_TRANSITION (element)) {
     case GST_STATE_NULL_TO_READY:
       gst_ladspa_activate(ladspa);
@@ -713,53 +618,46 @@ gst_ladspa_change_state (GstElement *element)
 }
 
 static void
-gst_ladspa_activate(GstLADSPA *ladspa)
+gst_ladspa_activate (GstLADSPA *ladspa)
 {
   LADSPA_Descriptor *desc;
   desc = ladspa->descriptor;
   
-  if (ladspa->activated){
+  if (ladspa->activated)
     gst_ladspa_deactivate(ladspa);
-  }
   
-  GST_DEBUG ("activating");
+  DEBUG_OBJ (ladspa, "activating");
 
   /* activate the plugin (function might be null) */
-  if (desc->activate != NULL) {
+  if (desc->activate != NULL)
     desc->activate(ladspa->handle);
-  }
 
   ladspa->activated = TRUE;
 }
 
 static void
-gst_ladspa_deactivate(GstLADSPA *ladspa)
+gst_ladspa_deactivate (GstLADSPA *ladspa)
 {
   LADSPA_Descriptor *desc;
   desc = ladspa->descriptor;
 
-  GST_DEBUG ("deactivating");
+  DEBUG_OBJ (ladspa, "deactivating");
 
   /* deactivate the plugin (function might be null) */
-  if (ladspa->activated && (desc->deactivate != NULL)) {
+  if (ladspa->activated && (desc->deactivate != NULL))
     desc->deactivate(ladspa->handle);
-  }
 
   ladspa->activated = FALSE;
 }
 
 static void
-gst_ladspa_loop(GstElement *element)
+gst_ladspa_loop (GstElement *element)
 {
-  guint        bufferbytesize, i, numsrcpads, numsinkpads, num_empty_pads;
+  guint        i, j, numsrcpads, numsinkpads;
   guint        num_processed, num_to_process;
-  GstEvent     *event = NULL;
-  guint32       waiting;
-  guint32       got_bytes;
+  gint         largest_buffer;
   LADSPA_Data  **data_in, **data_out;
   GstBuffer    **buffers_in, **buffers_out;
-  GstBufferPool *bufpool;
-  GstByteStream **bytestreams;
  
   GstLADSPA       *ladspa = (GstLADSPA *)element;
   GstLADSPAClass  *oclass = (GstLADSPAClass*)(G_OBJECT_GET_CLASS (ladspa));
@@ -768,212 +666,162 @@ gst_ladspa_loop(GstElement *element)
   numsinkpads = oclass->numsinkpads;
   numsrcpads = oclass->numsrcpads;
   
+  /* fixme: these mallocs need to die */
   data_in = g_new0(LADSPA_Data*, numsinkpads);
   data_out = g_new0(LADSPA_Data*, numsrcpads);
   buffers_in = g_new0(GstBuffer*, numsinkpads);
   buffers_out = g_new0(GstBuffer*, numsrcpads);
-  bytestreams = g_new0(GstByteStream*, numsinkpads);
   
-  /* find a bufferpool */
-  if (numsrcpads > 0 && (bufpool = gst_pad_get_bufferpool (ladspa->srcpads[0]))) {
-    GST_DEBUG ("Got bufferpool from first source pad");
-  } else {
-    bufferbytesize = sizeof (LADSPA_Data) * ladspa->buffersize;
-    bufpool = gst_buffer_pool_get_default (bufferbytesize, ladspa->numbuffers);
-    GST_DEBUG ("Created default bufferpool, %d x %d bytes", ladspa->numbuffers, bufferbytesize);
-  }
+  largest_buffer = -1;
 
-  /* get the bytestreams for each pad */
-  for (i=0 ; i<numsinkpads ; i++){
-    bytestreams[i] = gst_bytestream_new (ladspa->sinkpads[i]);
-  }
-
-  /* since this is a loop element, we just loop here til things fall apart. */
-  do {
-    /* we need to get the buffers_out first so we can know how many bytes to process */
-    for (i=0 ; i<numsrcpads ; i++){
-      buffers_out[i] = gst_buffer_new_from_pool (bufpool, 0, 0);
-      GST_BUFFER_TIMESTAMP(buffers_out[i]) = ladspa->timestamp;
-      data_out[i] = (LADSPA_Data *) GST_BUFFER_DATA(buffers_out[i]);
-    }
-
-    bufferbytesize = GST_BUFFER_SIZE (buffers_out[0]);
-    ladspa->buffersize = bufferbytesize / sizeof (LADSPA_Data);
-
-    num_empty_pads = 0;
-    /* first get all the necessary data from the input ports */
-    for (i=0 ; i<numsinkpads ; i++){  
-      GST_DEBUG ("pulling %u bytes through channel %d's bytestream", bufferbytesize, i);
-      got_bytes = gst_bytestream_read (bytestreams[i], buffers_in + i, bufferbytesize);
-
-      if (got_bytes != bufferbytesize) {
-        /* we need to check for an event. */
-        gst_bytestream_get_status (bytestreams[i], &waiting, &event);
-
-        if (event && GST_EVENT_TYPE(event) == GST_EVENT_EOS) {
-          /* if we get an EOS event from one of our sink pads, we assume that
-             pad's finished handling data. delete the bytestream, free up the
-             pad, and free up the memory associated with the input channel. */
-          GST_DEBUG ("got an EOS event on sinkpad %d", i);
-        }
-        /* CHECKME should maybe check for other events and try to pull more data here */
-        num_empty_pads++;
+  /* first get all the necessary data from the input ports */
+  for (i=0 ; i<numsinkpads ; i++){  
+  get_buffer:
+    buffers_in[i] = gst_pad_pull (ladspa->sinkpads[i]);
+    
+    if (GST_IS_EVENT (buffers_in[i])) {
+      /* push it out on all pads */
+      gst_data_ref_by_count ((GstData*)buffers_in[i], numsrcpads);
+      for (j=0; j<numsrcpads; j++)
+        gst_pad_push (ladspa->srcpads[j], buffers_in[i]);
+      if (GST_EVENT_TYPE (buffers_in[i]) == GST_EVENT_EOS) {
+        /* shut down */
+        gst_element_set_eos (element);
+        return;
       } else {
-        data_in[i] = (LADSPA_Data *) GST_BUFFER_DATA(buffers_in[i]);
-        GST_BUFFER_TIMESTAMP(buffers_in[i]) = ladspa->timestamp;
+        goto get_buffer;
       }
     }
 
-    if (num_empty_pads > 0){
-      if (num_empty_pads < numsinkpads){
-        /* only some pads have EOS, need to create some empty buffers */
-        for (i=0 ; i<numsinkpads ; i++){
-          if (buffers_in[i] == NULL){
-            int x;
-            LADSPA_Data  *data;
-            buffers_in[i] = gst_buffer_new_from_pool (ladspa->bufpool, 0, 0);
-            GST_BUFFER_TIMESTAMP(buffers_in[i]) = ladspa->timestamp;
-            data_in[i] = data = (LADSPA_Data *) GST_BUFFER_DATA(buffers_in[i]); 
-            for (x=0 ; x < ladspa->buffersize ; x++) 
-              data[x] = 0.0F;
-
-            data_in[i] = (LADSPA_Data *) GST_BUFFER_DATA(buffers_in[i]);
-            GST_BUFFER_TIMESTAMP(buffers_in[i]) = ladspa->timestamp;
-          }
-        }
-      }
-      else {
-        /* all pads have EOS, time to quit */
-        /* CHECKME do I have to push EOS events here? */
-        GST_DEBUG ("All sink pads have EOS, finishing.");
-        break;
-      }
-    }
-    
-    GST_DPMAN_PREPROCESS(ladspa->dpman, ladspa->buffersize, ladspa->timestamp);
-    num_processed = 0;
-
-    /* split up processing of the buffer into chunks so that dparams can
-     * be updated when required.
-     * In many cases the buffer will be processed in one chunk anyway.
-     */
-    while(GST_DPMAN_PROCESS(ladspa->dpman, num_processed)) {
-
-      num_to_process = GST_DPMAN_FRAMES_TO_PROCESS(ladspa->dpman);
-      for (i=0 ; i<numsinkpads ; i++){
-        desc->connect_port (ladspa->handle, oclass->sinkpad_portnums[i], data_in[i]);
-      }
-      for (i=0 ; i<numsrcpads ; i++){
-        desc->connect_port (ladspa->handle, oclass->srcpad_portnums[i], data_out[i]);
-      }
-      desc->run(ladspa->handle, num_to_process);
-      for (i=0 ; i<numsinkpads ; i++){
-        data_in[i] += num_to_process;
-      }
-      for (i=0 ; i<numsrcpads ; i++){
-        data_out[i] += num_to_process;
-      }
+    if (largest_buffer < 0)
+      largest_buffer = GST_BUFFER_SIZE (buffers_in[i])/sizeof(gfloat);
+    else
+      largest_buffer = MIN (GST_BUFFER_SIZE (buffers_in[i])/sizeof(gfloat), largest_buffer);
+    data_in[i] = (LADSPA_Data *) GST_BUFFER_DATA(buffers_in[i]);
+    GST_BUFFER_TIMESTAMP(buffers_in[i]) = ladspa->timestamp;
+  }
 
-      num_processed += num_to_process;
-    }
-    
-    for (i=0 ; i<numsrcpads ; i++) {
-      GST_DEBUG ("pushing buffer (%p) on src pad %d", buffers_out[i], i);
-      gst_pad_push (ladspa->srcpads[i], buffers_out[i]);
-      
-      data_out[i] = NULL;
-      buffers_out[i] = NULL;
+  if (!ladspa->bufpool) {
+    gst_element_error (element, "Caps were never set, bailing...");
+    return;
+  }
+
+  i=0;
+  if (!ladspa->inplace_broken) {
+    for (; i<numsrcpads && i<numsinkpads; i++) {
+      /* reuse input buffers */
+      buffers_out[i] = buffers_in[i];
+      data_out[i] = data_in[i];
     }
-    for (i=0 ; i<numsinkpads ; i++) {
-      gst_buffer_unref(buffers_in[i]);
-      data_in[i] = NULL;
-      buffers_in[i] = NULL;
-    }      
+  }
+  for (; i<numsrcpads; i++) {
+    /* we have to make new buffers -- at least we're taking them from a pool */
+    buffers_out[i] = gst_buffer_new_from_pool (ladspa->bufpool, 0, 0);
+    GST_BUFFER_TIMESTAMP (buffers_out[i]) = ladspa->timestamp;
+    data_out[i] = (LADSPA_Data*)GST_BUFFER_DATA (buffers_out[i]);
+  }
+  
+  GST_DPMAN_PREPROCESS(ladspa->dpman, largest_buffer, ladspa->timestamp);
+  num_processed = 0;
 
-    ladspa->timestamp += ladspa->buffersize * 10^9 / ladspa->samplerate;
+  /* split up processing of the buffer into chunks so that dparams can
+   * be updated when required.
+   * In many cases the buffer will be processed in one chunk anyway.
+   */
+  while (GST_DPMAN_PROCESS (ladspa->dpman, num_processed)) {
+    num_to_process = GST_DPMAN_FRAMES_TO_PROCESS(ladspa->dpman);
 
-    gst_element_yield (element);
-  } while (TRUE);
+    for (i=0 ; i<numsinkpads ; i++)
+      desc->connect_port (ladspa->handle, oclass->sinkpad_portnums[i], data_in[i]);
+    for (i=0 ; i<numsrcpads ; i++)
+      desc->connect_port (ladspa->handle, oclass->srcpad_portnums[i], data_out[i]);
 
-  gst_buffer_pool_unref(bufpool);
+    desc->run(ladspa->handle, num_to_process);
 
-  for (i=0 ; i<numsinkpads ; i++){
-    gst_bytestream_destroy (bytestreams[i]);
+    for (i=0 ; i<numsinkpads ; i++)
+      data_in[i] += num_to_process;
+    for (i=0 ; i<numsrcpads ; i++)
+      data_out[i] += num_to_process;
+    
+    num_processed += num_to_process;
+  }
+    
+  for (i=0 ; i<numsinkpads ; i++) {
+    if (i >= numsrcpads || buffers_out[i] != buffers_in[i])
+      gst_buffer_unref(buffers_in[i]);
+    data_in[i] = NULL;
+    buffers_in[i] = NULL;
+  }      
+  for (i=0 ; i<numsrcpads ; i++) {
+    DEBUG_OBJ (ladspa, "pushing buffer (%p) on src pad %d", buffers_out[i], i);
+    gst_pad_push (ladspa->srcpads[i], buffers_out[i]);
+    
+    data_out[i] = NULL;
+    buffers_out[i] = NULL;
   }
+  
+  ladspa->timestamp += ladspa->buffer_frames * GST_SECOND / ladspa->samplerate;
+
+  /* FIXME: move these mallocs and frees to the state-change handler */
 
   g_free (buffers_out);
   g_free (buffers_in);
   g_free (data_out);
   g_free (data_in);
-  g_free (bytestreams);
 }
 
 static void
-gst_ladspa_chain (GstPad *pad, GstBuffer *buf)
+gst_ladspa_chain (GstPad *pad, GstBuffer *buffer_in)
 {
   LADSPA_Descriptor *desc;
   LADSPA_Data *data_in, **data_out = NULL;
   GstBuffer **buffers_out = NULL;
-
   unsigned long num_samples;
   guint num_to_process, num_processed, i, numsrcpads;
-  
   GstLADSPA *ladspa;
   GstLADSPAClass *oclass;
 
-  g_return_if_fail(pad != NULL);
-  g_return_if_fail(GST_IS_PAD(pad));
-  g_return_if_fail(buf != NULL);
-
-  ladspa = (GstLADSPA *)gst_pad_get_parent (pad);
-  g_return_if_fail(ladspa != NULL);
-
-  /* this might happen if caps nego hasn't happened */
-  g_return_if_fail(ladspa->handle != NULL);
-
+  ladspa = (GstLADSPA*)GST_OBJECT_PARENT (pad);
   oclass = (GstLADSPAClass *) (G_OBJECT_GET_CLASS (ladspa));
+  data_in = (LADSPA_Data *) GST_BUFFER_DATA(buffer_in);
+  num_samples = GST_BUFFER_SIZE(buffer_in) / sizeof(gfloat);
+  numsrcpads = oclass->numsrcpads;
+  desc = ladspa->descriptor;
 
-  if (GST_IS_EVENT (buf)) {    
-    /* push the event out all the src pads */
-    for (i=0 ; i<oclass->numsrcpads ; i++){
-      gst_pad_push (ladspa->srcpads[0], buf);
-    }
+  /* we shouldn't get events here... */
+  g_return_if_fail (GST_IS_BUFFER (buffer_in));
+  
+  if (!ladspa->bufpool) {
+    gst_element_error ((GstElement*)ladspa, "Caps were never set, bailing...");
     return;
   }
 
-  data_in = (LADSPA_Data *) GST_BUFFER_DATA(buf);
-  num_samples = GST_BUFFER_SIZE(buf) / sizeof(gfloat);
-  numsrcpads = oclass->numsrcpads;
-
-  desc = ladspa->descriptor;
-
-  if (numsrcpads > 0){
-    guint num_created_buffers = 0; 
+  /* FIXME: this function shouldn't need to malloc() anything */
+  if (numsrcpads > 0) {
     buffers_out = g_new(GstBuffer*, numsrcpads);
     data_out = g_new(LADSPA_Data*, numsrcpads);
+  }
 
-    if (ladspa->inplace_broken){
-      num_created_buffers = numsrcpads;
-    }
-    else {
-      /* we can share the buffer for input and output */
-      buffers_out[0] = buf;
-      data_out[0] = (LADSPA_Data *)GST_BUFFER_DATA(buf);
-      num_created_buffers = numsrcpads - 1;
-    }
-
-    if (num_created_buffers > 0){
-      ladspa->bufpool = gst_buffer_pool_get_default (sizeof (LADSPA_Data) * GST_BUFFER_SIZE(buf), ladspa->numbuffers);
-
-      for (i = numsrcpads - num_created_buffers ; i<numsrcpads ; i++){
-        buffers_out[i] = gst_buffer_new_from_pool (ladspa->bufpool, 0, 0);
-        GST_BUFFER_TIMESTAMP(buffers_out[i]) = GST_BUFFER_TIMESTAMP(buf);
-        data_out[i] = (LADSPA_Data *) GST_BUFFER_DATA(buffers_out[i]);
-      }
-    }
+  i=0;
+  if (!ladspa->inplace_broken && numsrcpads) {
+    /* reuse the first (chained) buffer */
+    buffers_out[i] = buffer_in;
+    DEBUG ("reuse: %d", GST_BUFFER_SIZE (buffer_in));
+    data_out[i] = data_in;
+    i++;
+  }
+  for (; i<numsrcpads; i++) {
+    /* we have to make new buffers -- at least we're taking them from a pool */
+    buffers_out[i] = gst_buffer_new_from_pool (ladspa->bufpool, 0, 0);
+    /* the size of the buffer returned from the pool is the maximum size; this
+       chained buffer might be smaller */
+    GST_BUFFER_SIZE (buffers_out[i]) = GST_BUFFER_SIZE (buffer_in);
+    DEBUG ("new %d", GST_BUFFER_SIZE (buffer_in));
+    GST_BUFFER_TIMESTAMP (buffers_out[i]) = ladspa->timestamp;
+    data_out[i] = (LADSPA_Data*)GST_BUFFER_DATA (buffers_out[i]);
   }
 
-  GST_DPMAN_PREPROCESS(ladspa->dpman, num_samples, GST_BUFFER_TIMESTAMP(buf));
+  GST_DPMAN_PREPROCESS(ladspa->dpman, num_samples, GST_BUFFER_TIMESTAMP(buffer_in));
   num_processed = 0;
 
   /* split up processing of the buffer into chunks so that dparams can
@@ -984,29 +832,31 @@ gst_ladspa_chain (GstPad *pad, GstBuffer *buf)
     num_to_process = GST_DPMAN_FRAMES_TO_PROCESS(ladspa->dpman);
 
     desc->connect_port(ladspa->handle,oclass->sinkpad_portnums[0],data_in);  
-    for (i=0 ; i<numsrcpads ; i++){
+    for (i=0 ; i<numsrcpads ; i++)
       desc->connect_port(ladspa->handle,oclass->srcpad_portnums[i],data_out[i]);
-    }
+
     desc->run(ladspa->handle, num_to_process);
     
     data_in += num_to_process;
-    for (i=0 ; i<numsrcpads ; i++){
+    for (i=0 ; i<numsrcpads ; i++)
       data_out[i] += num_to_process;
-    }
+
     num_processed += num_to_process;
   }
 
-  if (numsrcpads > 0){
-    for (i=0 ; i<numsrcpads ; i++){
+  if (!numsrcpads || buffers_out[0] != buffer_in)
+    gst_buffer_unref(buffer_in);
+
+  if (numsrcpads) {
+    for (i=0; i<numsrcpads; i++) {
+      DEBUG_OBJ (ladspa, "pushing buffer (%p, length %d) on src pad %d",
+                 buffers_out[i], GST_BUFFER_SIZE (buffers_out[i])/sizeof(float), i);
       gst_pad_push (ladspa->srcpads[i], buffers_out[i]);
     }
+
     g_free(buffers_out);
     g_free(data_out);
-    return;
   }
-
-  /* if we have reached here, there are no src pads */
-  gst_buffer_unref(buf);
 }
 
 static GstBuffer *
@@ -1014,43 +864,25 @@ gst_ladspa_get(GstPad *pad)
 {  
   GstLADSPA *ladspa;
   GstLADSPAClass *oclass;
-
   GstBuffer *buf;
   LADSPA_Data *data;
   LADSPA_Descriptor *desc;
-
   guint num_to_process, num_processed;
 
-  g_return_val_if_fail(pad != NULL, NULL);
-  g_return_val_if_fail(GST_IS_PAD(pad), NULL);
-
   ladspa = (GstLADSPA *)gst_pad_get_parent (pad);
-  g_return_val_if_fail(ladspa != NULL, NULL);
-
-  /* this might happen if caps nego hasn't happened */
-  g_return_val_if_fail(ladspa->handle != NULL, NULL);
-
   oclass = (GstLADSPAClass*)(G_OBJECT_GET_CLASS(ladspa));
+  desc = ladspa->descriptor;
 
-  /* force src pad to set caps */
-  if (ladspa->newcaps) {
+  if (!ladspa->bufpool) {
+    /* capsnego hasn't happened... */
     gst_ladspa_force_src_caps(ladspa, ladspa->srcpads[0]);
   }
 
-  /* get a bufferpool */
-  if (ladspa->bufpool == NULL) {
-    ladspa->bufpool = gst_pad_get_bufferpool (ladspa->srcpads[0]);
-    if (ladspa->bufpool == NULL) {
-      ladspa->bufpool = gst_buffer_pool_get_default (sizeof (LADSPA_Data) * ladspa->buffersize, ladspa->numbuffers);
-    }
-  }
-
   buf = gst_buffer_new_from_pool (ladspa->bufpool, 0, 0);
   GST_BUFFER_TIMESTAMP(buf) = ladspa->timestamp;
   data = (LADSPA_Data *) GST_BUFFER_DATA(buf);  
 
-  desc = ladspa->descriptor;
-  GST_DPMAN_PREPROCESS(ladspa->dpman, GST_BUFFER_SIZE (buf) / sizeof (gfloat), ladspa->timestamp);
+  GST_DPMAN_PREPROCESS(ladspa->dpman, ladspa->buffer_frames, ladspa->timestamp);
   num_processed = 0;
 
   /* split up processing of the buffer into chunks so that dparams can
@@ -1064,6 +896,7 @@ gst_ladspa_get(GstPad *pad)
     ladspa->timestamp += num_to_process * GST_SECOND / ladspa->samplerate;
 
     desc->connect_port(ladspa->handle,oclass->srcpad_portnums[0],data);  
+
     desc->run(ladspa->handle, num_to_process);
     
     data += num_to_process;
@@ -1102,8 +935,8 @@ ladspa_describe_plugin(const char *pcFullFilename,
     gchar *type_name;
 
     /* construct the type */
-    type_name = g_strdup_printf("ladspa_%s",desc->Label);
-    g_strcanon (type_name, G_CSET_A_2_Z G_CSET_a_2_z G_CSET_DIGITS "-_+", '-');
+    type_name = g_strdup_printf("ladspa-%s",desc->Label);
+    g_strcanon (type_name, G_CSET_A_2_Z G_CSET_a_2_z G_CSET_DIGITS "-+", '-');
     /* if it's already registered, drop it */
     if (g_type_from_name(type_name)) {
       g_free(type_name);
@@ -1116,7 +949,7 @@ ladspa_describe_plugin(const char *pcFullFilename,
     details = g_new0(GstElementDetails,1);
     details->longname = g_strdup(desc->Name);
     details->klass = "Filter/Audio/LADSPA";
-    details->license = "LGPL";
+    details->license = g_strdup (desc->Copyright);
     details->description = details->longname;
     details->version = g_strdup_printf("%ld",desc->UniqueID);
     details->author = g_strdup(desc->Maker);
@@ -1133,32 +966,27 @@ ladspa_describe_plugin(const char *pcFullFilename,
                         (gpointer)desc);
     
 
-    /* only add sink padtemplate if there are sinkpads */
     for (j=0;j<desc->PortCount;j++) {
-      if (LADSPA_IS_PORT_AUDIO(desc->PortDescriptors[j]) &&
-          LADSPA_IS_PORT_INPUT(desc->PortDescriptors[j])) {
-        sinktempl = ladspa_sink_factory();
-        gst_element_factory_add_pad_template (factory, sinktempl);
-        break;
-      }
-    }
-
-    /* only add src padtemplate if there are srcpads */
-    for (j=0;j<desc->PortCount;j++) {
-      if (LADSPA_IS_PORT_AUDIO(desc->PortDescriptors[j]) &&
-          LADSPA_IS_PORT_OUTPUT(desc->PortDescriptors[j])) {
-        srctempl = ladspa_src_factory();
-        gst_element_factory_add_pad_template (factory, srctempl);
-        break;
+      if (LADSPA_IS_PORT_AUDIO(desc->PortDescriptors[j])) {
+        gchar *name = g_strdup((gchar *)desc->PortNames[j]);
+        g_strcanon (name, G_CSET_A_2_Z G_CSET_a_2_z G_CSET_DIGITS "-", '-');
+        /* the factories take ownership of the name */
+        if (LADSPA_IS_PORT_INPUT(desc->PortDescriptors[j]))
+          gst_element_factory_add_pad_template (factory, ladspa_sink_factory (name));
+        else
+          gst_element_factory_add_pad_template (factory, ladspa_src_factory (name));
       }
     }
-
   }
 }
 
 static gboolean
 plugin_init (GModule *module, GstPlugin *plugin)
 {
+  GST_DEBUG_CATEGORY_INIT (ladspa_debug, "ladspa",
+                           GST_DEBUG_FG_GREEN | GST_DEBUG_BG_BLACK | GST_DEBUG_BOLD,
+                           "LADSPA");
+
   ladspa_descriptors = g_hash_table_new(NULL,NULL);
   parent_class = g_type_class_ref(GST_TYPE_ELEMENT);
 
@@ -1181,4 +1009,3 @@ GstPluginDesc plugin_desc = {
   "ladspa",
   plugin_init
 };
-
index 7b8c9b49cbc3d4e63f56fe4d1d9b091c14148110..89a039e9c4f2eb694c4573263a8e3a8d57a79cc9 100644 (file)
@@ -60,12 +60,11 @@ struct _GstLADSPA {
   GstPad **sinkpads, 
          **srcpads;
 
-         
   GstBufferPool *bufpool;
 
-  gboolean newcaps, activated;
+  gboolean activated;
 
-  gint samplerate, buffersize, numbuffers;
+  gint samplerate, buffer_frames;
   gint64 timestamp;
   gboolean inplace_broken;
 };
index 998bf9e730f2afe845a55b908be19fcc6e1ba873..4570c1082e85ba3f5b77f229f89c001f028b4df3 100644 (file)
@@ -1,9 +1,5 @@
-/* GStreamer
- * Copyright (C) 1999,2000 Erik Walthinsen <omega@cse.ogi.edu>
- *                    2000 Wim Taymans <wtay@chello.be>
- *                    2003 Andy Wingo <wingo at pobox dot com>
- *
- * gstsf.c: libsndfile plugin for GStreamer
+/* GStreamer libsndfile plugin
+ * Copyright (C) 2003 Andy Wingo <wingo at pobox dot com>
  *
  * This library is free software; you can redistribute it and/or
  * modify it under the terms of the GNU Library General Public
  */
 
 
-#ifdef HAVE_CONFIG_H
-#include "config.h"
-#endif
-#include <gst/gst.h>
 #include <string.h>
+#include <gst/gst.h>
+
+#include <config.h>
+#include <gst/audio/audio.h>
+
 #include "gstsf.h"
 
+
 static GstElementDetails sfsrc_details = {
   "Sndfile Source",
   "Source/Audio",
@@ -58,9 +56,6 @@ enum {
   ARG_CREATE_PADS
 };
 
-#define GST_SF_BUF_BYTES 2048
-#define GST_SF_BUF_FRAMES (GST_SF_BUF_BYTES / sizeof(float))
-
 GST_PAD_TEMPLATE_FACTORY (sf_src_factory,
   "src%d",
   GST_PAD_SRC,
@@ -68,12 +63,11 @@ GST_PAD_TEMPLATE_FACTORY (sf_src_factory,
   GST_CAPS_NEW (
     "sf_src",
     "audio/x-raw-float",
-    "rate",       GST_PROPS_INT_RANGE (1, G_MAXINT),
-    "intercept",  GST_PROPS_FLOAT(0.0),
-    "slope",      GST_PROPS_FLOAT(1.0),
-    "channels",   GST_PROPS_INT (1),
-    "width",     GST_PROPS_INT (32),
-    "endianness", GST_PROPS_INT (G_BYTE_ORDER)
+    "rate",            GST_PROPS_INT_RANGE (1, G_MAXINT),
+    "width",           GST_PROPS_INT (32),
+    "endianness",      GST_PROPS_INT (G_BYTE_ORDER),
+    "buffer-frames",   GST_PROPS_INT_RANGE (1, G_MAXINT),
+    "channels",                GST_PROPS_INT (1)
   )
 );
 
@@ -84,12 +78,11 @@ GST_PAD_TEMPLATE_FACTORY (sf_sink_factory,
   GST_CAPS_NEW (
     "sf_sink",
     "audio/x-raw-float",
-    "rate",       GST_PROPS_INT_RANGE (1, G_MAXINT),
-    "intercept",  GST_PROPS_FLOAT(0.0),
-    "slope",      GST_PROPS_FLOAT(1.0),
-    "channels",   GST_PROPS_INT (1),
-    "width",     GST_PROPS_INT (32),
-    "endianness", GST_PROPS_INT (G_BYTE_ORDER)
+    "rate",            GST_PROPS_INT_RANGE (1, G_MAXINT),
+    "width",           GST_PROPS_INT (32),
+    "endianness",      GST_PROPS_INT (G_BYTE_ORDER),
+    "buffer-frames",   GST_PROPS_INT_RANGE (1, G_MAXINT),
+    "channels",                GST_PROPS_INT (1)
   )
 );
 
@@ -163,27 +156,38 @@ gst_sf_minor_types_get_type (void)
   return sf_minor_types_type;
 }
 
-static void    gst_sf_class_init       (GstSFClass *klass);
-static void    gst_sf_init             (GstSF *this);
-
-static gboolean gst_sf_open_file       (GstSF *this);
-static void    gst_sf_close_file       (GstSF *this);
+static void            gst_sf_class_init       (GstSFClass *klass);
+static void            gst_sf_init             (GstSF *this);
+static void            gst_sf_dispose          (GObject *object);
+static void            gst_sf_set_property     (GObject *object, guint prop_id,
+                                                 const GValue *value, GParamSpec *pspec);
+static void            gst_sf_get_property     (GObject *object, guint prop_id, 
+                                                 GValue *value, GParamSpec *pspec);
+
+static GstClock*       gst_sf_get_clock        (GstElement *element);
+static void            gst_sf_set_clock        (GstElement *element, GstClock *clock);
+static GstPad*         gst_sf_request_new_pad  (GstElement *element, GstPadTemplate *templ,
+                                                 const gchar *unused);
+static void            gst_sf_release_request_pad (GstElement *element, GstPad *pad);
+static GstElementStateReturn gst_sf_change_state (GstElement *element);
 
-static void    gst_sf_loop             (GstElement *element);
+static GstPadLinkReturn gst_sf_link            (GstPad *pad, GstCaps *caps);
 
-static void    gst_sf_set_property     (GObject *object, guint prop_id, const GValue *value, 
-                                         GParamSpec *pspec);
-static void    gst_sf_get_property     (GObject *object, guint prop_id, GValue *value, 
-                                         GParamSpec *pspec);
+static void            gst_sf_loop             (GstElement *element);
 
-static GstPad* gst_sf_request_new_pad  (GstElement *element, GstPadTemplate *templ,
-                                         const gchar *unused);
+static GstClockTime    gst_sf_get_time         (GstClock *clock, gpointer data);
 
-static GstElementStateReturn gst_sf_change_state (GstElement *element);
-static GstPadLinkReturn gst_sf_link    (GstPad *pad, GstCaps *caps);
+static gboolean        gst_sf_open_file        (GstSF *this);
+static void            gst_sf_close_file       (GstSF *this);
 
 static GstElementClass *parent_class = NULL;
 
+GST_DEBUG_CATEGORY_STATIC (gstsf_debug);
+#define INFO(...) \
+    GST_CAT_LEVEL_LOG (gstsf_debug, GST_LEVEL_INFO, NULL, __VA_ARGS__)
+#define INFO_OBJ(obj,...) \
+    GST_CAT_LEVEL_LOG (gstsf_debug, GST_LEVEL_INFO, obj, __VA_ARGS__)
+
 GType
 gst_sf_get_type (void) 
 {
@@ -281,64 +285,33 @@ gst_sf_class_init (GstSFClass *klass)
     g_object_class_install_property (gobject_class, ARG_CREATE_PADS, pspec);
   }
  
+  gobject_class->dispose = gst_sf_dispose;
   gobject_class->set_property = gst_sf_set_property;
   gobject_class->get_property = gst_sf_get_property;
 
+  gstelement_class->get_clock = gst_sf_get_clock;
+  gstelement_class->set_clock = gst_sf_set_clock;
   gstelement_class->change_state = gst_sf_change_state;
   gstelement_class->request_new_pad = gst_sf_request_new_pad;
+  gstelement_class->release_pad = gst_sf_release_request_pad;
 }
 
 static void 
 gst_sf_init (GstSF *this) 
 {
   gst_element_set_loop_function (GST_ELEMENT (this), gst_sf_loop);
+  this->provided_clock = gst_audio_clock_new ("sfclock", gst_sf_get_time, this);
+  gst_object_set_parent (GST_OBJECT (this->provided_clock), GST_OBJECT (this));
 }
 
-static GstPad*
-gst_sf_request_new_pad (GstElement *element, GstPadTemplate *templ,
-                        const gchar *unused) 
+static void
+gst_sf_dispose (GObject *object)
 {
-  gchar *name;
-  GstSF *this;
-  GstSFChannel *channel;
+  GstSF *this = (GstSF*)object;
 
-  this = GST_SF (element);
-  channel = g_new0 (GstSFChannel, 1);
-  
-  if (templ->direction == GST_PAD_SINK) {
-    /* we have an SFSink */
-    name = g_strdup_printf ("sink%d", this->channelcount);
-    this->numchannels++;
-    if (this->file) {
-      gst_sf_close_file (this);
-      gst_sf_open_file (this);
-    }
-  } else {
-    /* we have an SFSrc */
-    name = g_strdup_printf ("src%d", this->channelcount);
-  }
-  
-  channel->pad = gst_pad_new_from_template (templ, name);
-  gst_element_add_pad (GST_ELEMENT (this), channel->pad);
-  gst_pad_set_link_function (channel->pad, gst_sf_link);
-  
-  this->channels = g_list_append (this->channels, channel);
-  this->channelcount++;
-  
-  GST_DEBUG ("sf added pad %s\n", name);
-
-  g_free (name);
-  return channel->pad;
-}
-
-static GstPadLinkReturn
-gst_sf_link (GstPad *pad, GstCaps *caps)
-{
-  GstSF *this = (GstSF*)GST_OBJECT_PARENT (pad);
-  
-  gst_caps_get_int (caps, "rate", &this->rate);
+  gst_object_unparent (GST_OBJECT (this->provided_clock));
 
-  return GST_PAD_LINK_OK;
+  G_OBJECT_CLASS (parent_class)->dispose (object);
 }
 
 static void
@@ -420,6 +393,156 @@ gst_sf_get_property (GObject *object, guint prop_id, GValue *value, GParamSpec *
   }
 }
 
+static GstClock*
+gst_sf_get_clock (GstElement *element)
+{
+  GstSF *this = GST_SF (element);
+
+  return this->provided_clock;
+}
+
+static void
+gst_sf_set_clock (GstElement *element, GstClock *clock)
+{
+  GstSF *this = GST_SF (element);
+  
+  this->clock = clock;
+}
+
+static GstClockTime 
+gst_sf_get_time (GstClock *clock, gpointer data) 
+{
+  GstSF *this = GST_SF (data);
+
+  return this->time;
+}
+
+static GstElementStateReturn
+gst_sf_change_state (GstElement *element)
+{
+  GstSF *this = GST_SF (element);
+
+  switch (GST_STATE_TRANSITION (element)) {
+    case GST_STATE_NULL_TO_READY:
+      break;
+    case GST_STATE_READY_TO_PAUSED:
+      break;
+    case GST_STATE_PAUSED_TO_PLAYING:
+      gst_audio_clock_set_active (GST_AUDIO_CLOCK (this->provided_clock), TRUE);
+      break;
+    case GST_STATE_PLAYING_TO_PAUSED:
+      gst_audio_clock_set_active (GST_AUDIO_CLOCK (this->provided_clock), FALSE);
+      break;
+    case GST_STATE_PAUSED_TO_READY:
+      break;
+    case GST_STATE_READY_TO_NULL:
+      if (GST_FLAG_IS_SET (this, GST_SF_OPEN))
+        gst_sf_close_file (this);
+      break;
+  }
+
+  if (GST_ELEMENT_CLASS (parent_class)->change_state)
+    return GST_ELEMENT_CLASS (parent_class)->change_state (element);
+
+  return GST_STATE_SUCCESS;
+}
+
+static GstPad*
+gst_sf_request_new_pad (GstElement *element, GstPadTemplate *templ,
+                        const gchar *unused) 
+{
+  gchar *name;
+  GstSF *this;
+  GstSFChannel *channel;
+
+  this = GST_SF (element);
+  channel = g_new0 (GstSFChannel, 1);
+  
+  if (templ->direction == GST_PAD_SINK) {
+    /* we have an SFSink */
+    name = g_strdup_printf ("sink%d", this->channelcount);
+    this->numchannels++;
+    if (this->file) {
+      gst_sf_close_file (this);
+      gst_sf_open_file (this);
+    }
+  } else {
+    /* we have an SFSrc */
+    name = g_strdup_printf ("src%d", this->channelcount);
+  }
+  
+  channel->pad = gst_pad_new_from_template (templ, name);
+  gst_element_add_pad (GST_ELEMENT (this), channel->pad);
+  gst_pad_set_link_function (channel->pad, gst_sf_link);
+  
+  this->channels = g_list_append (this->channels, channel);
+  this->channelcount++;
+  
+  INFO_OBJ (element, "added pad %s\n", name);
+
+  g_free (name);
+  return channel->pad;
+}
+
+static void
+gst_sf_release_request_pad (GstElement *element, GstPad *pad)
+{
+  GstSF *this;
+  GstSFChannel *channel = NULL;
+  GList *l;
+
+  this = GST_SF (element);
+  
+  if (GST_STATE (element) == GST_STATE_PLAYING) {
+    g_warning ("You can't release a request pad if the element is PLAYING, sorry.");
+    return;
+  }
+
+  for (l=this->channels; l; l=l->next) {
+    if (GST_SF_CHANNEL (l)->pad == pad) {
+      channel = GST_SF_CHANNEL (l);
+      break;
+    }
+  }
+
+  g_return_if_fail (channel != NULL);
+
+  INFO_OBJ (element, "Releasing request pad %s", GST_PAD_NAME (channel->pad));
+
+  if (GST_FLAG_IS_SET (element, GST_SF_OPEN))
+    gst_sf_close_file (this);
+
+  gst_element_remove_pad (element, channel->pad);
+  this->channels = g_list_remove (this->channels, channel);
+  this->numchannels--;
+  g_free (channel);
+}
+
+static GstPadLinkReturn
+gst_sf_link (GstPad *pad, GstCaps *caps)
+{
+  GstSF *this = (GstSF*)GST_OBJECT_PARENT (pad);
+  
+  if (GST_CAPS_IS_FIXED (caps)) {
+    gst_caps_get_int (caps, "rate", &this->rate);
+    gst_caps_get_int (caps, "buffer-frames", &this->buffer_frames);
+
+    INFO_OBJ (this, "linked pad %s:%s with fixed caps, frames=%d, rate=%d",
+              GST_DEBUG_PAD_NAME (pad), this->rate, this->buffer_frames);
+
+    if (this->numchannels) {
+      /* we can go ahead and allocate our buffer */
+      if (this->buffer)
+        g_free (this->buffer);
+      this->buffer = g_malloc (this->numchannels * this->buffer_frames * sizeof (float));
+      memset (this->buffer, 0, this->numchannels * this->buffer_frames * sizeof (float));
+    }
+    return GST_PAD_LINK_OK;
+  }
+
+  return GST_PAD_LINK_DELAYED;
+}
+
 static gboolean
 gst_sf_open_file (GstSF *this)
 {
@@ -428,8 +551,10 @@ gst_sf_open_file (GstSF *this)
   
   g_return_val_if_fail (!GST_FLAG_IS_SET (this, GST_SF_OPEN), FALSE);
 
+  this->time = 0;
+
   if (!this->filename) {
-    gst_element_error (GST_ELEMENT (this), "sndfile::location was not set");
+    gst_element_error (GST_ELEMENT (this), "sndfile: 'location' was not set");
     return FALSE;
   }
     
@@ -437,15 +562,26 @@ gst_sf_open_file (GstSF *this)
     mode = SFM_READ;
     info.format = 0;
   } else {
+    if (!this->rate) {
+      INFO_OBJ (this, "Not opening %s yet because caps are not set", this->filename);
+      return FALSE;
+    } else if (!this->numchannels) {
+      INFO_OBJ (this, "Not opening %s yet because we have no input channels", this->filename);
+      return FALSE;
+    }
+
     mode = SFM_WRITE;
     this->format = this->format_major | this->format_subtype;
     info.samplerate = this->rate;
     info.channels = this->numchannels;
     info.format = this->format;
 
+    INFO_OBJ (this, "Opening %s with rate %d, %d channels, format 0x%x",
+              this->filename, info.samplerate, info.channels, info.format);
+
     if (!sf_format_check (&info)) {
       gst_element_error (GST_ELEMENT (this),
-                         g_strdup_printf ("Input parameters (rate:%d, channels:%d, format:%x) invalid",
+                         g_strdup_printf ("Input parameters (rate:%d, channels:%d, format:0x%x) invalid",
                                           info.samplerate, info.channels, info.format));
       return FALSE;
     }
@@ -478,7 +614,6 @@ gst_sf_open_file (GstSF *this)
       GST_SF_CHANNEL (l)->caps_set = FALSE;
   }
 
-  this->buffer = g_malloc (this->numchannels * GST_SF_BUF_BYTES);
   GST_FLAG_SET (this, GST_SF_OPEN);
 
   return TRUE;
@@ -491,6 +626,8 @@ gst_sf_close_file (GstSF *this)
 
   g_return_if_fail (GST_FLAG_IS_SET (this, GST_SF_OPEN));
 
+  INFO_OBJ (this, "Closing file %s", this->filename);
+
   if ((err = sf_close (this->file)))
     gst_element_error (GST_ELEMENT (this),
                        g_strdup_printf ("sndfile: could not close file \"%s\": %s",
@@ -513,25 +650,36 @@ gst_sf_loop (GstElement *element)
   this = (GstSF*)element;
   
   if (this->channels == NULL) {
-    gst_element_error (element, "You must connect at least one pad to soundfile elements.");
+    gst_element_error (element, "You must connect at least one pad to sndfile elements.");
     return;
   }
-  if (!GST_FLAG_IS_SET (this, GST_SF_OPEN))
-    if (!gst_sf_open_file (this))
-      return; /* we've already set gst_element_error */
 
   if (GST_IS_SFSRC (this)) {
     sf_count_t read;
     gint i, j;
     int eos = 0;
+    int buffer_frames = this->buffer_frames;
     int nchannels = this->numchannels;
     GstSFChannel *channel = NULL;
     gfloat *data;
     gfloat *buf = this->buffer;
     GstBuffer *out;
 
-    read = sf_readf_float (this->file, buf, GST_SF_BUF_FRAMES);
-    if (read < GST_SF_BUF_FRAMES)
+    if (!GST_FLAG_IS_SET (this, GST_SF_OPEN))
+      if (!gst_sf_open_file (this))
+        return; /* we've already set gst_element_error */
+
+    if (buffer_frames == 0) {
+      /* we have to set the caps later */
+      buffer_frames = this->buffer_frames = 1024;
+    }
+    if (buf == NULL) {
+      buf = this->buffer = g_malloc (this->numchannels * this->buffer_frames * sizeof (float));
+      memset (this->buffer, 0, this->numchannels * this->buffer_frames * sizeof (float));
+    }
+
+    read = sf_readf_float (this->file, buf, buffer_frames);
+    if (read < buffer_frames)
       eos = 1;
 
     if (read)
@@ -548,16 +696,12 @@ gst_sf_loop (GstElement *element)
             caps = gst_caps_copy
               (GST_PAD_TEMPLATE_CAPS (GST_PAD_PAD_TEMPLATE (GST_SF_CHANNEL (l)->pad)));
           gst_caps_set (caps, "rate", GST_PROPS_INT (this->rate), NULL);
-          /* we know it's fixed, yo. */
-          GST_CAPS_FLAG_SET (caps, GST_CAPS_FIXED);
+          gst_caps_set (caps, "buffer-frames", GST_PROPS_INT (this->buffer_frames), NULL);
           if (!gst_pad_try_set_caps (GST_SF_CHANNEL (l)->pad, caps)) {
             gst_element_error (GST_ELEMENT (this),
                                g_strdup_printf ("Opened file with sample rate %d, but could not set caps",
                                                 this->rate));
-            sf_close (this->file);
-            this->file = NULL;
-            g_free (this->buffer);
-            this->buffer = NULL;
+            gst_sf_close_file (this->file);
             return;
           }
           channel->caps_set = TRUE;
@@ -570,35 +714,81 @@ gst_sf_loop (GstElement *element)
         gst_pad_push (channel->pad, out);
       }
 
+    this->time += read * (GST_SECOND / this->rate);
+    gst_audio_clock_update_time ((GstAudioClock*)this->provided_clock, this->time);
+
     if (eos) {
       if (this->loop) {
         sf_seek (this->file, (sf_count_t)0, SEEK_SET);
         eos = 0;
       } else {
         for (l=this->channels; l; l=l->next)
-          gst_pad_push (GST_SF_CHANNEL (l)->pad, gst_event_new (GST_EVENT_EOS));
+          gst_pad_push (GST_SF_CHANNEL (l)->pad, (GstBuffer*)gst_event_new (GST_EVENT_EOS));
         gst_element_set_eos (element);
       }
     }
   } else {
-    /* unimplemented */
-  }
-}
+    sf_count_t written, num_to_write;
+    gint i, j;
+    int buffer_frames = this->buffer_frames;
+    int nchannels = this->numchannels;
+    GstSFChannel *channel = NULL;
+    gfloat *data;
+    gfloat *buf = this->buffer;
+    GstBuffer *in;
 
-static GstElementStateReturn
-gst_sf_change_state (GstElement *element)
-{
-  g_return_val_if_fail (GST_IS_SF (element), GST_STATE_FAILURE);
+    /* the problem: we can't allocate a buffer for pulled data before caps is
+     * set, and we can't open the file without the sample rate from the
+     * caps... */
 
-  /* if going to NULL then close the file */
-  if (GST_STATE_PENDING (element) == GST_STATE_NULL) 
-    if (GST_FLAG_IS_SET (element, GST_SF_OPEN))
-      gst_sf_close_file (GST_SF (element));
+    num_to_write = buffer_frames;
 
-  if (GST_ELEMENT_CLASS (parent_class)->change_state)
-    return GST_ELEMENT_CLASS (parent_class)->change_state (element);
+    INFO_OBJ (this, "looping, buffer_frames=%d, nchannels=%d", buffer_frames, nchannels);
 
-  return GST_STATE_SUCCESS;
+    for (i=0,l=this->channels; l; l=l->next,i++) {
+      channel = GST_SF_CHANNEL (l);
+      
+      in = gst_pad_pull (channel->pad);
+
+      if (buffer_frames == 0) {
+        /* pulling a buffer from the pad should have caused capsnego to occur,
+           which then would set this->buffer_frames to a new value */
+        buffer_frames = this->buffer_frames;
+        if (buffer_frames == 0) {
+          gst_element_error (element, "Caps were never set, bailing...");
+          return;
+        }
+        buf = this->buffer;
+        num_to_write = buffer_frames;
+      }
+
+      if (!GST_FLAG_IS_SET (this, GST_SF_OPEN))
+        if (!gst_sf_open_file (this))
+          return; /* we've already set gst_element_error */
+
+      if (GST_IS_EVENT (in)) {
+        num_to_write = 0;
+      } else {
+        data = (gfloat*)GST_BUFFER_DATA (in);
+        num_to_write = MIN (num_to_write, GST_BUFFER_SIZE (in) / sizeof (gfloat));
+        for (j=0; j<num_to_write; j++)
+          buf[j * nchannels + i % nchannels] = data[j];
+      }
+      gst_data_unref ((GstData*)in);
+    }
+
+    if (num_to_write) {
+      written = sf_writef_float (this->file, buf, num_to_write);
+      if (written != num_to_write)
+        gst_element_error (element, "Error writing file: %s", sf_strerror (this->file));
+    }
+
+    this->time += num_to_write * (GST_SECOND / this->rate);
+    gst_audio_clock_update_time ((GstAudioClock*)this->provided_clock, this->time);
+
+    if (num_to_write != buffer_frames)
+      gst_element_set_eos (element);
+  }
 }
 
 static gboolean
@@ -606,6 +796,13 @@ plugin_init (GModule *module, GstPlugin *plugin)
 {
   GstElementFactory *factory;
   
+  if (!gst_library_load ("gstaudio"))
+    return FALSE;
+
+  GST_DEBUG_CATEGORY_INIT (gstsf_debug, "sf",
+                           GST_DEBUG_FG_WHITE | GST_DEBUG_BG_GREEN | GST_DEBUG_BOLD,
+                           "libsndfile plugin");
+
   factory = gst_element_factory_new ("sfsrc", GST_TYPE_SFSRC,
                                      &sfsrc_details);
   g_return_val_if_fail (factory != NULL, FALSE);
index 1a8e6eaab790ed5ad618b3b165b1645bbdaf14d0..3197effd001403389ce9d9e3e66606fcef3940b5 100644 (file)
@@ -1,9 +1,5 @@
-/* GStreamer
- * Copyright (C) 1999,2000 Erik Walthinsen <omega@cse.ogi.edu>
- *                    2000 Wim Taymans <wtay@chello.be>
- *                    2003 Andy Wingo <wingo at pobox dot com>
- *
- * gstsf.c: libsndfile plugin for GStreamer
+/* GStreamer libsndfile plugin
+ * Copyright (C) 2003 Andy Wingo <wingo at pobox dot com>
  *
  * This library is free software; you can redistribute it and/or
  * modify it under the terms of the GNU Library General Public
@@ -87,6 +83,8 @@ struct _GstSF {
   GstElement element;
   GList *channels;
 
+  GstClock *clock, *provided_clock;
+
   gchar *filename;
   SNDFILE *file;
   void *buffer;
@@ -98,7 +96,11 @@ struct _GstSF {
   gint format_major;
   gint format_subtype;
   gint format;
+
   gint rate;
+  gint buffer_frames;
+
+  guint64 time;
 };
 
 struct _GstSFClass {
index 7b1106f238b3d9a6d538577bcaee877131d70ab2..678bc83a60d7278ac6d36b341b259daf15894eea 100644 (file)
@@ -1 +1 @@
-2003-06-09 22:00 GMT
+2003-07-05 22:00 GMT
index 059f79e5aab4821de72ab98c648e95c230f2ef9c..fa9abaf472ed09a5f8ff77b1349c885bb12c1fbc 100644 (file)
@@ -2,10 +2,10 @@ librarydir = $(libdir)/gstreamer-@GST_MAJORMINOR@
 
 library_LTLIBRARIES = libgstaudio.la
 
-libgstaudio_la_SOURCES = audio.c
+libgstaudio_la_SOURCES = audio.c audioclock.c
 
 libgstaudioincludedir = $(includedir)/gstreamer-@GST_MAJORMINOR@/gst/audio
-libgstaudioinclude_HEADERS = audio.h
+libgstaudioinclude_HEADERS = audio.h audioclock.h
 
 libgstaudio_la_LIBADD =
 libgstaudio_la_CFLAGS = $(GST_CFLAGS) $(GST_OPT_CFLAGS)
index cdc339e7e234698bd4360bef4534e6f447120e6d..3190edc2541d12615798f39ea8c4e7689495a2e1 100644 (file)
@@ -177,7 +177,7 @@ gst_audio_is_buffer_framed (GstPad* pad, GstBuffer* buf)
 static gboolean
 plugin_init (GModule *module, GstPlugin *plugin)
 {
-  gst_plugin_set_longname (plugin, "Convenience routines for audio plugins");
+  gst_plugin_set_longname (plugin, "Support services for audio plugins");
   return TRUE;
 }
 
index 4bf62d493226306a3e7e5fe6f1d15841813ebfcf..a737e4680f3dde969901f68ebc161233a2b03fee 100644 (file)
@@ -20,6 +20,8 @@
 
 #include <gst/gst.h>
 
+#include <gst/audio/audioclock.h>
+
 /* For people that are looking at this source: the purpose of these defines is
  * to make GstCaps a bit easier, in that you don't have to know all of the
  * properties that need to be defined. you can just use these macros. currently
diff --git a/gst-libs/gst/audio/audioclock.c b/gst-libs/gst/audio/audioclock.c
new file mode 100644 (file)
index 0000000..342009f
--- /dev/null
@@ -0,0 +1,194 @@
+/* GStreamer
+ * Copyright (C) 1999,2000 Erik Walthinsen <omega@cse.ogi.edu>
+ *                    2000 Wim Taymans <wtay@chello.be>
+ *
+ * audioclock.c: Clock for use by audio plugins
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public
+ * License along with this library; if not, write to the
+ * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+ * Boston, MA 02111-1307, USA.
+ */
+
+#include "audioclock.h"
+
+static void            gst_audio_clock_class_init      (GstAudioClockClass *klass);
+static void            gst_audio_clock_init            (GstAudioClock *clock);
+
+static GstClockTime    gst_audio_clock_get_internal_time (GstClock *clock);
+static GstClockReturn  gst_audio_clock_id_wait_async   (GstClock *clock,
+                                                         GstClockEntry *entry);
+static void            gst_audio_clock_id_unschedule   (GstClock *clock,
+                                                         GstClockEntry *entry);
+
+static GstSystemClockClass *parent_class = NULL;
+/* static guint gst_audio_clock_signals[LAST_SIGNAL] = { 0 }; */
+  
+GType
+gst_audio_clock_get_type (void)
+{ 
+  static GType clock_type = 0;
+           
+  if (!clock_type) {
+    static const GTypeInfo clock_info = {
+      sizeof (GstAudioClockClass),
+      NULL,
+      NULL,
+      (GClassInitFunc) gst_audio_clock_class_init,
+      NULL,
+      NULL,
+      sizeof (GstAudioClock),
+      4,
+      (GInstanceInitFunc) gst_audio_clock_init,
+      NULL
+    };
+    clock_type = g_type_register_static (GST_TYPE_SYSTEM_CLOCK, "GstAudioClock",
+                                         &clock_info, 0);
+  }
+  return clock_type;
+}
+
+
+static void
+gst_audio_clock_class_init (GstAudioClockClass *klass)
+{
+  GObjectClass *gobject_class;
+  GstObjectClass *gstobject_class;
+  GstClockClass *gstclock_class;
+
+  gobject_class = (GObjectClass*) klass;
+  gstobject_class = (GstObjectClass*) klass;
+  gstclock_class = (GstClockClass*) klass;
+
+  parent_class = g_type_class_ref (GST_TYPE_SYSTEM_CLOCK);
+
+  gstclock_class->get_internal_time    = gst_audio_clock_get_internal_time;
+  gstclock_class->wait_async           = gst_audio_clock_id_wait_async;
+  gstclock_class->unschedule           = gst_audio_clock_id_unschedule;
+}
+
+static void
+gst_audio_clock_init (GstAudioClock *clock)
+{
+  gst_object_set_name (GST_OBJECT (clock), "GstAudioClock");
+
+  clock->prev1 = 0;
+  clock->prev2 = 0;
+}
+
+GstClock*
+gst_audio_clock_new (gchar *name, GstAudioClockGetTimeFunc func, gpointer user_data)
+{
+  GstAudioClock *aclock = GST_AUDIO_CLOCK (g_object_new (GST_TYPE_AUDIO_CLOCK, NULL));
+
+  aclock->func = func;
+  aclock->user_data = user_data;
+  aclock->adjust = 0;
+
+  return (GstClock*)aclock;
+}
+
+void
+gst_audio_clock_set_active (GstAudioClock *aclock, gboolean active)
+{
+  GTimeVal timeval;
+  GstClockTime time;
+  GstClockTime audiotime;
+
+  g_get_current_time (&timeval);
+  time = GST_TIMEVAL_TO_TIME (timeval);
+  audiotime = aclock->func ((GstClock*)aclock, aclock->user_data);
+
+  if (active) {
+    aclock->adjust = time - audiotime;
+  }
+  else {
+    aclock->adjust = audiotime - time;
+  }
+
+  aclock->active = active;
+}
+
+static GstClockTime
+gst_audio_clock_get_internal_time (GstClock *clock)
+{
+  GstAudioClock *aclock = GST_AUDIO_CLOCK (clock);
+  
+  if (aclock->active) {
+    GstClockTime audiotime;
+    
+    audiotime = aclock->func (clock, aclock->user_data) + aclock->adjust;
+
+    return audiotime;
+  }
+  else {
+    GstClockTime time;
+    GTimeVal timeval;
+    
+    g_get_current_time (&timeval);
+    time = GST_TIMEVAL_TO_TIME (timeval);
+
+    return time;
+  }
+}
+
+void
+gst_audio_clock_update_time (GstAudioClock *aclock, GstClockTime time)
+{
+  /* I don't know of a purpose in updating these; perhaps they can be removed */
+  aclock->prev2 = aclock->prev1;
+  aclock->prev1 = time;
+
+  /* FIXME: the wait_async subsystem should be made threadsafe, but I don't want
+   * to lock and unlock a mutex on every iteration... */
+  while (aclock->async_entries) {
+    GstClockEntry *entry = (GstClockEntry*)aclock->async_entries->data;
+    
+    if (entry->time > time)
+      break;
+
+    entry->func ((GstClock*)aclock, time, entry, entry->user_data);
+
+    aclock->async_entries = g_slist_delete_link (aclock->async_entries,
+                                                 aclock->async_entries);
+    /* do I need to free the entry? */
+  }
+}
+
+static gint
+compare_clock_entries (GstClockEntry *entry1, GstClockEntry *entry2)
+{
+  return entry1->time - entry2->time;
+}
+
+static GstClockReturn
+gst_audio_clock_id_wait_async (GstClock *clock, GstClockEntry *entry)
+{
+  GstAudioClock *aclock = (GstAudioClock*)clock;
+  
+  aclock->async_entries = g_slist_insert_sorted (aclock->async_entries,
+                                                 entry,
+                                                 (GCompareFunc)compare_clock_entries);
+
+  /* is this the proper return val? */
+  return GST_CLOCK_EARLY;
+}
+
+static void
+gst_audio_clock_id_unschedule (GstClock *clock, GstClockEntry *entry)
+{
+  GstAudioClock *aclock = (GstAudioClock*)clock;
+  
+  aclock->async_entries = g_slist_remove (aclock->async_entries,
+                                          entry);
+}
diff --git a/gst-libs/gst/audio/audioclock.h b/gst-libs/gst/audio/audioclock.h
new file mode 100644 (file)
index 0000000..8ac984b
--- /dev/null
@@ -0,0 +1,83 @@
+/* GStreamer
+ * Copyright (C) 1999,2000 Erik Walthinsen <omega@cse.ogi.edu>
+ *                    2000 Wim Taymans <wtay@chello.be>
+ *
+ * audioclock.h: Clock for use by audio plugins
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public
+ * License along with this library; if not, write to the
+ * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+ * Boston, MA 02111-1307, USA.
+ */
+
+
+#ifndef __GST_AUDIO_CLOCK_H__
+#define __GST_AUDIO_CLOCK_H__
+
+#include <gst/gstsystemclock.h>
+
+#ifdef __cplusplus
+extern "C" {
+#endif /* __cplusplus */
+
+
+#define GST_TYPE_AUDIO_CLOCK \
+  (gst_audio_clock_get_type())
+#define GST_AUDIO_CLOCK(obj) \
+  (G_TYPE_CHECK_INSTANCE_CAST((obj),GST_TYPE_AUDIO_CLOCK,GstAudioClock))
+#define GST_AUDIO_CLOCK_CLASS(klass) \
+  (G_TYPE_CHECK_CLASS_CAST((klass),GST_TYPE_AUDIO_CLOCK,GstAudioClockClass))
+#define GST_IS_AUDIO_CLOCK(obj) \
+  (G_TYPE_CHECK_INSTANCE_TYPE((obj),GST_TYPE_AUDIO_CLOCK))
+#define GST_IS_AUDIO_CLOCK_CLASS(obj) \
+  (G_TYPE_CHECK_CLASS_TYPE((klass),GST_TYPE_AUDIO_CLOCK))
+
+typedef struct _GstAudioClock GstAudioClock;
+typedef struct _GstAudioClockClass GstAudioClockClass;
+
+typedef GstClockTime (*GstAudioClockGetTimeFunc) (GstClock *clock, gpointer user_data);
+
+
+struct _GstAudioClock {
+  GstSystemClock clock;
+
+  GstClockTime prev1, prev2;
+
+  /* --- protected --- */
+  GstAudioClockGetTimeFunc func;
+  gpointer user_data;
+
+  GstClockTimeDiff adjust;
+
+  GSList *async_entries;
+
+  gboolean active;
+};
+
+struct _GstAudioClockClass {
+  GstSystemClockClass parent_class;
+};
+
+GType           gst_audio_clock_get_type       (void);
+GstClock*      gst_audio_clock_new             (gchar *name, GstAudioClockGetTimeFunc func,
+                                                 gpointer user_data);
+void           gst_audio_clock_set_active      (GstAudioClock *aclock, gboolean active);
+
+void           gst_audio_clock_update_time     (GstAudioClock *aclock, GstClockTime time);
+
+#ifdef __cplusplus
+}
+#endif /* __cplusplus */
+
+
+#endif /* __GST_AUDIO_CLOCK_H__ */