1 /*
2 Copyright 2007-2019 David Robillard <http://drobilla.net>
3
4 Permission to use, copy, modify, and/or distribute this software for any
5 purpose with or without fee is hereby granted, provided that the above
6 copyright notice and this permission notice appear in all copies.
7
8 THIS SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
9 WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
10 MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
11 ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
12 WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
13 ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
14 OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
15 */
16
17 #include "lilv_config.h"
18
19 #include "lilv/lilv.h"
20 #include "lv2/core/lv2.h"
21 #include "lv2/event/event.h"
22 #include "lv2/port-groups/port-groups.h"
23 #include "lv2/presets/presets.h"
24
25 #include <math.h>
26 #include <stdbool.h>
27 #include <stdint.h>
28 #include <stdio.h>
29 #include <stdlib.h>
30 #include <string.h>
31
32 LilvNode* applies_to_pred = NULL;
33 LilvNode* control_class = NULL;
34 LilvNode* event_class = NULL;
35 LilvNode* group_pred = NULL;
36 LilvNode* label_pred = NULL;
37 LilvNode* preset_class = NULL;
38 LilvNode* designation_pred = NULL;
39 LilvNode* supports_event_pred = NULL;
40
41 static void
print_port(const LilvPlugin * p,uint32_t index,float * mins,float * maxes,float * defaults)42 print_port(const LilvPlugin* p,
43 uint32_t index,
44 float* mins,
45 float* maxes,
46 float* defaults)
47 {
48 const LilvPort* port = lilv_plugin_get_port_by_index(p, index);
49
50 printf("\n\tPort %d:\n", index);
51
52 if (!port) {
53 printf("\t\tERROR: Illegal/nonexistent port\n");
54 return;
55 }
56
57 bool first = true;
58
59 const LilvNodes* classes = lilv_port_get_classes(p, port);
60 printf("\t\tType: ");
61 LILV_FOREACH(nodes, i, classes) {
62 const LilvNode* value = lilv_nodes_get(classes, i);
63 if (!first) {
64 printf("\n\t\t ");
65 }
66 printf("%s", lilv_node_as_uri(value));
67 first = false;
68 }
69
70 if (lilv_port_is_a(p, port, event_class)) {
71 LilvNodes* supported = lilv_port_get_value(
72 p, port, supports_event_pred);
73 if (lilv_nodes_size(supported) > 0) {
74 printf("\n\t\tSupported events:\n");
75 LILV_FOREACH(nodes, i, supported) {
76 const LilvNode* value = lilv_nodes_get(supported, i);
77 printf("\t\t\t%s\n", lilv_node_as_uri(value));
78 }
79 }
80 lilv_nodes_free(supported);
81 }
82
83 LilvScalePoints* points = lilv_port_get_scale_points(p, port);
84 if (points) {
85 printf("\n\t\tScale Points:\n");
86 }
87 LILV_FOREACH(scale_points, i, points) {
88 const LilvScalePoint* point = lilv_scale_points_get(points, i);
89 printf("\t\t\t%s = \"%s\"\n",
90 lilv_node_as_string(lilv_scale_point_get_value(point)),
91 lilv_node_as_string(lilv_scale_point_get_label(point)));
92 }
93 lilv_scale_points_free(points);
94
95 const LilvNode* sym = lilv_port_get_symbol(p, port);
96 printf("\n\t\tSymbol: %s\n", lilv_node_as_string(sym));
97
98 LilvNode* name = lilv_port_get_name(p, port);
99 printf("\t\tName: %s\n", lilv_node_as_string(name));
100 lilv_node_free(name);
101
102 LilvNodes* groups = lilv_port_get_value(p, port, group_pred);
103 if (lilv_nodes_size(groups) > 0) {
104 printf("\t\tGroup: %s\n",
105 lilv_node_as_string(lilv_nodes_get_first(groups)));
106 }
107 lilv_nodes_free(groups);
108
109 LilvNodes* designations = lilv_port_get_value(p, port, designation_pred);
110 if (lilv_nodes_size(designations) > 0) {
111 printf("\t\tDesignation: %s\n",
112 lilv_node_as_string(lilv_nodes_get_first(designations)));
113 }
114 lilv_nodes_free(designations);
115
116 if (lilv_port_is_a(p, port, control_class)) {
117 if (!isnan(mins[index])) {
118 printf("\t\tMinimum: %f\n", mins[index]);
119 }
120 if (!isnan(maxes[index])) {
121 printf("\t\tMaximum: %f\n", maxes[index]);
122 }
123 if (!isnan(defaults[index])) {
124 printf("\t\tDefault: %f\n", defaults[index]);
125 }
126 }
127
128 LilvNodes* properties = lilv_port_get_properties(p, port);
129 if (lilv_nodes_size(properties) > 0) {
130 printf("\t\tProperties: ");
131 }
132 first = true;
133 LILV_FOREACH(nodes, i, properties) {
134 if (!first) {
135 printf("\t\t ");
136 }
137 printf("%s\n", lilv_node_as_uri(lilv_nodes_get(properties, i)));
138 first = false;
139 }
140 if (lilv_nodes_size(properties) > 0) {
141 printf("\n");
142 }
143 lilv_nodes_free(properties);
144 }
145
146 static void
print_plugin(LilvWorld * world,const LilvPlugin * p)147 print_plugin(LilvWorld* world,
148 const LilvPlugin* p)
149 {
150 LilvNode* val = NULL;
151
152 printf("%s\n\n", lilv_node_as_uri(lilv_plugin_get_uri(p)));
153
154 val = lilv_plugin_get_name(p);
155 if (val) {
156 printf("\tName: %s\n", lilv_node_as_string(val));
157 lilv_node_free(val);
158 }
159
160 const LilvPluginClass* pclass = lilv_plugin_get_class(p);
161 const LilvNode* class_label = lilv_plugin_class_get_label(pclass);
162 if (class_label) {
163 printf("\tClass: %s\n", lilv_node_as_string(class_label));
164 }
165
166 val = lilv_plugin_get_author_name(p);
167 if (val) {
168 printf("\tAuthor: %s\n", lilv_node_as_string(val));
169 lilv_node_free(val);
170 }
171
172 val = lilv_plugin_get_author_email(p);
173 if (val) {
174 printf("\tAuthor Email: %s\n", lilv_node_as_uri(val));
175 lilv_node_free(val);
176 }
177
178 val = lilv_plugin_get_author_homepage(p);
179 if (val) {
180 printf("\tAuthor Homepage: %s\n", lilv_node_as_uri(val));
181 lilv_node_free(val);
182 }
183
184 if (lilv_plugin_has_latency(p)) {
185 uint32_t latency_port = lilv_plugin_get_latency_port_index(p);
186 printf("\tHas latency: yes, reported by port %d\n", latency_port);
187 } else {
188 printf("\tHas latency: no\n");
189 }
190
191 printf("\tBundle: %s\n",
192 lilv_node_as_uri(lilv_plugin_get_bundle_uri(p)));
193
194 const LilvNode* binary_uri = lilv_plugin_get_library_uri(p);
195 if (binary_uri) {
196 printf("\tBinary: %s\n",
197 lilv_node_as_uri(lilv_plugin_get_library_uri(p)));
198 }
199
200 LilvUIs* uis = lilv_plugin_get_uis(p);
201 if (lilv_nodes_size(uis) > 0) {
202 printf("\tUIs:\n");
203 LILV_FOREACH(uis, i, uis) {
204 const LilvUI* ui = lilv_uis_get(uis, i);
205 printf("\t\t%s\n", lilv_node_as_uri(lilv_ui_get_uri(ui)));
206
207 const char* binary = lilv_node_as_uri(lilv_ui_get_binary_uri(ui));
208
209 const LilvNodes* types = lilv_ui_get_classes(ui);
210 LILV_FOREACH(nodes, t, types) {
211 printf("\t\t\tClass: %s\n",
212 lilv_node_as_uri(lilv_nodes_get(types, t)));
213 }
214
215 if (binary) {
216 printf("\t\t\tBinary: %s\n", binary);
217 }
218
219 printf("\t\t\tBundle: %s\n",
220 lilv_node_as_uri(lilv_ui_get_bundle_uri(ui)));
221 }
222 }
223 lilv_uis_free(uis);
224
225 printf("\tData URIs: ");
226 const LilvNodes* data_uris = lilv_plugin_get_data_uris(p);
227 bool first = true;
228 LILV_FOREACH(nodes, i, data_uris) {
229 if (!first) {
230 printf("\n\t ");
231 }
232 printf("%s", lilv_node_as_uri(lilv_nodes_get(data_uris, i)));
233 first = false;
234 }
235 printf("\n");
236
237 /* Required Features */
238
239 LilvNodes* features = lilv_plugin_get_required_features(p);
240 if (features) {
241 printf("\tRequired Features: ");
242 }
243 first = true;
244 LILV_FOREACH(nodes, i, features) {
245 if (!first) {
246 printf("\n\t ");
247 }
248 printf("%s", lilv_node_as_uri(lilv_nodes_get(features, i)));
249 first = false;
250 }
251 if (features) {
252 printf("\n");
253 }
254 lilv_nodes_free(features);
255
256 /* Optional Features */
257
258 features = lilv_plugin_get_optional_features(p);
259 if (features) {
260 printf("\tOptional Features: ");
261 }
262 first = true;
263 LILV_FOREACH(nodes, i, features) {
264 if (!first) {
265 printf("\n\t ");
266 }
267 printf("%s", lilv_node_as_uri(lilv_nodes_get(features, i)));
268 first = false;
269 }
270 if (features) {
271 printf("\n");
272 }
273 lilv_nodes_free(features);
274
275 /* Extension Data */
276
277 LilvNodes* data = lilv_plugin_get_extension_data(p);
278 if (data) {
279 printf("\tExtension Data: ");
280 }
281 first = true;
282 LILV_FOREACH(nodes, i, data) {
283 if (!first) {
284 printf("\n\t ");
285 }
286 printf("%s", lilv_node_as_uri(lilv_nodes_get(data, i)));
287 first = false;
288 }
289 if (data) {
290 printf("\n");
291 }
292 lilv_nodes_free(data);
293
294 /* Presets */
295
296 LilvNodes* presets = lilv_plugin_get_related(p, preset_class);
297 if (presets) {
298 printf("\tPresets: \n");
299 }
300 LILV_FOREACH(nodes, i, presets) {
301 const LilvNode* preset = lilv_nodes_get(presets, i);
302 lilv_world_load_resource(world, preset);
303 LilvNodes* titles = lilv_world_find_nodes(
304 world, preset, label_pred, NULL);
305 if (titles) {
306 const LilvNode* title = lilv_nodes_get_first(titles);
307 printf("\t %s\n", lilv_node_as_string(title));
308 lilv_nodes_free(titles);
309 } else {
310 fprintf(stderr, "Preset <%s> has no rdfs:label\n",
311 lilv_node_as_string(lilv_nodes_get(presets, i)));
312 }
313 }
314 lilv_nodes_free(presets);
315
316 /* Ports */
317
318 const uint32_t num_ports = lilv_plugin_get_num_ports(p);
319 float* mins = (float*)calloc(num_ports, sizeof(float));
320 float* maxes = (float*)calloc(num_ports, sizeof(float));
321 float* defaults = (float*)calloc(num_ports, sizeof(float));
322 lilv_plugin_get_port_ranges_float(p, mins, maxes, defaults);
323
324 for (uint32_t i = 0; i < num_ports; ++i) {
325 print_port(p, i, mins, maxes, defaults);
326 }
327
328 free(mins);
329 free(maxes);
330 free(defaults);
331 }
332
333 static void
print_version(void)334 print_version(void)
335 {
336 printf(
337 "lv2info (lilv) " LILV_VERSION "\n"
338 "Copyright 2007-2019 David Robillard <http://drobilla.net>\n"
339 "License: <http://www.opensource.org/licenses/isc-license>\n"
340 "This is free software: you are free to change and redistribute it.\n"
341 "There is NO WARRANTY, to the extent permitted by law.\n");
342 }
343
344 static void
print_usage(void)345 print_usage(void)
346 {
347 printf(
348 "Usage: lv2info [OPTION]... PLUGIN_URI\n"
349 "Print information about an installed LV2 plugin.\n\n"
350 " -p FILE Write Turtle description of plugin to FILE\n"
351 " -m FILE Add record of plugin to manifest FILE\n"
352 " --help Display this help and exit\n"
353 " --version Display version information and exit\n\n"
354 "For -p and -m, Turtle files are appended to (not overwritten),\n"
355 "and @prefix directives are only written if the file was empty.\n"
356 "This allows several plugins to be added to a single file.\n");
357 }
358
359 int
main(int argc,char ** argv)360 main(int argc, char** argv)
361 {
362 if (argc == 1) {
363 print_usage();
364 return 1;
365 }
366
367 const char* plugin_file = NULL;
368 const char* manifest_file = NULL;
369 const char* plugin_uri = NULL;
370 for (int i = 1; i < argc; ++i) {
371 if (!strcmp(argv[i], "--version")) {
372 print_version();
373 return 0;
374 } else if (!strcmp(argv[i], "--help")) {
375 print_usage();
376 return 0;
377 } else if (!strcmp(argv[i], "-p")) {
378 plugin_file = argv[++i];
379 } else if (!strcmp(argv[i], "-m")) {
380 manifest_file = argv[++i];
381 } else if (argv[i][0] == '-') {
382 print_usage();
383 return 1;
384 } else if (i == argc - 1) {
385 plugin_uri = argv[i];
386 }
387 }
388
389 int ret = 0;
390
391 LilvWorld* world = lilv_world_new();
392 lilv_world_load_all(world);
393
394 LilvNode* uri = lilv_new_uri(world, plugin_uri);
395 if (!uri) {
396 fprintf(stderr, "Invalid plugin URI\n");
397 lilv_world_free(world);
398 return 1;
399 }
400
401 applies_to_pred = lilv_new_uri(world, LV2_CORE__appliesTo);
402 control_class = lilv_new_uri(world, LILV_URI_CONTROL_PORT);
403 event_class = lilv_new_uri(world, LILV_URI_EVENT_PORT);
404 group_pred = lilv_new_uri(world, LV2_PORT_GROUPS__group);
405 label_pred = lilv_new_uri(world, LILV_NS_RDFS "label");
406 preset_class = lilv_new_uri(world, LV2_PRESETS__Preset);
407 designation_pred = lilv_new_uri(world, LV2_CORE__designation);
408 supports_event_pred = lilv_new_uri(world, LV2_EVENT__supportsEvent);
409
410 const LilvPlugins* plugins = lilv_world_get_all_plugins(world);
411 const LilvPlugin* p = lilv_plugins_get_by_uri(plugins, uri);
412
413 if (p && plugin_file) {
414 LilvNode* base = lilv_new_file_uri(world, NULL, plugin_file);
415
416 FILE* plugin_fd = fopen(plugin_file, "a");
417 if (plugin_fd) {
418 lilv_plugin_write_description(world, p, base, plugin_fd);
419 fclose(plugin_fd);
420 } else {
421 fprintf(stderr, "error: Failed to open %s\n", plugin_file);
422 }
423
424 if (manifest_file) {
425 FILE* manifest_fd = fopen(manifest_file, "a");
426 if (manifest_fd) {
427 lilv_plugin_write_manifest_entry(
428 world, p, base, manifest_fd, plugin_file);
429 fclose(manifest_fd);
430 } else {
431 fprintf(stderr, "error: Failed to open %s\n", manifest_file);
432 }
433 }
434 lilv_node_free(base);
435 } else if (p) {
436 print_plugin(world, p);
437 } else {
438 fprintf(stderr, "Plugin not found.\n");
439 }
440
441 ret = (p != NULL ? 0 : -1);
442
443 lilv_node_free(uri);
444
445 lilv_node_free(supports_event_pred);
446 lilv_node_free(designation_pred);
447 lilv_node_free(preset_class);
448 lilv_node_free(label_pred);
449 lilv_node_free(group_pred);
450 lilv_node_free(event_class);
451 lilv_node_free(control_class);
452 lilv_node_free(applies_to_pred);
453
454 lilv_world_free(world);
455 return ret;
456 }
457
458