// Copyright 2007-2019 David Robillard // SPDX-License-Identifier: ISC #include "lilv/lilv.h" #include "lv2/core/lv2.h" #include "lv2/event/event.h" #include "lv2/port-groups/port-groups.h" #include "lv2/presets/presets.h" #include #include #include #include #include #include static LilvNode* applies_to_pred = NULL; static LilvNode* control_class = NULL; static LilvNode* event_class = NULL; static LilvNode* group_pred = NULL; static LilvNode* label_pred = NULL; static LilvNode* preset_class = NULL; static LilvNode* designation_pred = NULL; static LilvNode* supports_event_pred = NULL; static void print_port(const LilvPlugin* p, uint32_t index, const float* mins, const float* maxes, const float* defaults) { const LilvPort* port = lilv_plugin_get_port_by_index(p, index); printf("\n\tPort %u:\n", index); if (!port) { printf("\t\tERROR: Illegal/nonexistent port\n"); return; } bool first = true; const LilvNodes* classes = lilv_port_get_classes(p, port); printf("\t\tType: "); LILV_FOREACH (nodes, i, classes) { const LilvNode* value = lilv_nodes_get(classes, i); if (!first) { printf("\n\t\t "); } printf("%s", lilv_node_as_uri(value)); first = false; } if (lilv_port_is_a(p, port, event_class)) { LilvNodes* supported = lilv_port_get_value(p, port, supports_event_pred); if (lilv_nodes_size(supported) > 0) { printf("\n\t\tSupported events:\n"); LILV_FOREACH (nodes, i, supported) { const LilvNode* value = lilv_nodes_get(supported, i); printf("\t\t\t%s\n", lilv_node_as_uri(value)); } } lilv_nodes_free(supported); } LilvScalePoints* points = lilv_port_get_scale_points(p, port); if (points) { printf("\n\t\tScale Points:\n"); } LILV_FOREACH (scale_points, i, points) { const LilvScalePoint* point = lilv_scale_points_get(points, i); printf("\t\t\t%s = \"%s\"\n", lilv_node_as_string(lilv_scale_point_get_value(point)), lilv_node_as_string(lilv_scale_point_get_label(point))); } lilv_scale_points_free(points); const LilvNode* sym = lilv_port_get_symbol(p, port); printf("\n\t\tSymbol: %s\n", lilv_node_as_string(sym)); LilvNode* name = lilv_port_get_name(p, port); printf("\t\tName: %s\n", lilv_node_as_string(name)); lilv_node_free(name); LilvNodes* groups = lilv_port_get_value(p, port, group_pred); if (lilv_nodes_size(groups) > 0) { printf("\t\tGroup: %s\n", lilv_node_as_string(lilv_nodes_get_first(groups))); } lilv_nodes_free(groups); LilvNodes* designations = lilv_port_get_value(p, port, designation_pred); if (lilv_nodes_size(designations) > 0) { printf("\t\tDesignation: %s\n", lilv_node_as_string(lilv_nodes_get_first(designations))); } lilv_nodes_free(designations); if (lilv_port_is_a(p, port, control_class)) { if (!isnan(mins[index])) { printf("\t\tMinimum: %f\n", mins[index]); } if (!isnan(maxes[index])) { printf("\t\tMaximum: %f\n", maxes[index]); } if (!isnan(defaults[index])) { printf("\t\tDefault: %f\n", defaults[index]); } } LilvNodes* properties = lilv_port_get_properties(p, port); if (lilv_nodes_size(properties) > 0) { printf("\t\tProperties: "); } first = true; LILV_FOREACH (nodes, i, properties) { if (!first) { printf("\t\t "); } printf("%s\n", lilv_node_as_uri(lilv_nodes_get(properties, i))); first = false; } if (lilv_nodes_size(properties) > 0) { printf("\n"); } lilv_nodes_free(properties); } static void print_plugin(LilvWorld* world, const LilvPlugin* p) { LilvNode* val = NULL; printf("%s\n\n", lilv_node_as_uri(lilv_plugin_get_uri(p))); val = lilv_plugin_get_name(p); if (val) { printf("\tName: %s\n", lilv_node_as_string(val)); lilv_node_free(val); } const LilvPluginClass* pclass = lilv_plugin_get_class(p); const LilvNode* class_label = lilv_plugin_class_get_label(pclass); if (class_label) { printf("\tClass: %s\n", lilv_node_as_string(class_label)); } val = lilv_plugin_get_author_name(p); if (val) { printf("\tAuthor: %s\n", lilv_node_as_string(val)); lilv_node_free(val); } val = lilv_plugin_get_author_email(p); if (val) { printf("\tAuthor Email: %s\n", lilv_node_as_uri(val)); lilv_node_free(val); } val = lilv_plugin_get_author_homepage(p); if (val) { printf("\tAuthor Homepage: %s\n", lilv_node_as_uri(val)); lilv_node_free(val); } if (lilv_plugin_has_latency(p)) { uint32_t latency_port = lilv_plugin_get_latency_port_index(p); printf("\tHas latency: yes, reported by port %u\n", latency_port); } else { printf("\tHas latency: no\n"); } printf("\tBundle: %s\n", lilv_node_as_uri(lilv_plugin_get_bundle_uri(p))); const LilvNode* binary_uri = lilv_plugin_get_library_uri(p); if (binary_uri) { printf("\tBinary: %s\n", lilv_node_as_uri(lilv_plugin_get_library_uri(p))); } LilvUIs* uis = lilv_plugin_get_uis(p); if (lilv_nodes_size(uis) > 0) { printf("\tUIs:\n"); LILV_FOREACH (uis, i, uis) { const LilvUI* ui = lilv_uis_get(uis, i); printf("\t\t%s\n", lilv_node_as_uri(lilv_ui_get_uri(ui))); const char* binary = lilv_node_as_uri(lilv_ui_get_binary_uri(ui)); const LilvNodes* types = lilv_ui_get_classes(ui); LILV_FOREACH (nodes, t, types) { printf("\t\t\tClass: %s\n", lilv_node_as_uri(lilv_nodes_get(types, t))); } if (binary) { printf("\t\t\tBinary: %s\n", binary); } printf("\t\t\tBundle: %s\n", lilv_node_as_uri(lilv_ui_get_bundle_uri(ui))); } } lilv_uis_free(uis); printf("\tData URIs: "); const LilvNodes* data_uris = lilv_plugin_get_data_uris(p); bool first = true; LILV_FOREACH (nodes, i, data_uris) { if (!first) { printf("\n\t "); } printf("%s", lilv_node_as_uri(lilv_nodes_get(data_uris, i))); first = false; } printf("\n"); /* Required Features */ LilvNodes* features = lilv_plugin_get_required_features(p); if (features) { printf("\tRequired Features: "); } first = true; LILV_FOREACH (nodes, i, features) { if (!first) { printf("\n\t "); } printf("%s", lilv_node_as_uri(lilv_nodes_get(features, i))); first = false; } if (features) { printf("\n"); } lilv_nodes_free(features); /* Optional Features */ features = lilv_plugin_get_optional_features(p); if (features) { printf("\tOptional Features: "); } first = true; LILV_FOREACH (nodes, i, features) { if (!first) { printf("\n\t "); } printf("%s", lilv_node_as_uri(lilv_nodes_get(features, i))); first = false; } if (features) { printf("\n"); } lilv_nodes_free(features); /* Extension Data */ LilvNodes* data = lilv_plugin_get_extension_data(p); if (data) { printf("\tExtension Data: "); } first = true; LILV_FOREACH (nodes, i, data) { if (!first) { printf("\n\t "); } printf("%s", lilv_node_as_uri(lilv_nodes_get(data, i))); first = false; } if (data) { printf("\n"); } lilv_nodes_free(data); /* Presets */ LilvNodes* presets = lilv_plugin_get_related(p, preset_class); if (presets) { printf("\tPresets: \n"); } LILV_FOREACH (nodes, i, presets) { const LilvNode* preset = lilv_nodes_get(presets, i); lilv_world_load_resource(world, preset); LilvNodes* titles = lilv_world_find_nodes(world, preset, label_pred, NULL); if (titles) { const LilvNode* title = lilv_nodes_get_first(titles); printf("\t %s\n", lilv_node_as_string(title)); lilv_nodes_free(titles); } else { fprintf(stderr, "Preset <%s> has no rdfs:label\n", lilv_node_as_string(lilv_nodes_get(presets, i))); } } lilv_nodes_free(presets); /* Ports */ const uint32_t num_ports = lilv_plugin_get_num_ports(p); float* mins = (float*)calloc(num_ports, sizeof(float)); float* maxes = (float*)calloc(num_ports, sizeof(float)); float* defaults = (float*)calloc(num_ports, sizeof(float)); lilv_plugin_get_port_ranges_float(p, mins, maxes, defaults); for (uint32_t i = 0; i < num_ports; ++i) { print_port(p, i, mins, maxes, defaults); } free(mins); free(maxes); free(defaults); } static void print_version(void) { printf("lv2info (lilv) " LILV_VERSION "\n" "Copyright 2007-2021 David Robillard \n" "License: \n" "This is free software: you are free to change and redistribute it.\n" "There is NO WARRANTY, to the extent permitted by law.\n"); } static void print_usage(void) { printf("Usage: lv2info [OPTION]... PLUGIN_URI\n" "Print information about an installed LV2 plugin.\n\n" " -V, --version Display version information and exit\n" " -h, --help Display this help and exit\n" " -m FILE Add record of plugin to manifest FILE\n" " -p FILE Write Turtle description of plugin to FILE\n\n" "For -p and -m, Turtle files are appended to (not overwritten),\n" "and @prefix directives are only written if the file was empty.\n" "This allows several plugins to be added to a single file.\n"); } int main(int argc, char** argv) { if (argc == 1) { print_usage(); return 1; } const char* plugin_file = NULL; const char* manifest_file = NULL; const char* plugin_uri = NULL; for (int i = 1; i < argc; ++i) { if (!strcmp(argv[i], "-V") || !strcmp(argv[i], "--version")) { print_version(); return 0; } if (!strcmp(argv[i], "-h") || !strcmp(argv[i], "--help")) { print_usage(); return 0; } if (!strcmp(argv[i], "-p")) { plugin_file = argv[++i]; } else if (!strcmp(argv[i], "-m")) { manifest_file = argv[++i]; } else if (argv[i][0] == '-') { print_usage(); return 1; } else if (i == argc - 1) { plugin_uri = argv[i]; } } int ret = 0; LilvWorld* world = lilv_world_new(); lilv_world_load_all(world); LilvNode* uri = lilv_new_uri(world, plugin_uri); if (!uri) { fprintf(stderr, "Invalid plugin URI\n"); lilv_world_free(world); return 1; } applies_to_pred = lilv_new_uri(world, LV2_CORE__appliesTo); control_class = lilv_new_uri(world, LILV_URI_CONTROL_PORT); event_class = lilv_new_uri(world, LILV_URI_EVENT_PORT); group_pred = lilv_new_uri(world, LV2_PORT_GROUPS__group); label_pred = lilv_new_uri(world, LILV_NS_RDFS "label"); preset_class = lilv_new_uri(world, LV2_PRESETS__Preset); designation_pred = lilv_new_uri(world, LV2_CORE__designation); supports_event_pred = lilv_new_uri(world, LV2_EVENT__supportsEvent); const LilvPlugins* plugins = lilv_world_get_all_plugins(world); const LilvPlugin* p = lilv_plugins_get_by_uri(plugins, uri); if (p && plugin_file) { LilvNode* base = lilv_new_file_uri(world, NULL, plugin_file); FILE* plugin_fd = fopen(plugin_file, "a"); if (plugin_fd) { lilv_plugin_write_description(world, p, base, plugin_fd); fclose(plugin_fd); } else { fprintf(stderr, "error: Failed to open %s\n", plugin_file); } if (manifest_file) { FILE* manifest_fd = fopen(manifest_file, "a"); if (manifest_fd) { lilv_plugin_write_manifest_entry( world, p, base, manifest_fd, plugin_file); fclose(manifest_fd); } else { fprintf(stderr, "error: Failed to open %s\n", manifest_file); } } lilv_node_free(base); } else if (p) { print_plugin(world, p); } else { fprintf(stderr, "Plugin not found.\n"); } ret = (p != NULL ? 0 : -1); lilv_node_free(uri); lilv_node_free(supports_event_pred); lilv_node_free(designation_pred); lilv_node_free(preset_class); lilv_node_free(label_pred); lilv_node_free(group_pred); lilv_node_free(event_class); lilv_node_free(control_class); lilv_node_free(applies_to_pred); lilv_world_free(world); return ret; }