From ba954374496502524ccd4b96731ecb16ff8ab5d9 Mon Sep 17 00:00:00 2001 From: Thomas Vander Stichele Date: Sun, 23 Dec 2001 15:26:43 +0000 Subject: added ladspa, doesn't have checks yet though Original commit message from CVS: added ladspa, doesn't have checks yet though --- ext/ladspa/Makefile.am | 8 + ext/ladspa/gstladspa.c | 877 +++++++++++++++++++++++++++++++++++++++++++++++++ ext/ladspa/gstladspa.h | 103 ++++++ ext/ladspa/ladspa.h | 512 +++++++++++++++++++++++++++++ ext/ladspa/load.c | 186 +++++++++++ ext/ladspa/search.c | 129 ++++++++ ext/ladspa/utils.h | 62 ++++ 7 files changed, 1877 insertions(+) create mode 100644 ext/ladspa/Makefile.am create mode 100644 ext/ladspa/gstladspa.c create mode 100644 ext/ladspa/gstladspa.h create mode 100644 ext/ladspa/ladspa.h create mode 100644 ext/ladspa/load.c create mode 100644 ext/ladspa/search.c create mode 100644 ext/ladspa/utils.h (limited to 'ext/ladspa') 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 + * <2001> Steve Baker + * + * 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 +#include + +//#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;iPortCount;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;iPortCount;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;inumcontrols;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;iPortCount;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;inumcontrols;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;inumsinkpads;i++){ + ladspa->buffers[i] = gst_pad_pull(ladspa->sinkpads[i]); + printf("pulling buffer %d\n", i); + } + + for (i=0;inumsinkpads;i++) { +// desc->connect_port(ladspa->handle,i,&(ladspa->controls[i])); + } + + for (i=0;inumsrcpads && inumsinkpads;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;jPortCount;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 + * + * 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 +#include + +#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 +#include +#include +#include + +/*****************************************************************************/ + +#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 +#include +#include +#include +#include +#include +#include + +/*****************************************************************************/ + +#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 */ -- cgit v1.2.1