ladspa: improved port to gstreamer 1.0

Fixes: #698927
This commit is contained in:
Juan Manuel Borges Caño 2013-05-03 11:34:34 +02:00 committed by Stefan Sauer
parent f1a6d84a6c
commit 70e208d08e
12 changed files with 2864 additions and 740 deletions

View file

@ -346,7 +346,7 @@ GST_PLUGINS_NONPORTED=" cdxaparse \
videomeasure videosignal vmnc \
linsys vcd \
apexsink cdaudio dc1394 dirac directfb \
gsettings ladspa \
gsettings \
musepack musicbrainz nas neon ofa openal sdl sndfile timidity \
directdraw direct3d9 acm wininet \
xvid lv2 teletextdec sndio osx_video quicktime"

View file

@ -1,15 +1,32 @@
plugin_LTLIBRARIES = libgstladspa.la
libgstladspa_la_SOURCES = gstladspa.c
libgstladspa_la_SOURCES = \
gstladspautils.c \
gstladspafilter.c \
gstladspasource.c \
gstladspasink.c \
gstladspa.c
libgstladspa_la_CFLAGS = \
-I$(top_srcdir)/gst-libs \
$(GST_PLUGINS_BASE_CFLAGS) \
$(GST_CFLAGS) $(LRDF_CFLAGS)
$(GST_BASE_CFLAGS) \
$(GST_CFLAGS) \
$(LRDF_CFLAGS) \
$(GST_PLUGINS_BAD_CFLAGS)
libgstladspa_la_LIBADD = \
$(top_builddir)/gst-libs/gst/signalprocessor/libgstsignalprocessor-@GST_API_VERSION@.la \
$(GST_PLUGINS_BASE_LIBS) -lgstaudio-$(GST_API_VERSION) \
$(LIBM) $(LRDF_LIBS)
$(GST_PLUGINS_BASE_LIBS) \
-lgstaudio-$(GST_API_VERSION) \
$(GST_BASE_LIBS) \
$(GST_LIBS) \
$(LIBM) \
$(LRDF_LIBS) \
$(GST_LIBS)
libgstladspa_la_LDFLAGS = $(GST_PLUGIN_LDFLAGS)
libgstladspa_la_LIBTOOLFLAGS = $(GST_PLUGIN_LIBTOOLFLAGS)
noinst_HEADERS = gstladspa.h
noinst_HEADERS = \
gstladspautils.h \
gstladspafilter.h \
gstladspasource.h \
gstladspasink.h \
gstladspa.h

View file

@ -1,7 +1,8 @@
/* GStreamer
/* GStreamer LADSPA plugin
* 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
@ -18,31 +19,131 @@
* Free Software Foundation, Inc., 51 Franklin St, Fifth Floor,
* Boston, MA 02110-1301, USA.
*/
/**
* SECTION:element-ladspa
* @short_description: bridge for ladspa (Linux Audio Developer's Simple Plugin API)
* @short_description: bridge for LADSPA (Linux Audio Developer's Simple Plugin API)
* @see_also: #GstAudioConvert #GstAudioResample, #GstAudioTestSrc, #GstAutoAudioSink
*
* The ladspa (Linux Audio Developer's Simple Plugin API) element is a bridge
* for plugins using the <ulink url="http://www.ladspa.org/">ladspa</ulink> API.
* It scans all installed ladspa plugins and registers them as gstreamer
* elements. If available it can also parse lrdf files and use the metadata for
* element classification.
* The LADSPA (Linux Audio Developer's Simple Plugin API) element is a bridge
* for plugins using the <ulink url="http://www.ladspa.org/">LADSPA</ulink> API.
* It scans all installed LADSPA plugins and registers them as gstreamer
* elements. If available it can also parse LRDF files and use the metadata for
* element classification. The functionality you get depends on the LADSPA plugins
* you have installed.
*
* First off all you can apply not live LADSPA filters without this plugin:
*
* <refsect2>
* <title>Example LADSPA line without this plugins</title>
* |[
* (padsp) listplugins
* (padsp) analyseplugin cmt.so amp_mono
* gst-launch -e filesrc location="$myfile" ! decodebin ! audioconvert ! audioresample ! "audio/x-raw,format=S16LE,rate=48000,channels=1" ! wavenc ! filesink location="testin.wav"
* (padsp) applyplugin testin.wav testout.wav cmt.so amp_mono 2
* gst-launch playbin uri=file://"$PWD"/testout.wav
* ]| Decode any audio file into wav with the format expected for the specific ladspa plugin to be applied, apply the ladspa filter and play it.
* </refsect2>
*
* Now with this plugin:
*
* <refsect2>
* <title>Example LADSPA line with this plugins</title>
* |[
* gst-launch autoaudiosrc ! ladspa-cmt-so-amp-mono gain=2 ! ladspa-caps-so-plate ! ladspa-tap-echo-so-tap-stereo-echo l-delay=500 r-haas-delay=500 ! tee name=myT myT. ! queue ! autoaudiosink myT. ! queue ! audioconvert ! goom ! videoconvert ! xvimagesink pixel-aspect-ratio=3/4
* ]| Get audio input, filter it through CAPS Plate and TAP Stereo Echo, play it and show a visualization (recommended hearphones).
* </refsect2>
*
* In case you wonder the plugin naming scheme, quoting ladspa.h:
* "Plugin types should be identified by file and label rather than by
* index or plugin name, which may be changed in new plugin versions."
* This is really the best way then, and so it is less prone to conflicts.
*
* Also it is worth noting that LADSPA provides a control in and out interface,
* on top of the audio in and out one, so some parameters are readable too.
*
* You can see the listing of plugins available with:
* <refsect2>
* <title>Inspecting the plugins list</title>
* |[
* gst-inspect ladspa
* ]| List available LADSPA plugins on gstreamer.
* </refsect2>
*
* You can see the parameters of any plugin with:
* <refsect2>
* <title>Inspecting the plugins</title>
* |[
* gst-inspect ladspa-retro-flange-1208-so-retroFlange
* ]| List details of the plugin, parameters, range and defaults included.
* </refsect2>
*
* The elements categorize in:
* <itemizedlist>
* <listitem><para>Filter/Effect/Audio/LADSPA:</para>
* <refsect2>
* <title>Example Filter/Effect/Audio/LADSPA line with this plugins</title>
* |[
* gst-launch filesrc location="$myfile" ! decodebin ! audioconvert ! audioresample ! ladspa-calf-so-reverb decay-time=15 high-frq-damp=20000 room-size=5 diffusion=1 wet-amount=2 dry-amount=2 pre-delay=50 bass-cut=20000 treble-cut=20000 ! ladspa-tap-echo-so-tap-stereo-echo l-delay=500 r-haas-delay=500 ! autoaudiosink
* ]| Decode any audio file, filter it through Calf Reverb LADSPA then TAP Stereo Echo, and play it.
* </refsect2>
* <refsect2>
* </listitem>
* <listitem><para>Source/Audio/LADSPA:</para>
* <refsect2>
* <title>Example Source/Audio/LADSPA line with this plugins</title>
* |[
* gst-launch -e ladspasrc-sine-so-sine-fcac frequency=220 amplitude=100 ! audioconvert ! "audio/x-raw,rate=22050" ! autoaudiosink
* ]| Generate a sine wave with Sine Oscillator (Freq:control, Amp:control), convert it to 22050 Hz and play it.
* </refsect2>
* <refsect2>
* <title>Example Source/Audio/LADSPA line with this plugins</title>
* |[
* gst-launch -e ladspasrc-caps-so-click bpm=240 volume=1 ! autoaudiosink
* ]| Generate clicks with CAPS Click - Metronome at 240 beats per minute and play it.
* </refsect2>
* <refsect2>
* <title>Example Source/Audio/LADSPA line with this plugins</title>
* |[
* gst-launch -e ladspasrc-random-1661-so-random-fcsc-oa ! ladspa-cmt-so-amp-mono gain=1.5 ! ladspa-caps-so-plate ! tee name=myT myT. ! queue ! autoaudiosink myT. ! queue ! audioconvert ! wavescope ! videoconvert ! autovideosink
* ]| Generate random wave, filter it trhough Mono Amplifier and Versatile Plate Reverb, and play, while showing, it.
* </refsect2>
* </listitem>
* <listitem><para>Sink/Audio/LADSPA:</para>
* <refsect2>
* <title>Example Sink/Audio/LADSPA line with this plugins</title>
* |[
* gst-launch -e autoaudiosrc ! ladspa-cmt-so-amp-mono gain=2 ! ladspa-caps-so-plate ! ladspa-tap-echo-so-tap-stereo-echo l-delay=500 r-haas-delay=500 ! tee name=myT myT. ! audioconvert ! audioresample ! queue ! ladspasink-cmt-so-null-ai myT. ! audioconvert ! audioresample ! queue ! goom ! videoconvert ! xvimagesink pixel-aspect-ratio=3/4
* ]| Get audio input, filter it trhough Mono Amplifier, CAPS Plate LADSPA and TAP Stereo Echo, explicitily anulate audio with Null (Audio Input), and play a visualization (recommended hearphones).
* </refsect2>
* </listitem>
* </itemizedlist>
*/
#ifdef HAVE_CONFIG_H
#include "config.h"
#endif
#include <string.h>
#include <math.h>
#include <gst/audio/audio.h>
#include "gstladspa.h"
#include <ladspa.h> /* main ladspa sdk include file */
#include "gstladspautils.h"
#include "gstladspafilter.h"
#include "gstladspasource.h"
#include "gstladspasink.h"
#include <gst/gst-i18n-plugin.h>
#include <gmodule.h>
#include <string.h>
#include <ladspa.h>
#ifdef HAVE_LRDF
#include <lrdf.h>
#endif
/* 1.0 and the 1.1 preliminary headers don't define a version, but 1.1 final
does */
GST_DEBUG_CATEGORY (ladspa_debug);
#define GST_CAT_DEFAULT ladspa_debug
/*
* 1.0 and the 1.1 preliminary headers don't define a version, but
* 1.1 finally does
*/
#ifndef LADSPA_VERSION
#define LADSPA_VERSION "1.0"
#endif
@ -52,666 +153,29 @@
"/usr/local/lib/ladspa" G_SEARCHPATH_SEPARATOR_S \
LIBDIR "/ladspa"
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_setup (GstSignalProcessor * sigproc, GstCaps * caps);
static gboolean gst_ladspa_start (GstSignalProcessor * sigproc);
static void gst_ladspa_stop (GstSignalProcessor * sigproc);
static void gst_ladspa_cleanup (GstSignalProcessor * sigproc);
static void gst_ladspa_process (GstSignalProcessor * sigproc, guint nframes);
static GstSignalProcessorClass *parent_class;
static GstPlugin *ladspa_plugin;
GST_DEBUG_CATEGORY_STATIC (ladspa_debug);
#define GST_CAT_DEFAULT ladspa_debug
static GQuark descriptor_quark = 0;
GQuark descriptor_quark = 0;
static void
gst_ladspa_base_init (gpointer g_class)
{
GstLADSPAClass *klass = (GstLADSPAClass *) g_class;
GstElementClass *element_class = GST_ELEMENT_CLASS (g_class);
GstSignalProcessorClass *gsp_class = GST_SIGNAL_PROCESSOR_CLASS (g_class);
LADSPA_Descriptor *desc;
guint j, audio_in_count, audio_out_count, control_in_count, control_out_count;
const gchar *klass_tags;
gchar *longname, *author;
#ifdef HAVE_LRDF
gchar *uri;
#endif
gchar *extra_klass_tags = NULL;
GST_DEBUG ("base_init %p", g_class);
desc = (LADSPA_Descriptor *) g_type_get_qdata (G_OBJECT_CLASS_TYPE (klass),
descriptor_quark);
g_assert (desc);
klass->descriptor = desc;
/* pad templates */
gsp_class->num_audio_in = 0;
gsp_class->num_audio_out = 0;
/* properties */
gsp_class->num_control_in = 0;
gsp_class->num_control_out = 0;
for (j = 0; j < desc->PortCount; j++) {
LADSPA_PortDescriptor p = desc->PortDescriptors[j];
if (LADSPA_IS_PORT_AUDIO (p)) {
gchar *name = g_strdup ((gchar *) desc->PortNames[j]);
/* FIXME: group stereo pairs into a stereo pad
* ladspa-fx have "XXX (Left)" and "XXX (Right)"
* where XXX={In,Input,Out,Output}
*/
GST_DEBUG ("LADSPA port name: \"%s\"", name);
/* replaces all spaces with underscores, and then remaining special chars
* with '-'
* FIXME: why, pads can have any name
*/
g_strdelimit (name, " ", '_');
g_strcanon (name, G_CSET_A_2_Z G_CSET_a_2_z G_CSET_DIGITS "_-><=", '-');
GST_DEBUG ("GStreamer pad name: \"%s\"", name);
if (LADSPA_IS_PORT_INPUT (p))
gst_signal_processor_class_add_pad_template (gsp_class, name,
GST_PAD_SINK, gsp_class->num_audio_in++, 1);
else
gst_signal_processor_class_add_pad_template (gsp_class, name,
GST_PAD_SRC, gsp_class->num_audio_out++, 1);
g_free (name);
} else if (LADSPA_IS_PORT_CONTROL (p)) {
if (LADSPA_IS_PORT_INPUT (p))
gsp_class->num_control_in++;
else
gsp_class->num_control_out++;
}
}
longname = g_locale_to_utf8 (desc->Name, -1, NULL, NULL, NULL);
if (!longname)
longname = g_strdup ("no description available");
author = g_locale_to_utf8 (desc->Maker, -1, NULL, NULL, NULL);
if (!author)
author = g_strdup ("no author available");
#ifdef HAVE_LRDF
/* libldrf support, we want to get extra class 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 ("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 ("title : %s", str);
}
if ((str = lrdf_get_setting_metadata (uri, "creator"))) {
GST_DEBUG ("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 ("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++) {
GST_LOG ("parent_type_uri : %s", uris->items[j]);
if ((str = lrdf_get_label (uris->items[j]))) {
GST_DEBUG ("parent_type_label : %s", str);
if (extra_klass_tags) {
gchar *old_tags = extra_klass_tags;
extra_klass_tags = g_strconcat (extra_klass_tags, "/", str, NULL);
g_free (old_tags);
} else {
extra_klass_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);
#endif
if (gsp_class->num_audio_in == 0)
klass_tags = "Source/Audio/LADSPA";
else if (gsp_class->num_audio_out == 0) {
if (gsp_class->num_control_out == 0)
klass_tags = "Sink/Audio/LADSPA";
else
klass_tags = "Sink/Analyzer/Audio/LADSPA";
} else
klass_tags = "Filter/Effect/Audio/LADSPA";
#ifdef HAVE_LRDF
if (extra_klass_tags) {
char *s = g_strconcat (klass_tags, extra_klass_tags, NULL);
g_free (extra_klass_tags);
extra_klass_tags = s;
}
#endif
GST_INFO ("tags : %s", klass_tags);
gst_element_class_set_metadata (element_class, longname,
extra_klass_tags ? extra_klass_tags : klass_tags, longname, author);
g_free (longname);
g_free (author);
g_free (extra_klass_tags);
klass->audio_in_portnums = g_new0 (gint, gsp_class->num_audio_in);
klass->audio_out_portnums = g_new0 (gint, gsp_class->num_audio_out);
klass->control_in_portnums = g_new0 (gint, gsp_class->num_control_in);
klass->control_out_portnums = g_new0 (gint, gsp_class->num_control_out);
audio_in_count = audio_out_count = control_in_count = control_out_count = 0;
for (j = 0; j < desc->PortCount; j++) {
LADSPA_PortDescriptor p = desc->PortDescriptors[j];
if (LADSPA_IS_PORT_AUDIO (p)) {
if (LADSPA_IS_PORT_INPUT (p))
klass->audio_in_portnums[audio_in_count++] = j;
else
klass->audio_out_portnums[audio_out_count++] = j;
} else if (LADSPA_IS_PORT_CONTROL (p)) {
if (LADSPA_IS_PORT_INPUT (p))
klass->control_in_portnums[control_in_count++] = j;
else
klass->control_out_portnums[control_out_count++] = j;
}
}
g_assert (audio_in_count == gsp_class->num_audio_in);
g_assert (audio_out_count == gsp_class->num_audio_out);
g_assert (control_in_count == gsp_class->num_control_in);
g_assert (control_out_count == gsp_class->num_control_out);
if (!LADSPA_IS_INPLACE_BROKEN (desc->Properties))
GST_SIGNAL_PROCESSOR_CLASS_SET_CAN_PROCESS_IN_PLACE (klass);
klass->descriptor = desc;
}
static gchar *
gst_ladspa_class_get_param_name (GstLADSPAClass * klass, gint portnum)
{
LADSPA_Descriptor *desc;
gchar *ret, *paren;
desc = klass->descriptor;
ret = g_strdup (desc->PortNames[portnum]);
paren = g_strrstr (ret, " (");
if (paren != NULL)
*paren = '\0';
/* this is the same thing that param_spec_* will do */
g_strcanon (ret, G_CSET_A_2_Z G_CSET_a_2_z G_CSET_DIGITS "-", '-');
/* satisfy glib2 (argname[0] must be [A-Za-z]) */
if (!((ret[0] >= 'a' && ret[0] <= 'z') || (ret[0] >= 'A' && ret[0] <= 'Z'))) {
gchar *tempstr = ret;
ret = g_strconcat ("param-", ret, NULL);
g_free (tempstr);
}
/* check for duplicate property names */
if (g_object_class_find_property (G_OBJECT_CLASS (klass), ret)) {
gint n = 1;
gchar *nret = g_strdup_printf ("%s-%d", ret, n++);
while (g_object_class_find_property (G_OBJECT_CLASS (klass), nret)) {
g_free (nret);
nret = g_strdup_printf ("%s-%d", ret, n++);
}
g_free (ret);
ret = nret;
}
GST_DEBUG ("built property name '%s' from port name '%s'", ret,
desc->PortNames[portnum]);
return ret;
}
static GParamSpec *
gst_ladspa_class_get_param_spec (GstLADSPAClass * klass, gint portnum)
{
LADSPA_Descriptor *desc;
GParamSpec *ret;
gchar *name;
gint hintdesc, perms;
gfloat lower, upper, def;
desc = klass->descriptor;
name = gst_ladspa_class_get_param_name (klass, 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, name, 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! */
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, name, lower, upper, def, perms);
} else {
ret = g_param_spec_float (name, name, name, lower, upper, def, perms);
}
g_free (name);
return ret;
}
static void
gst_ladspa_class_init (GstLADSPAClass * klass, LADSPA_Descriptor * desc)
{
GObjectClass *gobject_class;
GstSignalProcessorClass *gsp_class;
GParamSpec *p;
gint i, ix;
GST_DEBUG ("class_init %p", klass);
gobject_class = (GObjectClass *) klass;
gobject_class->set_property = gst_ladspa_set_property;
gobject_class->get_property = gst_ladspa_get_property;
gsp_class = GST_SIGNAL_PROCESSOR_CLASS (klass);
gsp_class->setup = gst_ladspa_setup;
gsp_class->start = gst_ladspa_start;
gsp_class->stop = gst_ladspa_stop;
gsp_class->cleanup = gst_ladspa_cleanup;
gsp_class->process = gst_ladspa_process;
/* properties have an offset of 1 */
ix = 1;
/* register properties */
for (i = 0; i < gsp_class->num_control_in; i++, ix++) {
p = gst_ladspa_class_get_param_spec (klass, klass->control_in_portnums[i]);
g_object_class_install_property (gobject_class, ix, p);
}
for (i = 0; i < gsp_class->num_control_out; i++, ix++) {
p = gst_ladspa_class_get_param_spec (klass, klass->control_out_portnums[i]);
g_object_class_install_property (gobject_class, ix, p);
}
}
static void
gst_ladspa_init (GstLADSPA * ladspa, GstLADSPAClass * klass)
{
ladspa->descriptor = klass->descriptor;
ladspa->activated = FALSE;
ladspa->inplace_broken =
LADSPA_IS_INPLACE_BROKEN (ladspa->descriptor->Properties);
}
static void
gst_ladspa_set_property (GObject * object, guint prop_id, const GValue * value,
GParamSpec * pspec)
{
GstSignalProcessor *gsp;
GstSignalProcessorClass *gsp_class;
gsp = GST_SIGNAL_PROCESSOR (object);
gsp_class = GST_SIGNAL_PROCESSOR_GET_CLASS (object);
/* remember, properties have an offset of 1 */
prop_id--;
/* only input ports */
g_return_if_fail (prop_id < gsp_class->num_control_in);
/* now see what type it is */
switch (pspec->value_type) {
case G_TYPE_BOOLEAN:
gsp->control_in[prop_id] = g_value_get_boolean (value) ? 1.f : 0.f;
break;
case G_TYPE_INT:
gsp->control_in[prop_id] = g_value_get_int (value);
break;
case G_TYPE_FLOAT:
gsp->control_in[prop_id] = g_value_get_float (value);
break;
default:
g_assert_not_reached ();
}
}
static void
gst_ladspa_get_property (GObject * object, guint prop_id, GValue * value,
GParamSpec * pspec)
{
GstSignalProcessor *gsp;
GstSignalProcessorClass *gsp_class;
gfloat *controls;
gsp = GST_SIGNAL_PROCESSOR (object);
gsp_class = GST_SIGNAL_PROCESSOR_GET_CLASS (object);
/* remember, properties have an offset of 1 */
prop_id--;
if (prop_id < gsp_class->num_control_in) {
controls = gsp->control_in;
} else if (prop_id < gsp_class->num_control_in + gsp_class->num_control_out) {
controls = gsp->control_out;
prop_id -= gsp_class->num_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_return_if_reached ();
}
}
static gboolean
gst_ladspa_setup (GstSignalProcessor * gsp, GstCaps * caps)
{
GstLADSPA *ladspa;
GstLADSPAClass *oclass;
GstSignalProcessorClass *gsp_class;
LADSPA_Descriptor *desc;
gint i;
gsp_class = GST_SIGNAL_PROCESSOR_GET_CLASS (gsp);
ladspa = (GstLADSPA *) gsp;
oclass = (GstLADSPAClass *) gsp_class;
desc = ladspa->descriptor;
g_return_val_if_fail (ladspa->handle == NULL, FALSE);
g_return_val_if_fail (ladspa->activated == FALSE, FALSE);
GST_DEBUG_OBJECT (ladspa, "instantiating the plugin at %d Hz",
gsp->sample_rate);
if (!(ladspa->handle = desc->instantiate (desc, gsp->sample_rate)))
goto no_instance;
/* connect the control ports */
for (i = 0; i < gsp_class->num_control_in; i++)
desc->connect_port (ladspa->handle,
oclass->control_in_portnums[i], &(gsp->control_in[i]));
for (i = 0; i < gsp_class->num_control_out; i++)
desc->connect_port (ladspa->handle,
oclass->control_out_portnums[i], &(gsp->control_out[i]));
return TRUE;
no_instance:
{
GST_WARNING_OBJECT (gsp, "could not create instance");
return FALSE;
}
}
static gboolean
gst_ladspa_start (GstSignalProcessor * gsp)
{
GstLADSPA *ladspa;
LADSPA_Descriptor *desc;
ladspa = (GstLADSPA *) gsp;
desc = ladspa->descriptor;
g_return_val_if_fail (ladspa->activated == FALSE, FALSE);
g_return_val_if_fail (ladspa->handle != NULL, FALSE);
GST_DEBUG_OBJECT (ladspa, "activating");
if (desc->activate)
desc->activate (ladspa->handle);
ladspa->activated = TRUE;
return TRUE;
}
static void
gst_ladspa_stop (GstSignalProcessor * gsp)
{
GstLADSPA *ladspa;
LADSPA_Descriptor *desc;
ladspa = (GstLADSPA *) gsp;
desc = ladspa->descriptor;
g_return_if_fail (ladspa->activated == TRUE);
g_return_if_fail (ladspa->handle != NULL);
GST_DEBUG_OBJECT (ladspa, "deactivating");
if (desc->activate)
desc->activate (ladspa->handle);
ladspa->activated = FALSE;
}
static void
gst_ladspa_cleanup (GstSignalProcessor * gsp)
{
GstLADSPA *ladspa;
LADSPA_Descriptor *desc;
ladspa = (GstLADSPA *) gsp;
desc = ladspa->descriptor;
g_return_if_fail (ladspa->activated == FALSE);
g_return_if_fail (ladspa->handle != NULL);
GST_DEBUG_OBJECT (ladspa, "cleaning up");
if (desc->cleanup)
desc->cleanup (ladspa->handle);
ladspa->handle = NULL;
}
static void
gst_ladspa_process (GstSignalProcessor * gsp, guint nframes)
{
GstSignalProcessorClass *gsp_class;
GstLADSPA *ladspa;
GstLADSPAClass *oclass;
LADSPA_Descriptor *desc;
guint i;
gsp_class = GST_SIGNAL_PROCESSOR_GET_CLASS (gsp);
ladspa = (GstLADSPA *) gsp;
oclass = (GstLADSPAClass *) gsp_class;
desc = ladspa->descriptor;
for (i = 0; i < gsp_class->num_audio_in; i++)
desc->connect_port (ladspa->handle, oclass->audio_in_portnums[i],
gsp->audio_in[i]);
for (i = 0; i < gsp_class->num_audio_out; i++)
desc->connect_port (ladspa->handle, oclass->audio_out_portnums[i],
gsp->audio_out[i]);
desc->run (ladspa->handle, nframes);
}
static void
ladspa_describe_plugin (LADSPA_Descriptor_Function descriptor_function)
ladspa_describe_plugin (GstPlugin * plugin,
const gchar * filename, LADSPA_Descriptor_Function descriptor_function)
{
const LADSPA_Descriptor *desc;
gint i;
guint i;
/* walk through all the plugins in this pluginlibrary */
i = 0;
while ((desc = descriptor_function (i++))) {
gchar *type_name;
GTypeInfo typeinfo = {
sizeof (GstLADSPAClass),
(GBaseInitFunc) gst_ladspa_base_init,
NULL,
(GClassInitFunc) gst_ladspa_class_init,
NULL,
desc,
sizeof (GstLADSPA),
0,
(GInstanceInitFunc) gst_ladspa_init,
};
GType type;
/* 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))
goto next;
/* walk through all the plugins in this plugin library */
for (i = 0; (desc = descriptor_function (i)); i++) {
guint audio_in, audio_out, control_in, control_out;
/* create the type now */
type =
g_type_register_static (GST_TYPE_SIGNAL_PROCESSOR, type_name, &typeinfo,
0);
/* FIXME: not needed anymore when we can add pad templates, etc in class_init
* as class_data contains the LADSPA_Descriptor too */
g_type_set_qdata (type, descriptor_quark, (gpointer) desc);
/* count ports of this plugin */
ladspa_count_ports (desc, &audio_in, &audio_out, &control_in, &control_out);
if (!gst_element_register (ladspa_plugin, type_name, GST_RANK_NONE, type))
goto next;
next:
g_free (type_name);
/* categorize and register it */
if (audio_in == 0)
ladspa_describe_source_plugin (plugin, filename, desc);
else if (audio_out == 0)
ladspa_describe_sink_plugin (plugin, filename, desc);
else
ladspa_describe_filter_plugin (plugin, filename, desc);
}
}
@ -742,13 +206,11 @@ ladspa_rdf_directory_search (const char *dir_name)
return TRUE;
}
#endif
/* search just the one directory.
*/
/* search just the one directory */
static gboolean
ladspa_plugin_directory_search (const char *dir_name)
ladspa_plugin_directory_search (GstPlugin * ladspa_plugin, const char *dir_name)
{
GDir *dir;
gchar *file_name;
@ -773,7 +235,7 @@ ladspa_plugin_directory_search (const char *dir_name)
(gpointer *) & descriptor_function)) {
/* we've found a ladspa_descriptor function, now introspect it. */
GST_INFO ("describe %s", file_name);
ladspa_describe_plugin (descriptor_function);
ladspa_describe_plugin (ladspa_plugin, entry_name, descriptor_function);
ok = TRUE;
} else {
/* it was a library, but not a LADSPA one. Unload it. */
@ -787,10 +249,9 @@ ladspa_plugin_directory_search (const char *dir_name)
return ok;
}
/* search the plugin path
*/
/* search the plugin path */
static gboolean
ladspa_plugin_path_search (void)
ladspa_plugin_path_search (GstPlugin * plugin)
{
const gchar *search_path;
gchar *ladspa_path;
@ -825,7 +286,8 @@ ladspa_plugin_path_search (void)
}
if (skip)
break;
/* transform path: /usr/lib/ladspa -> /usr/share/ladspa/rdf/
/*
* transform path: /usr/lib/ladspa -> /usr/share/ladspa/rdf/
* yes, this is ugly, but lrdf has not searchpath
*/
if ((pos = strstr (paths[i], "/lib/ladspa"))) {
@ -848,7 +310,7 @@ ladspa_plugin_path_search (void)
}
if (skip)
break;
res |= ladspa_plugin_directory_search (paths[i]);
res |= ladspa_plugin_directory_search (plugin, paths[i]);
}
g_strfreev (paths);
@ -860,8 +322,16 @@ ladspa_plugin_path_search (void)
static gboolean
plugin_init (GstPlugin * plugin)
{
GST_DEBUG_CATEGORY_INIT (ladspa_debug, "ladspa",
GST_DEBUG_FG_GREEN | GST_DEBUG_BG_BLACK | GST_DEBUG_BOLD, "LADSPA");
#ifdef ENABLE_NLS
GST_DEBUG ("binding text domain %s to locale dir %s", GETTEXT_PACKAGE,
LOCALEDIR);
bindtextdomain (GETTEXT_PACKAGE, LOCALEDIR);
bind_textdomain_codeset (GETTEXT_PACKAGE, "UTF-8");
#endif
GST_DEBUG_CATEGORY_INIT (ladspa_debug, "ladspa", 0, "LADSPA plugins");
descriptor_quark = g_quark_from_static_string ("ladspa-descriptor");
gst_plugin_add_dependency_simple (plugin,
"LADSPA_PATH",
@ -871,14 +341,8 @@ plugin_init (GstPlugin * plugin)
lrdf_init ();
#endif
parent_class = g_type_class_ref (GST_TYPE_SIGNAL_PROCESSOR);
ladspa_plugin = plugin;
descriptor_quark = g_quark_from_static_string ("ladspa-descriptor");
if (!ladspa_plugin_path_search ()) {
GST_WARNING ("no ladspa plugins found, check LADSPA_PATH");
}
if (!ladspa_plugin_path_search (plugin))
GST_WARNING ("no LADSPA plugins found, check LADSPA_PATH");
/* we don't want to fail, even if there are no elements registered */
return TRUE;
@ -887,5 +351,5 @@ plugin_init (GstPlugin * plugin)
GST_PLUGIN_DEFINE (GST_VERSION_MAJOR,
GST_VERSION_MINOR,
ladspa,
"All LADSPA plugins",
"LADSPA plugin",
plugin_init, VERSION, GST_LICENSE, GST_PACKAGE_NAME, GST_PACKAGE_ORIGIN)

View file

@ -1,5 +1,6 @@
/* GStreamer
* Copyright (C) <1999> Erik Walthinsen <omega@cse.ogi.edu>
* Copyright (C) 1999 Erik Walthinsen <omega@cse.ogi.edu>
* Copyright (C) 2013 Juan Manuel Borges Caño <juanmabcmail@gmail.com>
*
* gstladspa.h: Header for LADSPA plugin
*
@ -19,58 +20,15 @@
* Boston, MA 02110-1301, USA.
*/
#ifndef __GST_LADSPA_H__
#define __GST_LADSPA_H__
#include <ladspa.h>
#include <gst/gst.h>
#include <gst/signalprocessor/gstsignalprocessor.h>
G_BEGIN_DECLS
typedef struct _ladspa_control_info {
gchar *name;
gchar *param_name;
gfloat lowerbound, upperbound;
gfloat def;
gboolean lower, upper, samplerate;
gboolean toggled, logarithmic, integer, writable;
} ladspa_control_info;
typedef struct _GstLADSPA GstLADSPA;
typedef struct _GstLADSPAClass GstLADSPAClass;
struct _GstLADSPA {
GstSignalProcessor parent;
LADSPA_Descriptor *descriptor;
LADSPA_Handle *handle;
gboolean activated;
gboolean inplace_broken;
};
struct _GstLADSPAClass {
GstSignalProcessorClass parent_class;
LADSPA_Descriptor *descriptor;
gint *audio_in_portnums;
gint *audio_out_portnums;
gint *control_in_portnums;
gint *control_out_portnums;
};
extern GQuark descriptor_quark;
G_END_DECLS
#endif /* __GST_LADSPA_H__ */

View file

@ -0,0 +1,374 @@
/* GStreamer LADSPA filter category
* 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.
*/
#ifdef HAVE_CONFIG_H
#include "config.h"
#endif
#include "gstladspafilter.h"
#include "gstladspa.h"
#include "gstladspautils.h"
GST_DEBUG_CATEGORY_EXTERN (ladspa_debug);
#define GST_CAT_DEFAULT ladspa_debug
#define GST_LADSPA_FILTER_CLASS_TAGS "Filter/Effect/Audio/LADSPA"
static GstLADSPAFilterClass *gst_ladspa_filter_type_parent_class = NULL;
/*
* Assumes only same format (base of AudioFilter), not same channels.
*/
void
gst_my_audio_filter_class_add_pad_templates (GstAudioFilterClass * audio_class,
GstCaps * srccaps, GstCaps * sinkcaps)
{
GstElementClass *elem_class = GST_ELEMENT_CLASS (audio_class);
GstPadTemplate *pad_template;
g_return_if_fail (GST_IS_CAPS (srccaps) && GST_IS_CAPS (sinkcaps));
pad_template =
gst_pad_template_new (GST_BASE_TRANSFORM_SRC_NAME, GST_PAD_SRC,
GST_PAD_ALWAYS, srccaps);
gst_element_class_add_pad_template (elem_class, pad_template);
pad_template =
gst_pad_template_new (GST_BASE_TRANSFORM_SINK_NAME, GST_PAD_SINK,
GST_PAD_ALWAYS, sinkcaps);
gst_element_class_add_pad_template (elem_class, pad_template);
}
static GstCaps *
gst_ladspa_filter_type_fixate_caps (GstBaseTransform * base,
GstPadDirection direction, GstCaps * caps, GstCaps * othercaps)
{
GstStructure *structure;
gint rate;
structure = gst_caps_get_structure (caps, 0);
if (G_UNLIKELY (!gst_structure_get_int (structure, "rate", &rate)))
return othercaps;
othercaps = gst_caps_truncate (othercaps);
othercaps = gst_caps_make_writable (othercaps);
structure = gst_caps_get_structure (othercaps, 0);
gst_structure_fixate_field_nearest_int (structure, "rate", rate);
return othercaps;
}
static GstCaps *
gst_ladspa_filter_type_transform_caps (GstBaseTransform * base,
GstPadDirection direction, GstCaps * caps, GstCaps * filter)
{
GstCaps *srccaps, *sinkcaps;
GstCaps *ret = NULL;
srccaps = gst_pad_get_pad_template_caps (GST_BASE_TRANSFORM_SRC_PAD (base));
sinkcaps = gst_pad_get_pad_template_caps (GST_BASE_TRANSFORM_SINK_PAD (base));
switch (direction) {
case GST_PAD_SINK:
if (gst_caps_can_intersect (caps, sinkcaps))
ret = gst_caps_copy (srccaps);
else
ret = gst_caps_new_empty ();
break;
case GST_PAD_SRC:
if (gst_caps_can_intersect (caps, srccaps))
ret = gst_caps_copy (sinkcaps);
else
ret = gst_caps_new_empty ();
break;
default:
g_assert_not_reached ();
}
GST_DEBUG_OBJECT (ladspa_debug, "transformed %" GST_PTR_FORMAT, ret);
if (filter) {
GstCaps *intersection;
GST_DEBUG_OBJECT (ladspa_debug, "Using filter caps %" GST_PTR_FORMAT,
filter);
intersection =
gst_caps_intersect_full (filter, ret, GST_CAPS_INTERSECT_FIRST);
gst_caps_unref (ret);
ret = intersection;
GST_DEBUG_OBJECT (ladspa_debug, "Intersection %" GST_PTR_FORMAT, ret);
}
return ret;
}
static GstFlowReturn
gst_ladspa_filter_type_prepare_output_buffer (GstBaseTransform * base,
GstBuffer * inbuf, GstBuffer ** outbuf)
{
GstLADSPAFilter *ladspa = GST_LADSPA_FILTER (base);
GstLADSPAFilterClass *ladspa_class = GST_LADSPA_FILTER_GET_CLASS (ladspa);
guint samples;
samples =
gst_buffer_get_size (inbuf) / sizeof (LADSPA_Data) /
ladspa_class->ladspa.count.audio.in;
if (!gst_base_transform_is_in_place (base)) {
*outbuf =
gst_buffer_new_allocate (NULL,
samples * sizeof (LADSPA_Data) * ladspa_class->ladspa.count.audio.out,
NULL);
*outbuf = gst_buffer_make_writable (*outbuf);
return GST_FLOW_OK;
} else {
return
GST_BASE_TRANSFORM_CLASS
(gst_ladspa_filter_type_parent_class)->prepare_output_buffer (base,
inbuf, outbuf);
}
}
static gboolean
gst_ladspa_filter_type_setup (GstAudioFilter * audio,
const GstAudioInfo * info)
{
GstLADSPAFilter *ladspa = GST_LADSPA_FILTER (audio);
return gst_ladspa_setup (&ladspa->ladspa, GST_AUDIO_INFO_RATE (info));
}
static gboolean
gst_ladspa_filter_type_cleanup (GstBaseTransform * base)
{
GstLADSPAFilter *ladspa = GST_LADSPA_FILTER (base);
return gst_ladspa_cleanup (&ladspa->ladspa);
}
static GstFlowReturn
gst_ladspa_filter_type_transform_ip (GstBaseTransform * base,
GstBuffer * buf)
{
GstLADSPAFilter *ladspa = GST_LADSPA_FILTER (base);
GstMapInfo map;
guint samples;
gst_buffer_map (buf, &map, GST_MAP_READWRITE);
samples =
map.size / sizeof (LADSPA_Data) / ladspa->ladspa.klass->count.audio.in;
gst_ladspa_transform (&ladspa->ladspa, map.data, samples, map.data);
gst_buffer_unmap (buf, &map);
return GST_FLOW_OK;
}
static GstFlowReturn
gst_ladspa_filter_type_transform (GstBaseTransform * base,
GstBuffer * inbuf, GstBuffer * outbuf)
{
GstLADSPAFilter *ladspa = GST_LADSPA_FILTER (base);
GstMapInfo inmap, outmap;
guint samples;
gst_buffer_map (inbuf, &inmap, GST_MAP_READ);
gst_buffer_map (outbuf, &outmap, GST_MAP_WRITE);
samples =
inmap.size / sizeof (LADSPA_Data) / ladspa->ladspa.klass->count.audio.in;
gst_ladspa_transform (&ladspa->ladspa, outmap.data, samples, inmap.data);
gst_buffer_unmap (outbuf, &outmap);
gst_buffer_unmap (inbuf, &inmap);
return GST_FLOW_OK;
}
static void
gst_ladspa_filter_type_set_property (GObject * object, guint prop_id,
const GValue * value, GParamSpec * pspec)
{
GstLADSPAFilter *ladspa = GST_LADSPA_FILTER (object);
gst_ladspa_object_set_property (&ladspa->ladspa, object, prop_id, value,
pspec);
}
static void
gst_ladspa_filter_type_get_property (GObject * object, guint prop_id,
GValue * value, GParamSpec * pspec)
{
GstLADSPAFilter *ladspa = GST_LADSPA_FILTER (object);
gst_ladspa_object_get_property (&ladspa->ladspa, object, prop_id, value,
pspec);
}
static void
gst_ladspa_filter_type_init (GstLADSPAFilter * ladspa,
LADSPA_Descriptor * desc)
{
GstBaseTransform *base = GST_BASE_TRANSFORM (ladspa);
GstLADSPAFilterClass *ladspa_class = GST_LADSPA_FILTER_GET_CLASS (ladspa);
gst_ladspa_init (&ladspa->ladspa, &ladspa_class->ladspa);
/* even if channels are different LADSPA still maintains same samples */
gst_base_transform_set_in_place (base,
ladspa_class->ladspa.count.audio.in ==
ladspa_class->ladspa.count.audio.out
&& !LADSPA_IS_INPLACE_BROKEN (ladspa_class->ladspa.descriptor->
Properties));
}
static void
gst_ladspa_filter_type_dispose (GObject * object)
{
GstBaseTransform *base = GST_BASE_TRANSFORM (object);
gst_ladspa_filter_type_cleanup (base);
G_OBJECT_CLASS (gst_ladspa_filter_type_parent_class)->dispose (object);
}
static void
gst_ladspa_filter_type_finalize (GObject * object)
{
GstLADSPAFilter *ladspa = GST_LADSPA_FILTER (object);
gst_ladspa_finalize (&ladspa->ladspa);
G_OBJECT_CLASS (gst_ladspa_filter_type_parent_class)->finalize (object);
}
/*
* It is okay for plugins to 'leak' a one-time allocation. This will be freed when
* the application exits. When the plugins are scanned for the first time, this is
* done from a separate process to not impose the memory overhead on the calling
* application (among other reasons). Hence no need for class_finalize.
*/
static void
gst_ladspa_filter_type_base_init (GstLADSPAFilterClass * ladspa_class)
{
GObjectClass *object_class = G_OBJECT_CLASS (ladspa_class);
GstElementClass *elem_class = GST_ELEMENT_CLASS (ladspa_class);
GstAudioFilterClass *audio_class = GST_AUDIO_FILTER_CLASS (ladspa_class);
LADSPA_Descriptor *desc;
desc =
g_type_get_qdata (G_OBJECT_CLASS_TYPE (object_class), descriptor_quark);
g_assert (desc);
gst_ladspa_class_init (&ladspa_class->ladspa, desc);
gst_ladspa_element_class_set_metadata (&ladspa_class->ladspa, elem_class,
GST_LADSPA_FILTER_CLASS_TAGS);
gst_ladspa_filter_type_class_add_pad_templates (&ladspa_class->ladspa,
audio_class);
}
static void
gst_ladspa_filter_type_base_finalize (GstLADSPAFilterClass * ladspa_class)
{
gst_ladspa_class_finalize (&ladspa_class->ladspa);
}
static void
gst_ladspa_filter_type_class_init (GstLADSPAFilterClass * ladspa_class,
LADSPA_Descriptor * desc)
{
GObjectClass *object_class = G_OBJECT_CLASS (ladspa_class);
GstBaseTransformClass *base_class = GST_BASE_TRANSFORM_CLASS (ladspa_class);
GstAudioFilterClass *audio_class = GST_AUDIO_FILTER_CLASS (ladspa_class);
GST_DEBUG ("LADSPA filter class %p", ladspa_class);
gst_ladspa_filter_type_parent_class =
g_type_class_peek_parent (ladspa_class);
object_class->dispose =
GST_DEBUG_FUNCPTR (gst_ladspa_filter_type_dispose);
object_class->finalize =
GST_DEBUG_FUNCPTR (gst_ladspa_filter_type_finalize);
object_class->set_property =
GST_DEBUG_FUNCPTR (gst_ladspa_filter_type_set_property);
object_class->get_property =
GST_DEBUG_FUNCPTR (gst_ladspa_filter_type_get_property);
base_class->fixate_caps =
GST_DEBUG_FUNCPTR (gst_ladspa_filter_type_fixate_caps);
base_class->transform_caps =
GST_DEBUG_FUNCPTR (gst_ladspa_filter_type_transform_caps);
base_class->prepare_output_buffer =
GST_DEBUG_FUNCPTR (gst_ladspa_filter_type_prepare_output_buffer);
base_class->transform =
GST_DEBUG_FUNCPTR (gst_ladspa_filter_type_transform);
base_class->transform_ip =
GST_DEBUG_FUNCPTR (gst_ladspa_filter_type_transform_ip);
audio_class->setup = GST_DEBUG_FUNCPTR (gst_ladspa_filter_type_setup);
gst_ladspa_object_class_install_properties (&ladspa_class->ladspa,
object_class, 1);
}
G_DEFINE_ABSTRACT_TYPE (GstLADSPAFilter, gst_ladspa_filter,
GST_TYPE_AUDIO_FILTER);
static void
gst_ladspa_filter_init (GstLADSPAFilter * ladspa)
{
}
static void
gst_ladspa_filter_class_init (GstLADSPAFilterClass * ladspa_class)
{
}
/*
* Construct the type.
*/
void
ladspa_describe_filter_plugin (GstPlugin * plugin,
const gchar * filename, const LADSPA_Descriptor * desc)
{
GTypeInfo info = {
sizeof (GstLADSPAFilterClass),
(GBaseInitFunc) gst_ladspa_filter_type_base_init,
(GBaseFinalizeFunc) gst_ladspa_filter_type_base_finalize,
(GClassInitFunc) gst_ladspa_filter_type_class_init,
NULL,
desc,
sizeof (GstLADSPAFilter),
0,
(GInstanceInitFunc) gst_ladspa_filter_type_init,
NULL
};
gchar *tmp;
tmp = g_strdup_printf ("ladspa-%s-%s", filename, desc->Label);
ladspa_register_plugin (plugin, GST_TYPE_LADSPA_FILTER, tmp, &info,
descriptor_quark, filename, desc);
g_free (tmp);
}

View file

@ -0,0 +1,72 @@
/* GStreamer
* Copyright (C) 1999 Erik Walthinsen <omega@cse.ogi.edu>
* Copyright (C) 2013 Juan Manuel Borges Caño <juanmabcmail@gmail.com>
*
* gstladspafilter.h: Header for LADSPA filter
*
* 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.
*/
#ifndef __GST_LADSPA_FILTER_H__
#define __GST_LADSPA_FILTER_H__
#include <gst/gst.h>
#include <gst/audio/gstaudiofilter.h>
#include "gstladspautils.h"
G_BEGIN_DECLS
#define GST_TYPE_LADSPA_FILTER (gst_ladspa_filter_get_type())
#define GST_LADSPA_FILTER(obj) (G_TYPE_CHECK_INSTANCE_CAST((obj),GST_TYPE_LADSPA_FILTER,GstLADSPAFilter))
#define GST_LADSPA_FILTER_CAST(obj) ((GstLADSPAFilter *) (obj))
#define GST_LADSPA_FILTER_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST((klass),GST_TYPE_LADSPA_FILTER,GstLADSPAFilterClass))
#define GST_LADSPA_FILTER_CLASS_CAST(klass) ((GstLADSPAFilterClass *) (klass))
#define GST_LADSPA_FILTER_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS((obj),GST_TYPE_LADSPA_FILTER,GstLADSPAFilterClass))
#define GST_IS_LADSPA_FILTER(obj) (G_TYPE_CHECK_INSTANCE_TYPE((obj),GST_TYPE_LADSPA_FILTER))
#define GST_IS_LADSPA_FILTER_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE((klass),GST_TYPE_LADSPA_FILTER))
typedef struct _GstLADSPAFilter GstLADSPAFilter;
typedef struct _GstLADSPAFilterClass GstLADSPAFilterClass;
struct _GstLADSPAFilter
{
GstAudioFilter parent;
GstLADSPA ladspa;
};
struct _GstLADSPAFilterClass
{
GstAudioFilterClass parent_class;
GstLADSPAClass ladspa;
};
GType
gst_ladspa_filter_get_type (void);
void
ladspa_describe_filter_plugin (GstPlugin * plugin,
const gchar * filename, const LADSPA_Descriptor * desc);
void
gst_my_audio_filter_class_add_pad_templates (GstAudioFilterClass * audio_class,
GstCaps * srccaps, GstCaps * sinkcaps);
G_END_DECLS
#endif /* __GST_LADSPA_FILTER_H__ */

386
ext/ladspa/gstladspasink.c Normal file
View file

@ -0,0 +1,386 @@
/* GStreamer LADSPA sink category
* Copyright (C) 1999,2000 Erik Walthinsen <omega@cse.ogi.edu>
* 2001 Steve Baker <stevebaker_org@yahoo.co.uk>
* 2003 Andy Wingo <wingo at pobox.com>
* Copyright (C) 2005 Wim Taymans <wim@fluendo.com> (fakesink)
* 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.
*/
#ifdef HAVE_CONFIG_H
#include "config.h"
#endif
#include "gstladspasink.h"
#include "gstladspa.h"
#include "gstladspautils.h"
#include <gst/base/gstbasetransform.h>
#include <string.h>
GST_DEBUG_CATEGORY_EXTERN (ladspa_debug);
#define GST_CAT_DEFAULT ladspa_debug
#define GST_LADSPA_SINK_CLASS_TAGS "Sink/Audio/LADSPA"
#define GST_LADSPA_SINK_DEFAULT_SYNC TRUE
#define GST_LADSPA_SINK_DEFAULT_CAN_ACTIVATE_PUSH TRUE
#define GST_LADSPA_SINK_DEFAULT_CAN_ACTIVATE_PULL FALSE
#define GST_LADSPA_SINK_DEFAULT_NUM_BUFFERS -1
enum
{
GST_LADSPA_SINK_PROP_0,
GST_LADSPA_SINK_PROP_CAN_ACTIVATE_PUSH,
GST_LADSPA_SINK_PROP_CAN_ACTIVATE_PULL,
GST_LADSPA_SINK_PROP_NUM_BUFFERS,
GST_LADSPA_SINK_PROP_LAST
};
static GstLADSPASinkClass *gst_ladspa_sink_type_parent_class = NULL;
/*
* Boilerplates BaseSink add pad.
*/
void
gst_my_base_sink_class_add_pad_template (GstBaseSinkClass * base_class,
GstCaps * sinkcaps)
{
GstElementClass *elem_class = GST_ELEMENT_CLASS (base_class);
GstPadTemplate *pad_template;
g_return_if_fail (GST_IS_CAPS (sinkcaps));
pad_template =
gst_pad_template_new (GST_BASE_TRANSFORM_SINK_NAME, GST_PAD_SINK,
GST_PAD_ALWAYS, sinkcaps);
gst_element_class_add_pad_template (elem_class, pad_template);
}
static gboolean
gst_ladspa_sink_type_set_caps (GstBaseSink * base, GstCaps * caps)
{
GstLADSPASink *ladspa = GST_LADSPA_SINK (base);
GstAudioInfo info;
if (!gst_audio_info_from_caps (&info, caps)) {
GST_ERROR_OBJECT (base, "received invalid caps");
return FALSE;
}
GST_DEBUG_OBJECT (ladspa, "negotiated to caps %" GST_PTR_FORMAT, caps);
ladspa->info = info;
return gst_ladspa_setup (&ladspa->ladspa, GST_AUDIO_INFO_RATE (&info));
}
static gboolean
gst_ladspa_sink_type_query (GstBaseSink * base, GstQuery * query)
{
gboolean ret;
switch (GST_QUERY_TYPE (query)) {
case GST_QUERY_SEEKING:{
GstFormat fmt;
/* we don't supporting seeking */
gst_query_parse_seeking (query, &fmt, NULL, NULL, NULL);
gst_query_set_seeking (query, fmt, FALSE, 0, -1);
ret = TRUE;
break;
}
default:
ret =
GST_BASE_SINK_CLASS (gst_ladspa_sink_type_parent_class)->query
(base, query);
break;
}
return ret;
}
static GstFlowReturn
gst_ladspa_sink_type_preroll (GstBaseSink * base, GstBuffer * buffer)
{
GstLADSPASink *ladspa = GST_LADSPA_SINK (base);
if (ladspa->num_buffers_left == 0) {
GST_DEBUG_OBJECT (ladspa, "we are EOS");
return GST_FLOW_EOS;
}
return GST_FLOW_OK;
}
static GstFlowReturn
gst_ladspa_sink_type_render (GstBaseSink * base, GstBuffer * buf)
{
GstLADSPASink *ladspa = GST_LADSPA_SINK (base);
GstMapInfo info;
if (ladspa->num_buffers_left == 0)
goto eos;
if (ladspa->num_buffers_left != -1)
ladspa->num_buffers_left--;
gst_buffer_map (buf, &info, GST_MAP_READ);
gst_ladspa_transform (&ladspa->ladspa, NULL,
info.size / sizeof (LADSPA_Data) / ladspa->ladspa.klass->count.audio.in,
info.data);
gst_buffer_unmap (buf, &info);
if (ladspa->num_buffers_left == 0)
goto eos;
return GST_FLOW_OK;
/* ERRORS */
eos:
{
GST_DEBUG_OBJECT (ladspa, "we are EOS");
return GST_FLOW_EOS;
}
}
static GstStateChangeReturn
gst_ladspa_sink_type_change_state (GstElement * element,
GstStateChange transition)
{
GstLADSPASink *ladspa = GST_LADSPA_SINK (element);
GstStateChangeReturn ret = GST_STATE_CHANGE_SUCCESS;
switch (transition) {
case GST_STATE_CHANGE_READY_TO_PAUSED:
ladspa->num_buffers_left = ladspa->num_buffers;
break;
default:
break;
}
ret =
GST_ELEMENT_CLASS (gst_ladspa_sink_type_parent_class)->change_state
(element, transition);
return ret;
}
static void
gst_ladspa_sink_type_set_property (GObject * object, guint prop_id,
const GValue * value, GParamSpec * pspec)
{
GstLADSPASink *ladspa = GST_LADSPA_SINK (object);
switch (prop_id) {
case GST_LADSPA_SINK_PROP_CAN_ACTIVATE_PUSH:
GST_BASE_SINK (ladspa)->can_activate_push = g_value_get_boolean (value);
break;
case GST_LADSPA_SINK_PROP_CAN_ACTIVATE_PULL:
GST_BASE_SINK (ladspa)->can_activate_pull = g_value_get_boolean (value);
break;
case GST_LADSPA_SINK_PROP_NUM_BUFFERS:
ladspa->num_buffers = g_value_get_int (value);
break;
default:
gst_ladspa_object_set_property (&ladspa->ladspa, object, prop_id, value,
pspec);
break;
}
}
static void
gst_ladspa_sink_type_get_property (GObject * object, guint prop_id,
GValue * value, GParamSpec * pspec)
{
GstLADSPASink *ladspa = GST_LADSPA_SINK (object);
switch (prop_id) {
case GST_LADSPA_SINK_PROP_CAN_ACTIVATE_PUSH:
g_value_set_boolean (value, GST_BASE_SINK (ladspa)->can_activate_push);
break;
case GST_LADSPA_SINK_PROP_CAN_ACTIVATE_PULL:
g_value_set_boolean (value, GST_BASE_SINK (ladspa)->can_activate_pull);
break;
case GST_LADSPA_SINK_PROP_NUM_BUFFERS:
g_value_set_int (value, ladspa->num_buffers);
break;
default:
gst_ladspa_object_get_property (&ladspa->ladspa, object, prop_id, value,
pspec);
break;
}
}
static void
gst_ladspa_sink_type_init (GstLADSPASink * ladspa, LADSPA_Descriptor * desc)
{
GstLADSPASinkClass *ladspa_class = GST_LADSPA_SINK_GET_CLASS (ladspa);
GstBaseSink *base = GST_BASE_SINK (ladspa);
gst_ladspa_init (&ladspa->ladspa, &ladspa_class->ladspa);
ladspa->num_buffers = GST_LADSPA_SINK_DEFAULT_NUM_BUFFERS;
gst_base_sink_set_sync (base, GST_LADSPA_SINK_DEFAULT_SYNC);
}
static void
gst_ladspa_sink_type_dispose (GObject * object)
{
GstLADSPASink *ladspa = GST_LADSPA_SINK (object);
gst_ladspa_cleanup (&ladspa->ladspa);
G_OBJECT_CLASS (gst_ladspa_sink_type_parent_class)->dispose (object);
}
static void
gst_ladspa_sink_type_finalize (GObject * object)
{
GstLADSPASink *ladspa = GST_LADSPA_SINK (object);
gst_ladspa_finalize (&ladspa->ladspa);
G_OBJECT_CLASS (gst_ladspa_sink_type_parent_class)->finalize (object);
}
/*
* It is okay for plugins to 'leak' a one-time allocation. This will be freed when
* the application exits. When the plugins are scanned for the first time, this is
* done from a separate process to not impose the memory overhead on the calling
* application (among other reasons). Hence no need for class_finalize.
*/
static void
gst_ladspa_sink_type_base_init (GstLADSPASinkClass * ladspa_class)
{
GObjectClass *object_class = G_OBJECT_CLASS (ladspa_class);
GstElementClass *elem_class = GST_ELEMENT_CLASS (ladspa_class);
GstBaseSinkClass *base_class = GST_BASE_SINK_CLASS (ladspa_class);
LADSPA_Descriptor *desc;
desc =
g_type_get_qdata (G_OBJECT_CLASS_TYPE (object_class), descriptor_quark);
g_assert (desc);
gst_ladspa_class_init (&ladspa_class->ladspa, desc);
gst_ladspa_element_class_set_metadata (&ladspa_class->ladspa, elem_class,
GST_LADSPA_SINK_CLASS_TAGS);
gst_ladspa_sink_type_class_add_pad_template (&ladspa_class->ladspa,
base_class);
}
static void
gst_ladspa_sink_type_base_finalize (GstLADSPASinkClass * ladspa_class)
{
gst_ladspa_class_finalize (&ladspa_class->ladspa);
}
static void
gst_ladspa_sink_type_class_init (GstLADSPASinkClass * ladspa_class,
LADSPA_Descriptor * desc)
{
GObjectClass *object_class = G_OBJECT_CLASS (ladspa_class);
GstElementClass *elem_class = GST_ELEMENT_CLASS (ladspa_class);
GstBaseSinkClass *base_class = base_class =
GST_BASE_SINK_CLASS (ladspa_class);
gst_ladspa_sink_type_parent_class =
g_type_class_peek_parent (ladspa_class);
object_class->dispose = GST_DEBUG_FUNCPTR (gst_ladspa_sink_type_dispose);
object_class->finalize =
GST_DEBUG_FUNCPTR (gst_ladspa_sink_type_finalize);
object_class->set_property =
GST_DEBUG_FUNCPTR (gst_ladspa_sink_type_set_property);
object_class->get_property =
GST_DEBUG_FUNCPTR (gst_ladspa_sink_type_get_property);
elem_class->change_state =
GST_DEBUG_FUNCPTR (gst_ladspa_sink_type_change_state);
base_class->set_caps = GST_DEBUG_FUNCPTR (gst_ladspa_sink_type_set_caps);
base_class->preroll = GST_DEBUG_FUNCPTR (gst_ladspa_sink_type_preroll);
base_class->render = GST_DEBUG_FUNCPTR (gst_ladspa_sink_type_render);
base_class->query = GST_DEBUG_FUNCPTR (gst_ladspa_sink_type_query);
g_object_class_install_property (object_class,
GST_LADSPA_SINK_PROP_CAN_ACTIVATE_PUSH,
g_param_spec_boolean ("can-activate-push", "Can activate push",
"Can activate in push mode",
GST_LADSPA_SINK_DEFAULT_CAN_ACTIVATE_PUSH,
G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
g_object_class_install_property (object_class,
GST_LADSPA_SINK_PROP_CAN_ACTIVATE_PULL,
g_param_spec_boolean ("can-activate-pull", "Can activate pull",
"Can activate in pull mode",
GST_LADSPA_SINK_DEFAULT_CAN_ACTIVATE_PULL,
G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
g_object_class_install_property (object_class,
GST_LADSPA_SINK_PROP_NUM_BUFFERS, g_param_spec_int ("num-buffers",
"num-buffers", "Number of buffers to accept going EOS", -1, G_MAXINT,
GST_LADSPA_SINK_DEFAULT_NUM_BUFFERS,
G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
gst_ladspa_object_class_install_properties (&ladspa_class->ladspa,
object_class, GST_LADSPA_SINK_PROP_LAST);
}
G_DEFINE_ABSTRACT_TYPE (GstLADSPASink, gst_ladspa_sink, GST_TYPE_BASE_SINK);
static void
gst_ladspa_sink_init (GstLADSPASink * ladspa)
{
}
static void
gst_ladspa_sink_class_init (GstLADSPASinkClass * ladspa_class)
{
}
/*
* Construct the type.
*/
void
ladspa_describe_sink_plugin (GstPlugin * plugin,
const gchar * filename, const LADSPA_Descriptor * desc)
{
GTypeInfo info = {
sizeof (GstLADSPASinkClass),
(GBaseInitFunc) gst_ladspa_sink_type_base_init,
(GBaseFinalizeFunc) gst_ladspa_sink_type_base_finalize,
(GClassInitFunc) gst_ladspa_sink_type_class_init,
NULL,
desc,
sizeof (GstLADSPASink),
0,
(GInstanceInitFunc) gst_ladspa_sink_type_init,
NULL
};
gchar *tmp;
tmp = g_strdup_printf ("ladspasink-%s-%s", filename, desc->Label);
ladspa_register_plugin (plugin, GST_TYPE_LADSPA_SINK, tmp, &info,
descriptor_quark, filename, desc);
g_free (tmp);
}

View file

@ -0,0 +1,78 @@
/* GStreamer
* Copyright (C) 1999,2000 Erik Walthinsen <omega@cse.ogi.edu>
* 2000 Wim Taymans <wtay@chello.be>
* Copyright (C) 2013 Juan Manuel Borges Caño <juanmabcmail@gmail.com>
*
* gstladspasink.h: Header for LADSPA sink
*
* 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.
*/
#ifndef __GST_LADSPA_SINK_H__
#define __GST_LADSPA_SINK_H__
#include <gst/gst.h>
#include <gst/base/gstbasesink.h>
#include "gstladspautils.h"
G_BEGIN_DECLS
#define GST_TYPE_LADSPA_SINK (gst_ladspa_sink_get_type())
#define GST_LADSPA_SINK(obj) (G_TYPE_CHECK_INSTANCE_CAST((obj),GST_TYPE_LADSPA_SINK,GstLADSPASink))
#define GST_LADSPA_SINK_CAST(obj) ((GstLADSPASink *) (obj))
#define GST_LADSPA_SINK_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST((klass),GST_TYPE_LADSPA_SINK,GstLADSPASinkClass))
#define GST_LADSPA_SINK_CLASS_CAST(klass) ((GstLADSPASinkClass *) (klass))
#define GST_LADSPA_SINK_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS((obj),GST_TYPE_LADSPA_SINK,GstLADSPASinkClass))
#define GST_IS_LADSPA_SINK(obj) (G_TYPE_CHECK_INSTANCE_TYPE((obj),GST_TYPE_LADSPA_SINK))
#define GST_IS_LADSPA_SINK_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE((klass),GST_TYPE_LADSPA_SINK))
typedef struct _GstLADSPASink GstLADSPASink;
typedef struct _GstLADSPASinkClass GstLADSPASinkClass;
struct _GstLADSPASink
{
GstBaseSink parent;
GstLADSPA ladspa;
GstAudioInfo info;
gint num_buffers;
gint num_buffers_left;
};
struct _GstLADSPASinkClass
{
GstBaseSinkClass parent_class;
GstLADSPAClass ladspa;
};
GType
gst_ladspa_sink_get_type (void);
void
ladspa_describe_sink_plugin (GstPlugin * plugin,
const gchar * filename, const LADSPA_Descriptor * desc);
void
gst_my_base_sink_class_add_pad_template (GstBaseSinkClass * base_class,
GstCaps * sinkcaps);
G_END_DECLS
#endif /* __GST_LADSPA_SINK_H__ */

View file

@ -0,0 +1,633 @@
/* GStreamer LADSPA source category
* 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) 2005 Stefan Kost <ensonic@users.sf.net> (audiotestsrc)
* 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.
*/
#ifdef HAVE_CONFIG_H
#include "config.h"
#endif
#include "gstladspasource.h"
#include "gstladspa.h"
#include "gstladspautils.h"
#include <gst/base/gstbasetransform.h>
GST_DEBUG_CATEGORY_EXTERN (ladspa_debug);
#define GST_CAT_DEFAULT ladspa_debug
#define GST_LADSPA_SOURCE_CLASS_TAGS "Source/Audio/LADSPA"
#define GST_LADSPA_SOURCE_DEFAULT_SAMPLES_PER_BUFFER 1024
#define GST_LADSPA_SOURCE_DEFAULT_IS_LIVE FALSE
#define GST_LADSPA_SOURCE_DEFAULT_TIMESTAMP_OFFSET G_GINT64_CONSTANT (0)
#define GST_LADSPA_SOURCE_DEFAULT_CAN_ACTIVATE_PUSH TRUE
#define GST_LADSPA_SOURCE_DEFAULT_CAN_ACTIVATE_PULL FALSE
enum
{
GST_LADSPA_SOURCE_PROP_0,
GST_LADSPA_SOURCE_PROP_SAMPLES_PER_BUFFER,
GST_LADSPA_SOURCE_PROP_IS_LIVE,
GST_LADSPA_SOURCE_PROP_TIMESTAMP_OFFSET,
GST_LADSPA_SOURCE_PROP_CAN_ACTIVATE_PUSH,
GST_LADSPA_SOURCE_PROP_CAN_ACTIVATE_PULL,
GST_LADSPA_SOURCE_PROP_LAST
};
static GstLADSPASourceClass *gst_ladspa_source_type_parent_class = NULL;
/*
* Boilerplates BaseSrc add pad.
*/
void
gst_my_base_source_class_add_pad_template (GstBaseSrcClass * base_class,
GstCaps * srccaps)
{
GstElementClass *elem_class = GST_ELEMENT_CLASS (base_class);
GstPadTemplate *pad_template;
g_return_if_fail (GST_IS_CAPS (srccaps));
pad_template =
gst_pad_template_new (GST_BASE_TRANSFORM_SRC_NAME, GST_PAD_SRC,
GST_PAD_ALWAYS, srccaps);
gst_element_class_add_pad_template (elem_class, pad_template);
}
static GstCaps *
gst_ladspa_source_type_fixate (GstBaseSrc * base, GstCaps * caps)
{
GstLADSPASource *ladspa = GST_LADSPA_SOURCE (base);
GstStructure *structure;
caps = gst_caps_make_writable (caps);
structure = gst_caps_get_structure (caps, 0);
GST_DEBUG_OBJECT (ladspa, "fixating samplerate to %d", GST_AUDIO_DEF_RATE);
gst_structure_fixate_field_nearest_int (structure, "rate",
GST_AUDIO_DEF_RATE);
gst_structure_fixate_field_string (structure, "format", GST_AUDIO_NE (F32));
gst_structure_fixate_field_nearest_int (structure, "channels",
ladspa->ladspa.klass->count.audio.out);
caps =
GST_BASE_SRC_CLASS (gst_ladspa_source_type_parent_class)->fixate
(base, caps);
return caps;
}
static gboolean
gst_ladspa_source_type_set_caps (GstBaseSrc * base, GstCaps * caps)
{
GstLADSPASource *ladspa = GST_LADSPA_SOURCE (base);
GstAudioInfo info;
if (!gst_audio_info_from_caps (&info, caps)) {
GST_ERROR_OBJECT (base, "received invalid caps");
return FALSE;
}
GST_DEBUG_OBJECT (ladspa, "negotiated to caps %" GST_PTR_FORMAT, caps);
ladspa->info = info;
gst_base_src_set_blocksize (base,
GST_AUDIO_INFO_BPF (&info) * ladspa->samples_per_buffer);
return gst_ladspa_setup (&ladspa->ladspa, GST_AUDIO_INFO_RATE (&info));
}
static gboolean
gst_ladspa_source_type_query (GstBaseSrc * base, GstQuery * query)
{
GstLADSPASource *ladspa = GST_LADSPA_SOURCE (base);
gboolean res = FALSE;
switch (GST_QUERY_TYPE (query)) {
case GST_QUERY_CONVERT:
{
GstFormat src_fmt, dest_fmt;
gint64 src_val, dest_val;
gst_query_parse_convert (query, &src_fmt, &src_val, &dest_fmt, &dest_val);
if (!gst_audio_info_convert (&ladspa->info, src_fmt, src_val, dest_fmt,
&dest_val)) {
GST_DEBUG_OBJECT (ladspa, "query failed");
return FALSE;
}
gst_query_set_convert (query, src_fmt, src_val, dest_fmt, dest_val);
res = TRUE;
break;
}
case GST_QUERY_SCHEDULING:
{
/* if we can operate in pull mode */
gst_query_set_scheduling (query, GST_SCHEDULING_FLAG_SEEKABLE, 1, -1, 0);
gst_query_add_scheduling_mode (query, GST_PAD_MODE_PUSH);
if (ladspa->can_activate_pull)
gst_query_add_scheduling_mode (query, GST_PAD_MODE_PULL);
res = TRUE;
break;
}
default:
res =
GST_BASE_SRC_CLASS (gst_ladspa_source_type_parent_class)->query
(base, query);
break;
}
return res;
}
static void
gst_ladspa_source_type_get_times (GstBaseSrc * base, GstBuffer * buffer,
GstClockTime * start, GstClockTime * end)
{
/* for live sources, sync on the timestamp of the buffer */
if (gst_base_src_is_live (base)) {
GstClockTime timestamp = GST_BUFFER_TIMESTAMP (buffer);
if (GST_CLOCK_TIME_IS_VALID (timestamp)) {
/* get duration to calculate end time */
GstClockTime duration = GST_BUFFER_DURATION (buffer);
if (GST_CLOCK_TIME_IS_VALID (duration)) {
*end = timestamp + duration;
}
*start = timestamp;
}
} else {
*start = -1;
*end = -1;
}
}
/* seek to time, will be called when we operate in push mode. In pull mode we
* get the requested byte offset. */
static gboolean
gst_ladspa_source_type_do_seek (GstBaseSrc * base, GstSegment * segment)
{
GstLADSPASource *ladspa = GST_LADSPA_SOURCE (base);
GstClockTime time;
gint samplerate, bpf;
gint64 next_sample;
GST_DEBUG_OBJECT (ladspa, "seeking %" GST_SEGMENT_FORMAT, segment);
time = segment->position;
ladspa->reverse = (segment->rate < 0.0);
samplerate = GST_AUDIO_INFO_RATE (&ladspa->info);
bpf = GST_AUDIO_INFO_BPF (&ladspa->info);
/* now move to the time indicated, don't seek to the sample *after* the time */
next_sample = gst_util_uint64_scale_int (time, samplerate, GST_SECOND);
ladspa->next_byte = next_sample * bpf;
if (samplerate == 0)
ladspa->next_time = 0;
else
ladspa->next_time =
gst_util_uint64_scale_round (next_sample, GST_SECOND, samplerate);
GST_DEBUG_OBJECT (ladspa, "seeking next_sample=%" G_GINT64_FORMAT
" next_time=%" GST_TIME_FORMAT, next_sample,
GST_TIME_ARGS (ladspa->next_time));
g_assert (ladspa->next_time <= time);
ladspa->next_sample = next_sample;
if (!ladspa->reverse) {
if (GST_CLOCK_TIME_IS_VALID (segment->start)) {
segment->time = segment->start;
}
} else {
if (GST_CLOCK_TIME_IS_VALID (segment->stop)) {
segment->time = segment->stop;
}
}
if (GST_CLOCK_TIME_IS_VALID (segment->stop)) {
time = segment->stop;
ladspa->sample_stop =
gst_util_uint64_scale_round (time, samplerate, GST_SECOND);
ladspa->check_seek_stop = TRUE;
} else {
ladspa->check_seek_stop = FALSE;
}
ladspa->eos_reached = FALSE;
return TRUE;
}
static gboolean
gst_ladspa_source_type_is_seekable (GstBaseSrc * base)
{
/* we're seekable... */
return TRUE;
}
static GstFlowReturn
gst_ladspa_source_type_fill (GstBaseSrc * base, guint64 offset,
guint length, GstBuffer * buffer)
{
GstLADSPASource *ladspa;
GstClockTime next_time;
gint64 next_sample, next_byte;
gint bytes, samples;
GstElementClass *eclass;
GstMapInfo map;
gint samplerate, bpf;
ladspa = GST_LADSPA_SOURCE (base);
/* example for tagging generated data */
if (!ladspa->tags_pushed) {
GstTagList *taglist;
taglist = gst_tag_list_new (GST_TAG_DESCRIPTION, "ladspa wave", NULL);
eclass = GST_ELEMENT_CLASS (gst_ladspa_source_type_parent_class);
if (eclass->send_event)
eclass->send_event (GST_ELEMENT (base), gst_event_new_tag (taglist));
else
gst_tag_list_unref (taglist);
ladspa->tags_pushed = TRUE;
}
if (ladspa->eos_reached) {
GST_INFO_OBJECT (ladspa, "eos");
return GST_FLOW_EOS;
}
samplerate = GST_AUDIO_INFO_RATE (&ladspa->info);
bpf = GST_AUDIO_INFO_BPF (&ladspa->info);
/* if no length was given, use our default length in samples otherwise convert
* the length in bytes to samples. */
if (length == -1)
samples = ladspa->samples_per_buffer;
else
samples = length / bpf;
/* if no offset was given, use our next logical byte */
if (offset == -1)
offset = ladspa->next_byte;
/* now see if we are at the byteoffset we think we are */
if (offset != ladspa->next_byte) {
GST_DEBUG_OBJECT (ladspa, "seek to new offset %" G_GUINT64_FORMAT, offset);
/* we have a discont in the expected sample offset, do a 'seek' */
ladspa->next_sample = offset / bpf;
ladspa->next_time =
gst_util_uint64_scale_int (ladspa->next_sample, GST_SECOND, samplerate);
ladspa->next_byte = offset;
}
/* check for eos */
if (ladspa->check_seek_stop &&
(ladspa->sample_stop > ladspa->next_sample) &&
(ladspa->sample_stop < ladspa->next_sample + samples)
) {
/* calculate only partial buffer */
ladspa->generate_samples_per_buffer =
ladspa->sample_stop - ladspa->next_sample;
next_sample = ladspa->sample_stop;
ladspa->eos_reached = TRUE;
} else {
/* calculate full buffer */
ladspa->generate_samples_per_buffer = samples;
next_sample =
ladspa->next_sample + (ladspa->reverse ? (-samples) : samples);
}
bytes = ladspa->generate_samples_per_buffer * bpf;
next_byte = ladspa->next_byte + (ladspa->reverse ? (-bytes) : bytes);
next_time = gst_util_uint64_scale_int (next_sample, GST_SECOND, samplerate);
GST_LOG_OBJECT (ladspa, "samplerate %d", samplerate);
GST_LOG_OBJECT (ladspa,
"next_sample %" G_GINT64_FORMAT ", ts %" GST_TIME_FORMAT, next_sample,
GST_TIME_ARGS (next_time));
gst_buffer_set_size (buffer, bytes);
GST_BUFFER_OFFSET (buffer) = ladspa->next_sample;
GST_BUFFER_OFFSET_END (buffer) = next_sample;
if (!ladspa->reverse) {
GST_BUFFER_TIMESTAMP (buffer) =
ladspa->timestamp_offset + ladspa->next_time;
GST_BUFFER_DURATION (buffer) = next_time - ladspa->next_time;
} else {
GST_BUFFER_TIMESTAMP (buffer) = ladspa->timestamp_offset + next_time;
GST_BUFFER_DURATION (buffer) = ladspa->next_time - next_time;
}
gst_object_sync_values (GST_OBJECT (ladspa), GST_BUFFER_TIMESTAMP (buffer));
ladspa->next_time = next_time;
ladspa->next_sample = next_sample;
ladspa->next_byte = next_byte;
GST_LOG_OBJECT (ladspa, "generating %u samples at ts %" GST_TIME_FORMAT,
ladspa->generate_samples_per_buffer,
GST_TIME_ARGS (GST_BUFFER_TIMESTAMP (buffer)));
gst_buffer_map (buffer, &map, GST_MAP_WRITE);
gst_ladspa_transform (&ladspa->ladspa, map.data,
ladspa->generate_samples_per_buffer, NULL);
gst_buffer_unmap (buffer, &map);
return GST_FLOW_OK;
}
static gboolean
gst_ladspa_source_type_start (GstBaseSrc * base)
{
GstLADSPASource *ladspa = GST_LADSPA_SOURCE (base);
ladspa->next_sample = 0;
ladspa->next_byte = 0;
ladspa->next_time = 0;
ladspa->check_seek_stop = FALSE;
ladspa->eos_reached = FALSE;
ladspa->tags_pushed = FALSE;
return TRUE;
}
static gboolean
gst_ladspa_source_type_stop (GstBaseSrc * base)
{
GstLADSPASource *ladspa = GST_LADSPA_SOURCE (base);
return gst_ladspa_cleanup (&ladspa->ladspa);
}
static void
gst_ladspa_source_type_set_property (GObject * object, guint prop_id,
const GValue * value, GParamSpec * pspec)
{
GstLADSPASource *ladspa = GST_LADSPA_SOURCE (object);
switch (prop_id) {
case GST_LADSPA_SOURCE_PROP_SAMPLES_PER_BUFFER:
ladspa->samples_per_buffer = g_value_get_int (value);
gst_base_src_set_blocksize (GST_BASE_SRC (ladspa),
GST_AUDIO_INFO_BPF (&ladspa->info) * ladspa->samples_per_buffer);
break;
case GST_LADSPA_SOURCE_PROP_IS_LIVE:
gst_base_src_set_live (GST_BASE_SRC (ladspa),
g_value_get_boolean (value));
break;
case GST_LADSPA_SOURCE_PROP_TIMESTAMP_OFFSET:
ladspa->timestamp_offset = g_value_get_int64 (value);
break;
case GST_LADSPA_SOURCE_PROP_CAN_ACTIVATE_PUSH:
GST_BASE_SRC (ladspa)->can_activate_push = g_value_get_boolean (value);
break;
case GST_LADSPA_SOURCE_PROP_CAN_ACTIVATE_PULL:
ladspa->can_activate_pull = g_value_get_boolean (value);
break;
default:
gst_ladspa_object_set_property (&ladspa->ladspa, object, prop_id, value,
pspec);
break;
}
}
static void
gst_ladspa_source_type_get_property (GObject * object, guint prop_id,
GValue * value, GParamSpec * pspec)
{
GstLADSPASource *ladspa = GST_LADSPA_SOURCE (object);
switch (prop_id) {
case GST_LADSPA_SOURCE_PROP_SAMPLES_PER_BUFFER:
g_value_set_int (value, ladspa->samples_per_buffer);
break;
case GST_LADSPA_SOURCE_PROP_IS_LIVE:
g_value_set_boolean (value, gst_base_src_is_live (GST_BASE_SRC (ladspa)));
break;
case GST_LADSPA_SOURCE_PROP_TIMESTAMP_OFFSET:
g_value_set_int64 (value, ladspa->timestamp_offset);
break;
case GST_LADSPA_SOURCE_PROP_CAN_ACTIVATE_PUSH:
g_value_set_boolean (value, GST_BASE_SRC (ladspa)->can_activate_push);
break;
case GST_LADSPA_SOURCE_PROP_CAN_ACTIVATE_PULL:
g_value_set_boolean (value, ladspa->can_activate_pull);
break;
default:
gst_ladspa_object_get_property (&ladspa->ladspa, object, prop_id, value,
pspec);
break;
}
}
static void
gst_ladspa_source_type_init (GstLADSPASource * ladspa,
LADSPA_Descriptor * desc)
{
GstLADSPASourceClass *ladspa_class = GST_LADSPA_SOURCE_GET_CLASS (ladspa);
gst_ladspa_init (&ladspa->ladspa, &ladspa_class->ladspa);
/* we operate in time */
gst_base_src_set_format (GST_BASE_SRC (ladspa), GST_FORMAT_TIME);
gst_base_src_set_live (GST_BASE_SRC (ladspa),
GST_LADSPA_SOURCE_DEFAULT_IS_LIVE);
ladspa->samples_per_buffer = GST_LADSPA_SOURCE_DEFAULT_SAMPLES_PER_BUFFER;
ladspa->generate_samples_per_buffer = ladspa->samples_per_buffer;
ladspa->timestamp_offset = GST_LADSPA_SOURCE_DEFAULT_TIMESTAMP_OFFSET;
ladspa->can_activate_pull = GST_LADSPA_SOURCE_DEFAULT_CAN_ACTIVATE_PULL;
gst_base_src_set_blocksize (GST_BASE_SRC (ladspa), -1);
}
static void
gst_ladspa_source_type_dispose (GObject * object)
{
GstLADSPASource *ladspa = GST_LADSPA_SOURCE (object);
gst_ladspa_cleanup (&ladspa->ladspa);
G_OBJECT_CLASS (gst_ladspa_source_type_parent_class)->dispose (object);
}
static void
gst_ladspa_source_type_finalize (GObject * object)
{
GstLADSPASource *ladspa = GST_LADSPA_SOURCE (object);
gst_ladspa_finalize (&ladspa->ladspa);
G_OBJECT_CLASS (gst_ladspa_source_type_parent_class)->finalize (object);
}
/*
* It is okay for plugins to 'leak' a one-time allocation. This will be freed when
* the application exits. When the plugins are scanned for the first time, this is
* done from a separate process to not impose the memory overhead on the calling
* application (among other reasons). Hence no need for class_finalize.
*/
static void
gst_ladspa_source_type_base_init (GstLADSPASourceClass * ladspa_class)
{
GObjectClass *object_class = G_OBJECT_CLASS (ladspa_class);
GstElementClass *elem_class = GST_ELEMENT_CLASS (ladspa_class);
GstBaseSrcClass *base_class = GST_BASE_SRC_CLASS (ladspa_class);
LADSPA_Descriptor *desc;
desc =
g_type_get_qdata (G_OBJECT_CLASS_TYPE (object_class), descriptor_quark);
g_assert (desc);
gst_ladspa_class_init (&ladspa_class->ladspa, desc);
gst_ladspa_element_class_set_metadata (&ladspa_class->ladspa, elem_class,
GST_LADSPA_SOURCE_CLASS_TAGS);
gst_ladspa_source_type_class_add_pad_template (&ladspa_class->ladspa,
base_class);
}
static void
gst_ladspa_source_type_base_finalize (GstLADSPASourceClass * ladspa_class)
{
gst_ladspa_class_finalize (&ladspa_class->ladspa);
}
static void
gst_ladspa_source_type_class_init (GstLADSPASourceClass * ladspa_class,
LADSPA_Descriptor * desc)
{
GObjectClass *object_class = (GObjectClass *) ladspa_class;
GstBaseSrcClass *base_class = (GstBaseSrcClass *) ladspa_class;
gst_ladspa_source_type_parent_class =
g_type_class_peek_parent (ladspa_class);
object_class->dispose =
GST_DEBUG_FUNCPTR (gst_ladspa_source_type_dispose);
object_class->finalize =
GST_DEBUG_FUNCPTR (gst_ladspa_source_type_finalize);
object_class->set_property =
GST_DEBUG_FUNCPTR (gst_ladspa_source_type_set_property);
object_class->get_property =
GST_DEBUG_FUNCPTR (gst_ladspa_source_type_get_property);
base_class->set_caps =
GST_DEBUG_FUNCPTR (gst_ladspa_source_type_set_caps);
base_class->fixate = GST_DEBUG_FUNCPTR (gst_ladspa_source_type_fixate);
base_class->is_seekable =
GST_DEBUG_FUNCPTR (gst_ladspa_source_type_is_seekable);
base_class->do_seek = GST_DEBUG_FUNCPTR (gst_ladspa_source_type_do_seek);
base_class->query = GST_DEBUG_FUNCPTR (gst_ladspa_source_type_query);
base_class->get_times =
GST_DEBUG_FUNCPTR (gst_ladspa_source_type_get_times);
base_class->start = GST_DEBUG_FUNCPTR (gst_ladspa_source_type_start);
base_class->stop = GST_DEBUG_FUNCPTR (gst_ladspa_source_type_stop);
base_class->fill = GST_DEBUG_FUNCPTR (gst_ladspa_source_type_fill);
g_object_class_install_property (object_class,
GST_LADSPA_SOURCE_PROP_SAMPLES_PER_BUFFER,
g_param_spec_int ("samplesperbuffer", "Samples per buffer",
"Number of samples in each outgoing buffer", 1, G_MAXINT,
GST_LADSPA_SOURCE_DEFAULT_SAMPLES_PER_BUFFER,
G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
g_object_class_install_property (object_class, GST_LADSPA_SOURCE_PROP_IS_LIVE,
g_param_spec_boolean ("is-live", "Is Live",
"Whether to act as a live source", GST_LADSPA_SOURCE_DEFAULT_IS_LIVE,
G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
g_object_class_install_property (object_class,
GST_LADSPA_SOURCE_PROP_TIMESTAMP_OFFSET,
g_param_spec_int64 ("timestamp-offset", "Timestamp offset",
"An offset added to timestamps set on buffers (in ns)", G_MININT64,
G_MAXINT64, GST_LADSPA_SOURCE_DEFAULT_TIMESTAMP_OFFSET,
G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
g_object_class_install_property (object_class,
GST_LADSPA_SOURCE_PROP_CAN_ACTIVATE_PUSH,
g_param_spec_boolean ("can-activate-push", "Can activate push",
"Can activate in push mode",
GST_LADSPA_SOURCE_DEFAULT_CAN_ACTIVATE_PUSH,
G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
g_object_class_install_property (object_class,
GST_LADSPA_SOURCE_PROP_CAN_ACTIVATE_PULL,
g_param_spec_boolean ("can-activate-pull", "Can activate pull",
"Can activate in pull mode",
GST_LADSPA_SOURCE_DEFAULT_CAN_ACTIVATE_PULL,
G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
gst_ladspa_object_class_install_properties (&ladspa_class->ladspa,
object_class, GST_LADSPA_SOURCE_PROP_LAST);
}
G_DEFINE_ABSTRACT_TYPE (GstLADSPASource, gst_ladspa_source, GST_TYPE_BASE_SRC);
static void
gst_ladspa_source_init (GstLADSPASource * ladspa)
{
}
static void
gst_ladspa_source_class_init (GstLADSPASourceClass * ladspa_class)
{
}
/*
* Construct the type.
*/
void
ladspa_describe_source_plugin (GstPlugin * plugin,
const gchar * filename, const LADSPA_Descriptor * desc)
{
GTypeInfo info = {
sizeof (GstLADSPASourceClass),
(GBaseInitFunc) gst_ladspa_source_type_base_init,
(GBaseFinalizeFunc) gst_ladspa_source_type_base_finalize,
(GClassInitFunc) gst_ladspa_source_type_class_init,
NULL,
desc,
sizeof (GstLADSPASource),
0,
(GInstanceInitFunc) gst_ladspa_source_type_init,
NULL
};
gchar *tmp;
tmp = g_strdup_printf ("ladspasrc-%s-%s", filename, desc->Label);
ladspa_register_plugin (plugin, GST_TYPE_LADSPA_SOURCE, tmp, &info,
descriptor_quark, filename, desc);
g_free (tmp);
}

View file

@ -0,0 +1,90 @@
/* GStreamer
* Copyright (C) 1999 Erik Walthinsen <omega@cse.ogi.edu>
* Copyright (C) 2005 Stefan Kost <ensonic@users.sf.net> (audiotestsrc)
* Copyright (C) 2013 Juan Manuel Borges Caño <juanmabcmail@gmail.com>
*
* gstladspasource.h: Header for LADSPA source
*
* 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.
*/
#ifndef __GST_LADSPA_SOURCE_H__
#define __GST_LADSPA_SOURCE_H__
#include <gst/gst.h>
#include <gst/base/gstbasesrc.h>
#include "gstladspautils.h"
G_BEGIN_DECLS
#define GST_TYPE_LADSPA_SOURCE (gst_ladspa_source_get_type())
#define GST_LADSPA_SOURCE(obj) (G_TYPE_CHECK_INSTANCE_CAST((obj),GST_TYPE_LADSPA_SOURCE,GstLADSPASource))
#define GST_LADSPA_SOURCE_CAST(obj) ((GstLADSPASource *) (obj))
#define GST_LADSPA_SOURCE_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST((klass),GST_TYPE_LADSPA_SOURCE,GstLADSPASourceClass))
#define GST_LADSPA_SOURCE_CLASS_CAST(klass) ((GstLADSPASourceClass *) (klass))
#define GST_LADSPA_SOURCE_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS((obj),GST_TYPE_LADSPA_SOURCE,GstLADSPASourceClass))
#define GST_IS_LADSPA_SOURCE(obj) (G_TYPE_CHECK_INSTANCE_TYPE((obj),GST_TYPE_LADSPA_SOURCE))
#define GST_IS_LADSPA_SOURCE_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE((klass),GST_TYPE_LADSPA_SOURCE))
typedef struct _GstLADSPASource GstLADSPASource;
typedef struct _GstLADSPASourceClass GstLADSPASourceClass;
struct _GstLADSPASource
{
GstBaseSrc parent;
GstLADSPA ladspa;
/* audio parameters */
GstAudioInfo info;
gint samples_per_buffer;
/*< private > */
gboolean tags_pushed; /* send tags just once ? */
GstClockTimeDiff timestamp_offset; /* base offset */
GstClockTime next_time; /* next timestamp */
gint64 next_sample; /* next sample to send */
gint64 next_byte; /* next byte to send */
gint64 sample_stop;
gboolean check_seek_stop;
gboolean eos_reached;
gint generate_samples_per_buffer; /* used to generate a partial buffer */
gboolean can_activate_pull;
gboolean reverse; /* play backwards */
};
struct _GstLADSPASourceClass
{
GstBaseSrcClass parent_class;
GstLADSPAClass ladspa;
};
GType
gst_ladspa_source_get_type (void);
void
ladspa_describe_source_plugin (GstPlugin * plugin,
const gchar * filename, const LADSPA_Descriptor * desc);
void
gst_my_base_source_class_add_pad_template (GstBaseSrcClass * base_class,
GstCaps * srccaps);
G_END_DECLS
#endif /* __GST_LADSPA_SOURCE_H__ */

892
ext/ladspa/gstladspautils.c Normal file
View file

@ -0,0 +1,892 @@
/* 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);
}

160
ext/ladspa/gstladspautils.h Normal file
View file

@ -0,0 +1,160 @@
/* GStreamer
* Copyright (C) 2013 Juan Manuel Borges Caño <juanmabcmail@gmail.com>
*
* gstladspautils.h: Header for LADSPA plugin utils
*
* 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.
*/
#ifndef __GST_LADSPA_UTILS_H__
#define __GST_LADSPA_UTILS_H__
#include <gst/gst.h>
#include <gst/audio/gstaudiofilter.h>
#include <gst/base/gstbasesrc.h>
#include <gst/base/gstbasesink.h>
#include <ladspa.h>
G_BEGIN_DECLS
typedef struct _GstLADSPA GstLADSPA;
typedef struct _GstLADSPAClass GstLADSPAClass;
struct _GstLADSPA
{
GstLADSPAClass *klass;
LADSPA_Handle *handle;
gboolean activated;
unsigned long rate;
struct
{
struct
{
LADSPA_Data *in;
LADSPA_Data *out;
} control;
struct
{
LADSPA_Data **in;
LADSPA_Data **out;
} audio;
} ports;
};
struct _GstLADSPAClass
{
guint properties;
LADSPA_Descriptor *descriptor;
struct
{
struct
{
guint in;
guint out;
} control;
struct
{
guint in;
guint out;
} audio;
} count;
struct
{
struct
{
unsigned long *in;
unsigned long *out;
} control;
struct
{
unsigned long *in;
unsigned long *out;
} audio;
} map;
};
gboolean
gst_ladspa_transform (GstLADSPA * ladspa, guint8 * outdata, guint samples,
guint8 * indata);
gboolean
gst_ladspa_setup (GstLADSPA * ladspa, unsigned long rate);
gboolean
gst_ladspa_cleanup (GstLADSPA * ladspa);
void
gst_ladspa_object_set_property (GstLADSPA * ladspa, GObject * object,
guint prop_id, const GValue * value, GParamSpec * pspec);
void
gst_ladspa_object_get_property (GstLADSPA * ladspa, GObject * object,
guint prop_id, GValue * value, GParamSpec * pspec);
void
gst_ladspa_object_class_install_properties (GstLADSPAClass * ladspa_class,
GObjectClass * object_class, guint offset);
void
gst_ladspa_element_class_set_metadata (GstLADSPAClass * ladspa_class,
GstElementClass * elem_class, const gchar * ladspa_class_tags);
void
gst_ladspa_filter_type_class_add_pad_templates (GstLADSPAClass * ladspa_class,
GstAudioFilterClass * audio_class);
void
gst_ladspa_source_type_class_add_pad_template (GstLADSPAClass * ladspa_class,
GstBaseSrcClass * audio_class);
void
gst_ladspa_sink_type_class_add_pad_template (GstLADSPAClass * ladspa_class,
GstBaseSinkClass * base_class);
void
gst_ladspa_init (GstLADSPA * ladspa, GstLADSPAClass * ladspa_class);
void
gst_ladspa_finalize (GstLADSPA * ladspa);
void
gst_ladspa_class_init (GstLADSPAClass * ladspa_class, LADSPA_Descriptor * desc);
void
gst_ladspa_class_finalize (GstLADSPAClass * ladspa_class);
void
ladspa_count_ports (const LADSPA_Descriptor * desc, guint * audio_in,
guint * audio_out, guint * control_in, guint * control_out);
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);
G_END_DECLS
#endif /* __GST_LADSPA_UTILS_H__ */