1 /* GStreamer
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 * 2016 Thibault Saunier <thibault.saunier@collabora.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-lv2
25 * @title: lv2
26 * @short_description: bridge for LV2.
27 *
28 * LV2 is a standard for plugins and matching host applications,
29 * mainly targeted at audio processing and generation. It is intended as
30 * a successor to LADSPA (Linux Audio Developer's Simple Plugin API).
31 *
32 * The LV2 element is a bridge for plugins using the
33 * <ulink url="http://www.lv2plug.in/">LV2</ulink> API. It scans all
34 * installed LV2 plugins and registers them as gstreamer elements.
35 */
36
37 #ifdef HAVE_CONFIG_H
38 #include "config.h"
39 #endif
40
41 #include <string.h>
42 #include "gstlv2.h"
43
44 #include <gst/audio/audio-channels.h>
45 #include <lv2/lv2plug.in/ns/ext/port-groups/port-groups.h>
46 #include "lv2/lv2plug.in/ns/ext/event/event.h"
47 #include "lv2/lv2plug.in/ns/ext/presets/presets.h"
48 #include "lv2/lv2plug.in/ns/ext/state/state.h"
49
50 GST_DEBUG_CATEGORY (lv2_debug);
51 #define GST_CAT_DEFAULT lv2_debug
52
53 #if defined (G_OS_WIN32)
54 #define GST_LV2_ENVVARS "APPDATA/LV2:COMMONPROGRAMFILES/LV2"
55 #define GST_LV2_DEFAULT_PATH NULL
56 #elif defined (HAVE_OSX)
57 #define GST_LV2_ENVVARS "HOME/Library/Audio/Plug-Ins/LV2:HOME/.lv2"
58 #define GST_LV2_DEFAULT_PATH \
59 "/usr/local/lib/lv2:/usr/lib/lv2:/Library/Audio/Plug-Ins/LV2"
60 #elif defined (G_OS_UNIX)
61 #define GST_LV2_ENVVARS "HOME/.lv2"
62 #define GST_LV2_DEFAULT_PATH \
63 "/usr/lib/lv2:" \
64 "/usr/lib64/lv2:" \
65 "/usr/local/lib/lv2:" \
66 "/usr/local/lib64/lv2:" \
67 LIBDIR "/lv2"
68 #else
69 #error "Unsupported OS"
70 #endif
71
72 LilvWorld *world = NULL;
73 LilvNode *atom_class = NULL;
74 LilvNode *audio_class = NULL;
75 LilvNode *control_class = NULL;
76 LilvNode *cv_class = NULL;
77 LilvNode *event_class = NULL;
78 LilvNode *input_class = NULL;
79 LilvNode *output_class = NULL;
80 LilvNode *preset_class = NULL;
81 LilvNode *state_iface = NULL;
82 LilvNode *state_uri = NULL;
83
84 LilvNode *integer_prop = NULL;
85 LilvNode *toggled_prop = NULL;
86 LilvNode *designation_pred = NULL;
87 LilvNode *in_place_broken_pred = NULL;
88 LilvNode *optional_pred = NULL;
89 LilvNode *group_pred = NULL;
90 LilvNode *supports_event_pred = NULL;
91 LilvNode *label_pred = NULL;
92
93 LilvNode *center_role = NULL;
94 LilvNode *left_role = NULL;
95 LilvNode *right_role = NULL;
96 LilvNode *rear_center_role = NULL;
97 LilvNode *rear_left_role = NULL;
98 LilvNode *rear_right_role = NULL;
99 LilvNode *lfe_role = NULL;
100 LilvNode *center_left_role = NULL;
101 LilvNode *center_right_role = NULL;
102 LilvNode *side_left_role = NULL;
103 LilvNode *side_right_role = NULL;
104
105 GstStructure *lv2_meta_all = NULL;
106
107 static void
lv2_plugin_register_element(GstPlugin * plugin,GstStructure * lv2_meta)108 lv2_plugin_register_element (GstPlugin * plugin, GstStructure * lv2_meta)
109 {
110 guint audio_in, audio_out;
111
112 gst_structure_get_uint (lv2_meta, "audio-in", &audio_in);
113 gst_structure_get_uint (lv2_meta, "audio-out", &audio_out);
114
115 if (audio_in == 0) {
116 gst_lv2_source_register_element (plugin, lv2_meta);
117 } else {
118 gst_lv2_filter_register_element (plugin, lv2_meta);
119 }
120 }
121
122 static void
lv2_count_ports(const LilvPlugin * lv2plugin,guint * audio_in,guint * audio_out,guint * control)123 lv2_count_ports (const LilvPlugin * lv2plugin, guint * audio_in,
124 guint * audio_out, guint * control)
125 {
126 GHashTable *port_groups = g_hash_table_new_full (g_str_hash, g_str_equal,
127 g_free, NULL);
128 guint i;
129
130 *audio_in = *audio_out = *control = 0;
131 for (i = 0; i < lilv_plugin_get_num_ports (lv2plugin); i++) {
132 const LilvPort *port = lilv_plugin_get_port_by_index (lv2plugin, i);
133
134 if (lilv_port_is_a (lv2plugin, port, audio_class)) {
135 const gboolean is_input = lilv_port_is_a (lv2plugin, port, input_class);
136 LilvNodes *lv2group = lilv_port_get (lv2plugin, port, group_pred);
137
138 if (lv2group) {
139 const gchar *uri = lilv_node_as_uri (lv2group);
140
141 if (g_hash_table_contains (port_groups, uri))
142 continue;
143
144 g_hash_table_add (port_groups, g_strdup (uri));
145 lilv_node_free (lv2group);
146 }
147
148 if (is_input)
149 (*audio_in)++;
150 else
151 (*audio_out)++;
152 } else if (lilv_port_is_a (lv2plugin, port, control_class) ||
153 lilv_port_is_a (lv2plugin, port, cv_class)) {
154 (*control)++;
155 }
156 }
157 g_hash_table_unref (port_groups);
158 }
159
160 /* search the plugin path */
161 static gboolean
lv2_plugin_discover(GstPlugin * plugin)162 lv2_plugin_discover (GstPlugin * plugin)
163 {
164 guint audio_in, audio_out, control;
165 LilvIter *i;
166 const LilvPlugins *plugins = lilv_world_get_all_plugins (world);
167
168 for (i = lilv_plugins_begin (plugins); !lilv_plugins_is_end (plugins, i);
169 i = lilv_plugins_next (plugins, i)) {
170 GstStructure *lv2_meta = NULL;
171 GValue value = { 0, };
172 const LilvPlugin *lv2plugin = lilv_plugins_get (plugins, i);
173 const gchar *plugin_uri, *p;
174 gchar *type_name;
175 gboolean can_do_presets;
176
177 plugin_uri = lilv_node_as_uri (lilv_plugin_get_uri (lv2plugin));
178
179 /* check if we support the required host features */
180 if (!gst_lv2_check_required_features (lv2plugin)) {
181 GST_FIXME ("lv2 plugin %s needs host features", plugin_uri);
182 continue;
183 }
184
185 /* construct the type name from plugin URI */
186 if ((p = strstr (plugin_uri, "://"))) {
187 /* cut off the protocol (e.g. http://) */
188 type_name = g_strdup (&p[3]);
189 } else {
190 type_name = g_strdup (plugin_uri);
191 }
192 g_strcanon (type_name, G_CSET_A_2_Z G_CSET_a_2_z G_CSET_DIGITS "-+", '-');
193
194 /* if it's already registered, drop it */
195 if (g_type_from_name (type_name))
196 goto next;
197
198 /* check if this has any audio ports */
199 lv2_count_ports (lv2plugin, &audio_in, &audio_out, &control);
200
201 if (audio_in == 0 && audio_out == 0) {
202 GST_FIXME ("plugin %s has no audio pads", type_name);
203 goto next;
204 } else if (audio_in == 0) {
205 if (audio_out != 1) {
206 GST_FIXME ("plugin %s is not a GstBaseSrc (num_src_pads: %d)",
207 type_name, audio_out);
208 goto next;
209 }
210 } else if (audio_out == 0) {
211 GST_FIXME ("plugin %s is a sink element (num_sink_pads: %d"
212 " num_src_pads: %d)", type_name, audio_in, audio_out);
213 goto next;
214 } else {
215 if (audio_in != 1 || audio_out != 1) {
216 GST_FIXME ("plugin %s is not a GstAudioFilter (num_sink_pads: %d"
217 " num_src_pads: %d)", type_name, audio_in, audio_out);
218 goto next;
219 }
220 }
221
222 /* check supported extensions */
223 can_do_presets = lilv_plugin_has_extension_data (lv2plugin, state_iface)
224 || lilv_plugin_has_feature (lv2plugin, state_uri)
225 || (control > 0);
226 GST_INFO ("plugin %s can%s do presets", type_name,
227 (can_do_presets ? "" : "'t"));
228
229 lv2_meta = gst_structure_new ("lv2",
230 "element-uri", G_TYPE_STRING, plugin_uri,
231 "element-type-name", G_TYPE_STRING, type_name,
232 "audio-in", G_TYPE_UINT, audio_in,
233 "audio-out", G_TYPE_UINT, audio_out,
234 "can-do-presets", G_TYPE_BOOLEAN, can_do_presets, NULL);
235
236 g_value_init (&value, GST_TYPE_STRUCTURE);
237 g_value_set_boxed (&value, lv2_meta);
238 gst_structure_set_value (lv2_meta_all, type_name, &value);
239 g_value_unset (&value);
240
241 // don't free type_name
242 continue;
243
244 next:
245 g_free (type_name);
246 }
247
248 return TRUE;
249 }
250
251 static gboolean
plugin_init(GstPlugin * plugin)252 plugin_init (GstPlugin * plugin)
253 {
254 gboolean res = FALSE;
255 gint n = 0;
256
257 GST_DEBUG_CATEGORY_INIT (lv2_debug, "lv2",
258 GST_DEBUG_FG_GREEN | GST_DEBUG_BG_BLACK | GST_DEBUG_BOLD, "LV2");
259
260 world = lilv_world_new ();
261 lilv_world_load_all (world);
262 gst_lv2_host_init ();
263
264 /* have been added after lilv-0.22.0, which is the last release */
265 #ifndef LILV_URI_ATOM_PORT
266 #define LILV_URI_ATOM_PORT "http://lv2plug.in/ns/ext/atom#AtomPort"
267 #endif
268 #ifndef LILV_URI_CV_PORT
269 #define LILV_URI_CV_PORT "http://lv2plug.in/ns/lv2core#CVPort"
270 #endif
271
272 atom_class = lilv_new_uri (world, LILV_URI_ATOM_PORT);
273 audio_class = lilv_new_uri (world, LILV_URI_AUDIO_PORT);
274 control_class = lilv_new_uri (world, LILV_URI_CONTROL_PORT);
275 cv_class = lilv_new_uri (world, LILV_URI_CV_PORT);
276 event_class = lilv_new_uri (world, LILV_URI_EVENT_PORT);
277 input_class = lilv_new_uri (world, LILV_URI_INPUT_PORT);
278 output_class = lilv_new_uri (world, LILV_URI_OUTPUT_PORT);
279 preset_class = lilv_new_uri (world, LV2_PRESETS__Preset);
280 state_iface = lilv_new_uri (world, LV2_STATE__interface);
281 state_uri = lilv_new_uri (world, LV2_STATE_URI);
282
283 integer_prop = lilv_new_uri (world, LV2_CORE__integer);
284 toggled_prop = lilv_new_uri (world, LV2_CORE__toggled);
285 designation_pred = lilv_new_uri (world, LV2_CORE__designation);
286 in_place_broken_pred = lilv_new_uri (world, LV2_CORE__inPlaceBroken);
287 optional_pred = lilv_new_uri (world, LV2_CORE__optionalFeature);
288 group_pred = lilv_new_uri (world, LV2_PORT_GROUPS__group);
289 supports_event_pred = lilv_new_uri (world, LV2_EVENT__supportsEvent);
290 label_pred = lilv_new_uri (world, LILV_NS_RDFS "label");
291
292 center_role = lilv_new_uri (world, LV2_PORT_GROUPS__center);
293 left_role = lilv_new_uri (world, LV2_PORT_GROUPS__left);
294 right_role = lilv_new_uri (world, LV2_PORT_GROUPS__right);
295 rear_center_role = lilv_new_uri (world, LV2_PORT_GROUPS__rearCenter);
296 rear_left_role = lilv_new_uri (world, LV2_PORT_GROUPS__rearLeft);
297 rear_right_role = lilv_new_uri (world, LV2_PORT_GROUPS__rearLeft);
298 lfe_role = lilv_new_uri (world, LV2_PORT_GROUPS__lowFrequencyEffects);
299 center_left_role = lilv_new_uri (world, LV2_PORT_GROUPS__centerLeft);
300 center_right_role = lilv_new_uri (world, LV2_PORT_GROUPS__centerRight);
301 side_left_role = lilv_new_uri (world, LV2_PORT_GROUPS__sideLeft);
302 side_right_role = lilv_new_uri (world, LV2_PORT_GROUPS__sideRight);
303
304 gst_plugin_add_dependency_simple (plugin,
305 "LV2_PATH:" GST_LV2_ENVVARS, GST_LV2_DEFAULT_PATH, NULL,
306 GST_PLUGIN_DEPENDENCY_FLAG_RECURSE);
307
308 /* ensure GstAudioChannelPosition type is registered */
309 if (!gst_audio_channel_position_get_type ())
310 return FALSE;
311
312 lv2_meta_all = (GstStructure *) gst_plugin_get_cache_data (plugin);
313 if (lv2_meta_all) {
314 n = gst_structure_n_fields (lv2_meta_all);
315 }
316 GST_INFO_OBJECT (plugin, "%d entries in cache", n);
317 if (!n) {
318 lv2_meta_all = gst_structure_new_empty ("lv2");
319 if ((res = lv2_plugin_discover (plugin))) {
320 n = gst_structure_n_fields (lv2_meta_all);
321 GST_INFO_OBJECT (plugin, "%d entries after scanning", n);
322 gst_plugin_set_cache_data (plugin, lv2_meta_all);
323 }
324 } else {
325 res = TRUE;
326 }
327
328 if (n) {
329 gint i;
330 const gchar *name;
331 const GValue *value;
332
333 GST_INFO_OBJECT (plugin, "register types");
334
335 for (i = 0; i < n; i++) {
336 name = gst_structure_nth_field_name (lv2_meta_all, i);
337 value = gst_structure_get_value (lv2_meta_all, name);
338 if (G_VALUE_TYPE (value) == GST_TYPE_STRUCTURE) {
339 GstStructure *lv2_meta = g_value_get_boxed (value);
340
341 lv2_plugin_register_element (plugin, lv2_meta);
342 }
343 }
344 }
345
346 if (!res) {
347 GST_WARNING_OBJECT (plugin, "no lv2 plugins found, check LV2_PATH");
348 }
349
350 /* we don't want to fail, even if there are no elements registered */
351 return TRUE;
352 }
353
354 #ifdef __GNUC__
355 __attribute__ ((destructor))
356 #endif
plugin_cleanup(GstPlugin * plugin)357 static void plugin_cleanup (GstPlugin * plugin)
358 {
359 lilv_node_free (atom_class);
360 lilv_node_free (audio_class);
361 lilv_node_free (control_class);
362 lilv_node_free (cv_class);
363 lilv_node_free (event_class);
364 lilv_node_free (input_class);
365 lilv_node_free (output_class);
366 lilv_node_free (preset_class);
367 lilv_node_free (state_iface);
368 lilv_node_free (state_uri);
369
370 lilv_node_free (integer_prop);
371 lilv_node_free (toggled_prop);
372 lilv_node_free (designation_pred);
373 lilv_node_free (in_place_broken_pred);
374 lilv_node_free (optional_pred);
375 lilv_node_free (group_pred);
376 lilv_node_free (supports_event_pred);
377 lilv_node_free (label_pred);
378
379 lilv_node_free (center_role);
380 lilv_node_free (left_role);
381 lilv_node_free (right_role);
382 lilv_node_free (rear_center_role);
383 lilv_node_free (rear_left_role);
384 lilv_node_free (rear_right_role);
385 lilv_node_free (lfe_role);
386 lilv_node_free (center_left_role);
387 lilv_node_free (center_right_role);
388 lilv_node_free (side_left_role);
389 lilv_node_free (side_right_role);
390
391 lilv_world_free (world);
392 }
393
394 GST_PLUGIN_DEFINE (GST_VERSION_MAJOR,
395 GST_VERSION_MINOR,
396 lv2,
397 "All LV2 plugins",
398 plugin_init, VERSION, GST_LICENSE, GST_PACKAGE_NAME, GST_PACKAGE_ORIGIN)
399