From: Juan Manuel Borges Caño Date: Fri, 3 May 2013 09:34:34 +0000 (+0200) Subject: ladspa: improved port to gstreamer 1.0 X-Git-Tag: 1.19.3~507^2~13758 X-Git-Url: http://review.tizen.org/git/?a=commitdiff_plain;h=70e208d08e0330112260b1ea3503f50e203977a8;p=platform%2Fupstream%2Fgstreamer.git ladspa: improved port to gstreamer 1.0 Fixes: #698927 --- diff --git a/configure.ac b/configure.ac index 2bc3219..6e0a5e8 100644 --- a/configure.ac +++ b/configure.ac @@ -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" diff --git a/ext/ladspa/Makefile.am b/ext/ladspa/Makefile.am index a6e2024..d9b9c2f 100644 --- a/ext/ladspa/Makefile.am +++ b/ext/ladspa/Makefile.am @@ -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 diff --git a/ext/ladspa/gstladspa.c b/ext/ladspa/gstladspa.c index f79a051..0acb245 100644 --- a/ext/ladspa/gstladspa.c +++ b/ext/ladspa/gstladspa.c @@ -1,7 +1,8 @@ -/* GStreamer +/* GStreamer LADSPA plugin * Copyright (C) 1999 Erik Walthinsen * 2001 Steve Baker * 2003 Andy Wingo + * Copyright (C) 2013 Juan Manuel Borges Caño * * 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 LADSPA 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. * - * The ladspa (Linux Audio Developer's Simple Plugin API) element is a bridge - * for plugins using the ladspa 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. + * First off all you can apply not live LADSPA filters without this plugin: + * + * + * Example LADSPA line without this plugins + * |[ + * (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. + * + * + * Now with this plugin: + * + * + * Example LADSPA line with this plugins + * |[ + * 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). + * + * + * 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: + * + * Inspecting the plugins list + * |[ + * gst-inspect ladspa + * ]| List available LADSPA plugins on gstreamer. + * + * + * You can see the parameters of any plugin with: + * + * Inspecting the plugins + * |[ + * gst-inspect ladspa-retro-flange-1208-so-retroFlange + * ]| List details of the plugin, parameters, range and defaults included. + * + * + * The elements categorize in: + * + * Filter/Effect/Audio/LADSPA: + * + * Example Filter/Effect/Audio/LADSPA line with this plugins + * |[ + * 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. + * + * + * + * Source/Audio/LADSPA: + * + * Example Source/Audio/LADSPA line with this plugins + * |[ + * 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. + * + * + * Example Source/Audio/LADSPA line with this plugins + * |[ + * 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. + * + * + * Example Source/Audio/LADSPA line with this plugins + * |[ + * 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. + * + * + * Sink/Audio/LADSPA: + * + * Example Sink/Audio/LADSPA line with this plugins + * |[ + * 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). + * + * + * */ + #ifdef HAVE_CONFIG_H #include "config.h" #endif -#include -#include -#include -#include "gstladspa.h" -#include /* main ladspa sdk include file */ +#include "gstladspautils.h" +#include "gstladspafilter.h" +#include "gstladspasource.h" +#include "gstladspasink.h" +#include + +#include +#include +#include #ifdef HAVE_LRDF #include #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; - - -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); - } -} +GQuark descriptor_quark = 0; static void -gst_ladspa_init (GstLADSPA * ladspa, GstLADSPAClass * klass) +ladspa_describe_plugin (GstPlugin * plugin, + const gchar * filename, LADSPA_Descriptor_Function descriptor_function) { - 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; + const 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]); + /* 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; - desc->run (ladspa->handle, nframes); -} + /* count ports of this plugin */ + ladspa_count_ports (desc, &audio_in, &audio_out, &control_in, &control_out); -static void -ladspa_describe_plugin (LADSPA_Descriptor_Function descriptor_function) -{ - const LADSPA_Descriptor *desc; - gint 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; - - /* 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); - - 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) diff --git a/ext/ladspa/gstladspa.h b/ext/ladspa/gstladspa.h index fe78d17..727b56c 100644 --- a/ext/ladspa/gstladspa.h +++ b/ext/ladspa/gstladspa.h @@ -1,5 +1,6 @@ /* GStreamer - * Copyright (C) <1999> Erik Walthinsen + * Copyright (C) 1999 Erik Walthinsen + * Copyright (C) 2013 Juan Manuel Borges Caño * * gstladspa.h: Header for LADSPA plugin * @@ -19,58 +20,15 @@ * Boston, MA 02110-1301, USA. */ - #ifndef __GST_LADSPA_H__ #define __GST_LADSPA_H__ - -#include - #include -#include - - 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__ */ diff --git a/ext/ladspa/gstladspafilter.c b/ext/ladspa/gstladspafilter.c new file mode 100644 index 0000000..2411c06 --- /dev/null +++ b/ext/ladspa/gstladspafilter.c @@ -0,0 +1,374 @@ +/* GStreamer LADSPA filter category + * Copyright (C) 1999 Erik Walthinsen + * 2001 Steve Baker + * 2003 Andy Wingo + * Copyright (C) 2013 Juan Manuel Borges Caño + * + * 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); +} diff --git a/ext/ladspa/gstladspafilter.h b/ext/ladspa/gstladspafilter.h new file mode 100644 index 0000000..94c0dda --- /dev/null +++ b/ext/ladspa/gstladspafilter.h @@ -0,0 +1,72 @@ +/* GStreamer + * Copyright (C) 1999 Erik Walthinsen + * Copyright (C) 2013 Juan Manuel Borges Caño + * + * 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 +#include +#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__ */ diff --git a/ext/ladspa/gstladspasink.c b/ext/ladspa/gstladspasink.c new file mode 100644 index 0000000..db91b01 --- /dev/null +++ b/ext/ladspa/gstladspasink.c @@ -0,0 +1,386 @@ +/* GStreamer LADSPA sink category + * Copyright (C) 1999,2000 Erik Walthinsen + * 2001 Steve Baker + * 2003 Andy Wingo + * Copyright (C) 2005 Wim Taymans (fakesink) + * Copyright (C) 2013 Juan Manuel Borges Caño + * + * 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 + +#include + +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); +} diff --git a/ext/ladspa/gstladspasink.h b/ext/ladspa/gstladspasink.h new file mode 100644 index 0000000..6bed7f0 --- /dev/null +++ b/ext/ladspa/gstladspasink.h @@ -0,0 +1,78 @@ +/* GStreamer + * Copyright (C) 1999,2000 Erik Walthinsen + * 2000 Wim Taymans + * Copyright (C) 2013 Juan Manuel Borges Caño + * + * 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 +#include +#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__ */ diff --git a/ext/ladspa/gstladspasource.c b/ext/ladspa/gstladspasource.c new file mode 100644 index 0000000..202004c --- /dev/null +++ b/ext/ladspa/gstladspasource.c @@ -0,0 +1,633 @@ +/* GStreamer LADSPA source category + * Copyright (C) 1999 Erik Walthinsen + * 2001 Steve Baker + * 2003 Andy Wingo + * Copyright (C) 2005 Stefan Kost (audiotestsrc) + * Copyright (C) 2013 Juan Manuel Borges Caño + * + * 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_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); +} diff --git a/ext/ladspa/gstladspasource.h b/ext/ladspa/gstladspasource.h new file mode 100644 index 0000000..15df9ba --- /dev/null +++ b/ext/ladspa/gstladspasource.h @@ -0,0 +1,90 @@ +/* GStreamer + * Copyright (C) 1999 Erik Walthinsen + * Copyright (C) 2005 Stefan Kost (audiotestsrc) + * Copyright (C) 2013 Juan Manuel Borges Caño + * + * 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 +#include +#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__ */ diff --git a/ext/ladspa/gstladspautils.c b/ext/ladspa/gstladspautils.c new file mode 100644 index 0000000..e5f8af6 --- /dev/null +++ b/ext/ladspa/gstladspautils.c @@ -0,0 +1,892 @@ +/* GStreamer LADSPA utils + * Copyright (C) 1999 Erik Walthinsen + * 2001 Steve Baker + * 2003 Andy Wingo + * Copyright (C) 2013 Juan Manuel Borges Caño + * + * 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 + +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 ", + "Andy Wingo ", + "Steve Baker ", + "Erik Walthinsen ", + "Stefan Kost ", + "Wim Taymans ", 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); +} diff --git a/ext/ladspa/gstladspautils.h b/ext/ladspa/gstladspautils.h new file mode 100644 index 0000000..7ab5e1d --- /dev/null +++ b/ext/ladspa/gstladspautils.h @@ -0,0 +1,160 @@ +/* GStreamer + * Copyright (C) 2013 Juan Manuel Borges Caño + * + * 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 +#include +#include +#include + +#include + +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__ */