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