summaryrefslogtreecommitdiffstats
path: root/ext/ladspa
diff options
context:
space:
mode:
Diffstat (limited to 'ext/ladspa')
-rw-r--r--ext/ladspa/Makefile.am8
-rw-r--r--ext/ladspa/gstladspa.c877
-rw-r--r--ext/ladspa/gstladspa.h103
-rw-r--r--ext/ladspa/ladspa.h512
-rw-r--r--ext/ladspa/load.c186
-rw-r--r--ext/ladspa/search.c129
-rw-r--r--ext/ladspa/utils.h62
7 files changed, 1877 insertions, 0 deletions
diff --git a/ext/ladspa/Makefile.am b/ext/ladspa/Makefile.am
new file mode 100644
index 00000000..5a7dcd10
--- /dev/null
+++ b/ext/ladspa/Makefile.am
@@ -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
index 00000000..81740c25
--- /dev/null
+++ b/ext/ladspa/gstladspa.c
@@ -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
index 00000000..80a7b458
--- /dev/null
+++ b/ext/ladspa/gstladspa.h
@@ -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
index 00000000..5e2fa98d
--- /dev/null
+++ b/ext/ladspa/ladspa.h
@@ -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
index 00000000..148f98fb
--- /dev/null
+++ b/ext/ladspa/load.c
@@ -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
index 00000000..31940474
--- /dev/null
+++ b/ext/ladspa/search.c
@@ -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
index 00000000..1be64d09
--- /dev/null
+++ b/ext/ladspa/utils.h
@@ -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 */