mirror of
https://gitlab.freedesktop.org/gstreamer/gstreamer.git
synced 2024-11-30 21:51:09 +00:00
893 lines
25 KiB
C
893 lines
25 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>
|
||
|
* Copyright (C) 2013 Juan Manuel Borges Caño <juanmabcmail@gmail.com>
|
||
|
*
|
||
|
* 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 "gstladspautils.h"
|
||
|
#include "gstladspafilter.h"
|
||
|
#include "gstladspasource.h"
|
||
|
#include "gstladspasink.h"
|
||
|
|
||
|
#include <math.h>
|
||
|
|
||
|
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)
|
||
|
{
|
||
|
LADSPA_Descriptor *desc;
|
||
|
gchar *name, **namev, **v, *tmp;
|
||
|
guint i;
|
||
|
|
||
|
desc = ladspa_class->descriptor;
|
||
|
|
||
|
/* 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)
|
||
|
{
|
||
|
LADSPA_Descriptor *desc;
|
||
|
GParamSpec *ret;
|
||
|
gchar *name;
|
||
|
gint hintdesc, perms;
|
||
|
gfloat lower, upper, def;
|
||
|
|
||
|
desc = ladspa_class->descriptor;
|
||
|
|
||
|
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)
|
||
|
{
|
||
|
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 Kost <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=%d) : %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->ladspa.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->ladspa.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->ladspa.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,
|
||
|
LADSPA_Descriptor * descriptor)
|
||
|
{
|
||
|
guint mapper;
|
||
|
struct
|
||
|
{
|
||
|
struct
|
||
|
{
|
||
|
guint in, out;
|
||
|
} control;
|
||
|
struct
|
||
|
{
|
||
|
guint in, out;
|
||
|
} audio;
|
||
|
} count;
|
||
|
|
||
|
GST_DEBUG ("LADSPA initializing class");
|
||
|
|
||
|
ladspa_class->descriptor = descriptor;
|
||
|
ladspa_class->properties = 1;
|
||
|
|
||
|
ladspa_count_ports (ladspa_class->descriptor, &ladspa_class->count.audio.in,
|
||
|
&ladspa_class->count.audio.out, &ladspa_class->count.control.in,
|
||
|
&ladspa_class->count.control.out);
|
||
|
|
||
|
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);
|
||
|
|
||
|
count.audio.in = count.audio.out = count.control.in = count.control.out = 0;
|
||
|
|
||
|
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[count.audio.in++] = mapper;
|
||
|
else
|
||
|
ladspa_class->map.audio.out[count.audio.out++] = mapper;
|
||
|
} else if (LADSPA_IS_PORT_CONTROL (p)) {
|
||
|
if (LADSPA_IS_PORT_INPUT (p))
|
||
|
ladspa_class->map.control.in[count.control.in++] = mapper;
|
||
|
else
|
||
|
ladspa_class->map.control.out[count.control.out++] = mapper;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
g_assert (count.control.out == ladspa_class->count.control.out);
|
||
|
g_assert (count.control.in == ladspa_class->count.control.in);
|
||
|
|
||
|
g_assert (count.audio.out == ladspa_class->count.audio.out);
|
||
|
g_assert (count.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;
|
||
|
}
|
||
|
|
||
|
void
|
||
|
ladspa_count_ports (const LADSPA_Descriptor * descriptor,
|
||
|
guint * audio_in, guint * audio_out, guint * control_in,
|
||
|
guint * control_out)
|
||
|
{
|
||
|
guint i;
|
||
|
|
||
|
*audio_in = *audio_out = *control_in = *control_out = 0;
|
||
|
|
||
|
for (i = 0; i < descriptor->PortCount; i++) {
|
||
|
LADSPA_PortDescriptor p = descriptor->PortDescriptors[i];
|
||
|
|
||
|
if (LADSPA_IS_PORT_AUDIO (p)) {
|
||
|
if (LADSPA_IS_PORT_INPUT (p))
|
||
|
(*audio_in)++;
|
||
|
else
|
||
|
(*audio_out)++;
|
||
|
} else if (LADSPA_IS_PORT_CONTROL (p)) {
|
||
|
if (LADSPA_IS_PORT_INPUT (p))
|
||
|
(*control_in)++;
|
||
|
else
|
||
|
(*control_out)++;
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
/*
|
||
|
* Register the type.
|
||
|
*/
|
||
|
void
|
||
|
ladspa_register_plugin (GstPlugin * plugin, GType parent_type,
|
||
|
const gchar * tmp, const GTypeInfo * info, GQuark descriptor_quark,
|
||
|
const gchar * filename, const LADSPA_Descriptor * desc)
|
||
|
{
|
||
|
gchar *name;
|
||
|
GType type;
|
||
|
|
||
|
name = g_ascii_strdown (tmp, -1);
|
||
|
g_strcanon (name, G_CSET_A_2_Z G_CSET_a_2_z G_CSET_DIGITS "-+", '-');
|
||
|
|
||
|
/* if it's not already registered, do it */
|
||
|
if (!g_type_from_name (name)) {
|
||
|
/* create the type now */
|
||
|
type = g_type_register_static (parent_type, name, info, 0);
|
||
|
|
||
|
/* base init is expected to initialize dynamic data */
|
||
|
g_type_set_qdata (type, descriptor_quark, (gpointer) desc);
|
||
|
|
||
|
/* register the element */
|
||
|
gst_element_register (plugin, name, GST_RANK_NONE, type);
|
||
|
} else
|
||
|
GST_WARNING ("Plugin identifier collision for %s (%s:%lu/%s)", name,
|
||
|
filename, desc->UniqueID, desc->Label);
|
||
|
|
||
|
g_free (name);
|
||
|
}
|