gstreamer/ext/ladspa/gstladspautils.c
Stefan Sauer f65bdac49b ladspa: use the registry cache for plugin details
Split the introspection and registration part. This way we only need to open all
plugins when updating the registry. When reading the registry we can register
the elements entierly from the cache.
2013-05-28 10:27:17 +02:00

855 lines
24 KiB
C

/* GStreamer LADSPA utils
* Copyright (C) 1999 Erik Walthinsen <omega@cse.ogi.edu>
* 2001 Steve Baker <stevebaker_org@yahoo.co.uk>
* 2003 Andy Wingo <wingo at pobox.com>
* 2013 Juan Manuel Borges Caño <juanmabcmail@gmail.com>
* 2013 Stefan Sauer <ensonic@users.sf.net>
*
* 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., 51 Franklin St, Fifth Floor,
* Boston, MA 02110-1301, USA.
*/
/*
* This module is smartly shared between the source, transform and
* sink elements. Handling any specific LADSPA <-> gstreamer interaction.
*
* FIXME:
* Assigning channel orders could be tricky since LADSPA seems to not
* specify order of channels in a really nice computer parseable way,
* stereo is probably wrong, more than stereo is crazy. LADSPA has
* no channel order. All that could be done is to parse the port names
* for "(Left)/(Right)", "-L/-R" or ":l/:r" - these are the 3 patterns
* seen most of the time. By now, it just let's them pass in / pass out.
* Some nice effort might be done to set channel-masks and/or channel
* positions correctly, if this is needed and expected, users will tell.
*
* This affects mainly interleaving, right now, it just interleaves all
* input and output ports. This is the right thing in 90% of the cases,
* but will e.g. create a 4 channel out for a plugin that has 2 stereo
* 'pairs'.
*
* Also, gstreamer supports not-interleaved audio, where you just memcpy
* each channel after each other: c1...c1c2....c2 and so on. This is not
* taken into account, but could be added to the _transform and caps easily
* if users demands it.
*/
#ifdef HAVE_CONFIG_H
#include "config.h"
#endif
#include "gstladspa.h"
#include "gstladspautils.h"
#include "gstladspafilter.h"
#include "gstladspasource.h"
#include "gstladspasink.h"
#include <math.h>
#ifdef HAVE_LRDF
#include <lrdf.h>
#endif
GST_DEBUG_CATEGORY_EXTERN (ladspa_debug);
#define GST_CAT_DEFAULT ladspa_debug
/*
* Interleaved buffer: (c1c2c1c2...)
* De-interleaved buffer: (c1c1...c2c2...)
*/
static inline void
gst_ladspa_ladspa_deinterleave_data (GstLADSPA * ladspa, LADSPA_Data * outdata,
guint samples, guint8 * indata)
{
guint i, j;
const guint audio_in = ladspa->klass->count.audio.in;
for (i = 0; i < audio_in; i++)
for (j = 0; j < samples; j++)
outdata[i * samples + j] = ((LADSPA_Data *) indata)[j * audio_in + i];
}
/*
* Interleaved buffer: (c1c2c1c2...)
* De-interleaved buffer: (c1c1...c2c2...)
*/
static inline void
gst_ladspa_interleave_ladspa_data (GstLADSPA * ladspa, guint8 * outdata,
guint samples, LADSPA_Data * indata)
{
guint i, j;
const guint audio_out = ladspa->klass->count.audio.out;
for (i = 0; i < audio_out; i++)
for (j = 0; j < samples; j++)
((LADSPA_Data *) outdata)[j * audio_out + i] = indata[i * samples + j];
}
/*
* Connect the audio in ports.
*/
static inline void
gst_ladspa_connect_audio_in (GstLADSPA * ladspa, guint samples,
LADSPA_Data * data)
{
guint i;
for (i = 0; i < ladspa->klass->count.audio.in; i++) {
ladspa->ports.audio.in[i] = data + (i * samples);
ladspa->klass->descriptor->connect_port (ladspa->handle,
ladspa->klass->map.audio.in[i], ladspa->ports.audio.in[i]);
}
}
/*
* Connect the audio out ports.
*/
static inline void
gst_ladspa_connect_audio_out (GstLADSPA * ladspa, guint samples,
LADSPA_Data * data)
{
guint i;
for (i = 0; i < ladspa->klass->count.audio.out; i++) {
ladspa->ports.audio.out[i] = data + (i * samples);
ladspa->klass->descriptor->connect_port (ladspa->handle,
ladspa->klass->map.audio.out[i], ladspa->ports.audio.out[i]);
}
}
/*
* Process a block of audio with the ladspa plugin.
*/
static inline void
gst_ladspa_run (GstLADSPA * ladspa, guint nframes)
{
ladspa->klass->descriptor->run (ladspa->handle, nframes);
}
/*
* The data entry/exit point.
*/
gboolean
gst_ladspa_transform (GstLADSPA * ladspa, guint8 * outdata, guint samples,
guint8 * indata)
{
LADSPA_Data *in, *out;
in = g_new0 (LADSPA_Data, samples * ladspa->klass->count.audio.in);
out = g_new0 (LADSPA_Data, samples * ladspa->klass->count.audio.out);
gst_ladspa_ladspa_deinterleave_data (ladspa, in, samples, indata);
gst_ladspa_connect_audio_in (ladspa, samples, in);
gst_ladspa_connect_audio_out (ladspa, samples, out);
gst_ladspa_run (ladspa, samples);
gst_ladspa_interleave_ladspa_data (ladspa, outdata, samples, out);
g_free (out);
g_free (in);
return TRUE;
}
static gboolean
gst_ladspa_activate (GstLADSPA * ladspa)
{
g_return_val_if_fail (ladspa->handle != NULL, FALSE);
g_return_val_if_fail (ladspa->activated == FALSE, FALSE);
GST_DEBUG ("activating LADSPA plugin");
if (ladspa->klass->descriptor->activate)
ladspa->klass->descriptor->activate (ladspa->handle);
ladspa->activated = TRUE;
return TRUE;
}
static gboolean
gst_ladspa_deactivate (GstLADSPA * ladspa)
{
g_return_val_if_fail (ladspa->handle != NULL, FALSE);
g_return_val_if_fail (ladspa->activated == TRUE, FALSE);
GST_DEBUG ("LADSPA deactivating plugin");
if (ladspa->klass->descriptor->deactivate)
ladspa->klass->descriptor->deactivate (ladspa->handle);
ladspa->activated = FALSE;
return TRUE;
}
static gboolean
gst_ladspa_open (GstLADSPA * ladspa, unsigned long rate)
{
guint i;
GST_DEBUG ("LADSPA instantiating plugin at %lu Hz", rate);
if (!(ladspa->handle =
ladspa->klass->descriptor->instantiate (ladspa->klass->descriptor,
rate))) {
GST_WARNING ("could not instantiate LADSPA plugin");
return FALSE;
}
ladspa->rate = rate;
/* connect the control ports */
for (i = 0; i < ladspa->klass->count.control.in; i++)
ladspa->klass->descriptor->connect_port (ladspa->handle,
ladspa->klass->map.control.in[i], &(ladspa->ports.control.in[i]));
for (i = 0; i < ladspa->klass->count.control.out; i++)
ladspa->klass->descriptor->connect_port (ladspa->handle,
ladspa->klass->map.control.out[i], &(ladspa->ports.control.out[i]));
return TRUE;
}
static void
gst_ladspa_close (GstLADSPA * ladspa)
{
g_return_if_fail (ladspa->handle != NULL);
g_return_if_fail (ladspa->activated == FALSE);
GST_DEBUG ("LADSPA deinstantiating plugin");
if (ladspa->klass->descriptor->cleanup)
ladspa->klass->descriptor->cleanup (ladspa->handle);
ladspa->rate = 0;
ladspa->handle = NULL;
}
/*
* Safe open.
*/
gboolean
gst_ladspa_setup (GstLADSPA * ladspa, unsigned long rate)
{
gboolean ret = TRUE;
GST_DEBUG ("LADSPA setting up plugin");
if (ladspa->handle && ladspa->rate != rate) {
if (ladspa->activated)
gst_ladspa_deactivate (ladspa);
gst_ladspa_close (ladspa);
}
if (!ladspa->handle) {
gst_ladspa_open (ladspa, rate);
if (!(ret = gst_ladspa_activate (ladspa)))
gst_ladspa_close (ladspa);
}
return ret;
}
/*
* Safe close.
*/
gboolean
gst_ladspa_cleanup (GstLADSPA * ladspa)
{
gboolean ret = TRUE;
GST_DEBUG ("LADSPA cleaning up plugin");
if (ladspa->handle) {
if (ladspa->activated)
ret = gst_ladspa_deactivate (ladspa);
gst_ladspa_close (ladspa);
}
return ret;
}
static gchar *
gst_ladspa_object_class_get_param_name (GstLADSPAClass * ladspa_class,
GObjectClass * object_class, unsigned long portnum)
{
const LADSPA_Descriptor *desc = ladspa_class->descriptor;
gchar *name, **namev, **v, *tmp;
guint i;
/* beauty in the mess */
name = g_strdup ("");
namev = g_strsplit_set (desc->PortNames[portnum], "[]()", 0);
for (i = 0, v = namev; *v; i++, v++) {
if (!(i % 2)) {
tmp = name;
name = g_strconcat (name, *v, NULL);
g_free (tmp);
}
}
g_strfreev (namev);
g_strstrip (name);
tmp = name;
name = g_ascii_strdown (name, -1);
g_free (tmp);
/* this is the same thing that param_spec_* will do */
g_strcanon (name, G_CSET_A_2_Z G_CSET_a_2_z G_CSET_DIGITS "-", '-');
/* satisfy glib2 (argname[0] must be [A-Za-z]) */
if (!((name[0] >= 'a' && name[0] <= 'z') || (name[0] >= 'A'
&& name[0] <= 'Z'))) {
tmp = name;
name = g_strconcat ("param-", name, NULL);
g_free (tmp);
}
/* check for duplicate property names */
if (g_object_class_find_property (G_OBJECT_CLASS (object_class), name)) {
gint n = 1;
gchar *nprop = g_strdup_printf ("%s-%d", name, n++);
while (g_object_class_find_property (G_OBJECT_CLASS (object_class), nprop)) {
g_free (nprop);
nprop = g_strdup_printf ("%s-%d", name, n++);
}
g_free (name);
name = nprop;
}
GST_DEBUG ("LADSPA built property name '%s' from port name '%s'", name,
desc->PortNames[portnum]);
return name;
}
static GParamSpec *
gst_ladspa_object_class_get_param_spec (GstLADSPAClass * ladspa_class,
GObjectClass * object_class, unsigned long portnum)
{
const LADSPA_Descriptor *desc = ladspa_class->descriptor;
GParamSpec *ret;
gchar *name;
gint hintdesc, perms;
gfloat lower, upper, def;
name =
gst_ladspa_object_class_get_param_name (ladspa_class, object_class,
portnum);
perms = G_PARAM_READABLE;
if (LADSPA_IS_PORT_INPUT (desc->PortDescriptors[portnum]))
perms |= G_PARAM_WRITABLE | G_PARAM_CONSTRUCT;
if (LADSPA_IS_PORT_CONTROL (desc->PortDescriptors[portnum]))
perms |= GST_PARAM_CONTROLLABLE;
/* short name for hint descriptor */
hintdesc = desc->PortRangeHints[portnum].HintDescriptor;
if (LADSPA_IS_HINT_TOGGLED (hintdesc)) {
ret =
g_param_spec_boolean (name, name, desc->PortNames[portnum], FALSE,
perms);
g_free (name);
return ret;
}
if (LADSPA_IS_HINT_BOUNDED_BELOW (hintdesc))
lower = desc->PortRangeHints[portnum].LowerBound;
else
lower = -G_MAXFLOAT;
if (LADSPA_IS_HINT_BOUNDED_ABOVE (hintdesc))
upper = desc->PortRangeHints[portnum].UpperBound;
else
upper = G_MAXFLOAT;
if (LADSPA_IS_HINT_SAMPLE_RATE (hintdesc)) {
/* FIXME:! (*= ladspa->rate?, *= GST_AUDIO_DEF_RATE?) */
lower *= 44100;
upper *= 44100;
}
if (LADSPA_IS_HINT_INTEGER (hintdesc)) {
lower = CLAMP (lower, G_MININT, G_MAXINT);
upper = CLAMP (upper, G_MININT, G_MAXINT);
}
/* default to lower bound */
def = lower;
#ifdef LADSPA_IS_HINT_HAS_DEFAULT
if (LADSPA_IS_HINT_HAS_DEFAULT (hintdesc)) {
if (LADSPA_IS_HINT_DEFAULT_0 (hintdesc))
def = 0.0;
else if (LADSPA_IS_HINT_DEFAULT_1 (hintdesc))
def = 1.0;
else if (LADSPA_IS_HINT_DEFAULT_100 (hintdesc))
def = 100.0;
else if (LADSPA_IS_HINT_DEFAULT_440 (hintdesc))
def = 440.0;
if (LADSPA_IS_HINT_DEFAULT_MINIMUM (hintdesc))
def = lower;
else if (LADSPA_IS_HINT_DEFAULT_MAXIMUM (hintdesc))
def = upper;
else if (LADSPA_IS_HINT_LOGARITHMIC (hintdesc)) {
if (LADSPA_IS_HINT_DEFAULT_LOW (hintdesc))
def = exp (0.75 * log (lower) + 0.25 * log (upper));
else if (LADSPA_IS_HINT_DEFAULT_MIDDLE (hintdesc))
def = exp (0.5 * log (lower) + 0.5 * log (upper));
else if (LADSPA_IS_HINT_DEFAULT_HIGH (hintdesc))
def = exp (0.25 * log (lower) + 0.75 * log (upper));
} else {
if (LADSPA_IS_HINT_DEFAULT_LOW (hintdesc))
def = 0.75 * lower + 0.25 * upper;
else if (LADSPA_IS_HINT_DEFAULT_MIDDLE (hintdesc))
def = 0.5 * lower + 0.5 * upper;
else if (LADSPA_IS_HINT_DEFAULT_HIGH (hintdesc))
def = 0.25 * lower + 0.75 * upper;
}
}
#endif /* LADSPA_IS_HINT_HAS_DEFAULT */
if (lower > upper) {
gfloat tmp;
/* silently swap */
tmp = lower;
lower = upper;
upper = tmp;
}
def = CLAMP (def, lower, upper);
if (LADSPA_IS_HINT_INTEGER (hintdesc)) {
ret =
g_param_spec_int (name, name, desc->PortNames[portnum], lower, upper,
def, perms);
} else {
ret =
g_param_spec_float (name, name, desc->PortNames[portnum], lower, upper,
def, perms);
}
g_free (name);
return ret;
}
void
gst_ladspa_object_set_property (GstLADSPA * ladspa, GObject * object,
guint prop_id, const GValue * value, GParamSpec * pspec)
{
/* remember, properties have an offset */
prop_id -= ladspa->klass->properties;
/* only input ports */
g_return_if_fail (prop_id < ladspa->klass->count.control.in);
/* now see what type it is */
switch (pspec->value_type) {
case G_TYPE_BOOLEAN:
ladspa->ports.control.in[prop_id] =
g_value_get_boolean (value) ? 1.f : 0.f;
break;
case G_TYPE_INT:
ladspa->ports.control.in[prop_id] = g_value_get_int (value);
break;
case G_TYPE_FLOAT:
ladspa->ports.control.in[prop_id] = g_value_get_float (value);
break;
default:
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
}
}
void
gst_ladspa_object_get_property (GstLADSPA * ladspa, GObject * object,
guint prop_id, GValue * value, GParamSpec * pspec)
{
LADSPA_Data *controls;
/* remember, properties have an offset */
prop_id -= ladspa->klass->properties;
if (prop_id < ladspa->klass->count.control.in) {
controls = ladspa->ports.control.in;
} else if (prop_id <
ladspa->klass->count.control.in + ladspa->klass->count.control.out) {
controls = ladspa->ports.control.out;
prop_id -= ladspa->klass->count.control.in;
} else {
g_return_if_reached ();
}
/* now see what type it is */
switch (pspec->value_type) {
case G_TYPE_BOOLEAN:
g_value_set_boolean (value, controls[prop_id] > 0.5);
break;
case G_TYPE_INT:
g_value_set_int (value, CLAMP (controls[prop_id], G_MININT, G_MAXINT));
break;
case G_TYPE_FLOAT:
g_value_set_float (value, controls[prop_id]);
break;
default:
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
}
}
void
gst_ladspa_object_class_install_properties (GstLADSPAClass * ladspa_class,
GObjectClass * object_class, guint offset)
{
GParamSpec *p;
gint i, ix;
ladspa_class->properties = offset;
/* properties have an offset */
ix = ladspa_class->properties;
/* register properties */
for (i = 0; i < ladspa_class->count.control.in; i++, ix++) {
p = gst_ladspa_object_class_get_param_spec (ladspa_class, object_class,
ladspa_class->map.control.in[i]);
g_object_class_install_property (object_class, ix, p);
}
for (i = 0; i < ladspa_class->count.control.out; i++, ix++) {
p = gst_ladspa_object_class_get_param_spec (ladspa_class, object_class,
ladspa_class->map.control.out[i]);
g_object_class_install_property (object_class, ix, p);
}
}
void
gst_ladspa_element_class_set_metadata (GstLADSPAClass * ladspa_class,
GstElementClass * elem_class, const gchar * ladspa_class_tags)
{
const LADSPA_Descriptor *desc = ladspa_class->descriptor;
gchar *longname, *author, *extra_ladspa_class_tags = NULL, *tmp;
#ifdef HAVE_LRDF
gchar *uri;
#endif
longname = g_locale_to_utf8 (desc->Name, -1, NULL, NULL, NULL);
if (!longname)
longname = g_strdup ("no LADSPA description available");
/* FIXME: no plugin author field different from element author field */
tmp = g_locale_to_utf8 (desc->Maker, -1, NULL, NULL, NULL);
if (!tmp)
tmp = g_strdup ("no LADSPA author available");
author =
g_strjoin (", ", tmp,
"Juan Manuel Borges Caño <juanmabcmail@gmail.com>",
"Andy Wingo <wingo at pobox.com>",
"Steve Baker <stevebaker_org@yahoo.co.uk>",
"Erik Walthinsen <omega@cse.ogi.edu>",
"Stefan Sauer <ensonic@users.sf.net>",
"Wim Taymans <wim@fluendo.com>", NULL);
g_free (tmp);
#ifdef HAVE_LRDF
/* libldrf support, we want to get extra klass information here */
uri = g_strdup_printf (LADSPA_BASE "%ld", desc->UniqueID);
if (uri) {
lrdf_statement query = { 0, };
lrdf_uris *uris;
gchar *str, *base_type = NULL;
GST_DEBUG ("LADSPA uri (id=%lu) : %s", desc->UniqueID, uri);
/* we can take this directly from 'desc', keep this example for future
attributes.
if ((str = lrdf_get_setting_metadata (uri, "title"))) {
GST_DEBUG ("LADSPA title : %s", str);
}
if ((str = lrdf_get_setting_metadata (uri, "creator"))) {
GST_DEBUG ("LADSPA creator : %s", str);
}
*/
/* get the rdf:type for this plugin */
query.subject = uri;
query.predicate = (char *) RDF_BASE "type";
query.object = (char *) "?";
query.next = NULL;
uris = lrdf_match_multi (&query);
if (uris) {
if (uris->count == 1) {
base_type = g_strdup (uris->items[0]);
GST_DEBUG ("LADSPA base_type : %s", base_type);
}
lrdf_free_uris (uris);
}
/* query taxonomy */
if (base_type) {
uris = lrdf_get_all_superclasses (base_type);
if (uris) {
guint32 j;
for (j = 0; j < uris->count; j++) {
if ((str = lrdf_get_label (uris->items[j]))) {
GST_DEBUG ("LADSPA parent_type_label : %s", str);
if (extra_ladspa_class_tags) {
gchar *old_tags = extra_ladspa_class_tags;
extra_ladspa_class_tags =
g_strconcat (extra_ladspa_class_tags, "/", str, NULL);
g_free (old_tags);
} else {
extra_ladspa_class_tags = g_strconcat ("/", str, NULL);
}
}
}
lrdf_free_uris (uris);
}
g_free (base_type);
}
/* we can use this for the presets
uris = lrdf_get_setting_uris (desc->UniqueID);
if (uris) {
guint32 j;
for (j = 0; j < uris->count; j++) {
GST_INFO ("setting_uri : %s", uris->items[j]);
if ((str = lrdf_get_label (uris->items[j]))) {
GST_INFO ("setting_label : %s", str);
}
}
lrdf_free_uris (uris);
}
*/
}
g_free (uri);
if (extra_ladspa_class_tags) {
char *s = g_strconcat (ladspa_class_tags, extra_ladspa_class_tags, NULL);
g_free (extra_ladspa_class_tags);
extra_ladspa_class_tags = s;
}
#endif
GST_INFO ("tags : %s", ladspa_class_tags);
gst_element_class_set_metadata (elem_class, longname,
extra_ladspa_class_tags ? extra_ladspa_class_tags : ladspa_class_tags,
longname, author);
g_free (extra_ladspa_class_tags);
g_free (author);
g_free (longname);
}
void
gst_ladspa_filter_type_class_add_pad_templates (GstLADSPAClass *
ladspa_class, GstAudioFilterClass * audio_class)
{
GstCaps *srccaps, *sinkcaps;
srccaps = gst_caps_new_simple ("audio/x-raw",
"format", G_TYPE_STRING, GST_AUDIO_NE (F32),
"channels", G_TYPE_INT, ladspa_class->count.audio.out,
"rate", GST_TYPE_INT_RANGE, 1, G_MAXINT,
"layout", G_TYPE_STRING, "interleaved", NULL);
sinkcaps = gst_caps_new_simple ("audio/x-raw",
"format", G_TYPE_STRING, GST_AUDIO_NE (F32),
"channels", G_TYPE_INT, ladspa_class->count.audio.in,
"rate", GST_TYPE_INT_RANGE, 1, G_MAXINT,
"layout", G_TYPE_STRING, "interleaved", NULL);
gst_my_audio_filter_class_add_pad_templates (audio_class, srccaps, sinkcaps);
gst_caps_unref (sinkcaps);
gst_caps_unref (srccaps);
}
void
gst_ladspa_source_type_class_add_pad_template (GstLADSPAClass *
ladspa_class, GstBaseSrcClass * base_class)
{
GstCaps *srccaps;
srccaps = gst_caps_new_simple ("audio/x-raw",
"format", G_TYPE_STRING, GST_AUDIO_NE (F32),
"channels", G_TYPE_INT, ladspa_class->count.audio.out,
"rate", GST_TYPE_INT_RANGE, 1, G_MAXINT,
"layout", G_TYPE_STRING, "interleaved", NULL);
gst_my_base_source_class_add_pad_template (base_class, srccaps);
gst_caps_unref (srccaps);
}
void
gst_ladspa_sink_type_class_add_pad_template (GstLADSPAClass * ladspa_class,
GstBaseSinkClass * base_class)
{
GstCaps *sinkcaps;
sinkcaps = gst_caps_new_simple ("audio/x-raw",
"format", G_TYPE_STRING, GST_AUDIO_NE (F32),
"channels", G_TYPE_INT, ladspa_class->count.audio.in,
"rate", GST_TYPE_INT_RANGE, 1, G_MAXINT,
"layout", G_TYPE_STRING, "interleaved", NULL);
gst_my_base_sink_class_add_pad_template (base_class, sinkcaps);
gst_caps_unref (sinkcaps);
}
void
gst_ladspa_init (GstLADSPA * ladspa, GstLADSPAClass * ladspa_class)
{
GST_DEBUG ("LADSPA initializing component");
ladspa->klass = ladspa_class;
ladspa->handle = NULL;
ladspa->activated = FALSE;
ladspa->rate = 0;
ladspa->ports.audio.in =
g_new0 (LADSPA_Data *, ladspa->klass->count.audio.in);
ladspa->ports.audio.out =
g_new0 (LADSPA_Data *, ladspa->klass->count.audio.out);
ladspa->ports.control.in =
g_new0 (LADSPA_Data, ladspa->klass->count.control.in);
ladspa->ports.control.out =
g_new0 (LADSPA_Data, ladspa->klass->count.control.out);
}
void
gst_ladspa_finalize (GstLADSPA * ladspa)
{
GST_DEBUG ("LADSPA finalizing component");
g_free (ladspa->ports.control.out);
ladspa->ports.control.out = NULL;
g_free (ladspa->ports.control.in);
ladspa->ports.control.in = NULL;
g_free (ladspa->ports.audio.out);
ladspa->ports.audio.out = NULL;
g_free (ladspa->ports.audio.in);
ladspa->ports.audio.in = NULL;
}
void
gst_ladspa_class_init (GstLADSPAClass * ladspa_class, GType type)
{
guint mapper, ix;
guint audio_in = 0, audio_out = 0, control_in = 0, control_out = 0;
const GValue *value =
gst_structure_get_value (ladspa_meta_all, g_type_name (type));
GstStructure *ladspa_meta = g_value_get_boxed (value);
const gchar *file_name;
LADSPA_Descriptor_Function descriptor_function;
GST_DEBUG ("LADSPA initializing class");
file_name = gst_structure_get_string (ladspa_meta, "plugin-filename");
ladspa_class->plugin =
g_module_open (file_name, G_MODULE_BIND_LAZY | G_MODULE_BIND_LOCAL);
g_module_symbol (ladspa_class->plugin, "ladspa_descriptor",
(gpointer *) & descriptor_function);
gst_structure_get_uint (ladspa_meta, "element-ix", &ix);
ladspa_class->descriptor = descriptor_function (ix);
gst_structure_get_uint (ladspa_meta, "audio-in",
&ladspa_class->count.audio.in);
gst_structure_get_uint (ladspa_meta, "audio-out",
&ladspa_class->count.audio.out);
gst_structure_get_uint (ladspa_meta, "control-in",
&ladspa_class->count.control.in);
gst_structure_get_uint (ladspa_meta, "control-out",
&ladspa_class->count.control.out);
ladspa_class->properties = 1;
ladspa_class->map.audio.in =
g_new0 (unsigned long, ladspa_class->count.audio.in);
ladspa_class->map.audio.out =
g_new0 (unsigned long, ladspa_class->count.audio.out);
ladspa_class->map.control.in =
g_new0 (unsigned long, ladspa_class->count.control.in);
ladspa_class->map.control.out =
g_new0 (unsigned long, ladspa_class->count.control.out);
for (mapper = 0; mapper < ladspa_class->descriptor->PortCount; mapper++) {
LADSPA_PortDescriptor p = ladspa_class->descriptor->PortDescriptors[mapper];
if (LADSPA_IS_PORT_AUDIO (p)) {
if (LADSPA_IS_PORT_INPUT (p))
ladspa_class->map.audio.in[audio_in++] = mapper;
else
ladspa_class->map.audio.out[audio_out++] = mapper;
} else if (LADSPA_IS_PORT_CONTROL (p)) {
if (LADSPA_IS_PORT_INPUT (p))
ladspa_class->map.control.in[control_in++] = mapper;
else
ladspa_class->map.control.out[control_out++] = mapper;
}
}
g_assert (control_out == ladspa_class->count.control.out);
g_assert (control_in == ladspa_class->count.control.in);
g_assert (audio_out == ladspa_class->count.audio.out);
g_assert (audio_in == ladspa_class->count.audio.in);
}
void
gst_ladspa_class_finalize (GstLADSPAClass * ladspa_class)
{
GST_DEBUG ("LADSPA finalizing class");
g_free (ladspa_class->map.control.out);
ladspa_class->map.control.out = NULL;
g_free (ladspa_class->map.control.in);
ladspa_class->map.control.in = NULL;
g_free (ladspa_class->map.audio.out);
ladspa_class->map.audio.out = NULL;
g_free (ladspa_class->map.audio.in);
ladspa_class->map.audio.in = NULL;
g_module_close (ladspa_class->plugin);
ladspa_class->plugin = NULL;
}
/*
* Create the type & register the element.
*/
void
ladspa_register_element (GstPlugin * plugin, GType parent_type,
const GTypeInfo * info, GstStructure * ladspa_meta)
{
const gchar *type_name =
gst_structure_get_string (ladspa_meta, "element-type-name");
gst_element_register (plugin, type_name, GST_RANK_NONE,
g_type_register_static (parent_type, type_name, info, 0));
}