added ladspa, doesn't have checks yet though
authorThomas Vander Stichele <thomas@apestaart.org>
Sun, 23 Dec 2001 15:26:43 +0000 (15:26 +0000)
committerThomas Vander Stichele <thomas@apestaart.org>
Sun, 23 Dec 2001 15:26:43 +0000 (15:26 +0000)
Original commit message from CVS:
added ladspa, doesn't have checks yet though

TODO
ext/ladspa/Makefile.am [new file with mode: 0644]
ext/ladspa/gstladspa.c [new file with mode: 0644]
ext/ladspa/gstladspa.h [new file with mode: 0644]
ext/ladspa/ladspa.h [new file with mode: 0644]
ext/ladspa/load.c [new file with mode: 0644]
ext/ladspa/search.c [new file with mode: 0644]
ext/ladspa/utils.h [new file with mode: 0644]

diff --git a/TODO b/TODO
index 1be5931..36e7754 100644 (file)
--- a/TODO
+++ b/TODO
@@ -25,3 +25,5 @@
 * festival should have checks and stuff and added properly
 
 * fix ffmpeg
+
+* add ladspa header check stuff
diff --git a/ext/ladspa/Makefile.am b/ext/ladspa/Makefile.am
new file mode 100644 (file)
index 0000000..5a7dcd1
--- /dev/null
@@ -0,0 +1,8 @@
+plugindir = $(libdir)/gst
+
+plugin_LTLIBRARIES = libgstladspa.la
+
+libgstladspa_la_SOURCES = gstladspa.c search.c load.c
+libgstladspa_la_CFLAGS = $(GST_CFLAGS)
+
+noinst_HEADERS = gstladspa.h ladspa.h utils.h
diff --git a/ext/ladspa/gstladspa.c b/ext/ladspa/gstladspa.c
new file mode 100644 (file)
index 0000000..81740c2
--- /dev/null
@@ -0,0 +1,877 @@
+/* GStreamer
+ * Copyright (C) <1999> Erik Walthinsen <omega@cse.ogi.edu>
+ *               <2001> Steve Baker <stevebaker_org@yahoo.co.uk>
+ *
+ * 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 <string.h>
+#include <math.h>
+
+//#define DEBUG_ENABLED
+#include "gstladspa.h"
+#include "ladspa.h"
+#include "search.h"
+#include "utils.h"
+
+
+static GstPadTemplate* 
+ladspa_src_factory (void)
+{
+  return 
+    gst_padtemplate_new (
+    "src",
+    GST_PAD_SRC,
+    GST_PAD_ALWAYS,
+    gst_caps_new (
+    "ladspa_src",
+      "audio/raw",
+      gst_props_new (
+        "format",     GST_PROPS_STRING ("float"),
+        "layout",     GST_PROPS_STRING ("gfloat"),
+        "intercept",  GST_PROPS_FLOAT(0.0),
+        "slope",      GST_PROPS_FLOAT(1.0),
+        "channels",   GST_PROPS_INT (1),
+        "rate",       GST_PROPS_INT_RANGE (0,G_MAXINT),
+      NULL)),
+    NULL);
+}
+
+static GstPadTemplate* 
+ladspa_sink_factory (void) 
+{
+  return 
+    gst_padtemplate_new (
+      "sink",
+      GST_PAD_SINK,
+      GST_PAD_ALWAYS,
+      gst_caps_new (
+      "float2int_sink",
+        "audio/raw",
+        gst_props_new (
+          "format",     GST_PROPS_STRING ("float"),
+          "layout",     GST_PROPS_STRING ("gfloat"),
+          "intercept",  GST_PROPS_FLOAT(0.0),
+          "slope",      GST_PROPS_FLOAT(1.0),
+          "channels",   GST_PROPS_INT (1),
+          "rate",       GST_PROPS_INT_RANGE (0,G_MAXINT),
+      NULL)),
+    NULL);
+}
+
+enum {
+  ARG_0,
+  ARG_LOOP_BASED,
+  ARG_SAMPLERATE,
+  ARG_BUFFERSIZE,
+  ARG_LAST,
+};
+
+static GstPadTemplate *srctempl, *sinktempl;
+
+static void                    gst_ladspa_class_init           (GstLADSPAClass *klass);
+static void                    gst_ladspa_init                 (GstLADSPA *ladspa);
+
+static GstPadNegotiateReturn gst_ladspa_negotiate_sink_mono (GstPad *pad, GstCaps **caps, gpointer *data);
+static GstPadNegotiateReturn gst_ladspa_negotiate_src_mono (GstPad *pad, GstCaps **caps, gpointer *data);
+static GstPadNegotiateReturn gst_ladspa_negotiate_src_get_mono (GstPad *pad, GstCaps **caps, gpointer *data);
+static void gst_ladspa_force_caps(GstLADSPA *ladspa, GstPad *pad);
+
+static void                    gst_ladspa_set_property         (GObject *object, guint prop_id, const GValue *value, GParamSpec *pspec);
+static void                    gst_ladspa_get_property         (GObject *object, guint prop_id, GValue *value, GParamSpec *pspec);
+
+static gboolean gst_ladspa_instantiate (GstLADSPA *ladspa);
+static void gst_ladspa_activate(GstLADSPA *ladspa);
+static void gst_ladspa_deactivate(GstLADSPA *ladspa);
+
+static GstElementStateReturn   gst_ladspa_change_state         (GstElement *element);
+static void                    gst_ladspa_loop                 (GstElement *element);
+static void      gst_ladspa_chain_inplace_mono (GstPad *pad,GstBuffer *buf);
+static GstBuffer * gst_ladspa_get_mono(GstPad *pad);
+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 void
+gst_ladspa_class_init (GstLADSPAClass *klass)
+{
+  GObjectClass *gobject_class;
+  GstElementClass *gstelement_class;
+  LADSPA_Descriptor *desc;
+  gint i,current_portnum,sinkcount,srccount,controlcount;
+  gint hintdesc;
+  gint argtype,argperms;
+  GParamSpec *paramspec = NULL;
+  gchar *argname;
+
+  gobject_class = (GObjectClass*)klass;
+  gstelement_class = (GstElementClass*)klass;
+
+  gobject_class->set_property = gst_ladspa_set_property;
+  gobject_class->get_property = gst_ladspa_get_property;
+
+  gstelement_class->change_state = gst_ladspa_change_state;
+
+  g_object_class_install_property(G_OBJECT_CLASS(klass), ARG_LOOP_BASED,
+    g_param_spec_boolean("loop-based","loop-based","loop-based",
+                         FALSE,G_PARAM_READWRITE));
+
+  // look up and store the ladspa descriptor
+  klass->descriptor = g_hash_table_lookup(ladspa_descriptors,GINT_TO_POINTER(G_TYPE_FROM_CLASS(klass)));
+  desc = klass->descriptor;
+
+  klass->numports = desc->PortCount;
+
+  klass->numsinkpads = 0;
+  klass->numsrcpads = 0;
+  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])){
+      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);
+  sinkcount = 0;
+  srccount = 0;
+  controlcount = 0;
+
+  // 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])){
+      //g_print("input port %d\n", i);
+      klass->sinkpad_portnums[sinkcount++] = i;
+    }
+      
+    if (LADSPA_IS_PORT_AUDIO(desc->PortDescriptors[i]) && 
+        LADSPA_IS_PORT_OUTPUT(desc->PortDescriptors[i])){
+      //g_print("output port %d\n", i);
+      klass->srcpad_portnums[srccount++] = i;
+    }
+      
+    if (LADSPA_IS_PORT_CONTROL(desc->PortDescriptors[i]) && 
+        LADSPA_IS_PORT_INPUT(desc->PortDescriptors[i])){
+      //g_print("control port %d\n", 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
+  klass->control_info = g_new0(ladspa_control_info,klass->numcontrols);
+    
+  for (i=0;i<klass->numcontrols;i++) {
+
+    current_portnum = klass->control_portnums[i];
+    
+    // short name for hint descriptor
+    hintdesc = desc->PortRangeHints[current_portnum].HintDescriptor;
+
+    // get the various bits
+    if (LADSPA_IS_HINT_TOGGLED(hintdesc))
+      klass->control_info[i].toggled = TRUE;
+    if (LADSPA_IS_HINT_LOGARITHMIC(hintdesc))
+      klass->control_info[i].logarithmic = TRUE;
+    if (LADSPA_IS_HINT_INTEGER(hintdesc))
+      klass->control_info[i].integer = TRUE;
+
+    // figure out the argument details
+    if (klass->control_info[i].toggled) argtype = G_TYPE_BOOLEAN;
+    else if (klass->control_info[i].integer) argtype = G_TYPE_INT;
+    else argtype = G_TYPE_FLOAT;
+
+    // grab the bounds
+    if (LADSPA_IS_HINT_BOUNDED_BELOW(hintdesc)) {
+      klass->control_info[i].lower = TRUE;
+      klass->control_info[i].lowerbound =
+        desc->PortRangeHints[current_portnum].LowerBound;
+    } else {
+      if (argtype==G_TYPE_INT) klass->control_info[i].lowerbound = (gfloat)G_MININT;
+      if (argtype==G_TYPE_FLOAT) klass->control_info[i].lowerbound = G_MINFLOAT;
+    }
+    
+    if (LADSPA_IS_HINT_BOUNDED_ABOVE(hintdesc)) {
+      klass->control_info[i].upper = TRUE;
+      klass->control_info[i].upperbound =
+        desc->PortRangeHints[current_portnum].UpperBound;
+      if (LADSPA_IS_HINT_SAMPLE_RATE(hintdesc))
+        klass->control_info[i].samplerate = TRUE;
+    } 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;
+    }
+
+    if (LADSPA_IS_PORT_INPUT(desc->PortDescriptors[current_portnum])) {
+      argperms = G_PARAM_READWRITE;
+      klass->control_info[i].writable = TRUE;
+    } else {
+      argperms = G_PARAM_READABLE;
+      klass->control_info[i].writable = FALSE;
+    }
+
+    klass->control_info[i].name = g_strdup(desc->PortNames[current_portnum]);
+    argname = g_strdup(klass->control_info[i].name);
+    // this is the same thing that param_spec_* will do
+    g_strcanon (argname, G_CSET_A_2_Z G_CSET_a_2_z G_CSET_DIGITS "-", '-');
+    
+    // check for duplicate property names
+    if (g_object_class_find_property(G_OBJECT_CLASS(klass), argname) != NULL){
+      gint numarg=1;
+      gchar *numargname = g_strdup_printf("%s_%d",argname,numarg++);
+      while (g_object_class_find_property(G_OBJECT_CLASS(klass), numargname) != NULL){
+        g_free(numargname);
+        numargname = g_strdup_printf("%s_%d",argname,numarg++);
+      }
+      argname = numargname;
+    }
+    
+    if (argtype==G_TYPE_BOOLEAN){
+      paramspec = g_param_spec_boolean(argname,argname,argname, FALSE, argperms);
+    } else if (argtype==G_TYPE_INT){      
+      paramspec = g_param_spec_int(argname,argname,argname, 
+        (gint)klass->control_info[i].lowerbound, (gint)klass->control_info[i].upperbound, 0, argperms);
+    } else {
+      paramspec = g_param_spec_float(argname,argname,argname, 
+        klass->control_info[i].lowerbound, klass->control_info[i].upperbound, 
+        (klass->control_info[i].lowerbound + klass->control_info[i].upperbound) / 2.0f, argperms);
+    }
+    g_object_class_install_property(G_OBJECT_CLASS(klass), i+ARG_LAST, paramspec);
+    
+    g_print("added arg %s from %s\n",argname, klass->control_info[i].name);
+  }
+}
+
+static void
+gst_ladspa_init (GstLADSPA *ladspa)
+{
+  GstLADSPAClass *oclass = (GstLADSPAClass*)(G_OBJECT_GET_CLASS(ladspa));
+  
+  LADSPA_Descriptor *desc;
+  gint i,sinkcount,srccount,controlcount;
+
+  desc = oclass->descriptor;
+  ladspa->descriptor = oclass->descriptor;
+  
+  // allocate the various arrays
+  ladspa->srcpads = g_new0(GstPad*,oclass->numsrcpads);
+  ladspa->sinkpads = g_new0(GstPad*,oclass->numsinkpads);
+  ladspa->controls = g_new(gfloat,oclass->numcontrols);
+
+  // walk through the ports and add all the pads
+  sinkcount = 0;
+  srccount = 0;
+  controlcount = 0;
+  for (i=0;i<desc->PortCount;i++) {
+    if (LADSPA_IS_PORT_AUDIO(desc->PortDescriptors[i]) &&
+        LADSPA_IS_PORT_INPUT(desc->PortDescriptors[i])) {
+      ladspa->sinkpads[sinkcount] = gst_pad_new_from_template 
+      (sinktempl, (gchar *)desc->PortNames[i]);
+      gst_element_add_pad(GST_ELEMENT(ladspa),ladspa->sinkpads[sinkcount]);
+      sinkcount++;
+    }
+    if (LADSPA_IS_PORT_AUDIO(desc->PortDescriptors[i]) &&
+        LADSPA_IS_PORT_OUTPUT(desc->PortDescriptors[i])) {
+      ladspa->srcpads[srccount] = gst_pad_new_from_template 
+        (srctempl, (gchar *)desc->PortNames[i]);
+      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])) {
+          
+      // use the lowerbound as the default value if it exists
+      if (oclass->control_info[controlcount].lower){
+        ladspa->controls[controlcount]=oclass->control_info[controlcount].lowerbound;
+      } else {
+        ladspa->controls[controlcount] = 0.0;
+      }
+      controlcount++;
+    }
+  }
+
+  ladspa->samplerate = 0;
+  ladspa->buffersize = 0;
+  ladspa->newcaps = FALSE;
+  ladspa->activated = FALSE;
+  
+  // mono chain
+  if (sinkcount==1 && srccount==1){
+    //g_print("inplace mono chain mode\n");
+    gst_pad_set_negotiate_function (ladspa->sinkpads[0], gst_ladspa_negotiate_sink_mono);
+    gst_pad_set_chain_function(ladspa->sinkpads[0],gst_ladspa_chain_inplace_mono);
+    gst_pad_set_negotiate_function (ladspa->srcpads[0], gst_ladspa_negotiate_src_mono);
+  }
+  
+  // mono get (no sink pads)
+  if (sinkcount==0 && srccount == 1){
+    //g_print("get mode\n");
+    ladspa->newcaps = TRUE;
+    ladspa->samplerate = 44100;
+    ladspa->buffersize = 64;
+    gst_pad_set_get_function(ladspa->srcpads[0],gst_ladspa_get_mono);
+    gst_pad_set_negotiate_function (ladspa->srcpads[0], gst_ladspa_negotiate_src_get_mono);
+    gst_ladspa_instantiate(ladspa);
+  }
+  
+  // multi srcpad get
+  if (sinkcount==0 && srccount > 1){
+    //g_print("multi get mode\n");
+    ladspa->newcaps = TRUE;
+    ladspa->samplerate = 44100;
+    ladspa->buffersize = 64;
+    gst_pad_set_get_function(ladspa->srcpads[0],gst_ladspa_get);
+    gst_pad_set_negotiate_function (ladspa->srcpads[0], gst_ladspa_negotiate_src_get_mono);
+    gst_ladspa_instantiate(ladspa);
+    ladspa->buffers = g_new0(GstBuffer*,oclass->numsrcpads);
+  }
+}
+
+static GstPadNegotiateReturn
+gst_ladspa_negotiate_src_mono (GstPad *pad, GstCaps **caps, gpointer *data)
+{
+  GstLADSPA *ladspa = (GstLADSPA*)GST_OBJECT_PARENT (pad);
+  
+  // have to instantiate ladspa plugin when samplerate changes (groan)
+  if (ladspa->samplerate != gst_caps_get_int (*caps, "rate")){
+    ladspa->samplerate = gst_caps_get_int (*caps, "rate");
+    if (!gst_ladspa_instantiate(ladspa)) return GST_PAD_NEGOTIATE_FAIL;
+  }
+  return gst_pad_negotiate_proxy (pad, ladspa->sinkpads[0], caps);
+}
+
+static GstPadNegotiateReturn
+gst_ladspa_negotiate_sink_mono (GstPad *pad, GstCaps **caps, gpointer *data)
+{
+  GstLADSPA *ladspa = (GstLADSPA*)GST_OBJECT_PARENT (pad);
+  
+  // have to instantiate ladspa plugin when samplerate changes (groan)
+  if (ladspa->samplerate != gst_caps_get_int (*caps, "rate")){
+    ladspa->samplerate = gst_caps_get_int (*caps, "rate");
+    if (!gst_ladspa_instantiate(ladspa)) return GST_PAD_NEGOTIATE_FAIL;
+  }
+  return gst_pad_negotiate_proxy (pad, ladspa->srcpads[0], caps);
+}
+
+static GstPadNegotiateReturn 
+gst_ladspa_negotiate_src_get_mono (GstPad *pad, GstCaps **caps, gpointer *data) 
+{
+  GstLADSPA *ladspa;
+  //g_print("gst_ladspa_negotiate_src_get_mono\n");
+  if (*caps) {
+    g_return_val_if_fail (pad != NULL, GST_PAD_NEGOTIATE_FAIL);
+    ladspa = (GstLADSPA*)GST_OBJECT_PARENT (pad);
+    ladspa->samplerate = gst_caps_get_int (*caps, "rate");
+    if (!gst_ladspa_instantiate(ladspa)) return GST_PAD_NEGOTIATE_FAIL;
+    return GST_PAD_NEGOTIATE_AGREE;
+  }
+  return GST_PAD_NEGOTIATE_FAIL;
+}
+
+static void
+gst_ladspa_force_caps(GstLADSPA *ladspa, GstPad *pad) {
+  
+  // g_print("forcing caps\n");
+  gst_pad_set_caps (pad, gst_caps_new (
+    "ladspa_src_caps",
+    "audio/raw",
+    gst_props_new (
+      "format",     GST_PROPS_STRING ("float"),
+      "layout",     GST_PROPS_STRING ("gfloat"),
+      "intercept",  GST_PROPS_FLOAT(0.0),
+      "slope",      GST_PROPS_FLOAT(1.0),
+      "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;
+      break;
+    case ARG_BUFFERSIZE:
+      ladspa->buffersize = g_value_get_int (value);
+      break;
+  }
+  
+  // is it a ladspa plugin arg?
+  if (cid<0) return;
+
+/*
+  if (id == ARG_LOOP_BASED) {
+    // we can only do this in NULL state
+    g_return_if_fail (GST_STATE(object) != GST_STATE_NULL);
+    ladspa->loopbased = g_value_get_boolean (value);
+    if (ladspa->loopbased) {
+      gst_element_set_loop_function (GST_ELEMENT (ladspa), gst_ladspa_loop);
+    } else {
+      gst_element_set_loop_function (GST_ELEMENT (ladspa), NULL);
+    }
+  }
+*/
+
+  oclass = (GstLADSPAClass*)(G_OBJECT_GET_CLASS (object));
+
+  // verify it exists and is a control (not a port)
+  g_return_if_fail(cid < oclass->numcontrols);
+  
+  control_info = &(oclass->control_info[cid]);
+  g_return_if_fail (control_info->name != NULL);
+
+  // check to see if it's writable
+  g_return_if_fail (control_info->writable);
+
+  // g_print("set arg %s to %f\n",control_info->name,ladspa->controls[cid]);
+
+  // 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;
+  }    
+}
+
+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;
+  // 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;
+
+  //g_print("got arg %s as %f\n",control_info->name,ladspa->controls[cid]);
+
+  // 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]);
+  }
+}
+
+static gboolean
+gst_ladspa_instantiate (GstLADSPA *ladspa)
+{
+  LADSPA_Descriptor *desc;
+  int i;
+  GstLADSPAClass *oclass = (GstLADSPAClass*)(G_OBJECT_GET_CLASS (ladspa));
+  gboolean was_activated;
+  
+  desc = ladspa->descriptor;
+  
+  // check for old handle
+  was_activated = ladspa->activated;
+  if (ladspa->handle != NULL){
+    gst_ladspa_deactivate(ladspa);
+    desc->cleanup(ladspa->handle);
+  }
+        
+  // instantiate the plugin  
+  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
+    //g_print("added control port %d\n", oclass->control_portnums[i]);
+    desc->connect_port(ladspa->handle,
+                       oclass->control_portnums[i],
+                       &(ladspa->controls[i]));
+  }
+
+  // reactivate if it was activated before the reinstantiation
+  if (was_activated){
+    gst_ladspa_activate(ladspa);
+  }
+  return TRUE;
+}
+
+static GstElementStateReturn
+gst_ladspa_change_state (GstElement *element)
+{
+  LADSPA_Descriptor *desc;
+  GstLADSPA *ladspa = (GstLADSPA*)element;
+//  GstLADSPAClass *oclass = (GstLADSPAClass*)(G_OBJECT(ladspa)->klass);
+  desc = ladspa->descriptor;
+
+  //g_print("changing state\n");
+  switch (GST_STATE_TRANSITION (element)) {
+    case GST_STATE_NULL_TO_READY:
+      gst_ladspa_activate(ladspa);
+      break;
+    case GST_STATE_READY_TO_NULL:
+      gst_ladspa_deactivate(ladspa);
+      break;
+    default:
+      break;
+  }
+
+  if (GST_ELEMENT_CLASS (parent_class)->change_state)
+    return GST_ELEMENT_CLASS (parent_class)->change_state (element);
+
+  return GST_STATE_SUCCESS;
+}
+
+static void
+gst_ladspa_activate(GstLADSPA *ladspa)
+{
+  LADSPA_Descriptor *desc;
+  desc = ladspa->descriptor;
+  
+  if (ladspa->activated){
+    gst_ladspa_deactivate(ladspa);
+  }
+  
+  //g_print("activating\n");
+
+  // activate the plugin (function might be null)
+  if (desc->activate != NULL){
+    desc->activate(ladspa->handle);
+  }
+  
+  ladspa->activated = TRUE;
+}
+
+static void
+gst_ladspa_deactivate(GstLADSPA *ladspa)
+{
+  LADSPA_Descriptor *desc;
+  desc = ladspa->descriptor;
+
+  //g_print("deactivating\n");
+
+  // deactivate the plugin (function might be null)  
+  if (ladspa->activated && desc->deactivate != NULL){
+    desc->deactivate(ladspa->handle);
+  }
+  ladspa->activated = FALSE;
+}
+
+static void
+gst_ladspa_loop (GstElement *element)
+{
+  int i;
+  GstLADSPA *ladspa = (GstLADSPA *)element;
+  GstLADSPAClass *oclass = (GstLADSPAClass*)(G_OBJECT_GET_CLASS (ladspa));
+  LADSPA_Descriptor *desc;
+
+  desc = ladspa->descriptor;
+  do {
+    printf("looping something\n");
+
+    // first get all the necessary data from the input ports
+    for (i=0;i<oclass->numsinkpads;i++){  
+      ladspa->buffers[i] = gst_pad_pull(ladspa->sinkpads[i]);
+      printf("pulling buffer %d\n", i);
+    }
+    
+    for (i=0;i<oclass->numsinkpads;i++) {
+//      desc->connect_port(ladspa->handle,i,&(ladspa->controls[i]));
+    }
+
+    for (i=0;i<oclass->numsrcpads && i<oclass->numsinkpads;i++){
+      printf("pushing buffer %d\n", i);
+      gst_pad_push (ladspa->srcpads[i], ladspa->buffers[i]);
+      ladspa->buffers[i] = NULL;
+    }
+    
+  } while (!GST_ELEMENT_IS_COTHREAD_STOPPING (element));
+}
+
+static void
+gst_ladspa_chain_inplace_mono (GstPad *pad,GstBuffer *buf)
+{
+  LADSPA_Descriptor *desc;
+  LADSPA_Data *data;
+  unsigned long num_samples;
+  
+  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);
+
+  oclass = (GstLADSPAClass*)(G_OBJECT_GET_CLASS (ladspa));
+  data=(LADSPA_Data*)GST_BUFFER_DATA(buf);
+  num_samples = GST_BUFFER_SIZE(buf) / sizeof(gfloat);
+  
+  desc = ladspa->descriptor;
+
+  desc->connect_port(ladspa->handle,oclass->sinkpad_portnums[0],data);
+  desc->connect_port(ladspa->handle,oclass->srcpad_portnums[0],data);
+
+  desc->run(ladspa->handle,num_samples);
+  
+  desc->connect_port(ladspa->handle,oclass->sinkpad_portnums[0],NULL);
+  desc->connect_port(ladspa->handle,oclass->srcpad_portnums[0],NULL);
+
+  gst_pad_push (ladspa->srcpads[0], buf);  
+}
+
+static GstBuffer *
+gst_ladspa_get_mono(GstPad *pad)
+{
+  LADSPA_Descriptor *desc;
+  LADSPA_Data *data;
+  
+  GstLADSPA *ladspa;
+  GstLADSPAClass *oclass;
+  GstBuffer *buf;
+
+  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));
+  
+  if (ladspa->newcaps) {
+    gst_ladspa_force_caps(ladspa, ladspa->srcpads[0]);
+  }
+  
+  buf = gst_buffer_new();
+  g_return_val_if_fail (buf, NULL);
+  data = g_new(LADSPA_Data, ladspa->buffersize);
+  GST_BUFFER_DATA(buf) = (gpointer) data;
+  GST_BUFFER_SIZE(buf) = sizeof(LADSPA_Data) * ladspa->buffersize;
+  GST_BUFFER_TIMESTAMP(buf) = ladspa->timestamp;
+
+  desc = ladspa->descriptor;
+  desc->connect_port(ladspa->handle,oclass->srcpad_portnums[0],data);
+  desc->run(ladspa->handle,(unsigned long)ladspa->buffersize);  
+  desc->connect_port(ladspa->handle,oclass->srcpad_portnums[0],NULL);
+  
+  return buf;
+}
+
+static GstBuffer *
+gst_ladspa_get(GstPad *pad)
+{
+  LADSPA_Descriptor *desc;
+  LADSPA_Data *data;
+  
+  GstLADSPA *ladspa;
+  GstLADSPAClass *oclass;
+  GstBuffer *buf;
+
+  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));
+  
+  if (ladspa->newcaps) {
+    gst_ladspa_force_caps(ladspa, ladspa->srcpads[0]);
+  }
+  
+  buf = gst_buffer_new();
+  g_return_val_if_fail (buf, NULL);
+  data = g_new(LADSPA_Data, ladspa->buffersize);
+  GST_BUFFER_DATA(buf) = (gpointer) data;
+  GST_BUFFER_SIZE(buf) = sizeof(LADSPA_Data) * ladspa->buffersize;
+  GST_BUFFER_TIMESTAMP(buf) = ladspa->timestamp;
+  ladspa->timestamp+= ladspa->buffersize * ladspa->samplerate * 10^9;
+
+  desc = ladspa->descriptor;
+  desc->connect_port(ladspa->handle,oclass->srcpad_portnums[0],data);
+  desc->run(ladspa->handle,(unsigned long)ladspa->buffersize);  
+  desc->connect_port(ladspa->handle,oclass->srcpad_portnums[0],NULL);
+  
+  return buf;
+}
+
+static void
+ladspa_describe_plugin(const char *pcFullFilename,
+                       void *pvPluginHandle,
+                       LADSPA_Descriptor_Function pfDescriptorFunction)
+{
+  const LADSPA_Descriptor *desc;
+  int i,j;
+  
+  GstElementDetails *details;
+  GTypeInfo typeinfo = {
+      sizeof(GstLADSPAClass),      NULL,
+      NULL,
+      (GClassInitFunc)gst_ladspa_class_init,
+      NULL,
+      NULL,
+      sizeof(GstLADSPA),
+      0,
+      (GInstanceInitFunc)gst_ladspa_init,
+  };
+  GType type;
+  GstElementFactory *factory;
+
+  // walk through all the plugins in this pluginlibrary
+  i = 0;
+  while ((desc = pfDescriptorFunction(i++))) {
+    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 "-_+", '-');
+    // if it's already registered, drop it
+    if (g_type_from_name(type_name)) {
+      g_free(type_name);
+      continue;
+    }
+    // create the type now
+    type = g_type_register_static(GST_TYPE_ELEMENT, type_name , &typeinfo, 0);
+
+    // construct the element details struct
+    details = g_new0(GstElementDetails,1);
+    details->longname = g_strdup(desc->Name);
+    details->klass = "Filter/LADSPA";
+    details->description = details->longname;
+    details->version = g_strdup_printf("%ld",desc->UniqueID);
+    details->author = g_strdup(desc->Maker);
+    details->copyright = g_strdup(desc->Copyright);
+
+    // register the plugin with gstreamer
+    factory = gst_elementfactory_new(type_name,type,details);
+    g_return_if_fail(factory != NULL);
+    gst_plugin_add_feature (ladspa_plugin, GST_PLUGIN_FEATURE (factory));
+
+    // add this plugin to the hash
+    g_hash_table_insert(ladspa_descriptors,
+                        GINT_TO_POINTER(type),
+                        (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_elementfactory_add_padtemplate (factory, sinktempl);
+        break;
+      }
+    }
+  
+    srctempl = ladspa_src_factory();
+    gst_elementfactory_add_padtemplate (factory, srctempl);
+
+  }
+}
+
+static gboolean
+plugin_init (GModule *module, GstPlugin *plugin)
+{
+  
+  ladspa_descriptors = g_hash_table_new(NULL,NULL);
+  parent_class = g_type_class_ref(GST_TYPE_ELEMENT);
+
+  ladspa_plugin = plugin;
+
+  LADSPAPluginSearch(ladspa_describe_plugin);
+
+  return TRUE;
+}
+
+GstPluginDesc plugin_desc = {
+  GST_VERSION_MAJOR,
+  GST_VERSION_MINOR,
+  "ladspa",
+  plugin_init
+};
+
diff --git a/ext/ladspa/gstladspa.h b/ext/ladspa/gstladspa.h
new file mode 100644 (file)
index 0000000..80a7b45
--- /dev/null
@@ -0,0 +1,103 @@
+/* GStreamer
+ * Copyright (C) <1999> Erik Walthinsen <omega@cse.ogi.edu>
+ *
+ * gstladspa.h: Header for LADSPA plugin
+ *
+ * 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_LADSPA_H__
+#define __GST_LADSPA_H__
+
+
+#include <config.h>
+#include <gst/gst.h>
+
+#include "ladspa.h"
+
+
+#ifdef __cplusplus
+extern "C" {
+#endif /* __cplusplus */
+
+/*
+#define GST_TYPE_LADSPA \
+  (gst_ladspa_get_type())
+#define GST_LADSPA(obj) \
+  (G_TYPE_CHECK_INSTANCE_CAST((obj),GST_TYPE_LADSPA,GstLADSPA))
+#define GST_LADSPA_CLASS(klass) \
+  (G_TYPE_CHECK_CLASS_CAST((klass),GST_TYPE_LADSPA,GstLADSPA))
+#define GST_IS_LADSPA(obj) \
+  (G_TYPE_CHECK_INSTANCE_TYPE((obj),GST_TYPE_LADSPA))
+#define GST_IS_LADSPA_CLASS(obj) \
+  (G_TYPE_CHECK_CLASS_TYPE((klass),GST_TYPE_LADSPA))
+*/
+
+typedef struct _ladspa_control_info {
+  gchar *name;
+  gfloat lowerbound, upperbound;
+  gboolean lower,upper,samplerate;
+  gboolean toggled, logarithmic, integer, writable;
+} ladspa_control_info;
+
+typedef struct _GstLADSPA GstLADSPA;
+typedef struct _GstLADSPAClass GstLADSPAClass;
+
+struct _GstLADSPA {
+  GstElement element;
+
+  LADSPA_Descriptor *descriptor;
+  LADSPA_Handle *handle;
+
+  gfloat *controls;
+  
+  GstPad **sinkpads, 
+         **srcpads;
+         
+  GstBuffer **buffers;
+
+  gboolean loopbased, newcaps, activated;
+
+  gint samplerate, buffersize;
+  gulong timestamp;
+
+};
+
+struct _GstLADSPAClass {
+  GstElementClass parent_class;
+
+  LADSPA_Descriptor *descriptor;
+
+  gint numports,
+       numsinkpads, 
+       numsrcpads, 
+       numcontrols;
+
+  gint *sinkpad_portnums, 
+       *srcpad_portnums, 
+       *control_portnums;
+
+  ladspa_control_info *control_info;
+};
+
+
+#ifdef __cplusplus
+}
+#endif /* __cplusplus */
+
+
+#endif /* __GST_LADSPA_H__ */
diff --git a/ext/ladspa/ladspa.h b/ext/ladspa/ladspa.h
new file mode 100644 (file)
index 0000000..5e2fa98
--- /dev/null
@@ -0,0 +1,512 @@
+/* ladspa.h
+
+   Linux Audio Developer's Simple Plugin API Version 1.0[LGPL].
+   Copyright (C) 2000-2001 Richard W.E. Furse, Paul Barton-Davis,
+   Stefan Westerfeld.
+   
+   This library is free software; you can redistribute it and/or
+   modify it under the terms of the GNU Lesser General Public License
+   as published by the Free Software Foundation; either version 2.1 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
+   Lesser General Public License for more details.
+   
+   You should have received a copy of the GNU Lesser 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 LADSPA_INCLUDED
+#define LADSPA_INCLUDED
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/*****************************************************************************/
+
+/* Overview: 
+
+   There is a large number of synthesis packages in use or development
+   on the Linux platform at this time. This API (`The Linux Audio
+   Developer's Simple Plugin API') attempts to give programmers the
+   ability to write simple `plugin' audio processors in C/C++ and link
+   them dynamically (`plug') into a range of these packages (`hosts').
+   It should be possible for any host and any plugin to communicate
+   completely through this interface.
+
+   This API is deliberately short and simple. To achieve compatibility
+   with a range of promising Linux sound synthesis packages it
+   attempts to find the `greatest common divisor' in their logical
+   behaviour. Having said this, certain limiting decisions are
+   implicit, notably the use of a fixed type (LADSPA_Data) for all
+   data transfer and absence of a parameterised `initialisation'
+   phase. See below for the LADSPA_Data typedef.
+
+   Plugins are expected to distinguish between control and audio
+   data. Plugins have `ports' that are inputs or outputs for audio or
+   control data and each plugin is `run' for a `block' corresponding
+   to a short time interval measured in samples. Audio data is
+   communicated using arrays of LADSPA_Data, allowing a block of audio
+   to be processed by the plugin in a single pass. Control data is
+   communicated using single LADSPA_Data values. Control data has a
+   single value at the start of a call to the `run()' or `run_adding()'
+   function, and may be considered to remain this value for its
+   duration. The plugin may assume that all its input and output ports
+   have been connected to the relevant data location (see the
+   `connect_port()' function below) before it is asked to run.
+
+   Plugins will reside in shared object files suitable for dynamic
+   linking by dlopen() and family. The file will provide a number of
+   `plugin types' that can be used to instantiate actual plugins
+   (sometimes known as `plugin instances') that can be connected
+   together to perform tasks.
+
+   This API contains very limited error-handling. */
+
+/*****************************************************************************/
+
+/* Fundamental data type passed in and out of plugin. This data type
+   is used to communicate audio samples and control values. It is
+   assumed that the plugin will work sensibly given any numeric input
+   value although it may have a preferred range (see hints below). */
+
+typedef float LADSPA_Data;
+
+/*****************************************************************************/
+
+/* Special Plugin Properties: 
+   Optional features of the plugin type are encapsulated in the
+   LADSPA_Properties type. This is assembled by ORing individual
+   properties together. */
+
+typedef int LADSPA_Properties;
+
+/* Property LADSPA_PROPERTY_REALTIME indicates that the plugin has a
+   real-time dependency (e.g. listens to a MIDI device) and so its
+   output must not be cached or subject to significant latency. */
+#define LADSPA_PROPERTY_REALTIME        0x1
+
+/* Property LADSPA_PROPERTY_INPLACE_BROKEN indicates that the plugin
+   may cease to work correctly if the host elects to use the same data
+   location for both input and output (see connect_port()). This
+   should be avoided as enabling this flag makes it impossible for
+   hosts to use the plugin to process audio `in-place.' */
+#define LADSPA_PROPERTY_INPLACE_BROKEN  0x2
+
+/* Property LADSPA_PROPERTY_HARD_RT_CAPABLE indicates that the plugin
+   is capable of running not only in a conventional host but also in a
+   `hard real-time' environment. To qualify for this the plugin must
+   satisfy all of the following:
+
+   (1) The plugin must not use malloc(), free() or other heap memory
+   management within its run() or run_adding() functions. All new
+   memory used in run() must be managed via the stack. These
+   restrictions only apply to the run() function.
+
+   (2) The plugin will not attempt to make use of any library
+   functions with the exceptions of functions in the ANSI standard C
+   and C maths libraries, which the host is expected to provide.
+
+   (3) The plugin will not access files, devices, pipes, sockets, IPC
+   or any other mechanism that might result in process or thread
+   blocking.
+      
+   (4) The plugin will take an amount of time to execute a run() or
+   run_adding() call approximately of form (A+B*SampleCount) where A
+   and B depend on the machine and host in use. This amount of time
+   may not depend on input signals or plugin state. The host is left
+   the responsibility to perform timings to estimate upper bounds for
+   A and B. */
+#define LADSPA_PROPERTY_HARD_RT_CAPABLE 0x4
+
+#define LADSPA_IS_REALTIME(x)        ((x) & LADSPA_PROPERTY_REALTIME)
+#define LADSPA_IS_INPLACE_BROKEN(x)  ((x) & LADSPA_PROPERTY_INPLACE_BROKEN)
+#define LADSPA_IS_HARD_RT_CAPABLE(x) ((x) & LADSPA_PROPERTY_HARD_RT_CAPABLE)
+
+/*****************************************************************************/
+
+/* Plugin Ports: 
+
+   Plugins have `ports' that are inputs or outputs for audio or
+   data. Ports can communicate arrays of LADSPA_Data (for audio
+   inputs/outputs) or single LADSPA_Data values (for control
+   input/outputs). This information is encapsulated in the
+   LADSPA_PortDescriptor type which is assembled by ORing individual
+   properties together.
+
+   Note that a port must be an input or an output port but not both
+   and that a port must be a control or audio port but not both. */
+
+typedef int LADSPA_PortDescriptor;
+
+/* Property LADSPA_PORT_INPUT indicates that the port is an input. */
+#define LADSPA_PORT_INPUT   0x1
+
+/* Property LADSPA_PORT_OUTPUT indicates that the port is an output. */
+#define LADSPA_PORT_OUTPUT  0x2
+
+/* Property LADSPA_PORT_CONTROL indicates that the port is a control
+   port. */
+#define LADSPA_PORT_CONTROL 0x4
+
+/* Property LADSPA_PORT_AUDIO indicates that the port is a audio
+   port. */
+#define LADSPA_PORT_AUDIO   0x8
+
+#define LADSPA_IS_PORT_INPUT(x)   ((x) & LADSPA_PORT_INPUT)
+#define LADSPA_IS_PORT_OUTPUT(x)  ((x) & LADSPA_PORT_OUTPUT)
+#define LADSPA_IS_PORT_CONTROL(x) ((x) & LADSPA_PORT_CONTROL)
+#define LADSPA_IS_PORT_AUDIO(x)   ((x) & LADSPA_PORT_AUDIO)
+
+/*****************************************************************************/
+
+/* Plugin Port Range Hints: 
+
+   The host may wish to provide a representation of data entering or
+   leaving a plugin (e.g. to generate a GUI automatically). To make
+   this more meaningful, the plugin should provide `hints' to the host
+   describing the usual values taken by the data.
+   
+   Note that these are only hints. The host may ignore them and the
+   plugin must not assume that data supplied to it is meaningful. If
+   the plugin receives invalid input data it is expected to continue
+   to run without failure and, where possible, produce a sensible
+   output (e.g. a high-pass filter given a negative cutoff frequency
+   might switch to an all-pass mode).
+    
+   Hints are meaningful for all input and output ports but hints for
+   input control ports are expected to be particularly useful.
+   
+   More hint information is encapsulated in the
+   LADSPA_PortRangeHintDescriptor type which is assembled by ORing
+   individual hint types together. Hints may require further
+   LowerBound and UpperBound information.
+
+   All the hint information for a particular port is aggregated in the
+   LADSPA_PortRangeHint structure. */
+
+typedef int LADSPA_PortRangeHintDescriptor;
+
+/* Hint LADSPA_HINT_BOUNDED_BELOW indicates that the LowerBound field
+   of the LADSPA_PortRangeHint should be considered meaningful. The
+   value in this field should be considered the (inclusive) lower
+   bound of the valid range. If LADSPA_HINT_SAMPLE_RATE is also
+   specified then the value of LowerBound should be multiplied by the
+   sample rate. */
+#define LADSPA_HINT_BOUNDED_BELOW 0x1
+
+/* Hint LADSPA_HINT_BOUNDED_ABOVE indicates that the UpperBound field
+   of the LADSPA_PortRangeHint should be considered meaningful. The
+   value in this field should be considered the (inclusive) upper
+   bound of the valid range. If LADSPA_HINT_SAMPLE_RATE is also
+   specified then the value of UpperBound should be multiplied by the
+   sample rate. */
+#define LADSPA_HINT_BOUNDED_ABOVE 0x2
+
+/* Hint LADSPA_HINT_TOGGLED indicates that the data item should be
+   considered a Boolean toggle. Data less than or equal to zero should
+   be considered `off' or `false,' and data above zero should be
+   considered `on' or `true.' LADSPA_HINT_TOGGLED may not be used in
+   conjunction with any other hint. */
+#define LADSPA_HINT_TOGGLED       0x4
+
+/* Hint LADSPA_HINT_SAMPLE_RATE indicates that any bounds specified
+   should be interpreted as multiples of the sample rate. For
+   instance, a frequency range from 0Hz to the Nyquist frequency (half
+   the sample rate) could be requested by this hint in conjunction
+   with LowerBound = 0 and UpperBound = 0.5. Hosts that support bounds
+   at all must support this hint to retain meaning. */
+#define LADSPA_HINT_SAMPLE_RATE   0x8
+
+/* Hint LADSPA_HINT_LOGARITHMIC indicates that it is likely that the
+   user will find it more intuitive to view values using a logarithmic
+   scale. This is particularly useful for frequencies and gains. */
+#define LADSPA_HINT_LOGARITHMIC   0x10
+
+/* Hint LADSPA_HINT_INTEGER indicates that a user interface would
+   probably wish to provide a stepped control taking only integer
+   values. Any bounds set should be slightly wider than the actual
+   integer range required to avoid floating point rounding errors. For
+   instance, the integer set {0,1,2,3} might be described as [-0.1,
+   3.1]. */
+#define LADSPA_HINT_INTEGER       0x20
+
+#define LADSPA_IS_HINT_BOUNDED_BELOW(x) ((x) & LADSPA_HINT_BOUNDED_BELOW)
+#define LADSPA_IS_HINT_BOUNDED_ABOVE(x) ((x) & LADSPA_HINT_BOUNDED_ABOVE)
+#define LADSPA_IS_HINT_TOGGLED(x)       ((x) & LADSPA_HINT_TOGGLED)
+#define LADSPA_IS_HINT_SAMPLE_RATE(x)   ((x) & LADSPA_HINT_SAMPLE_RATE)
+#define LADSPA_IS_HINT_LOGARITHMIC(x)   ((x) & LADSPA_HINT_LOGARITHMIC)
+#define LADSPA_IS_HINT_INTEGER(x)       ((x) & LADSPA_HINT_INTEGER)
+
+typedef struct _LADSPA_PortRangeHint {
+
+  /* Hints about the port. */
+  LADSPA_PortRangeHintDescriptor HintDescriptor;
+
+  /* Meaningful when hint LADSPA_HINT_BOUNDED_BELOW is active. When
+     LADSPA_HINT_SAMPLE_RATE is also active then this value should be
+     multiplied by the relevant sample rate. */
+  LADSPA_Data LowerBound;
+
+  /* Meaningful when hint LADSPA_HINT_BOUNDED_ABOVE is active. When
+     LADSPA_HINT_SAMPLE_RATE is also active then this value should be
+     multiplied by the relevant sample rate. */
+  LADSPA_Data UpperBound;
+
+} LADSPA_PortRangeHint;
+
+/*****************************************************************************/
+
+/* Plugin Handles: 
+
+   This plugin handle indicates a particular instance of the plugin
+   concerned. It is valid to compare this to NULL (0 for C++) but
+   otherwise the host should not attempt to interpret it. The plugin
+   may use it to reference internal instance data. */
+
+typedef void * LADSPA_Handle;
+
+/*****************************************************************************/
+
+/* Descriptor for a Type of Plugin: 
+
+   This structure is used to describe a plugin type. It provides a
+   number of functions to examine the type, instantiate it, link it to
+   buffers and workspaces and to run it. */
+
+typedef struct _LADSPA_Descriptor { 
+
+  /* This numeric identifier indicates the plugin type
+     uniquely. Plugin programmers may reserve ranges of IDs from a
+     central body to avoid clashes. Hosts may assume that IDs are
+     below 0x1000000. */
+  unsigned long UniqueID;
+
+  /* This identifier can be used as a unique, case-sensitive
+     identifier for the plugin type within the plugin file. Plugin
+     types should be identified by file and label rather than by index
+     or plugin name, which may be changed in new plugin
+     versions. Labels must not contain white-space characters. */
+  const char * Label;
+
+  /* This indicates a number of properties of the plugin. */
+  LADSPA_Properties Properties;
+
+  /* This member points to the null-terminated name of the plugin
+     (e.g. "Sine Oscillator"). */
+  const char * Name;
+
+  /* This member points to the null-terminated string indicating the
+     maker of the plugin. This can be an empty string but not NULL. */
+  const char * Maker;
+
+  /* This member points to the null-terminated string indicating any
+     copyright applying to the plugin. If no Copyright applies the
+     string "None" should be used. */
+  const char * Copyright;
+
+  /* This indicates the number of ports (input AND output) present on
+     the plugin. */
+  unsigned long PortCount;
+
+  /* This member indicates an array of port descriptors. Valid indices
+     vary from 0 to PortCount-1. */
+  const LADSPA_PortDescriptor * PortDescriptors;
+
+  /* This member indicates an array of null-terminated strings
+     describing ports (e.g. "Frequency (Hz)"). Valid indices vary from
+     0 to PortCount-1. */
+  const char * const * PortNames;
+
+  /* This member indicates an array of range hints for each port (see
+     above). Valid indices vary from 0 to PortCount-1. */
+  const LADSPA_PortRangeHint * PortRangeHints;
+
+  /* This may be used by the plugin developer to pass any custom
+     implementation data into an instantiate call. It must not be used
+     or interpreted by the host. It is expected that most plugin
+     writers will not use this facility as LADSPA_Handle should be
+     used to hold instance data. */
+  void * ImplementationData;
+
+  /* This member is a function pointer that instantiates a plugin. A
+     handle is returned indicating the new plugin instance. The
+     instantiation function accepts a sample rate as a parameter. The
+     plugin descriptor from which this instantiate function was found
+     must also be passed. This function must return NULL if
+     instantiation fails. 
+
+     Note that instance initialisation should generally occur in
+     activate() rather than here. */
+  LADSPA_Handle (*instantiate)(const struct _LADSPA_Descriptor * Descriptor,
+                               unsigned long                     SampleRate);
+
+  /* This member is a function pointer that connects a port on an
+     instantiated plugin to a memory location at which a block of data
+     for the port will be read/written. The data location is expected
+     to be an array of LADSPA_Data for audio ports or a single
+     LADSPA_Data value for control ports. Memory issues will be
+     managed by the host. The plugin must read/write the data at these
+     locations every time run() or run_adding() is called and the data
+     present at the time of this connection call should not be
+     considered meaningful.
+
+     connect_port() may be called more than once for a plugin instance
+     to allow the host to change the buffers that the plugin is
+     reading or writing. These calls may be made before or after
+     activate() or deactivate() calls.
+
+     connect_port() must be called at least once for each port before
+     run() or run_adding() is called. When working with blocks of
+     LADSPA_Data the plugin should pay careful attention to the block
+     size passed to the run function as the block allocated may only
+     just be large enough to contain the block of samples.
+
+     Plugin writers should be aware that the host may elect to use the
+     same buffer for more than one port and even use the same buffer
+     for both input and output (see LADSPA_PROPERTY_INPLACE_BROKEN).
+     However, overlapped buffers or use of a single buffer for both
+     audio and control data may result in unexpected behaviour. */
+   void (*connect_port)(LADSPA_Handle Instance,
+                        unsigned long Port,
+                        LADSPA_Data * DataLocation);
+
+  /* This member is a function pointer that initialises a plugin
+     instance and activates it for use. This is separated from
+     instantiate() to aid real-time support and so that hosts can
+     reinitialise a plugin instance by calling deactivate() and then
+     activate(). In this case the plugin instance must reset all state
+     information dependent on the history of the plugin instance
+     except for any data locations provided by connect_port() and any
+     gain set by set_run_adding_gain(). If there is nothing for
+     activate() to do then the plugin writer may provide a NULL rather
+     than an empty function.
+
+     When present, hosts must call this function once before run() (or
+     run_adding()) is called for the first time. This call should be
+     made as close to the run() call as possible and indicates to
+     real-time plugins that they are now live. Plugins should not rely
+     on a prompt call to run() after activate(). activate() may not be
+     called again unless deactivate() is called first. Note that
+     connect_port() may be called before or after a call to
+     activate(). */
+  void (*activate)(LADSPA_Handle Instance);
+
+  /* This method is a function pointer that runs an instance of a
+     plugin for a block. Two parameters are required: the first is a
+     handle to the particular instance to be run and the second
+     indicates the block size (in samples) for which the plugin
+     instance may run.
+
+     Note that if an activate() function exists then it must be called
+     before run() or run_adding(). If deactivate() is called for a
+     plugin instance then the plugin instance may not be reused until
+     activate() has been called again.
+
+     If the plugin has the property LADSPA_PROPERTY_HARD_RT_CAPABLE
+     then there are various things that the plugin should not do
+     within the run() or run_adding() functions (see above). */
+  void (*run)(LADSPA_Handle Instance,
+              unsigned long SampleCount);
+
+  /* This method is a function pointer that runs an instance of a
+     plugin for a block. This has identical behaviour to run() except
+     in the way data is output from the plugin. When run() is used,
+     values are written directly to the memory areas associated with
+     the output ports. However when run_adding() is called, values
+     must be added to the values already present in the memory
+     areas. Furthermore, output values written must be scaled by the
+     current gain set by set_run_adding_gain() (see below) before
+     addition.
+
+     run_adding() is optional. When it is not provided by a plugin,
+     this function pointer must be set to NULL. When it is provided,
+     the function set_run_adding_gain() must be provided also. */
+  void (*run_adding)(LADSPA_Handle Instance,
+                    unsigned long SampleCount);
+
+  /* This method is a function pointer that sets the output gain for
+     use when run_adding() is called (see above). If this function is
+     never called the gain is assumed to default to 1. Gain
+     information should be retained when activate() or deactivate()
+     are called.
+
+     This function should be provided by the plugin if and only if the
+     run_adding() function is provided. When it is absent this
+     function pointer must be set to NULL. */
+  void (*set_run_adding_gain)(LADSPA_Handle Instance,
+                             LADSPA_Data   Gain);
+
+  /* This is the counterpart to activate() (see above). If there is
+     nothing for deactivate() to do then the plugin writer may provide
+     a NULL rather than an empty function.
+
+     Hosts must deactivate all activated units after they have been
+     run() (or run_adding()) for the last time. This call should be
+     made as close to the last run() call as possible and indicates to
+     real-time plugins that they are no longer live. Plugins should
+     not rely on prompt deactivation. Note that connect_port() may be
+     called before or after a call to deactivate().
+
+     Deactivation is not similar to pausing as the plugin instance
+     will be reinitialised when activate() is called to reuse it. */
+  void (*deactivate)(LADSPA_Handle Instance);
+
+  /* Once an instance of a plugin has been finished with it can be
+     deleted using the following function. The instance handle passed
+     ceases to be valid after this call.
+  
+     If activate() was called for a plugin instance then a
+     corresponding call to deactivate() must be made before cleanup()
+     is called. */
+  void (*cleanup)(LADSPA_Handle Instance);
+
+} LADSPA_Descriptor;
+
+/**********************************************************************/
+
+/* Accessing a Plugin: */
+
+/* The exact mechanism by which plugins are loaded is host-dependent,
+   however all most hosts will need to know is the name of shared
+   object file containing the plugin types. To allow multiple hosts to
+   share plugin types, hosts may wish to check for environment
+   variable LADSPA_PATH. If present, this should contain a
+   colon-separated path indicating directories that should be searched
+   (in order) when loading plugin types.
+
+   A plugin programmer must include a function called
+   "ladspa_descriptor" with the following function prototype within
+   the shared object file. This function will have C-style linkage (if
+   you are using C++ this is taken care of by the `extern "C"' clause
+   at the top of the file).
+
+   A host will find the plugin shared object file by one means or
+   another, find the ladspa_descriptor() function, call it, and
+   proceed from there.
+
+   Plugin types are accessed by index (not ID) using values from 0
+   upwards. Out of range indexes must result in this function
+   returning NULL, so the plugin count can be determined by checking
+   for the least index that results in NULL being returned. */
+
+const LADSPA_Descriptor * ladspa_descriptor(unsigned long Index);
+
+/* Datatype corresponding to the ladspa_descriptor() function. */
+typedef const LADSPA_Descriptor * 
+(*LADSPA_Descriptor_Function)(unsigned long Index);
+
+/**********************************************************************/
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* LADSPA_INCLUDED */
+
+/* EOF */
diff --git a/ext/ladspa/load.c b/ext/ladspa/load.c
new file mode 100644 (file)
index 0000000..148f98f
--- /dev/null
@@ -0,0 +1,186 @@
+/* load.c
+
+   Free software by Richard W.E. Furse. Do with as you will. No
+   warranty. */
+
+/*****************************************************************************/
+
+#include <dlfcn.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
+/*****************************************************************************/
+
+#include "ladspa.h"
+#include "utils.h"
+
+/*****************************************************************************/
+
+/* This function provides a wrapping of dlopen(). When the filename is
+   not an absolute path (i.e. does not begin with / character), this
+   routine will search the LADSPA_PATH for the file. */
+static void *
+dlopenLADSPA(const char * pcFilename, int iFlag) {
+
+  char * pcBuffer;
+  const char * pcEnd;
+  const char * pcLADSPAPath;
+  const char * pcStart;
+  int iEndsInSO;
+  int iNeedSlash;
+  size_t iFilenameLength;
+  void * pvResult;
+
+  iFilenameLength = strlen(pcFilename);
+  pvResult = NULL;
+
+  if (pcFilename[0] == '/') {
+
+    /* The filename is absolute. Assume the user knows what he/she is
+       doing and simply dlopen() it. */
+
+    pvResult = dlopen(pcFilename, iFlag);
+    if (pvResult != NULL)
+      return pvResult;
+
+  }
+  else {
+
+    /* If the filename is not absolute then we wish to check along the
+       LADSPA_PATH path to see if we can find the file there. We do
+       NOT call dlopen() directly as this would find plugins on the
+       LD_LIBRARY_PATH, whereas the LADSPA_PATH is the correct place
+       to search. */
+
+    pcLADSPAPath = getenv("LADSPA_PATH");
+    
+    if (pcLADSPAPath) {
+
+      pcStart = pcLADSPAPath;
+      while (*pcStart != '\0') {
+       pcEnd = pcStart;
+       while (*pcEnd != ':' && *pcEnd != '\0')
+         pcEnd++;
+       
+       pcBuffer = malloc(iFilenameLength + 2 + (pcEnd - pcStart));
+       if (pcEnd > pcStart)
+         strncpy(pcBuffer, pcStart, pcEnd - pcStart);
+       iNeedSlash = 0;
+       if (pcEnd > pcStart)
+         if (*(pcEnd - 1) != '/') {
+           iNeedSlash = 1;
+           pcBuffer[pcEnd - pcStart] = '/';
+         }
+       strcpy(pcBuffer + iNeedSlash + (pcEnd - pcStart), pcFilename);
+       
+       pvResult = dlopen(pcBuffer, iFlag);
+       
+       free (pcBuffer);
+       if (pvResult != NULL)
+         return pvResult;
+       
+       pcStart = pcEnd;
+       if (*pcStart == ':')
+         pcStart++;
+      }
+    }
+  }
+
+  /* As a last ditch effort, check if filename does not end with
+     ".so". In this case, add this suffix and recurse. */
+  iEndsInSO = 0;
+  if (iFilenameLength > 3)
+    iEndsInSO = (strcmp(pcFilename + iFilenameLength - 3, ".so") == 0);
+  if (!iEndsInSO) {
+    pcBuffer = malloc(iFilenameLength + 4);
+    strcpy(pcBuffer, pcFilename);
+    strcat(pcBuffer, ".so");
+    pvResult = dlopenLADSPA(pcBuffer, iFlag);
+    free(pcBuffer);
+  }
+
+  if (pvResult != NULL)
+    return pvResult;
+
+  /* If nothing has worked, then at least we can make sure we set the
+     correct error message - and this should correspond to a call to
+     dlopen() with the actual filename requested. The dlopen() manual
+     page does not specify whether the first or last error message
+     will be kept when multiple calls are made to dlopen(). We've
+     covered the former case - now we can handle the latter by calling
+     dlopen() again here. */
+  return dlopen(pcFilename, iFlag);
+}
+
+/*****************************************************************************/
+
+void *
+loadLADSPAPluginLibrary(const char * pcPluginFilename) {
+
+  void * pvPluginHandle;
+
+  pvPluginHandle = dlopenLADSPA(pcPluginFilename, RTLD_NOW);
+  if (!pvPluginHandle) {
+    fprintf(stderr, 
+           "Failed to load plugin \"%s\": %s\n", 
+           pcPluginFilename,
+           dlerror());
+    exit(1);
+  }
+
+  return pvPluginHandle;
+}
+
+/*****************************************************************************/
+
+void 
+unloadLADSPAPluginLibrary(void * pvLADSPAPluginLibrary) {
+  dlclose(pvLADSPAPluginLibrary);
+}
+
+/*****************************************************************************/
+
+const LADSPA_Descriptor *
+findLADSPAPluginDescriptor(void * pvLADSPAPluginLibrary,
+                          const char * pcPluginLibraryFilename,
+                          const char * pcPluginLabel) {
+
+  const LADSPA_Descriptor * psDescriptor;
+  LADSPA_Descriptor_Function pfDescriptorFunction;
+  unsigned long lPluginIndex;
+
+  dlerror();
+  pfDescriptorFunction 
+    = (LADSPA_Descriptor_Function)dlsym(pvLADSPAPluginLibrary,
+                                       "ladspa_descriptor");
+  if (!pfDescriptorFunction) {
+    const char * pcError = dlerror();
+    if (pcError) {
+      fprintf(stderr,
+             "Unable to find ladspa_descriptor() function in plugin "
+             "library file \"%s\": %s.\n"
+             "Are you sure this is a LADSPA plugin file?\n", 
+             pcPluginLibraryFilename,
+             pcError);
+      exit(1);
+    }
+  }
+
+  for (lPluginIndex = 0;; lPluginIndex++) {
+    psDescriptor = pfDescriptorFunction(lPluginIndex);
+    if (psDescriptor == NULL) {
+      fprintf(stderr,
+             "Unable to find label \"%s\" in plugin library file \"%s\".\n",
+             pcPluginLabel,
+             pcPluginLibraryFilename);
+      exit(1);      
+    }
+    if (strcmp(psDescriptor->Label, pcPluginLabel) == 0)
+      return psDescriptor;
+  }
+}
+
+/*****************************************************************************/
+
+/* EOF */
diff --git a/ext/ladspa/search.c b/ext/ladspa/search.c
new file mode 100644 (file)
index 0000000..3194047
--- /dev/null
@@ -0,0 +1,129 @@
+/* search.c
+
+   Free software by Richard W.E. Furse. Do with as you will. No
+   warranty. */
+
+/*****************************************************************************/
+
+#include <dirent.h>
+#include <dlfcn.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <sys/types.h>
+#include <unistd.h>
+
+/*****************************************************************************/
+
+#include "ladspa.h"
+#include "utils.h"
+
+/*****************************************************************************/
+
+/* Search just the one directory. */
+static void
+LADSPADirectoryPluginSearch
+(const char * pcDirectory, 
+ LADSPAPluginSearchCallbackFunction fCallbackFunction) {
+
+  char * pcFilename;
+  DIR * psDirectory;
+  LADSPA_Descriptor_Function fDescriptorFunction;
+  long lDirLength;
+  long iNeedSlash;
+  struct dirent * psDirectoryEntry;
+  void * pvPluginHandle;
+
+  lDirLength = strlen(pcDirectory);
+  if (!lDirLength)
+    return;
+  if (pcDirectory[lDirLength - 1] == '/')
+    iNeedSlash = 0;
+  else
+    iNeedSlash = 1;
+
+  psDirectory = opendir(pcDirectory);
+  if (!psDirectory)
+    return;
+
+  while (1) {
+
+    psDirectoryEntry = readdir(psDirectory);
+    if (!psDirectoryEntry) {
+      closedir(psDirectory);
+      return;
+    }
+
+    pcFilename = malloc(lDirLength
+                       + strlen(psDirectoryEntry->d_name)
+                       + 1 + iNeedSlash);
+    strcpy(pcFilename, pcDirectory);
+    if (iNeedSlash)
+      strcat(pcFilename, "/");
+    strcat(pcFilename, psDirectoryEntry->d_name);
+    
+    pvPluginHandle = dlopen(pcFilename, RTLD_LAZY);
+    if (pvPluginHandle) {
+      /* This is a file and the file is a shared library! */
+
+      dlerror();
+      fDescriptorFunction
+       = (LADSPA_Descriptor_Function)dlsym(pvPluginHandle,
+                                           "ladspa_descriptor");
+      if (dlerror() == NULL && fDescriptorFunction) {
+       /* We've successfully found a ladspa_descriptor function. Pass
+           it to the callback function. */
+       fCallbackFunction(pcFilename,
+                         pvPluginHandle,
+                         fDescriptorFunction);
+      }
+      else {
+       /* It was a library, but not a LADSPA one. Unload it. */
+       dlclose(pcFilename);
+      }
+    }
+    free(pcFilename);
+  }
+}
+
+/*****************************************************************************/
+
+void 
+LADSPAPluginSearch(LADSPAPluginSearchCallbackFunction fCallbackFunction) {
+
+  char * pcBuffer;
+  const char * pcEnd;
+  const char * pcLADSPAPath;
+  const char * pcStart;
+
+  pcLADSPAPath = getenv("LADSPA_PATH");
+  if (!pcLADSPAPath) {
+//    fprintf(stderr,
+//         "Warning: You do not have a LADSPA_PATH "
+//         "environment variable set.\n");
+    return;
+  }
+  
+  pcStart = pcLADSPAPath;
+  while (*pcStart != '\0') {
+    pcEnd = pcStart;
+    while (*pcEnd != ':' && *pcEnd != '\0')
+      pcEnd++;
+    
+    pcBuffer = malloc(1 + pcEnd - pcStart);
+    if (pcEnd > pcStart)
+      strncpy(pcBuffer, pcStart, pcEnd - pcStart);
+    pcBuffer[pcEnd - pcStart] = '\0';
+    
+    LADSPADirectoryPluginSearch(pcBuffer, fCallbackFunction);
+    free(pcBuffer);
+
+    pcStart = pcEnd;
+    if (*pcStart == ':')
+      pcStart++;
+  }
+}
+
+/*****************************************************************************/
+
+/* EOF */
diff --git a/ext/ladspa/utils.h b/ext/ladspa/utils.h
new file mode 100644 (file)
index 0000000..1be64d0
--- /dev/null
@@ -0,0 +1,62 @@
+/* utils.h
+
+   Free software by Richard W.E. Furse. Do with as you will. No
+   warranty. */
+
+#ifndef LADSPA_SDK_LOAD_PLUGIN_LIB
+#define LADSPA_SDK_LOAD_PLUGIN_LIB
+
+/*****************************************************************************/
+
+#include "ladspa.h"
+
+/*****************************************************************************/
+
+/* Functions in load.c: */
+
+/* This function call takes a plugin library filename, searches for
+   the library along the LADSPA_PATH, loads it with dlopen() and
+   returns a plugin handle for use with findPluginDescriptor() or
+   unloadLADSPAPluginLibrary(). Errors are handled by writing a
+   message to stderr and calling exit(1). It is alright (although
+   inefficient) to call this more than once for the same file. */
+void * loadLADSPAPluginLibrary(const char * pcPluginFilename);
+
+/* This function unloads a LADSPA plugin library. */
+void unloadLADSPAPluginLibrary(void * pvLADSPAPluginLibrary);
+
+/* This function locates a LADSPA plugin within a plugin library
+   loaded with loadLADSPAPluginLibrary(). Errors are handled by
+   writing a message to stderr and calling exit(1). Note that the
+   plugin library filename is only included to help provide
+   informative error messages. */
+const LADSPA_Descriptor *
+findLADSPAPluginDescriptor(void * pvLADSPAPluginLibrary,
+                          const char * pcPluginLibraryFilename,
+                          const char * pcPluginLabel);
+
+/*****************************************************************************/
+
+/* Functions in search.c: */
+
+/* Callback function for use with LADSPAPluginSearch(). The callback
+   function passes the filename (full path), a plugin handle (dlopen()
+   style) and a LADSPA_DescriptorFunction (from which
+   LADSPA_Descriptors can be acquired). */
+typedef void LADSPAPluginSearchCallbackFunction
+(const char * pcFullFilename, 
+ void * pvPluginHandle,
+ LADSPA_Descriptor_Function fDescriptorFunction);
+
+/* Search through the $(LADSPA_PATH) (or a default path) for any
+   LADSPA plugin libraries. Each plugin library is tested using
+   dlopen() and dlsym(,"ladspa_descriptor"). After loading each
+   library, the callback function is called to process it. This
+   function leaves items passed to the callback function open. */
+void LADSPAPluginSearch(LADSPAPluginSearchCallbackFunction fCallbackFunction);
+
+/*****************************************************************************/
+
+#endif
+
+/* EOF */