From be95afee123c169b083049c2f441d3860a12fea0 Mon Sep 17 00:00:00 2001 From: David Robillard Date: Fri, 20 Apr 2007 03:57:41 +0000 Subject: Clean up API, hide more methods that should not be exposed to user. Document performance of most methods. Clean up and clarify documentation. git-svn-id: http://svn.drobilla.net/lad/slv2@460 a436a847-0d15-0410-975c-d299462d15a1 --- data/lv2.ttl | 3 +- doc/reference.doxygen.in | 2 +- hosts/lv2_jack_host.c | 7 +- hosts/lv2_simple_jack_host.c | 203 +++++++++++++++++++++++++++++++++++++------ slv2/lv2.h | 2 +- slv2/plugin.h | 60 +++++++++---- slv2/pluginlist.h | 36 +++----- slv2/port.h | 18 ++++ slv2/stringlist.h | 11 ++- slv2/types.h | 7 ++ slv2/world.h | 75 ++++++++-------- src/plugin.c | 12 ++- src/plugininstance.c | 9 +- src/pluginlist.c | 8 +- src/private_types.h | 27 ++++-- src/world.c | 57 +++++++----- utils/lv2_inspect.c | 50 ++++++----- utils/lv2_list.c | 2 +- 18 files changed, 421 insertions(+), 168 deletions(-) diff --git a/data/lv2.ttl b/data/lv2.ttl index d3ed0b3..d0d9f94 100644 --- a/data/lv2.ttl +++ b/data/lv2.ttl @@ -167,10 +167,9 @@ The plugin author MUST change the plugin URI if a port index is changed. """ . :symbol a rdf:Property ; - rdfs:domain :Port ; rdfs:label "symbol" ; rdfs:comment """ -A short name used to identify the port in an easily machine and human readable way. +A short name used as a machine and human readable identifier. The first character must be one of _, a-z or A-Z and subsequenct characters can be from _, a-z, A-Z and 0-9. diff --git a/doc/reference.doxygen.in b/doc/reference.doxygen.in index ce9cd2d..ac744a9 100644 --- a/doc/reference.doxygen.in +++ b/doc/reference.doxygen.in @@ -421,7 +421,7 @@ WARN_LOGFILE = INPUT = @top_srcdir@/doc/mainpage.dox \ @top_srcdir@/slv2/types.h \ - @top_srcdir@/slv2/model.h \ + @top_srcdir@/slv2/world.h \ @top_srcdir@/slv2/pluginlist.h \ @top_srcdir@/slv2/stringlist.h \ @top_srcdir@/slv2/plugin.h \ diff --git a/hosts/lv2_jack_host.c b/hosts/lv2_jack_host.c index a0bca74..4a9995f 100644 --- a/hosts/lv2_jack_host.c +++ b/hosts/lv2_jack_host.c @@ -74,6 +74,7 @@ main(int argc, char** argv) fprintf(stderr, "\nYou must specify a plugin URI to load.\n"); fprintf(stderr, "\nKnown plugins:\n\n"); list_plugins(plugins); + slv2_world_free(world); return EXIT_FAILURE; } @@ -82,7 +83,7 @@ main(int argc, char** argv) if (!host.plugin) { fprintf(stderr, "Failed to find plugin %s.\n", plugin_uri); - slv2_plugins_free(plugins); + slv2_world_free(world); return EXIT_FAILURE; } @@ -126,7 +127,7 @@ main(int argc, char** argv) /* Deactivate plugin and JACK */ slv2_instance_free(host.instance); - slv2_plugins_free(plugins); + slv2_plugins_free(world, plugins); printf("Shutting down JACK.\n"); for (unsigned long i=0; i < host.num_ports; ++i) { @@ -140,6 +141,7 @@ main(int argc, char** argv) } jack_client_close(host.jack_client); + slv2_plugins_free(world, plugins); slv2_world_free(world); return 0; @@ -166,7 +168,6 @@ void create_port(struct JackHost* host, uint32_t port_index) { - //struct Port* port = (Port*)malloc(sizeof(Port)); struct Port* const port = &host->ports[port_index]; port->class = SLV2_UNKNOWN_PORT_CLASS; diff --git a/hosts/lv2_simple_jack_host.c b/hosts/lv2_simple_jack_host.c index 0b938e3..d0fabc1 100644 --- a/hosts/lv2_simple_jack_host.c +++ b/hosts/lv2_simple_jack_host.c @@ -20,49 +20,200 @@ #include #include #include +#include + + +/** This program's data */ +struct JackHost { + jack_client_t* jack_client; /**< Jack client */ + SLV2Plugin plugin; /**< Plugin "class" (actually just a few strings) */ + SLV2Instance instance; /**< Plugin "instance" (loaded shared lib) */ + uint32_t num_ports; /**< Size of the two following arrays: */ + jack_port_t** jack_ports; /**< For audio ports, otherwise NULL */ + float* controls; /**< For control ports, otherwise 0.0f */ +}; + + +void die(const char* msg); +void create_port(struct JackHost* host, uint32_t port_index); +int jack_process_cb(jack_nframes_t nframes, void* data); +void list_plugins(SLV2Plugins list); + int -main(/*int argc, char** argv*/) +main(int argc, char** argv) { + struct JackHost host; + host.jack_client = NULL; + host.num_ports = 0; + host.jack_ports = NULL; + host.controls = NULL; + + /* Find all installed plugins */ SLV2World world = slv2_world_new(); slv2_world_load_all(world); + SLV2Plugins plugins = slv2_world_get_all_plugins(world); + + /* Find the plugin to run */ + const char* plugin_uri = (argc == 2) ? argv[1] : NULL; + if (!plugin_uri) { + fprintf(stderr, "\nYou must specify a plugin URI to load.\n"); + fprintf(stderr, "\nKnown plugins:\n\n"); + list_plugins(plugins); + slv2_world_free(world); + return EXIT_FAILURE; + } - /*printf("********** All plugins **********\n"); + printf("URI:\t%s\n", plugin_uri); + host.plugin = slv2_plugins_get_by_uri(plugins, plugin_uri); + + if (!host.plugin) { + fprintf(stderr, "Failed to find plugin %s.\n", plugin_uri); + slv2_world_free(world); + return EXIT_FAILURE; + } - SLV2Plugins plugins = slv2_model_get_all_plugins(model); + /* Get the plugin's name */ + char* name = slv2_plugin_get_name(host.plugin); + printf("Name:\t%s\n", name); + + /* Connect to JACK (with plugin name as client name) */ + host.jack_client = jack_client_open(name, JackNullOption, NULL); + free(name); + if (!host.jack_client) + die("Failed to connect to JACK."); + else + printf("Connected to JACK.\n"); + + /* Instantiate the plugin */ + host.instance = slv2_plugin_instantiate( + host.plugin, jack_get_sample_rate(host.jack_client), NULL); + if (!host.instance) + die("Failed to instantiate plugin.\n"); + else + printf("Succesfully instantiated plugin.\n"); - for (unsigned i=0; i < slv2_plugins_size(plugins); ++i) { - SLV2Plugin p = slv2_plugins_get_at(plugins, i); - printf("Plugin: %s\n", slv2_plugin_get_uri(p)); + jack_set_process_callback(host.jack_client, &jack_process_cb, (void*)(&host)); + + /* Create ports */ + host.num_ports = slv2_plugin_get_num_ports(host.plugin); + host.jack_ports = calloc((size_t)host.num_ports, sizeof(jack_port_t*)); + host.controls = calloc((size_t)host.num_ports, sizeof(float*)); + + for (uint32_t i=0; i < host.num_ports; ++i) + create_port(&host, i); + + /* Activate plugin and JACK */ + slv2_instance_activate(host.instance); + jack_activate(host.jack_client); + + /* Run */ + printf("Press enter to quit: "); + getc(stdin); + printf("\n"); + + /* Deactivate plugin and JACK */ + slv2_instance_free(host.instance); + + printf("Shutting down JACK.\n"); + for (unsigned long i=0; i < host.num_ports; ++i) { + if (host.jack_ports[i] != NULL) { + jack_port_unregister(host.jack_client, host.jack_ports[i]); + host.jack_ports[i] = NULL; + } } + jack_client_close(host.jack_client); + + slv2_world_free(world); + + return 0; +} + - slv2_plugins_free(plugins);*/ +/** Abort and exit on error */ +void +die(const char* msg) +{ + fprintf(stderr, "%s\n", msg); + exit(EXIT_FAILURE); +} + + +/** Creates a port and connects the plugin instance to it's data location. + * + * For audio ports, creates a jack port and connects plugin port to buffer. + * + * For control ports, sets controls array to default value and connects plugin + * port to that element. + */ +void +create_port(struct JackHost* host, + uint32_t index) +{ + SLV2Port port = slv2_plugin_get_port_by_index(host->plugin, index); + + /* Get the port symbol (label) for console printing */ + char* symbol = slv2_port_get_symbol(host->plugin, port); + /* Initialize the port array elements */ + host->jack_ports[index] = NULL; + host->controls[index] = 0.0f; - printf("********** Plugins with MIDI input **********\n"); + /* Get the 'class' of the port (control input, audio output, etc) */ + SLV2PortClass class = slv2_port_get_class(host->plugin, port); - /*const char* query = - "PREFIX : \n" - //"PREFIX llext: \n" - "SELECT DISTINCT ?plugin WHERE {\n" - " ?plugin a :Plugin ;\n" - " :port ?port .\n" - " ?port :symbol \"in\". \n" - //" :port [ a llext:MidiPort; a :InputPort ] .\n" - "}\n"; + /* Connect the port based on it's 'class' */ + switch (class) { + case SLV2_CONTROL_INPUT: + host->controls[index] = slv2_port_get_default_value(host->plugin, port); + slv2_instance_connect_port(host->instance, index, &host->controls[index]); + printf("Set %s to %f\n", symbol, host->controls[index]); + break; + case SLV2_CONTROL_OUTPUT: + slv2_instance_connect_port(host->instance, index, &host->controls[index]); + break; + case SLV2_AUDIO_INPUT: + host->jack_ports[index] = jack_port_register(host->jack_client, + symbol, JACK_DEFAULT_AUDIO_TYPE, JackPortIsInput, 0); + break; + case SLV2_AUDIO_OUTPUT: + host->jack_ports[index] = jack_port_register(host->jack_client, + symbol, JACK_DEFAULT_AUDIO_TYPE, JackPortIsOutput, 0); + break; + default: + // Simple examples don't have to be robust :) + die("ERROR: Unknown port type, aborting messily!"); + } - SLV2Plugins plugins = slv2_model_get_plugins_by_query(model, query); + free(symbol); +} - for (unsigned i=0; i < slv2_plugins_size(plugins); ++i) { - SLV2Plugin p = slv2_plugins_get_at(plugins, i); - printf("Plugin: %s\n", slv2_plugin_get_uri(p)); - } - - slv2_plugins_free(plugins); - */ - slv2_world_free(world); +/** Jack process callback. */ +int +jack_process_cb(jack_nframes_t nframes, void* data) +{ + struct JackHost* host = (struct JackHost*)data; + + /* Connect plugin ports directly to JACK buffers */ + for (uint32_t i=0; i < host->num_ports; ++i) + if (host->jack_ports[i] != NULL) + slv2_instance_connect_port(host->instance, i, + jack_port_get_buffer(host->jack_ports[i], nframes)); + /* Run plugin for this cycle */ + slv2_instance_run(host->instance, nframes); + return 0; } + + +void +list_plugins(SLV2Plugins list) +{ + for (unsigned i=0; i < slv2_plugins_size(list); ++i) { + SLV2Plugin p = slv2_plugins_get_at(list, i); + printf("%s\n", slv2_plugin_get_uri(p)); + } +} diff --git a/slv2/lv2.h b/slv2/lv2.h index 10ea542..3e7b641 100644 --- a/slv2/lv2.h +++ b/slv2/lv2.h @@ -113,7 +113,7 @@ extern "C" { * * This plugin handle indicates a particular instance of the plugin * concerned. It is valid to compare this to NULL (0 for C++) but - * otherwise the host MUST not attempt to interpret it. The plugin + * otherwise the host MUST NOT attempt to interpret it. The plugin * may use it to reference internal instance data. */ typedef void * LV2_Handle; diff --git a/slv2/plugin.h b/slv2/plugin.h index 47cb9ee..30394d3 100644 --- a/slv2/plugin.h +++ b/slv2/plugin.h @@ -53,11 +53,14 @@ extern "C" { * testing utilities, etc. * * \return true if \a plugin is valid. + * + * Time = Query */ bool slv2_plugin_verify(SLV2Plugin plugin); +#if 0 /** Duplicate a plugin. * * Use this if you want to keep an SLV2Plugin around but free the list it came @@ -68,15 +71,7 @@ slv2_plugin_verify(SLV2Plugin plugin); */ SLV2Plugin slv2_plugin_duplicate(SLV2Plugin plugin); - - -/** Free an SLV2Plugin. - * - * This should NEVER be used on a plugin contained in a Plugins. Use this - * only with plugins created with slv2_plugin_duplicate. - */ -void -slv2_plugin_free(SLV2Plugin plugin); +#endif /** Get the URI of \a plugin. @@ -92,6 +87,8 @@ slv2_plugin_free(SLV2Plugin plugin); * MUST have a different URI than it's predecessor. * * \return a shared string which must not be modified or free()'d. + * + * Time = O(1) */ const char* slv2_plugin_get_uri(SLV2Plugin plugin); @@ -102,8 +99,10 @@ slv2_plugin_get_uri(SLV2Plugin plugin); * Note this always returns fully qualified URIs. If you want local * filesystem paths, use slv2_uri_to_path. * - * \return a complete URL eg. "file:///usr/foo/SomeBundle.lv2/someplug.ttl", - * which is shared and must not be modified or free()'d. + * \return a list of complete URLs eg. "file:///foo/ABundle.lv2/aplug.ttl", + * which is shared and must not be modified or freed. + * + * Time = O(1) */ SLV2Strings slv2_plugin_get_data_uris(SLV2Plugin plugin); @@ -113,8 +112,10 @@ slv2_plugin_get_data_uris(SLV2Plugin plugin); * * Note this always returns a fully qualified URI. If you want a local * filesystem path, use slv2_uri_to_path. + * + * \return a shared string which must not be modified or freed. * - * \return a shared string which must not be modified or free()'d. + * Time = O(1) */ const char* slv2_plugin_get_library_uri(SLV2Plugin plugin); @@ -124,8 +125,10 @@ slv2_plugin_get_library_uri(SLV2Plugin plugin); /** Get the name of \a plugin. * * This is guaranteed to return the untranslated name (the doap:name in the - * data file without a language tag). Returned value must be free()'d by + * data file without a language tag). Returned value must be freed by * the caller. + * + * Time = Query */ char* slv2_plugin_get_name(SLV2Plugin plugin); @@ -141,6 +144,8 @@ slv2_plugin_get_name(SLV2Plugin plugin); * sensibly represented as an SLV2Strings (e.g. blank nodes). * * Return value must be freed by caller with slv2_strings_free. + * + * Time = Query */ SLV2Strings slv2_plugin_get_value(SLV2Plugin p, @@ -160,6 +165,8 @@ slv2_plugin_get_value(SLV2Plugin p, * sensibly represented as an SLV2Strings (e.g. blank nodes). * * Return value must be freed by caller with slv2_strings_free. + * + * Time = Query */ SLV2Strings slv2_plugin_get_value_for_subject(SLV2Plugin p, @@ -174,6 +181,8 @@ slv2_plugin_get_value_for_subject(SLV2Plugin p, * not what you want, see slv2_plugin_get_hints). * * Return value must be freed by caller with slv2_value_free. + * + * Time = Query */ SLV2Strings slv2_plugin_get_properties(SLV2Plugin p); @@ -185,12 +194,16 @@ slv2_plugin_get_properties(SLV2Plugin p); * ignored and the plugin will still function correctly. * * Return value must be freed by caller with slv2_value_free. + * + * Time = Query */ SLV2Strings slv2_plugin_get_hints(SLV2Plugin p); /** Get the number of ports on this plugin. + * + * Time = O(1) */ uint32_t slv2_plugin_get_num_ports(SLV2Plugin p); @@ -200,6 +213,8 @@ slv2_plugin_get_num_ports(SLV2Plugin p); * * The index of the latency port can be found with slv2_plugin_get_latency_port * ONLY if this function returns true. + * + * Time = Query */ bool slv2_plugin_has_latency(SLV2Plugin p); @@ -214,6 +229,8 @@ slv2_plugin_has_latency(SLV2Plugin p); * Any plugin that introduces unwanted latency that should be compensated for * (by hosts with the ability/need) MUST provide this port, which is a control * rate output port that reports the latency for each cycle in frames. + * + * Time = Query */ uint32_t slv2_plugin_get_latency_port(SLV2Plugin p); @@ -222,6 +239,8 @@ slv2_plugin_get_latency_port(SLV2Plugin p); /** Get a plugin's supported host features / extensions. * * This returns a list of all supported features (both required and optional). + * + * Time = Query */ SLV2Strings slv2_plugin_get_supported_features(SLV2Plugin p); @@ -231,6 +250,8 @@ slv2_plugin_get_supported_features(SLV2Plugin p); * * All feature URI's returned by this call MUST be passed to the plugin's * instantiate method for the plugin to instantiate successfully. + * + * Time = Query */ SLV2Strings slv2_plugin_get_required_features(SLV2Plugin p); @@ -241,6 +262,8 @@ slv2_plugin_get_required_features(SLV2Plugin p); * If the feature URI's returned by this method are passed to the plugin's * instantiate method, those features will be used by the function, otherwise * the plugin will act as it would if it did not support that feature at all. + * + * Time = Query */ SLV2Strings slv2_plugin_get_optional_features(SLV2Plugin p); @@ -248,9 +271,12 @@ slv2_plugin_get_optional_features(SLV2Plugin p); /** Query a plugin for a single variable. * + * \param plugin The plugin to query. * \param sparql_str A SPARQL SELECT query. * \param variable The variable to return results for. * \return All matches for \a variable. + * + * Time = Query */ SLV2Strings slv2_plugin_simple_query(SLV2Plugin plugin, @@ -260,7 +286,10 @@ slv2_plugin_simple_query(SLV2Plugin plugin, /** Query a plugin and return the number of results found. * + * \param plugin The plugin to query. * \param sparql_str A SPARQL SELECT query. + * + * Time = Query */ unsigned slv2_plugin_query_count(SLV2Plugin plugin, @@ -272,7 +301,7 @@ slv2_plugin_query_count(SLV2Plugin plugin, * To perform multiple calls on a port, the returned value should * be cached and used repeatedly. * - * O(1) + * Time = O(1) */ SLV2Port slv2_plugin_get_port_by_index(SLV2Plugin plugin, @@ -284,14 +313,13 @@ slv2_plugin_get_port_by_index(SLV2Plugin plugin, * To perform multiple calls on a port, the returned value should * be cached and used repeatedly. * - * O(num_ports) + * Time = O(n) */ SLV2Port slv2_plugin_get_port_by_symbol(SLV2Plugin plugin, const char* symbol); - /** @} */ #ifdef __cplusplus diff --git a/slv2/pluginlist.h b/slv2/pluginlist.h index cd1bbf8..a2978e1 100644 --- a/slv2/pluginlist.h +++ b/slv2/pluginlist.h @@ -39,27 +39,14 @@ typedef void* SLV2Plugins; */ -/** Create a new, empty plugin list. - * - * Returned object must be freed with slv2_plugins_free. - */ -SLV2Plugins -slv2_plugins_new(); - - /** Free a plugin list. * - * Note that all plugins in the list (eg those returned by the get_plugin - * functions) will be deleted as well. It is expected that hosts will - * keep the plugin list allocated until they are done with their plugins. - * If you want to keep a plugin around, but free the list it came from, you - * will have to copy it with slv2_plugin_duplicate(). - * - * \a list is invalid after this call (though it may be used again after a - * "list = slv2_plugins_new()") + * Freeing a plugin list does not destroy the plugins it contains (plugins + * are owned by the world). \a list is invalid after this call. */ void -slv2_plugins_free(SLV2Plugins list); +slv2_plugins_free(SLV2World world, + SLV2Plugins list); /** Get the number of plugins in the list. @@ -73,7 +60,7 @@ slv2_plugins_size(SLV2Plugins list); * Return value is shared (stored in \a list) and must not be freed or * modified by the caller in any way. * - * O(log2(n)) + * Time = O(log2(n)) * * \return NULL if plugin with \a url not found in \a list. */ @@ -84,15 +71,12 @@ slv2_plugins_get_by_uri(SLV2Plugins list, /** Get a plugin from the list by index. * - * \a index has no significance. Any \a index not less than - * slv2list_get_length(list) will return NULL. All plugins in a list can - * thus be easily enumerated by repeated calls to this function starting - * with \a index 0. - * - * Return value is shared (stored in \a list) and must not be freed or - * modified by the caller in any way. + * \a index has no significance other than as an index into this list. + * Any \a index not less than slv2_list_get_length(list) will return NULL, + * so all plugins in a list can be enumerated by repeated calls + * to this function starting with \a index = 0. * - * O(1) + * Time = O(1) * * \return NULL if \a index out of range. */ diff --git a/slv2/port.h b/slv2/port.h index 13ac5c1..d415f84 100644 --- a/slv2/port.h +++ b/slv2/port.h @@ -34,6 +34,8 @@ extern "C" { /** Port equivalent to slv2_plugin_get_value. + * + * Time = Query */ SLV2Strings slv2_port_get_value(SLV2Plugin plugin, @@ -42,6 +44,8 @@ slv2_port_get_value(SLV2Plugin plugin, /** Port equivalent to slv2_plugin_get_properties. + * + * Time = Query */ SLV2Strings slv2_port_get_properties(SLV2Plugin plugin, @@ -49,6 +53,8 @@ slv2_port_get_properties(SLV2Plugin plugin, /** Port equivalent to slv2_plugin_get_hints. + * + * Time = Query */ SLV2Strings slv2_port_get_hints(SLV2Plugin plugin, @@ -61,6 +67,8 @@ slv2_port_get_hints(SLV2Plugin plugin, * Returned string must be free()'d by caller. * * \return NULL when index is out of range + * + * Time = Query */ char* slv2_port_get_symbol(SLV2Plugin plugin, @@ -71,6 +79,8 @@ slv2_port_get_symbol(SLV2Plugin plugin, * This is guaranteed to return the untranslated name (the doap:name in the * data file without a language tag). Returned value must be free()'d by * the caller. + * + * Time = Query */ char* slv2_port_get_name(SLV2Plugin plugin, @@ -78,6 +88,8 @@ slv2_port_get_name(SLV2Plugin plugin, /** Get the class (input/output, data type, rate...) of a port. + * + * Time = Query */ SLV2PortClass slv2_port_get_class(SLV2Plugin plugin, @@ -87,6 +99,8 @@ slv2_port_get_class(SLV2Plugin plugin, /** Get the default value of a port. * * Only valid for ports with a data type of lv2:float. + * + * Time = Query */ float slv2_port_get_default_value(SLV2Plugin plugin, @@ -96,6 +110,8 @@ slv2_port_get_default_value(SLV2Plugin plugin, /** Get the minimum value of a port. * * Only valid for ports with a data type of lv2:float. + * + * Time = Query */ float slv2_port_get_minimum_value(SLV2Plugin plugin, @@ -105,6 +121,8 @@ slv2_port_get_minimum_value(SLV2Plugin plugin, /** Get the maximum value of a port. * * Only valid for ports with a data type of lv2:float. + * + * Time = Query */ float slv2_port_get_maximum_value(SLV2Plugin plugin, diff --git a/slv2/stringlist.h b/slv2/stringlist.h index 0c42318..eb613d6 100644 --- a/slv2/stringlist.h +++ b/slv2/stringlist.h @@ -53,18 +53,23 @@ slv2_strings_size(SLV2Strings list); /** Get a string from a string list at the given index. * * @return the element at \a index, or NULL if index is out of range. + * + * Time = O(1) */ const char* slv2_strings_get_at(SLV2Strings list, unsigned index); -/** Return whether \a list contains \a uri. +/** Return whether \a list contains \a string. + * + * Time = O(n) */ bool -slv2_strings_contains(SLV2Strings list, const char* uri); +slv2_strings_contains(SLV2Strings list, const char* string); -/** Free a string list. */ +/** Free a string list. + */ void slv2_strings_free(SLV2Strings); diff --git a/slv2/types.h b/slv2/types.h index f0f9b7f..c22b2be 100644 --- a/slv2/types.h +++ b/slv2/types.h @@ -43,12 +43,19 @@ typedef enum _PortClass { SLV2_MIDI_OUTPUT /**< MIDI output (LL extension) */ } SLV2PortClass; + +/** A port on a plugin. Opaque, but valid to compare to NULL. */ typedef struct _Port* SLV2Port; + +/** A plugin. Opaque, but valid to compare to NULL. */ typedef struct _Plugin* SLV2Plugin; + +/** The world. Opaque, but valid to compare to NULL. */ typedef struct _World* SLV2World; + #ifdef __cplusplus } #endif diff --git a/slv2/world.h b/slv2/world.h index 4f5b252..64f0fc2 100644 --- a/slv2/world.h +++ b/slv2/world.h @@ -28,17 +28,19 @@ extern "C" { /** \defgroup world Library context, data loading, etc. * - * These functions deal with the data model which other SLV2 methods - * operate with. The world contains an in-memory cache of all bundles - * manifest.ttl files, from which you can quickly query plugins, etc. + * The "world" represents all library state, and the data found in bundles' + * manifest.ttl (ie it is an in-memory index of all things LV2 found). + * Plugins (and plugin extensions) and the LV2 specification (and LV2 + * extensions) itself can be queried from the world for use. * * Normal hosts which just want to easily load plugins by URI are strongly - * recommended to simply find all installed data in the recommended way with - * \ref slv2_world_load_all rather than find and load bundles manually. - * - * Functions are provided for hosts that wish to access bundles explicitly and - * individually for some reason, this is intended for hosts which are tied to - * a specific bundle (shipped with the application). + * recommended to simply call \ref slv2_world_load_all to find all installed + * data in the recommended way. + * + * Normal hosts should NOT have to refer to bundles directly under normal + * circumstances. However, functions are provided to load individual bundles + * explicitly, intended for hosts which depend on a specific bundle + * (which is shipped with the application). * * @{ */ @@ -60,7 +62,7 @@ void slv2_world_free(SLV2World world); -/** Load all installed LV2 bundles on the system +/** Load all installed LV2 bundles on the system. * * This is the recommended way for hosts to load LV2 data. It does the most * reasonable thing to find all installed plugins, extensions, etc. on the @@ -70,60 +72,63 @@ slv2_world_free(SLV2World world); * * Use of other functions for loading bundles is \em highly discouraged * without a special reason to do so - use this one. - */ -void -slv2_world_load_all(SLV2World world); - - -/** Load all bundles found in \a search_path. * - * \param search_path A colon-delimited list of directories. These directories - * should contain LV2 bundle directories (ie the search path is a list of - * parent directories of bundles, not a list of bundle directories). - * - * If \a search_path is NULL, \a world will be unmodified. - * Use of this function is \b not recommended. Use \ref slv2_world_load_all. + * Time = Query */ void -slv2_world_load_path(SLV2World world, - const char* search_path); +slv2_world_load_all(SLV2World world); -/** Load a specific bundle into \a world. +/** Load a specific bundle. * - * \arg bundle_base_uri is a fully qualified URI to the bundle directory, + * \arg bundle_uri A fully qualified URI to the bundle directory, * with the trailing slash, eg. file:///usr/lib/lv2/someBundle/ * * Normal hosts should not use this function. * - * Hosts should not attach \em any long-term significance to bundle paths + * Hosts should \b never attach any long-term significance to bundle paths * as there are no guarantees they will remain consistent whatsoever. + * Plugins (and other things) are identified by URIs, \b not bundle or + * file names. + * * This function should only be used by apps which ship with a special - * bundle (which it knows exists at some path because they are part of - * the same package). + * bundle (which it knows exists at some path because the bundle is + * shipped with the application). + * + * Time = Query */ void slv2_world_load_bundle(SLV2World world, - const char* bundle_base_uri); + const char* bundle_uri); -/** Add all plugins present in \a world to \a list. +/** Return a list of all found plugins. + * + * The returned list contains just enough references to query + * or instantiate plugins. The data for a particular plugin will not be + * loaded into memory until a call to an slv2_plugin_* function results in + * a query (at which time the data is cached with the SLV2Plugin so future + * queries are very fast). * * Returned plugins contain a reference to this world, world must not be * destroyed until plugins are finished with. + * + * Time = O(1) */ SLV2Plugins slv2_world_get_all_plugins(SLV2World world); -/** Get plugins filtered by a user-defined filter function. +/** Return a list of found plugins filtered by a user-defined filter function. * - * All plugins in \a world that return true when passed to \a include - * (a pointer to a function that takes an SLV2Plugin and returns a bool) - * will be added to \a list. + * All plugins currently found in \a world that return true when passed to + * \a include (a pointer to a function which takes an SLV2Plugin and returns + * a bool) will be in the returned list. * * Returned plugins contain a reference to this world, world must not be * destroyed until plugins are finished with. + * + * Time = O(n * Time(include)) */ SLV2Plugins slv2_world_get_plugins_by_filter(SLV2World world, diff --git a/src/plugin.c b/src/plugin.c index b3577f2..722caa1 100644 --- a/src/plugin.c +++ b/src/plugin.c @@ -37,7 +37,7 @@ slv2_plugin_new(SLV2World world, librdf_uri* uri, const char* binary_uri) plugin->world = world; plugin->plugin_uri = librdf_new_uri_from_uri(uri); plugin->binary_uri = strdup(binary_uri); - plugin->data_uris = raptor_new_sequence((void (*)(void*))&raptor_free_uri, NULL); + plugin->data_uris = slv2_strings_new(); plugin->ports = raptor_new_sequence((void (*)(void*))&slv2_port_free, NULL); plugin->storage = NULL; plugin->rdf = NULL; @@ -69,7 +69,7 @@ slv2_plugin_free(SLV2Plugin p) p->storage = NULL; } - raptor_free_sequence(p->data_uris); + slv2_strings_free(p->data_uris); free(p); } @@ -81,6 +81,7 @@ slv2_plugin_query(SLV2Plugin plugin, const char* sparql_str); +/* SLV2Plugin slv2_plugin_duplicate(SLV2Plugin p) { @@ -105,6 +106,7 @@ slv2_plugin_duplicate(SLV2Plugin p) return result; } +*/ /** comparator for sorting */ @@ -136,9 +138,11 @@ slv2_plugin_load(SLV2Plugin p) } // Parse all the plugin's data files into RDF model - for (int i=0; i < raptor_sequence_size(p->data_uris); ++i) { - librdf_uri* data_uri = raptor_sequence_get_at(p->data_uris, i); + for (unsigned i=0; i < slv2_strings_size(p->data_uris); ++i) { + const char* data_uri_str = slv2_strings_get_at(p->data_uris, i); + librdf_uri* data_uri = librdf_new_uri(p->world->world, (const unsigned char*)data_uri_str); librdf_parser_parse_into_model(p->world->parser, data_uri, NULL, p->rdf); + librdf_free_uri(data_uri); } // Load ports diff --git a/src/plugininstance.c b/src/plugininstance.c index 6d957b6..013e33c 100644 --- a/src/plugininstance.c +++ b/src/plugininstance.c @@ -16,6 +16,8 @@ * 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. */ +#define _XOPEN_SOURCE 500 + #include #include #include @@ -65,7 +67,10 @@ slv2_plugin_instantiate(SLV2Plugin plugin, // Search for plugin by URI // FIXME: Kluge to get bundle path (containing directory of binary) - const char* const bundle_path = strrchr(plugin->binary_uri, '/') + 1; + char* bundle_path = strdup(plugin->binary_uri); + char* const bundle_path_end = strrchr(bundle_path, '/'); + if (bundle_path_end) + *(bundle_path_end+1) = '\0'; printf("Bundle path: %s\n", bundle_path); for (uint32_t i=0; 1; ++i) { @@ -95,6 +100,8 @@ slv2_plugin_instantiate(SLV2Plugin plugin, break; } } + + free(bundle_path); } assert(result); diff --git a/src/pluginlist.c b/src/pluginlist.c index 50bb2ad..d4cd058 100644 --- a/src/pluginlist.c +++ b/src/pluginlist.c @@ -36,14 +36,16 @@ SLV2Plugins slv2_plugins_new() { - return raptor_new_sequence((void (*)(void*))&slv2_plugin_free, NULL); + //return raptor_new_sequence((void (*)(void*))&slv2_plugin_free, NULL); + return raptor_new_sequence(NULL, NULL); } void -slv2_plugins_free(SLV2Plugins list) +slv2_plugins_free(SLV2World world, SLV2Plugins list) { - raptor_free_sequence(list); + if (list != world->plugins) + raptor_free_sequence(list); } #if 0 diff --git a/src/private_types.h b/src/private_types.h index 8f2f89d..48bed0a 100644 --- a/src/private_types.h +++ b/src/private_types.h @@ -37,6 +37,7 @@ struct _Port { //char* node_id; ///< RDF Node ID }; + SLV2Port slv2_port_new(uint32_t index, const char* symbol/*, const char* node_id*/); SLV2Port slv2_port_duplicate(SLV2Port port); void slv2_port_free(SLV2Port port); @@ -60,13 +61,16 @@ struct _Plugin { SLV2Plugin slv2_plugin_new(SLV2World world, librdf_uri* uri, const char* binary_uri); void slv2_plugin_load(SLV2Plugin p); +void slv2_plugin_free(SLV2Plugin plugin); -/** List of references to plugins available for loading */ -struct _PluginList { - size_t num_plugins; - struct _Plugin** plugins; -}; +/** Create a new, empty plugin list. + * + * Returned object must be freed with slv2_plugins_free. + */ +SLV2Plugins +slv2_plugins_new(); + /** Pimpl portion of SLV2Instance */ struct _InstanceImpl { @@ -84,6 +88,19 @@ struct _World { SLV2Plugins plugins; }; +/** Load all bundles found in \a search_path. + * + * \param search_path A colon-delimited list of directories. These directories + * should contain LV2 bundle directories (ie the search path is a list of + * parent directories of bundles, not a list of bundle directories). + * + * If \a search_path is NULL, \a world will be unmodified. + * Use of this function is \b not recommended. Use \ref slv2_world_load_all. + */ +void +slv2_world_load_path(SLV2World world, + const char* search_path); + #ifdef __cplusplus } diff --git a/src/world.c b/src/world.c index afb71ad..e10e7d4 100644 --- a/src/world.c +++ b/src/world.c @@ -59,7 +59,9 @@ slv2_world_free(SLV2World world) /*raptor_free_uri(slv2_ontology_uri); slv2_ontology_uri = NULL;*/ - slv2_plugins_free(world->plugins); + for (int i=0; i < raptor_sequence_size(world->plugins); ++i) + slv2_plugin_free(raptor_sequence_get_at(world->plugins, i)); + raptor_free_sequence(world->plugins); world->plugins = NULL; librdf_free_parser(world->parser); @@ -78,7 +80,25 @@ slv2_world_free(SLV2World world) } -/* private */ +void +slv2_world_load_bundle(SLV2World world, const char* bundle_uri_str) +{ + librdf_uri* bundle_uri = librdf_new_uri(world->world, + (const unsigned char*)bundle_uri_str); + + librdf_uri* manifest_uri = librdf_new_uri_relative_to_base( + bundle_uri, (const unsigned char*)"manifest.ttl"); + + librdf_parser_parse_into_model(world->parser, manifest_uri, NULL, world->model); + + librdf_free_uri(manifest_uri); + librdf_free_uri(bundle_uri); +} + + +/** Load all bundles under a directory. + * Private. + */ void slv2_world_load_directory(SLV2World world, const char* dir) { @@ -91,24 +111,17 @@ slv2_world_load_directory(SLV2World world, const char* dir) if (!strcmp(pfile->d_name, ".") || !strcmp(pfile->d_name, "..")) continue; - char* bundle_uri_str = slv2_strjoin("file://", dir, "/", pfile->d_name, "/", NULL); - librdf_uri* bundle_uri = librdf_new_uri(world->world, (unsigned char*)bundle_uri_str); + char* uri = slv2_strjoin("file://", dir, "/", pfile->d_name, "/", NULL); - DIR* bundle_dir = opendir(bundle_uri_str + 7); + // FIXME: Probably a better way to check if a dir exists + DIR* bundle_dir = opendir(uri + 7); if (bundle_dir != NULL) { closedir(bundle_dir); - - librdf_uri* manifest_uri = librdf_new_uri_relative_to_base( - bundle_uri, (const unsigned char*)"manifest.ttl"); - - librdf_parser_parse_into_model(world->parser, manifest_uri, NULL, world->model); - - librdf_free_uri(manifest_uri); + slv2_world_load_bundle(world, uri); } - free(bundle_uri_str); - librdf_free_uri(bundle_uri); + free(uri); } closedir(pdir); @@ -155,7 +168,8 @@ slv2_world_load_all(SLV2World world) { char* lv2_path = getenv("LV2_PATH"); - // Read all manifest files + /* 1. Read all manifest files into model */ + if (lv2_path) { slv2_world_load_path(world, lv2_path); } else { @@ -171,6 +185,8 @@ slv2_world_load_all(SLV2World world) } + /* 2. Query out things to cache */ + // Find all plugins and associated data files unsigned char* query_string = (unsigned char*) "PREFIX : \n" @@ -205,7 +221,8 @@ slv2_world_load_all(SLV2World world) plugin->world = world; // FIXME: check for duplicates - raptor_sequence_push(plugin->data_uris, librdf_new_uri_from_uri(data_uri)); + raptor_sequence_push(plugin->data_uris, + strdup((const char*)librdf_uri_as_string(data_uri))); raptor_sequence_push(world->plugins, plugin); @@ -267,16 +284,14 @@ slv2_world_get_all_plugins(SLV2World world) SLV2Plugins slv2_world_get_plugins_by_filter(SLV2World world, bool (*include)(SLV2Plugin)) { - SLV2Plugins all = slv2_world_get_all_plugins(world); SLV2Plugins result = slv2_plugins_new(); - for (int i=0; i < raptor_sequence_size(all); ++i) { - SLV2Plugin p = raptor_sequence_get_at(all, i); + for (int i=0; i < raptor_sequence_size(world->plugins); ++i) { + SLV2Plugin p = raptor_sequence_get_at(world->plugins, i); if (include(p)) - raptor_sequence_push(result, slv2_plugin_duplicate(p)); + raptor_sequence_push(result, p); } - slv2_plugins_free(all); return result; } diff --git a/utils/lv2_inspect.c b/utils/lv2_inspect.c index a6a949d..710b791 100644 --- a/utils/lv2_inspect.c +++ b/utils/lv2_inspect.c @@ -20,8 +20,6 @@ #include #include -// FIXME: remove -#include "../src/private_types.h" void print_port(SLV2Plugin p, uint32_t index) @@ -93,41 +91,53 @@ print_plugin(SLV2Plugin p) { char* str = NULL; - printf("<%s>\n", slv2_plugin_get_uri(p)); - - printf("FIXME: Data URIs\n"); - /*printf("\tData URIs:\n"); - SLV2Strings data_uris = slv2_plugin_get_data_uris(p); - for (unsigned i=0; i < slv2_strings_size(data_uris); ++i) - printf("\t\t%s\n", slv2_strings_get_at(data_uris, i)); - */ - - printf("\n\tLibrary URI: %s\n\n", slv2_plugin_get_library_uri(p)); - + printf("<%s>\n\n", slv2_plugin_get_uri(p)); + str = slv2_plugin_get_name(p); - printf("\tName: %s\n", str); + printf("\tName: %s\n\n", str); free(str); if (slv2_plugin_has_latency(p)) - printf("\tHas latency: yes\n"); + printf("\tHas latency: yes\n\n"); else - printf("\tHas latency: no\n"); + printf("\tHas latency: no\n\n"); + + printf("\tBinary: %s\n\n", slv2_plugin_get_library_uri(p)); + + printf("\tData URIs:\n"); + SLV2Strings data_uris = slv2_plugin_get_data_uris(p); + for (unsigned i=0; i < slv2_strings_size(data_uris); ++i) + printf("\t\t%s\n", slv2_strings_get_at(data_uris, i)); + + /* Properties */ - printf("\tProperties:\n"); SLV2Strings v = slv2_plugin_get_properties(p); + + if (slv2_strings_size(v) > 0) + printf("\n\tProperties:\n"); + for (unsigned i=0; i < slv2_strings_size(v); ++i) printf("\t\t%s\n", slv2_strings_get_at(v, i)); slv2_strings_free(v); - printf("\tHints:\n"); + + /* Hints */ + v = slv2_plugin_get_hints(p); + + if (slv2_strings_size(v) > 0) + printf("\n\tHints:\n"); + for (unsigned i=0; i < slv2_strings_size(v); ++i) printf("\t\t%s\n", slv2_strings_get_at(v, i)); slv2_strings_free(v); + + /* Ports */ + uint32_t num_ports = slv2_plugin_get_num_ports(p); - printf("\n\t# Ports: %d\n", num_ports); + //printf("\n\t# Ports: %d\n", num_ports); for (uint32_t i=0; i < num_ports; ++i) print_port(p, i); @@ -157,7 +167,7 @@ main(int argc, char** argv) return -1; } - slv2_plugins_free(plugins); + slv2_plugins_free(world, plugins); slv2_world_free(world); return (p != NULL ? 0 : -1); diff --git a/utils/lv2_list.c b/utils/lv2_list.c index 56afe4a..3e62c24 100644 --- a/utils/lv2_list.c +++ b/utils/lv2_list.c @@ -40,7 +40,7 @@ main()//int argc, char** argv) list_plugins(plugins); - slv2_plugins_free(plugins); + slv2_plugins_free(world, plugins); slv2_world_free(world); return 0; -- cgit v1.2.1