1 /* GStreamer LADSPA plugin
2  * Copyright (C) 1999 Erik Walthinsen <omega@cse.ogi.edu>
3  *               2001 Steve Baker <stevebaker_org@yahoo.co.uk>
4  *               2003 Andy Wingo <wingo at pobox.com>
5  * Copyright (C) 2013 Juan Manuel Borges Caño <juanmabcmail@gmail.com>
6  *
7  * This library is free software; you can redistribute it and/or
8  * modify it under the terms of the GNU Library General Public
9  * License as published by the Free Software Foundation; either
10  * version 2 of the License, or (at your option) any later version.
11  *
12  * This library is distributed in the hope that it will be useful,
13  * but WITHOUT ANY WARRANTY; without even the implied warranty of
14  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
15  * Library General Public License for more details.
16  *
17  * You should have received a copy of the GNU Library General Public
18  * License along with this library; if not, write to the
19  * Free Software Foundation, Inc., 51 Franklin St, Fifth Floor,
20  * Boston, MA 02110-1301, USA.
21  */
22 
23 /**
24  * SECTION:element-ladspa
25  * @title: ladspa
26  * @short_description: bridge for LADSPA (Linux Audio Developer's Simple Plugin API)
27  * @see_also: #GstAudioConvert #GstAudioResample, #GstAudioTestSrc, #GstAutoAudioSink
28  *
29  * The LADSPA (Linux Audio Developer's Simple Plugin API) element is a bridge
30  * for plugins using the <ulink url="http://www.ladspa.org/">LADSPA</ulink> API.
31  * It scans all installed LADSPA plugins and registers them as gstreamer
32  * elements. If available it can also parse LRDF files and use the metadata for
33  * element classification. The functionality you get depends on the LADSPA plugins
34  * you have installed.
35  *
36  * ## Example LADSPA line without this plugins
37  * |[
38  * (padsp) listplugins
39  * (padsp) analyseplugin cmt.so amp_mono
40  * gst-launch-1.0 -e filesrc location="$myfile" ! decodebin ! audioconvert ! audioresample ! "audio/x-raw,format=S16LE,rate=48000,channels=1" ! wavenc ! filesink location="testin.wav"
41  * (padsp) applyplugin testin.wav testout.wav cmt.so amp_mono 2
42  * gst-launch-1.0 playbin uri=file://"$PWD"/testout.wav
43  * ]| 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.
44  *
45  * Now with this plugin:
46  *
47  * ## Example LADSPA line with this plugins
48  * |[
49  * gst-launch-1.0 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
50  * ]| Get audio input, filter it through CAPS Plate and TAP Stereo Echo, play it and show a visualization (recommended hearphones).
51  *
52  * In case you wonder the plugin naming scheme, quoting ladspa.h:
53  *   "Plugin types should be identified by file and label rather than by
54  *   index or plugin name, which may be changed in new plugin versions."
55  * This is really the best way then, and so it is less prone to conflicts.
56  *
57  * Also it is worth noting that LADSPA provides a control in and out interface,
58  * on top of the audio in and out one, so some parameters are readable too.
59  *
60  * You can see the listing of plugins available with:
61  *
62  * ## Inspecting the plugins list
63  * |[
64  * gst-inspect ladspa
65  * ]| List available LADSPA plugins on gstreamer.
66  *
67  * You can see the parameters of any plugin with:
68  *
69  * ## Inspecting the plugins
70  * |[
71  * gst-inspect ladspa-retro-flange-1208-so-retroflange
72  * ]| List details of the plugin, parameters, range and defaults included.
73  *
74  * The elements categorize in:
75  *
76  * * Filter/Effect/Audio/LADSPA:
77  *
78  * ## Example Filter/Effect/Audio/LADSPA line with this plugins
79  * |[
80  * gst-launch-1.0 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
81  * ]| Decode any audio file, filter it through Calf Reverb LADSPA then TAP Stereo Echo, and play it.
82  *
83  * * Source/Audio/LADSPA:
84  *
85  * ## Example Source/Audio/LADSPA line with this plugins
86  * |[
87  * gst-launch-1.0 ladspasrc-sine-so-sine-fcac frequency=220 amplitude=100 ! audioconvert ! autoaudiosink
88  * ]| Generate a sine wave with Sine Oscillator (Freq:control, Amp:control) and play it.
89  *
90  * ## Example Source/Audio/LADSPA line with this plugins
91  * |[
92  * gst-launch-1.0 ladspasrc-caps-so-click bpm=240 volume=1 ! autoaudiosink
93  * ]| Generate clicks with CAPS Click - Metronome at 240 beats per minute and play it.
94  *
95  * ## Example Source/Audio/LADSPA line with this plugins
96  * |[
97  * gst-launch-1.0 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
98  * ]| Generate random wave, filter it trhough Mono Amplifier and Versatile Plate Reverb, and play, while showing, it.
99  *
100  * * Sink/Audio/LADSPA:
101  *
102  * ## Example Sink/Audio/LADSPA line with this plugins
103  * |[
104  * gst-launch-1.0 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
105  * ]| Get audio input, filter it trhough Mono Amplifier, CAPS Plate LADSPA and TAP Stereo Echo, explicitily anulate audio with Null (Audio Output), and play a visualization (recommended hearphones).
106  *
107  */
108 
109 #ifdef HAVE_CONFIG_H
110 #include "config.h"
111 #endif
112 
113 #include "gstladspautils.h"
114 #include "gstladspafilter.h"
115 #include "gstladspasource.h"
116 #include "gstladspasink.h"
117 #include <gst/gst-i18n-plugin.h>
118 
119 #include <string.h>
120 #include <ladspa.h>
121 #ifdef HAVE_LRDF
122 #include <lrdf.h>
123 #endif
124 
125 GST_DEBUG_CATEGORY (ladspa_debug);
126 #define GST_CAT_DEFAULT ladspa_debug
127 
128 /*
129  * 1.0 and the 1.1 preliminary headers don't define a version, but
130  * 1.1 finally does
131  */
132 #ifndef LADSPA_VERSION
133 #define LADSPA_VERSION "1.0"
134 #endif
135 
136 #if defined (G_OS_WIN32)
137 #define GST_LADSPA_ENVVARS "APPDATA/LADSPA:COMMONPROGRAMFILES/LADSPA"
138 #define GST_LADSPA_DEFAULT_PATH ""
139 #elif defined (HAVE_OSX)
140 #define GST_LADSPA_ENVVARS "HOME/Library/Audio/Plug-Ins/LADSPA:HOME/.ladspa"
141 #define GST_LADSPA_DEFAULT_PATH \
142   "/usr/local/lib/ladspa:/usr/lib/ladspa:/Library/Audio/Plug-Ins/LADSPA"
143 #elif defined (G_OS_UNIX)
144 #define GST_LADSPA_ENVVARS "HOME/.ladspa"
145 #define GST_LADSPA_DEFAULT_PATH \
146   "/usr/lib/ladspa:" \
147   "/usr/lib64/ladspa:" \
148   "/usr/local/lib/ladspa:" \
149   "/usr/local/lib64/ladspa:" \
150    LIBDIR "/ladspa"
151 #else
152 #error "Unsupported OS"
153 #endif
154 
155 GstStructure *ladspa_meta_all = NULL;
156 
157 static void
ladspa_plugin_register_element(GstPlugin * plugin,GstStructure * ladspa_meta)158 ladspa_plugin_register_element (GstPlugin * plugin, GstStructure * ladspa_meta)
159 {
160   guint audio_in, audio_out;
161 
162   gst_structure_get_uint (ladspa_meta, "audio-in", &audio_in);
163   gst_structure_get_uint (ladspa_meta, "audio-out", &audio_out);
164 
165   if (audio_in == 0) {
166     ladspa_register_source_element (plugin, ladspa_meta);
167   } else if (audio_out == 0) {
168     ladspa_register_sink_element (plugin, ladspa_meta);
169   } else {
170     ladspa_register_filter_element (plugin, ladspa_meta);
171   }
172 }
173 
174 static void
ladspa_count_ports(const LADSPA_Descriptor * descriptor,guint * audio_in,guint * audio_out,guint * control_in,guint * control_out)175 ladspa_count_ports (const LADSPA_Descriptor * descriptor,
176     guint * audio_in, guint * audio_out, guint * control_in,
177     guint * control_out)
178 {
179   guint i;
180 
181   *audio_in = *audio_out = *control_in = *control_out = 0;
182 
183   for (i = 0; i < descriptor->PortCount; i++) {
184     LADSPA_PortDescriptor p = descriptor->PortDescriptors[i];
185 
186     if (LADSPA_IS_PORT_AUDIO (p)) {
187       if (LADSPA_IS_PORT_INPUT (p))
188         (*audio_in)++;
189       else
190         (*audio_out)++;
191     } else if (LADSPA_IS_PORT_CONTROL (p)) {
192       if (LADSPA_IS_PORT_INPUT (p))
193         (*control_in)++;
194       else
195         (*control_out)++;
196     }
197   }
198 }
199 
200 static void
ladspa_describe_plugin(const gchar * file_name,const gchar * entry_name,LADSPA_Descriptor_Function descriptor_function)201 ladspa_describe_plugin (const gchar * file_name, const gchar * entry_name,
202     LADSPA_Descriptor_Function descriptor_function)
203 {
204   const LADSPA_Descriptor *desc;
205   guint i;
206 
207   /* walk through all the plugins in this plugin library */
208   for (i = 0; (desc = descriptor_function (i)); i++) {
209     GstStructure *ladspa_meta = NULL;
210     GValue value = { 0, };
211     gchar *tmp;
212     gchar *type_name;
213     guint audio_in, audio_out, control_in, control_out;
214 
215     /* count ports of this plugin */
216     ladspa_count_ports (desc, &audio_in, &audio_out, &control_in, &control_out);
217 
218     /* categorize  */
219     if (audio_in == 0 && audio_out == 0) {
220       GST_WARNING ("Skipping control only element (%s:%lu/%s)",
221           entry_name, desc->UniqueID, desc->Label);
222       continue;
223     } else if (audio_in == 0) {
224       tmp = g_strdup_printf ("ladspasrc-%s-%s", entry_name, desc->Label);
225     } else if (audio_out == 0) {
226       tmp = g_strdup_printf ("ladspasink-%s-%s", entry_name, desc->Label);
227     } else {
228       tmp = g_strdup_printf ("ladspa-%s-%s", entry_name, desc->Label);
229     }
230     type_name = g_ascii_strdown (tmp, -1);
231     g_free (tmp);
232     g_strcanon (type_name, G_CSET_A_2_Z G_CSET_a_2_z G_CSET_DIGITS "-+", '-');
233 
234     /* check if the type is already registered */
235     if (g_type_from_name (type_name)) {
236       GST_WARNING ("Plugin identifier collision for %s (%s:%lu/%s)", type_name,
237           entry_name, desc->UniqueID, desc->Label);
238       g_free (type_name);
239       continue;
240     }
241 
242     ladspa_meta = gst_structure_new ("ladspa",
243         "plugin-filename", G_TYPE_STRING, file_name,
244         "element-ix", G_TYPE_UINT, i,
245         "element-type-name", G_TYPE_STRING, type_name,
246         "audio-in", G_TYPE_UINT, audio_in,
247         "audio-out", G_TYPE_UINT, audio_out,
248         "control-in", G_TYPE_UINT, control_in,
249         "control-out", G_TYPE_UINT, control_out, NULL);
250 
251     g_value_init (&value, GST_TYPE_STRUCTURE);
252     g_value_set_boxed (&value, ladspa_meta);
253     gst_structure_set_value (ladspa_meta_all, type_name, &value);
254     g_value_unset (&value);
255   }
256 }
257 
258 #ifdef HAVE_LRDF
259 static gboolean
ladspa_rdf_directory_search(const char * dir_name)260 ladspa_rdf_directory_search (const char *dir_name)
261 {
262   GDir *dir;
263   gchar *file_name, *file_uri;
264   const gchar *entry_name;
265   gint ok;
266 
267   GST_INFO ("scanning directory for rdfs \"%s\"", dir_name);
268 
269   dir = g_dir_open (dir_name, 0, NULL);
270   if (!dir)
271     return FALSE;
272 
273   while ((entry_name = g_dir_read_name (dir))) {
274     file_name = g_build_filename (dir_name, entry_name, NULL);
275     file_uri = g_strconcat ("file://", file_name, NULL);
276     ok = lrdf_read_file (file_uri);
277     GST_INFO ("read %s : %d", file_uri, ok);
278     g_free (file_uri);
279     g_free (file_name);
280   }
281   g_dir_close (dir);
282 
283   return TRUE;
284 }
285 #endif
286 
287 /* search just the one directory */
288 static gboolean
ladspa_plugin_directory_search(GstPlugin * ladspa_plugin,const char * dir_name)289 ladspa_plugin_directory_search (GstPlugin * ladspa_plugin, const char *dir_name)
290 {
291   GDir *dir;
292   gchar *file_name;
293   const gchar *entry_name;
294   LADSPA_Descriptor_Function descriptor_function;
295   GModule *plugin;
296   gboolean ok = FALSE;
297 
298   GST_INFO ("scanning directory for plugins \"%s\"", dir_name);
299 
300   dir = g_dir_open (dir_name, 0, NULL);
301   if (!dir)
302     return FALSE;
303 
304   while ((entry_name = g_dir_read_name (dir))) {
305     /* Only attempt to open files with the module suffixes */
306     if (!g_str_has_suffix (entry_name, "." G_MODULE_SUFFIX)
307 #ifdef GST_EXTRA_MODULE_SUFFIX
308         && !g_str_has_suffix (entry_name, GST_EXTRA_MODULE_SUFFIX)
309 #endif
310         ) {
311       GST_TRACE ("Ignoring file %s as it has the wrong suffix for a plugin",
312           entry_name);
313       continue;
314     }
315 
316     file_name = g_build_filename (dir_name, entry_name, NULL);
317     GST_LOG ("Probing file %s as a LADSPA plugin", file_name);
318     plugin =
319         g_module_open (file_name, G_MODULE_BIND_LAZY | G_MODULE_BIND_LOCAL);
320     if (plugin) {
321       /* the file is a shared library */
322       if (g_module_symbol (plugin, "ladspa_descriptor",
323               (gpointer *) & descriptor_function)) {
324         /* we've found a ladspa_descriptor function, now introspect it. */
325         GST_INFO ("Found LADSPA descriptor in %s", file_name);
326         ladspa_describe_plugin (file_name, entry_name, descriptor_function);
327         ok = TRUE;
328       } else {
329         /* it was a library, but not a LADSPA one. Unload it. */
330         g_module_close (plugin);
331       }
332     }
333     g_free (file_name);
334   }
335   g_dir_close (dir);
336 
337   return ok;
338 }
339 
340 /* search the plugin path */
341 static gboolean
ladspa_plugin_path_search(GstPlugin * plugin)342 ladspa_plugin_path_search (GstPlugin * plugin)
343 {
344   const gchar *search_path, *path;
345   GString *ladspa_path;
346   gchar **paths;
347   gint i, j, path_entries;
348   gboolean res = FALSE, skip;
349 #ifdef HAVE_LRDF
350   gchar *pos, *prefix, *rdf_path;
351 #endif
352 
353   ladspa_path = g_string_new (NULL);
354 
355   search_path = g_getenv ("LADSPA_PATH");
356   if (search_path) {
357     g_string_append_printf (ladspa_path,
358         "%s" G_SEARCHPATH_SEPARATOR_S GST_LADSPA_DEFAULT_PATH, search_path);
359   } else {
360     g_string_append (ladspa_path, GST_LADSPA_DEFAULT_PATH);
361   }
362 
363 #ifdef G_OS_WIN32
364   path = g_getenv ("APPDATA");
365   if (path) {
366     gchar *path_subdir = g_build_filename (path, "LADSPA", NULL);
367     if (ladspa_path->len)
368       g_string_append_printf (ladspa_path, G_SEARCHPATH_SEPARATOR_S "%s",
369           path_subdir);
370     else
371       g_string_append (ladspa_path, path_subdir);
372     g_free (path_subdir);
373   }
374 
375   path = g_getenv ("COMMONPROGRAMFILES");
376   if (path) {
377     gchar *path_subdir = g_build_filename (path, "LADSPA", NULL);
378     if (ladspa_path->len)
379       g_string_append_printf (ladspa_path, G_SEARCHPATH_SEPARATOR_S "%s",
380           path_subdir);
381     else
382       g_string_append (ladspa_path, path_subdir);
383     g_free (path_subdir);
384   }
385 #else
386   path = g_getenv ("HOME");
387 
388   if (path) {
389     if (ladspa_path->len)
390       g_string_append_printf (ladspa_path, ":%s/.ladspa", path);
391     else
392       g_string_append_printf (ladspa_path, "%s/.ladspa", path);
393 
394 #if defined (HAVE_IOS) || defined (HAVE_OSX)
395     g_string_append_printf (ladspa_path, ":%s/Library/Audio/Plug-Ins/LADSPA",
396         path);
397 #endif
398   }
399 #endif
400 
401   paths = g_strsplit (ladspa_path->str, G_SEARCHPATH_SEPARATOR_S, 0);
402   path_entries = g_strv_length (paths);
403   GST_INFO ("%d dirs in search paths \"%s\"", path_entries, ladspa_path->str);
404 
405 #ifdef HAVE_LRDF
406   for (i = 0; i < path_entries; i++) {
407     skip = FALSE;
408     for (j = 0; j < i; j++) {
409       if (!strcmp (paths[i], paths[j])) {
410         skip = TRUE;
411         break;
412       }
413     }
414     if (skip)
415       break;
416     /*
417      * transform path: /usr/lib/ladspa -> /usr/share/ladspa/rdf/
418      * yes, this is ugly, but lrdf has not searchpath
419      */
420     if ((pos = strstr (paths[i], "/lib/ladspa"))) {
421       prefix = g_strndup (paths[i], (pos - paths[i]));
422       rdf_path = g_build_filename (prefix, "share", "ladspa", "rdf", NULL);
423       ladspa_rdf_directory_search (rdf_path);
424       g_free (rdf_path);
425       g_free (prefix);
426     }
427   }
428 #endif
429 
430   for (i = 0; i < path_entries; i++) {
431     skip = FALSE;
432     for (j = 0; j < i; j++) {
433       if (!strcmp (paths[i], paths[j])) {
434         skip = TRUE;
435         break;
436       }
437     }
438     if (skip)
439       break;
440     res |= ladspa_plugin_directory_search (plugin, paths[i]);
441   }
442   g_strfreev (paths);
443 
444   g_string_free (ladspa_path, TRUE);
445 
446   return res;
447 }
448 
449 static gboolean
plugin_init(GstPlugin * plugin)450 plugin_init (GstPlugin * plugin)
451 {
452   gboolean res = FALSE;
453   gint n = 0;
454 
455   GST_DEBUG_CATEGORY_INIT (ladspa_debug, "ladspa", 0, "LADSPA plugins");
456 
457 #ifdef ENABLE_NLS
458   GST_DEBUG_OBJECT (plugin, "binding text domain %s to locale dir %s",
459       GETTEXT_PACKAGE, LOCALEDIR);
460   bindtextdomain (GETTEXT_PACKAGE, LOCALEDIR);
461   bind_textdomain_codeset (GETTEXT_PACKAGE, "UTF-8");
462 #endif
463 
464   gst_plugin_add_dependency_simple (plugin,
465       "LADSPA_PATH:" GST_LADSPA_ENVVARS,
466       GST_LADSPA_DEFAULT_PATH, NULL, GST_PLUGIN_DEPENDENCY_FLAG_NONE);
467 
468 #ifdef HAVE_LRDF
469   lrdf_init ();
470 #endif
471 
472   ladspa_meta_all = (GstStructure *) gst_plugin_get_cache_data (plugin);
473   if (ladspa_meta_all) {
474     n = gst_structure_n_fields (ladspa_meta_all);
475   }
476   GST_INFO_OBJECT (plugin, "%d entries in cache", n);
477   if (!n) {
478     ladspa_meta_all = gst_structure_new_empty ("ladspa");
479     if ((res = ladspa_plugin_path_search (plugin))) {
480       n = gst_structure_n_fields (ladspa_meta_all);
481       GST_INFO_OBJECT (plugin, "%d entries after scanning", n);
482       gst_plugin_set_cache_data (plugin, ladspa_meta_all);
483     }
484   } else {
485     res = TRUE;
486   }
487 
488   if (n) {
489     gint i;
490     const gchar *name;
491     const GValue *value;
492 
493     GST_INFO_OBJECT (plugin, "register types");
494 
495     for (i = 0; i < n; i++) {
496       name = gst_structure_nth_field_name (ladspa_meta_all, i);
497       value = gst_structure_get_value (ladspa_meta_all, name);
498       if (G_VALUE_TYPE (value) == GST_TYPE_STRUCTURE) {
499         GstStructure *ladspa_meta = g_value_get_boxed (value);
500 
501         ladspa_plugin_register_element (plugin, ladspa_meta);
502       }
503     }
504   }
505 
506   if (!res) {
507     GST_WARNING_OBJECT (plugin, "no LADSPA plugins found, check LADSPA_PATH");
508   }
509 
510   /* we don't want to fail, even if there are no elements registered */
511   return TRUE;
512 }
513 
514 GST_PLUGIN_DEFINE (GST_VERSION_MAJOR,
515     GST_VERSION_MINOR,
516     ladspa,
517     "LADSPA plugin",
518     plugin_init, VERSION, GST_LICENSE, GST_PACKAGE_NAME, GST_PACKAGE_ORIGIN)
519