diff options
-rw-r--r-- | configure.ac | 17 | ||||
-rw-r--r-- | doc/reference.doxygen.in | 3 | ||||
-rw-r--r-- | hosts/Makefile.am | 6 | ||||
-rw-r--r-- | hosts/lv2_jack_host.c | 19 | ||||
-rw-r--r-- | hosts/lv2_simple_jack_host.c | 206 | ||||
-rw-r--r-- | slv2/Makefile.am | 7 | ||||
-rw-r--r-- | slv2/library.h | 48 | ||||
-rw-r--r-- | slv2/plugin.h | 40 | ||||
-rw-r--r-- | slv2/pluginlist.h | 80 | ||||
-rw-r--r-- | slv2/port.h | 35 | ||||
-rw-r--r-- | slv2/slv2.h | 7 | ||||
-rw-r--r-- | slv2/types.h | 16 | ||||
-rw-r--r-- | src/Makefile.am | 12 | ||||
-rw-r--r-- | src/library.c | 44 | ||||
-rw-r--r-- | src/plugin.c | 242 | ||||
-rw-r--r-- | src/plugininstance.c | 27 | ||||
-rw-r--r-- | src/pluginlist.c | 102 | ||||
-rw-r--r-- | src/port.c | 167 | ||||
-rw-r--r-- | src/private_types.h | 52 | ||||
-rw-r--r-- | src/query.c | 164 | ||||
-rw-r--r-- | utils/Makefile.am | 2 | ||||
-rw-r--r-- | utils/lv2_inspect.c | 52 | ||||
-rw-r--r-- | utils/lv2_list.c | 8 |
23 files changed, 631 insertions, 725 deletions
diff --git a/configure.ac b/configure.ac index f2d7050..04f6dcc 100644 --- a/configure.ac +++ b/configure.ac @@ -43,10 +43,23 @@ fi # Check for RAPTOR -PKG_CHECK_MODULES(RAPTOR, raptor >= 0.21) +#PKG_CHECK_MODULES(RAPTOR, raptor >= 0.21) # Check for RASQAL -PKG_CHECK_MODULES(RASQAL, rasqal >= 0.9.11) +#PKG_CHECK_MODULES(RASQAL, rasqal >= 0.9.11) + +# Check for Redland +#PKG_CHECK_MODULES(REDLAND, redland >= 1.0.0) +# No pkg-config?! Booo! +AC_CHECK_PROG(REDLAND_CONFIG, redland-config, redland-config) +if test "X$REDLAND_CONFIG" = X; then + AC_MSG_ERROR([SLV2 requires Redland (librdf), but redland-config not found.]) +else + REDLAND_CFLAGS=`$REDLAND_CONFIG --cflags` + REDLAND_LIBS=`$REDLAND_CONFIG --libs` + AC_SUBST(REDLAND_CFLAGS) + AC_SUBST(REDLAND_LIBS) +fi # Check for JACK build_jack="yes" diff --git a/doc/reference.doxygen.in b/doc/reference.doxygen.in index 8f4ebf1..ce9cd2d 100644 --- a/doc/reference.doxygen.in +++ b/doc/reference.doxygen.in @@ -421,6 +421,7 @@ WARN_LOGFILE = INPUT = @top_srcdir@/doc/mainpage.dox \ @top_srcdir@/slv2/types.h \ + @top_srcdir@/slv2/model.h \ @top_srcdir@/slv2/pluginlist.h \ @top_srcdir@/slv2/stringlist.h \ @top_srcdir@/slv2/plugin.h \ @@ -668,7 +669,7 @@ TOC_EXPAND = NO # top of each HTML page. The value NO (the default) enables the index and # the value YES disables it. -DISABLE_INDEX = YES +DISABLE_INDEX = NO # This tag can be used to set the number of enum values (range [1..20]) # that doxygen will group on one line in the generated HTML documentation. diff --git a/hosts/Makefile.am b/hosts/Makefile.am index a599bd4..068a25b 100644 --- a/hosts/Makefile.am +++ b/hosts/Makefile.am @@ -1,4 +1,4 @@ -AM_CFLAGS = -std=c99 -I$(top_srcdir) @RASQAL_CFLAGS@ +AM_CFLAGS = -std=c99 -I$(top_srcdir) @REDLAND_CFLAGS@ if WITH_JACK @@ -9,9 +9,9 @@ bin_PROGRAMS = lv2_jack_host lv2_simple_jack_host lv2_jack_host_CFLAGS = @JACK_CFLAGS@ $(AM_CFLAGS) lv2_jack_host_DEPENDENCIES = ../src/libslv2.la -lv2_jack_host_LDADD = ../src/libslv2.la @JACK_LIBS@ @RASQAL_LIBS@ +lv2_jack_host_LDADD = ../src/libslv2.la @JACK_LIBS@ @REDLAND_LIBS@ -lv2_simple_jack_host_LDADD = ../src/libslv2.la @JACK_LIBS@ @RASQAL_LIBS@ +lv2_simple_jack_host_LDADD = ../src/libslv2.la @JACK_LIBS@ @REDLAND_LIBS@ lv2_jack_host_SOURCES = \ jack_compat.h \ diff --git a/hosts/lv2_jack_host.c b/hosts/lv2_jack_host.c index 39e9c41..2e7a23f 100644 --- a/hosts/lv2_jack_host.c +++ b/hosts/lv2_jack_host.c @@ -30,8 +30,8 @@ #define MIDI_BUFFER_SIZE 1024 struct Port { - SLV2PortID id; SLV2PortClass class; + SLV2Port slv2_port; jack_port_t* jack_port; /**< For audio and MIDI ports, otherwise NULL */ float control; /**< For control ports, otherwise 0.0f */ LV2_MIDI* midi_buffer; /**< For midi ports, otherwise NULL */ @@ -62,11 +62,10 @@ main(int argc, char** argv) host.num_ports = 0; host.ports = NULL; - slv2_init(); - /* Find all installed plugins */ - SLV2Plugins plugins = slv2_plugins_new(); - slv2_plugins_load_all(plugins); + SLV2Model model = slv2_model_new(); + slv2_model_load_all(model); + SLV2Plugins plugins = slv2_model_get_all_plugins(model); /* Find the plugin to run */ const char* plugin_uri = (argc == 2) ? argv[1] : NULL; @@ -141,7 +140,7 @@ main(int argc, char** argv) } jack_client_close(host.jack_client); - slv2_finish(); + slv2_model_free(model); return 0; } @@ -170,8 +169,8 @@ create_port(struct JackHost* host, //struct Port* port = (Port*)malloc(sizeof(Port)); struct Port* const port = &host->ports[port_index]; - port->id = slv2_port_by_index(port_index); port->class = SLV2_UNKNOWN_PORT_CLASS; + port->slv2_port = slv2_plugin_get_port_by_index(host->plugin, port_index); port->jack_port = NULL; port->control = 0.0f; port->midi_buffer = NULL; @@ -179,15 +178,15 @@ create_port(struct JackHost* host, slv2_instance_connect_port(host->instance, port_index, NULL); /* Get the port symbol (label) for console printing */ - char* symbol = slv2_port_get_symbol(host->plugin, port->id); + char* symbol = slv2_port_get_symbol(host->plugin, port->slv2_port); /* Get the 'class' (not data type) of the port (control input, audio output, etc) */ - port->class = slv2_port_get_class(host->plugin, port->id); + port->class = slv2_port_get_class(host->plugin, port->slv2_port); /* Connect the port based on it's 'class' */ switch (port->class) { case SLV2_CONTROL_INPUT: - port->control = slv2_port_get_default_value(host->plugin, port->id); + port->control = slv2_port_get_default_value(host->plugin, port->slv2_port); slv2_instance_connect_port(host->instance, port_index, &port->control); printf("Set %s to %f\n", symbol, host->ports[port_index].control); break; diff --git a/hosts/lv2_simple_jack_host.c b/hosts/lv2_simple_jack_host.c index 3e808c2..260605c 100644 --- a/hosts/lv2_simple_jack_host.c +++ b/hosts/lv2_simple_jack_host.c @@ -20,201 +20,49 @@ #include <stdlib.h> #include <string.h> #include <slv2/slv2.h> -#include <jack/jack.h> - - -/** 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; + SLV2Model model = slv2_model_new(); + slv2_model_load_all(model); - slv2_init(); - /* Find all installed plugins */ - SLV2Plugins plugins = slv2_plugins_new(); - slv2_plugins_load_all(plugins); + /*printf("********** All plugins **********\n"); - /* 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); - return EXIT_FAILURE; - } + SLV2Plugins plugins = slv2_model_get_all_plugins(model); - 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_plugins_free(plugins); - return EXIT_FAILURE; + 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)); } - /* 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"); - - 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*)); + slv2_plugins_free(plugins);*/ - 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); + printf("********** Plugins with MIDI input **********\n"); - /* Run */ - printf("Press enter to quit: "); - getc(stdin); - printf("\n"); - - /* Deactivate plugin and JACK */ - slv2_instance_free(host.instance); - slv2_plugins_free(plugins); - - 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_finish(); - - return 0; -} - - -/** Abort and exit on error */ -void -die(const char* msg) -{ - fprintf(stderr, "%s\n", msg); - exit(EXIT_FAILURE); -} - + /*const char* query = + "PREFIX : <http://lv2plug.in/ontology#>\n" + //"PREFIX llext: <http://ll-plugins.nongnu.org/lv2/ext/>\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"; -/** 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) -{ - SLV2PortID id = slv2_port_by_index(index); + SLV2Plugins plugins = slv2_model_get_plugins_by_query(model, query); - /* Get the port symbol (label) for console printing */ - char* symbol = slv2_port_get_symbol(host->plugin, id); - - /* Initialize the port array elements */ - host->jack_ports[index] = NULL; - host->controls[index] = 0.0f; - - /* Get the 'class' of the port (control input, audio output, etc) */ - SLV2PortClass class = slv2_port_get_class(host->plugin, id); - - /* Connect the port based on it's 'class' */ - switch (class) { - case SLV2_CONTROL_INPUT: - host->controls[index] = slv2_port_get_default_value(host->plugin, id); - 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!"); + 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)); } - - free(symbol); -} - - -/** 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); + slv2_plugins_free(plugins); + */ + slv2_model_free(model); + 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/Makefile.am b/slv2/Makefile.am index 0a90fb1..2c69aef 100644 --- a/slv2/Makefile.am +++ b/slv2/Makefile.am @@ -4,10 +4,11 @@ slv2include_HEADERS = \ lv2.h \ types.h \ slv2.h \ - library.h \ + model.h \ plugin.h \ port.h \ pluginlist.h \ plugininstance.h \ - util.h \ - stringlist.h + stringlist.h \ + util.h + diff --git a/slv2/library.h b/slv2/library.h deleted file mode 100644 index c3d68ca..0000000 --- a/slv2/library.h +++ /dev/null @@ -1,48 +0,0 @@ -/* SLV2 - * Copyright (C) 2007 Dave Robillard <http://drobilla.net> - * - * This library is free software; you can redistribute it and/or modify it - * under the terms of the GNU General Public License as published by the Free - * Software Foundation; either version 2 of the License, or (at your option) - * any later version. - * - * This library is distributed in the hope that it will be useful, but WITHOUT - * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or - * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License - * for more details. - * - * You should have received a copy of the GNU General Public License along - * with this program; if not, write to the Free Software Foundation, Inc., - * 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. - */ - -#ifndef __SLV2_LIBRARY_H -#define __SLV2_LIBRARY_H - -#ifdef __cplusplus -extern "C" { -#endif - - -/** Initialize SLV2. - * - * This MUST be called before calling any other SLV2 functions, or fatal - * errors will likely occur. - */ -void -slv2_init(); - - -/** Finialize SLV2. - * - * Frees any resources allocated by slv2_init(). - */ -void -slv2_finish(); - - -#ifdef __cplusplus -} -#endif - -#endif /* __SLV2_LIBRARY_H */ diff --git a/slv2/plugin.h b/slv2/plugin.h index 77a0f61..7b8d62b 100644 --- a/slv2/plugin.h +++ b/slv2/plugin.h @@ -26,17 +26,18 @@ extern "C" { #include <stdint.h> #include <stdbool.h> #include <slv2/types.h> +#include <slv2/port.h> #include <slv2/stringlist.h> - -typedef struct _Plugin* SLV2Plugin; - - -/** \defgroup data Data file access +/** \defgroup data Plugin data access * * These functions work exclusively with the plugin's RDF data file. * They do not load or access the plugin dynamic library in any way. * + * An SLV2Plugin is a weak reference (ie URIs) to an LV2 plugin in the + * Model. Most functions which operate on an SLV2Plugin actually query + * the data in the model. + * * @{ */ @@ -162,8 +163,8 @@ slv2_plugin_get_value(SLV2Plugin p, */ SLV2Strings slv2_plugin_get_value_for_subject(SLV2Plugin p, - const char* subject, - const char* predicate); + const char* subject, + const char* predicate); /** Get the LV2 Properties of a plugin. @@ -266,6 +267,31 @@ slv2_plugin_query_count(SLV2Plugin plugin, const char* sparql_str); +/** Get a port on this plugin by \a index. + * + * To perform multiple calls on a port, the returned value should + * be cached and used repeatedly. + * + * O(1) + */ +SLV2Port +slv2_plugin_get_port_by_index(SLV2Plugin plugin, + uint32_t index); + + +/** Get a port on this plugin by \a symbol. + * + * To perform multiple calls on a port, the returned value should + * be cached and used repeatedly. + * + * O(num_ports) + */ +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 c1f1d04..c0c1b4d 100644 --- a/slv2/pluginlist.h +++ b/slv2/pluginlist.h @@ -19,6 +19,8 @@ #ifndef __SLV2_PLUGINLIST_H__ #define __SLV2_PLUGINLIST_H__ +#include <slv2/plugin.h> + #ifdef __cplusplus extern "C" { #endif @@ -27,18 +29,11 @@ extern "C" { typedef void* SLV2Plugins; -/** \defgroup plugins Discovery and lists of plugins - * - * These functions are for locating plugins installed on the system. - * - * Normal hosts which just want to easily load plugins by URI are strongly - * recommended to simply find all installed plugins with - * \ref slv2_plugins_load_all rather than find and load bundles manually. +/** \defgroup plugins Plugin lists * - * Functions are provided for hosts that wish to access bundles explicitly and - * individually for some reason, as well as make custom lists of plugins from - * a selection of bundles. This is mostly intended for hosts which are - * tied to a specific (bundled with the application) bundle. + * These functions work with lists of plugins which come from an + * SLV2Model. These lists contain only a weak reference to an LV2 plugin + * in the Model. * * @{ */ @@ -67,62 +62,6 @@ void slv2_plugins_free(SLV2Plugins list); -/** Filter plugins from one list into another. - * - * All plugins in \a source 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 dest. Plugins are duplicated into \a dest, it is safe - * to destroy \a source and continue to use \a dest after this call. - */ -void -slv2_plugins_filter(SLV2Plugins dest, - SLV2Plugins source, - bool (*include)(SLV2Plugin)); - - -/** Add all plugins installed on the system to \a list. - * - * This is the recommended way for hosts to access plugins. It does the most - * reasonable thing to find all installed plugins on a system. The environment - * variable LV2_PATH may be set to control the locations this function will - * look for plugins. - * - * Use of any functions for locating plugins other than this one is \em highly - * discouraged without a special reason to do so (and is just more work for the - * host author) - use this one. - */ -void -slv2_plugins_load_all(SLV2Plugins list); - - -/** Add all plugins found in \a search_path to \a list. - * - * If \a search_path is NULL, \a list will be unmodified. - * - * Use of this function is \b not recommended. Use \ref slv2_plugins_load_all. - */ -void -slv2_plugins_load_path(SLV2Plugins list, - const char* search_path); - - -/** Add all plugins found in the bundle at \a bundle_base_url to \a list. - * - * \arg bundle_base_url is a fully qualified path to the bundle directory, eg. - * "file:///usr/lib/lv2/someBundle" - * - * Use of this function is \b strongly discouraged - hosts should not attach - * \em any significance to bundle paths as there are no guarantees they will - * remain consistent whatsoever. This function should only be used by apps - * which ship with a special bundle (which it knows exists at some path). - * It is \b not to be used by normal hosts that want to load system - * installed plugins. Use \ref slv2_plugins_load_all for that. - */ -void -slv2_plugins_load_bundle(SLV2Plugins list, - const char* bundle_base_uri); - - /** Get the number of plugins in the list. */ unsigned @@ -133,8 +72,8 @@ 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. - * This functions is a search, slv2_plugins_get_at is - * significantly faster. + * + * O(log2(n)) * * \return NULL if plugin with \a url not found in \a list. */ @@ -153,12 +92,15 @@ slv2_plugins_get_by_uri(SLV2Plugins list, * Return value is shared (stored in \a list) and must not be freed or * modified by the caller in any way. * + * O(1) + * * \return NULL if \a index out of range. */ SLV2Plugin slv2_plugins_get_at(SLV2Plugins list, unsigned index); + /** @} */ #ifdef __cplusplus diff --git a/slv2/port.h b/slv2/port.h index 546a713..13ac5c1 100644 --- a/slv2/port.h +++ b/slv2/port.h @@ -25,6 +25,7 @@ extern "C" { #include <slv2/types.h> #include <slv2/plugin.h> +#include <slv2/port.h> #include <slv2/stringlist.h> /** \addtogroup data @@ -32,38 +33,26 @@ extern "C" { */ -/** Create an ID to reference a port by index. - */ -SLV2PortID -slv2_port_by_index(uint32_t index); - - -/** Create an ID to reference a port by symbol. - */ -SLV2PortID -slv2_port_by_symbol(const char* symbol); - - /** Port equivalent to slv2_plugin_get_value. */ SLV2Strings slv2_port_get_value(SLV2Plugin plugin, - SLV2PortID id, + SLV2Port port, const char* property); /** Port equivalent to slv2_plugin_get_properties. */ SLV2Strings -slv2_port_get_properties(SLV2Plugin p, - SLV2PortID id); +slv2_port_get_properties(SLV2Plugin plugin, + SLV2Port port); /** Port equivalent to slv2_plugin_get_hints. */ SLV2Strings -slv2_port_get_hints(SLV2Plugin p, - SLV2PortID id); +slv2_port_get_hints(SLV2Plugin plugin, + SLV2Port port); /** Get the symbol of a port given the index. @@ -75,7 +64,7 @@ slv2_port_get_hints(SLV2Plugin p, */ char* slv2_port_get_symbol(SLV2Plugin plugin, - SLV2PortID id); + SLV2Port port); /** Get the name of a port. * @@ -85,14 +74,14 @@ slv2_port_get_symbol(SLV2Plugin plugin, */ char* slv2_port_get_name(SLV2Plugin plugin, - SLV2PortID id); + SLV2Port port); /** Get the class (input/output, data type, rate...) of a port. */ SLV2PortClass slv2_port_get_class(SLV2Plugin plugin, - SLV2PortID id); + SLV2Port port); /** Get the default value of a port. @@ -101,7 +90,7 @@ slv2_port_get_class(SLV2Plugin plugin, */ float slv2_port_get_default_value(SLV2Plugin plugin, - SLV2PortID id); + SLV2Port port); /** Get the minimum value of a port. @@ -110,7 +99,7 @@ slv2_port_get_default_value(SLV2Plugin plugin, */ float slv2_port_get_minimum_value(SLV2Plugin plugin, - SLV2PortID id); + SLV2Port port); /** Get the maximum value of a port. @@ -119,7 +108,7 @@ slv2_port_get_minimum_value(SLV2Plugin plugin, */ float slv2_port_get_maximum_value(SLV2Plugin plugin, - SLV2PortID id); + SLV2Port port); /** @} */ diff --git a/slv2/slv2.h b/slv2/slv2.h index 539ea47..8691e28 100644 --- a/slv2/slv2.h +++ b/slv2/slv2.h @@ -23,13 +23,14 @@ extern "C" { #endif -#include <slv2/library.h> #include <slv2/types.h> +#include <slv2/model.h> #include <slv2/plugin.h> -#include <slv2/plugininstance.h> +#include <slv2/port.h> #include <slv2/pluginlist.h> +#include <slv2/plugininstance.h> #include <slv2/stringlist.h> -#include <slv2/port.h> + #ifdef __cplusplus } diff --git a/slv2/types.h b/slv2/types.h index 03c84e7..da53069 100644 --- a/slv2/types.h +++ b/slv2/types.h @@ -27,17 +27,6 @@ extern "C" { #endif - -/** Port ID type, to allow passing either symbol or index - * to port related functions. - */ -typedef struct _PortID { - bool is_index; /**< Otherwise, symbol */ - uint32_t index; - const char* symbol; -} SLV2PortID; - - /** Class (direction and type) of a port * * Note that ports may be of other classes not listed here, this is just @@ -54,6 +43,11 @@ typedef enum _PortClass { SLV2_MIDI_OUTPUT /**< MIDI output (LL extension) */ } SLV2PortClass; +typedef struct _Port* SLV2Port; + +typedef struct _Plugin* SLV2Plugin; + +typedef struct _Model* SLV2Model; #ifdef __cplusplus } diff --git a/src/Makefile.am b/src/Makefile.am index b28b75d..eb25597 100644 --- a/src/Makefile.am +++ b/src/Makefile.am @@ -1,16 +1,16 @@ -AM_CFLAGS = -std=c99 -I$(top_srcdir)/include -I$(top_srcdir) @RASQAL_CFLAGS@ -DLIBSLV2_SOURCE -DLV2_TTL_PATH=\"@lv2ttlpath@\" +AM_CFLAGS = -std=c99 -I$(top_srcdir)/include -I$(top_srcdir) @REDLAND_CFLAGS@ -DLIBSLV2_SOURCE -DLV2_TTL_PATH=\"@lv2ttlpath@\" AM_LDFLAGS = `pkg-config --libs rasqal` lib_LTLIBRARIES = libslv2.la -libslv2_la_LIBADD = @RASQAL_LIBS@ +libslv2_la_LIBADD = @REDLAND_LIBS@ libslv2_la_SOURCES = \ private_types.h \ + model.c \ plugin.c \ query.c \ port.c \ pluginlist.c \ - util.c \ - plugininstance.c \ - library.c \ - stringlist.c + plugininstance.c \ + stringlist.c \ + util.c diff --git a/src/library.c b/src/library.c deleted file mode 100644 index 4ad97c3..0000000 --- a/src/library.c +++ /dev/null @@ -1,44 +0,0 @@ -/* SLV2 - * Copyright (C) 2007 Dave Robillard <http://drobilla.net> - * - * This library is free software; you can redistribute it and/or modify it - * under the terms of the GNU General Public License as published by the Free - * Software Foundation; either version 2 of the License, or (at your option) - * any later version. - * - * This library is distributed in the hope that it will be useful, but WITHOUT - * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or - * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License - * for more details. - * - * You should have received a copy of the GNU General Public License along - * with this program; if not, write to the Free Software Foundation, Inc., - * 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. - */ - -#include "config.h" -#include <rasqal.h> -#include <slv2/slv2.h> - -raptor_uri* slv2_ontology_uri = NULL; - - -void -slv2_init() -{ - rasqal_init(); - - slv2_ontology_uri = raptor_new_uri((const unsigned char*) - "file://" LV2_TTL_PATH); -} - - -void -slv2_finish() -{ - raptor_free_uri(slv2_ontology_uri); - slv2_ontology_uri = NULL; - - rasqal_finish(); -} - diff --git a/src/plugin.c b/src/plugin.c index abb577b..ab36caf 100644 --- a/src/plugin.c +++ b/src/plugin.c @@ -21,7 +21,7 @@ #include <string.h> #include <stdlib.h> #include <assert.h> -#include <rasqal.h> +#include <librdf.h> #include <slv2/plugin.h> #include <slv2/types.h> #include <slv2/util.h> @@ -29,8 +29,54 @@ #include "private_types.h" +/* private */ +SLV2Plugin +slv2_plugin_new(SLV2Model model, librdf_uri* uri, const char* binary_uri) +{ + struct _Plugin* plugin = malloc(sizeof(struct _Plugin)); + plugin->model = model; + 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->ports = raptor_new_sequence((void (*)(void*))&slv2_port_free, NULL); + plugin->storage = NULL; + plugin->rdf = NULL; + + return plugin; +} + + +/* private */ +void +slv2_plugin_free(SLV2Plugin p) +{ + librdf_free_uri(p->plugin_uri); + p->plugin_uri = NULL; + + //free(p->bundle_url); + free(p->binary_uri); + + raptor_free_sequence(p->ports); + p->ports = NULL; + + if (p->rdf) { + librdf_free_model(p->rdf); + p->rdf = NULL; + } + + if (p->storage) { + librdf_free_storage(p->storage); + p->storage = NULL; + } + + raptor_free_sequence(p->data_uris); + + free(p); +} + + // FIXME: ew -rasqal_query_results* +librdf_query_results* slv2_plugin_query(SLV2Plugin plugin, const char* sparql_str); @@ -38,42 +84,127 @@ slv2_plugin_query(SLV2Plugin plugin, SLV2Plugin slv2_plugin_duplicate(SLV2Plugin p) { + fprintf(stderr, "FIXME: duplicate\n"); + assert(p); struct _Plugin* result = malloc(sizeof(struct _Plugin)); - result->plugin_uri = strdup(p->plugin_uri); - result->bundle_url = strdup(p->bundle_url); - result->lib_uri = strdup(p->lib_uri); + result->model = p->model; + result->plugin_uri = librdf_new_uri_from_uri(p->plugin_uri); + + //result->bundle_url = strdup(p->bundle_url); + result->binary_uri = strdup(p->binary_uri); result->data_uris = slv2_strings_new(); for (unsigned i=0; i < slv2_strings_size(p->data_uris); ++i) raptor_sequence_push(result->data_uris, strdup(slv2_strings_get_at(p->data_uris, i))); + + result->ports = raptor_new_sequence((void (*)(void*))&slv2_port_free, NULL); + for (int i=0; i < raptor_sequence_size(p->ports); ++i) + raptor_sequence_push(result->ports, slv2_port_duplicate(raptor_sequence_get_at(p->ports, i))); + + result->ports = NULL; + result->storage = NULL; + result->rdf = NULL; + return result; } +/** comparator for sorting */ +int +slv2_port_compare_by_index(const void* a, const void* b) +{ + SLV2Port port_a = *(SLV2Port*)a; + SLV2Port port_b = *(SLV2Port*)b; + + if (port_a->index < port_b->index) + return -1; + else if (port_a->index == port_b->index) + return 0; + else //if (port_a->index > port_b->index) + return 1; +} + + void -slv2_plugin_free(SLV2Plugin p) +slv2_plugin_load(SLV2Plugin p) { - free(p->plugin_uri); - free(p->bundle_url); - free(p->lib_uri); - slv2_strings_free(p->data_uris); - free(p); + //printf("Loading cache for %s\n", (const char*)librdf_uri_as_string(p->plugin_uri)); + + if (!p->storage) { + assert(!p->rdf); + p->storage = librdf_new_storage(p->model->world, "hashes", NULL, + "hash-type='memory'"); + p->rdf = librdf_new_model(p->model->world, p->storage, NULL); + } + + // 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); + librdf_parser_parse_into_model(p->model->parser, data_uri, NULL, p->rdf); + } + + // Load ports + const unsigned char* query = (const unsigned char*) + "PREFIX : <http://lv2plug.in/ontology#>\n" + "SELECT DISTINCT ?port ?symbol ?index WHERE {\n" + "<> :port ?port .\n" + "?port :symbol ?symbol ;\n" + " :index ?index .\n" + "}"; + + librdf_query* q = librdf_new_query(p->model->world, "sparql", + NULL, query, p->plugin_uri); + + librdf_query_results* results = librdf_query_execute(q, p->rdf); + + while (!librdf_query_results_finished(results)) { + + //librdf_node* port_node = librdf_query_results_get_binding_value(results, 0); + librdf_node* symbol_node = librdf_query_results_get_binding_value(results, 1); + librdf_node* index_node = librdf_query_results_get_binding_value(results, 2); + + //assert(librdf_node_is_blank(port_node)); + assert(librdf_node_is_literal(symbol_node)); + assert(librdf_node_is_literal(index_node)); + + //const char* id = (const char*)librdf_node_get_blank_identifier(port_node); + const char* symbol = (const char*)librdf_node_get_literal_value(symbol_node); + const char* index = (const char*)librdf_node_get_literal_value(index_node); + + //printf("%s: PORT: %s %s\n", p->plugin_uri, index, symbol); + + // Create a new SLV2Port + SLV2Port port = slv2_port_new((unsigned)atoi(index), symbol); + raptor_sequence_push(p->ports, port); + + librdf_free_node(symbol_node); + librdf_free_node(index_node); + + librdf_query_results_next(results); + } + + raptor_sequence_sort(p->ports, slv2_port_compare_by_index); + + if (results) + librdf_free_query_results(results); + + librdf_free_query(q); + + //printf("%p %s: NUM PORTS: %d\n", (void*)p, p->plugin_uri, slv2_plugin_get_num_ports(p)); } const char* slv2_plugin_get_uri(SLV2Plugin p) { - assert(p); - return p->plugin_uri; + return (const char*)librdf_uri_as_string(p->plugin_uri); } SLV2Strings slv2_plugin_get_data_uris(SLV2Plugin p) { - assert(p); return p->data_uris; } @@ -81,8 +212,7 @@ slv2_plugin_get_data_uris(SLV2Plugin p) const char* slv2_plugin_get_library_uri(SLV2Plugin p) { - assert(p); - return p->lib_uri; + return p->binary_uri; } @@ -91,42 +221,47 @@ slv2_plugin_verify(SLV2Plugin plugin) { char* query_str = "SELECT DISTINCT ?type ?name ?license ?port WHERE {\n" - "plugin: a ?type ;\n" + "<> a ?type ;\n" "doap:name ?name ;\n" "doap:license ?license ;\n" "lv2:port [ lv2:index ?port ] .\n}"; - rasqal_query_results* results = slv2_plugin_query(plugin, query_str); + librdf_query_results* results = slv2_plugin_query(plugin, query_str); bool has_type = false; bool has_name = false; bool has_license = false; bool has_port = false; - while (!rasqal_query_results_finished(results)) { - rasqal_literal* literal = rasqal_query_results_get_binding_value(results, 0); - const char* const type = (const char*)rasqal_literal_as_string(literal); - literal = rasqal_query_results_get_binding_value(results, 1); - const char* const name = (const char*)rasqal_literal_as_string(literal); - rasqal_literal* license = rasqal_query_results_get_binding_value(results, 2); - rasqal_literal* port = rasqal_query_results_get_binding_value(results, 3); + while (!librdf_query_results_finished(results)) { + librdf_node* type_node = librdf_query_results_get_binding_value(results, 0); + const char* const type_str = (const char*)librdf_node_get_literal_value(type_node); + librdf_node* name_node = librdf_query_results_get_binding_value(results, 1); + //const char* const name = (const char*)librdf_node_get_literal_value(name_node); + librdf_node* license_node = librdf_query_results_get_binding_value(results, 2); + librdf_node* port_node = librdf_query_results_get_binding_value(results, 3); - if (!strcmp(type, "http://lv2plug.in/ontology#Plugin")) + if (!strcmp(type_str, "http://lv2plug.in/ontology#Plugin")) has_type = true; - if (name) + if (name_node) has_name = true; - if (license) + if (license_node) has_license = true; - if (port) + if (port_node) has_port = true; - rasqal_query_results_next(results); + librdf_free_node(type_node); + librdf_free_node(name_node); + librdf_free_node(license_node); + librdf_free_node(port_node); + + librdf_query_results_next(results); } - rasqal_free_query_results(results); + librdf_free_query_results(results); if ( ! (has_type && has_name && has_license && has_port) ) { fprintf(stderr, "Invalid LV2 Plugin %s\n", slv2_plugin_get_uri(plugin)); @@ -161,8 +296,8 @@ slv2_plugin_get_value(SLV2Plugin p, assert(predicate); char* query = slv2_strjoin( - "SELECT DISTINCT ?value WHERE {\n" - "plugin: ", predicate, " ?value .\n" + "SELECT DISTINCT ?value WHERE {" + "<> ", predicate, " ?value .\n" "}\n", NULL); SLV2Strings result = slv2_plugin_simple_query(p, query, "value"); @@ -210,11 +345,7 @@ slv2_plugin_get_hints(SLV2Plugin p) uint32_t slv2_plugin_get_num_ports(SLV2Plugin p) { - const char* const query = - "SELECT DISTINCT ?port\n" - "WHERE { plugin: lv2:port ?port }\n"; - - return (uint32_t)slv2_plugin_query_count(p, query); + return raptor_sequence_size(p->ports); } @@ -223,7 +354,7 @@ slv2_plugin_has_latency(SLV2Plugin p) { const char* const query = "SELECT DISTINCT ?port WHERE {\n" - " plugin: lv2:port ?port .\n" + " <> lv2:port ?port .\n" " ?port lv2:portHint lv2:reportsLatency .\n" "}\n"; @@ -242,7 +373,7 @@ slv2_plugin_get_latency_port(SLV2Plugin p) { const char* const query = "SELECT DISTINCT ?value WHERE {\n" - " plugin: lv2:port ?port .\n" + " <> lv2:port ?port .\n" " ?port lv2:portHint lv2:reportsLatency ;\n" " lv2:index ?index .\n" "}\n"; @@ -264,9 +395,9 @@ slv2_plugin_get_supported_features(SLV2Plugin p) { const char* const query = "SELECT DISTINCT ?feature WHERE {\n" - " { plugin: lv2:optionalHostFeature ?feature }\n" + " { <> lv2:optionalHostFeature ?feature }\n" " UNION\n" - " { plugin: lv2:requiredHostFeature ?feature }\n" + " { <> lv2:requiredHostFeature ?feature }\n" "}\n"; SLV2Strings result = slv2_plugin_simple_query(p, query, "feature"); @@ -280,7 +411,7 @@ slv2_plugin_get_optional_features(SLV2Plugin p) { const char* const query = "SELECT DISTINCT ?feature WHERE {\n" - " plugin: lv2:optionalHostFeature ?feature .\n" + " <> lv2:optionalHostFeature ?feature .\n" "}\n"; SLV2Strings result = slv2_plugin_simple_query(p, query, "feature"); @@ -294,7 +425,7 @@ slv2_plugin_get_required_features(SLV2Plugin p) { const char* const query = "SELECT DISTINCT ?feature WHERE {\n" - " plugin: lv2:requiredHostFeature ?feature .\n" + " <> lv2:requiredHostFeature ?feature .\n" "}\n"; SLV2Strings result = slv2_plugin_simple_query(p, query, "feature"); @@ -302,3 +433,26 @@ slv2_plugin_get_required_features(SLV2Plugin p) return result; } + +SLV2Port +slv2_plugin_get_port_by_index(SLV2Plugin p, + uint32_t index) +{ + return raptor_sequence_get_at(p->ports, (int)index); +} + + +SLV2Port +slv2_plugin_get_port_by_symbol(SLV2Plugin p, + const char* symbol) +{ + // FIXME: sort plugins and do a binary search + for (int i=0; i < raptor_sequence_size(p->ports); ++i) { + SLV2Port port = raptor_sequence_get_at(p->ports, i); + if (!strcmp(port->symbol, symbol)) + return port; + } + + return NULL; +} + diff --git a/src/plugininstance.c b/src/plugininstance.c index 906bfb8..6d957b6 100644 --- a/src/plugininstance.c +++ b/src/plugininstance.c @@ -42,13 +42,15 @@ slv2_plugin_instantiate(SLV2Plugin plugin, } const char* const lib_uri = slv2_plugin_get_library_uri(plugin); - if (!lib_uri || slv2_uri_to_path(lib_uri) == NULL) + const char* const lib_path = slv2_uri_to_path(lib_uri); + + if (!lib_path) return NULL; dlerror(); - void* lib = dlopen(slv2_uri_to_path(lib_uri), RTLD_NOW); + void* lib = dlopen(lib_path, RTLD_NOW); if (!lib) { - fprintf(stderr, "Unable to open library %s (%s)\n", lib_uri, dlerror()); + fprintf(stderr, "Unable to open library %s (%s)\n", lib_path, dlerror()); return NULL; } @@ -56,31 +58,34 @@ slv2_plugin_instantiate(SLV2Plugin plugin, if (!df) { fprintf(stderr, "Could not find symbol 'lv2_descriptor', " - "%s is not a LV2 plugin.\n", lib_uri); + "%s is not a LV2 plugin.\n", lib_path); dlclose(lib); return NULL; } else { // Search for plugin by URI - const char* const bundle_path = slv2_uri_to_path(plugin->bundle_url); + // FIXME: Kluge to get bundle path (containing directory of binary) + const char* const bundle_path = strrchr(plugin->binary_uri, '/') + 1; + printf("Bundle path: %s\n", bundle_path); for (uint32_t i=0; 1; ++i) { + const LV2_Descriptor* ld = df(i); - + if (!ld) { fprintf(stderr, "Did not find plugin %s in %s\n", - plugin->plugin_uri, plugin->lib_uri); + slv2_plugin_get_uri(plugin), lib_path); dlclose(lib); break; // return NULL - } else if (!strcmp(ld->URI, (char*)plugin->plugin_uri)) { - //printf("Found %s at index %u in:\n\t%s\n\n", plugin->plugin_uri, i, lib_path); + } else if (!strcmp(ld->URI, slv2_plugin_get_uri(plugin))) { + + printf("Found %s at index %u in:\n\t%s\n\n", + librdf_uri_as_string(plugin->plugin_uri), i, lib_path); assert(ld->instantiate); // Create SLV2Instance to return result = malloc(sizeof(struct _Instance)); - /*result->plugin = malloc(sizeof(struct _Plugin)); - memcpy(result->plugin, plugin, sizeof(struct _Plugin));*/ result->lv2_descriptor = ld; result->lv2_handle = ld->instantiate(ld, sample_rate, (char*)bundle_path, host_features); struct _InstanceImpl* impl = malloc(sizeof(struct _InstanceImpl)); diff --git a/src/pluginlist.c b/src/pluginlist.c index 9dcb33c..50bb2ad 100644 --- a/src/pluginlist.c +++ b/src/pluginlist.c @@ -17,7 +17,6 @@ */ #define _XOPEN_SOURCE 500 -#include <rasqal.h> #include <limits.h> #include <string.h> #include <stdio.h> @@ -25,6 +24,7 @@ #include <sys/types.h> #include <assert.h> #include <dirent.h> +#include <librdf.h> #include <slv2/types.h> #include <slv2/plugin.h> #include <slv2/pluginlist.h> @@ -32,21 +32,6 @@ #include <slv2/util.h> #include "private_types.h" - -/* not exposed */ -struct _Plugin* -slv2_plugin_new() -{ - struct _Plugin* result = malloc(sizeof(struct _Plugin)); - result->plugin_uri = NULL; - result->bundle_url = NULL; - result->lib_uri = NULL; - - result->data_uris = slv2_strings_new(); - - return result; -} - SLV2Plugins slv2_plugins_new() @@ -61,7 +46,7 @@ slv2_plugins_free(SLV2Plugins list) raptor_free_sequence(list); } - +#if 0 void slv2_plugins_filter(SLV2Plugins dest, SLV2Plugins source, bool (*include)(SLV2Plugin)) { @@ -127,30 +112,33 @@ slv2_plugins_load_bundle(SLV2Plugins list, else strcat((char*)manifest_url, "/manifest.ttl"); - rasqal_query_results *results; - raptor_uri *base_url = raptor_new_uri(manifest_url); - rasqal_query *rq = rasqal_new_query("sparql", NULL); - + librdf_query_results *results; + librdf_uri *base_uri = librdf_new_uri(slv2_rdf_world, manifest_url); + /* Get all plugins explicitly mentioned in the manifest (discovery pass 1) */ char* query_string = "PREFIX : <http://lv2plug.in/ontology#>\n\n" "SELECT DISTINCT ?plugin_uri FROM <>\n" "WHERE { ?plugin_uri a :Plugin }\n"; + + librdf_query *rq = librdf_new_query(slv2_rdf_world, "sparql", NULL, + (unsigned char*)query_string, base_uri); + + //printf("%s\n\n", query_string); - rasqal_query_prepare(rq, (unsigned char*)query_string, base_url); - results = rasqal_query_execute(rq); + results = librdf_query_execute(rq, model->model); - while (!rasqal_query_results_finished(results)) { + while (!librdf_query_results_finished(results)) { - rasqal_literal* literal = rasqal_query_results_get_binding_value(results, 0); + librdf_node* literal = librdf_query_results_get_binding_value(results, 0); assert(literal); - if (!slv2_plugins_get_by_uri(list, (const char*)rasqal_literal_as_string(literal))) { + if (!slv2_plugins_get_by_uri(list, (const char*)librdf_node_get_literal_value(literal))) { /* Create a new plugin */ struct _Plugin* new_plugin = slv2_plugin_new(); - new_plugin->plugin_uri = strdup((const char*)rasqal_literal_as_string(literal)); + new_plugin->plugin_uri = strdup((const char*)librdf_node_get_literal_value(literal)); new_plugin->bundle_url = strdup(bundle_base_url); raptor_sequence_push(new_plugin->data_uris, strdup((const char*)manifest_url)); @@ -158,15 +146,13 @@ slv2_plugins_load_bundle(SLV2Plugins list, } - rasqal_query_results_next(results); + librdf_query_results_next(results); } if (results) - rasqal_free_query_results(results); - - rasqal_free_query(rq); + librdf_free_query_results(results); - rq = rasqal_new_query("sparql", NULL); + librdf_free_query(rq); /* Get all data files linked to plugins (discovery pass 2) */ query_string = @@ -175,22 +161,24 @@ slv2_plugins_load_bundle(SLV2Plugins list, "SELECT DISTINCT ?subject ?data_uri ?binary FROM <>\n" "WHERE { ?subject rdfs:seeAlso ?data_uri\n" "OPTIONAL { ?subject :binary ?binary } }\n"; + + rq = librdf_new_query(slv2_rdf_world, "sparql", NULL, + (unsigned char*)query_string, base_uri); //printf("%s\n\n", query_string); - rasqal_query_prepare(rq, (unsigned char*)query_string, base_url); - results = rasqal_query_execute(rq); + results = librdf_query_execute(rq, slv2_model); - while (!rasqal_query_results_finished(results)) { + while (!librdf_query_results_finished(results)) { - const char* subject = (const char*)rasqal_literal_as_string( - rasqal_query_results_get_binding_value(results, 0)); + const char* subject = (const char*)librdf_node_get_literal_value( + librdf_query_results_get_binding_value(results, 0)); - const char* data_uri = (const char*)rasqal_literal_as_string( - rasqal_query_results_get_binding_value(results, 1)); + const char* data_uri = (const char*)librdf_node_get_literal_value( + librdf_query_results_get_binding_value(results, 1)); - const char* binary = (const char*)rasqal_literal_as_string( - rasqal_query_results_get_binding_value(results, 2)); + const char* binary = (const char*)librdf_node_get_literal_value( + librdf_query_results_get_binding_value(results, 2)); SLV2Plugin plugin = slv2_plugins_get_by_uri(list, subject); @@ -200,16 +188,16 @@ slv2_plugins_load_bundle(SLV2Plugins list, if (plugin && binary && !plugin->lib_uri) ((struct _Plugin*)plugin)->lib_uri = strdup(binary); - rasqal_query_results_next(results); + librdf_query_results_next(results); } if (results) - rasqal_free_query_results(results); + librdf_free_query_results(results); - rasqal_free_query(rq); + librdf_free_query(rq); - raptor_free_uri(base_url); + librdf_free_uri(base_uri); free(manifest_url); } @@ -275,7 +263,7 @@ slv2_plugins_load_path(SLV2Plugins list, free(path); } - +#endif unsigned slv2_plugins_size(SLV2Plugins list) @@ -287,10 +275,28 @@ slv2_plugins_size(SLV2Plugins list) SLV2Plugin slv2_plugins_get_by_uri(SLV2Plugins list, const char* uri) { - for (int i=0; i < raptor_sequence_size(list); ++i) { + // good old fashioned binary search + + int lower = 0; + int upper = raptor_sequence_size(list) - 1; + int i; + + if (upper == 0) + return NULL; + + while (upper >= lower) { + i = lower + ((upper - lower) / 2); + SLV2Plugin p = raptor_sequence_get_at(list, i); - if (!strcmp(p->plugin_uri, uri)) + + int cmp = strcmp(slv2_plugin_get_uri(p), uri); + + if (cmp == 0) return p; + else if (cmp > 0) + upper = i - 1; + else + lower = i + 1; } return NULL; @@ -25,35 +25,49 @@ #include <slv2/port.h> #include <slv2/types.h> #include <slv2/util.h> +#include "private_types.h" -SLV2PortID -slv2_port_by_index(uint32_t index) +/* private */ +SLV2Port +slv2_port_new(uint32_t index, const char* symbol/*, const char* node_id*/) { - SLV2PortID ret; - ret.is_index = true; - ret.index = index; - ret.symbol = NULL; - return ret; + struct _Port* port = malloc(sizeof(struct _Port)); + port->index = index; + port->symbol = strdup(symbol); + //port->node_id = strdup(node_id); + return port; } -SLV2PortID -slv2_port_by_symbol(const char* symbol) +/* private */ +void +slv2_port_free(SLV2Port port) { - SLV2PortID ret; - ret.is_index = false; - ret.index = UINT_MAX; - ret.symbol = symbol; - return ret; + free(port->symbol); + //free(port->node_id); + free(port); +} + + +/* private */ +SLV2Port +slv2_port_duplicate(SLV2Port port) +{ + struct _Port* result = malloc(sizeof(struct _Port)); + result->index = port->index; + result->symbol = strdup(port->symbol); + //result->node_id = strdup(port->node_id); + return result; } SLV2PortClass slv2_port_get_class(SLV2Plugin p, - SLV2PortID id) + SLV2Port port) { - SLV2Strings class = slv2_port_get_value(p, id, "rdf:type"); + SLV2Strings class = slv2_port_get_value(p, port, "rdf:type"); + assert(class); SLV2PortClass ret = SLV2_UNKNOWN_PORT_CLASS; @@ -98,152 +112,129 @@ slv2_port_get_class(SLV2Plugin p, SLV2Strings slv2_port_get_value(SLV2Plugin p, - SLV2PortID id, + SLV2Port port, const char* property) { assert(property); SLV2Strings result = NULL; - if (id.is_index) { - char index_str[16]; - snprintf(index_str, (size_t)16, "%u", id.index); - - char* query = slv2_strjoin( - "SELECT DISTINCT ?value WHERE { \n" - "plugin: lv2:port ?port . \n" - "?port lv2:index ", index_str, " ;\n\t", - property, " ?value . \n}\n", NULL); + char* query = slv2_strjoin( + "SELECT DISTINCT ?value WHERE {\n" + "?port lv2:symbol \"", port->symbol, "\";\n\t", + property, " ?value .\n}", 0); + + result = slv2_plugin_simple_query(p, query, "value"); - result = slv2_plugin_simple_query(p, query, "value"); - - free(query); + free(query); - } else { - - char* query = slv2_strjoin( - "SELECT DISTINCT ?value WHERE { \n" - "plugin: lv2:port ?port . \n" - "?port lv2:symbol \"", id.symbol, "\" ;\n\t", - property, " ?value . \n}\n", NULL); - - result = slv2_plugin_simple_query(p, query, "value"); - - free(query); - } - return result; } char* slv2_port_get_symbol(SLV2Plugin p, - SLV2PortID id) + SLV2Port port) { - char* result = NULL; + char* symbol = NULL; - SLV2Strings prop - = slv2_port_get_value(p, id, "lv2:symbol"); + SLV2Strings result = slv2_port_get_value(p, port, "lv2:symbol"); - if (prop && slv2_strings_size(prop) == 1) - result = strdup(slv2_strings_get_at(prop, 0)); + if (result && slv2_strings_size(result) == 1) + symbol = strdup(slv2_strings_get_at(result, 0)); - slv2_strings_free(prop); + slv2_strings_free(result); - return result; + return symbol; } char* slv2_port_get_name(SLV2Plugin p, - SLV2PortID id) + SLV2Port port) { - char* result = NULL; + char* name = NULL; - SLV2Strings prop - = slv2_port_get_value(p, id, "lv2:name"); + SLV2Strings result = slv2_port_get_value(p, port, "lv2:name"); - if (prop && slv2_strings_size(prop) == 1) - result = strdup(slv2_strings_get_at(prop, 0)); + if (result && slv2_strings_size(result) == 1) + name = strdup(slv2_strings_get_at(result, 0)); - slv2_strings_free(prop); + slv2_strings_free(result); - return result; + return name; } float slv2_port_get_default_value(SLV2Plugin p, - SLV2PortID id) + SLV2Port port) { // FIXME: do casting properly in the SPARQL query - float result = 0.0f; + float value = 0.0f; - SLV2Strings prop - = slv2_port_get_value(p, id, "lv2:default"); + SLV2Strings result = slv2_port_get_value(p, port, "lv2:default"); - if (prop && slv2_strings_size(prop) == 1) - result = atof(slv2_strings_get_at(prop, 0)); + if (result && slv2_strings_size(result) == 1) + value = atof(slv2_strings_get_at(result, 0)); - slv2_strings_free(prop); + slv2_strings_free(result); - return result; + return value; } float slv2_port_get_minimum_value(SLV2Plugin p, - SLV2PortID id) + SLV2Port port) { - // FIXME: do casting properly in the SPARQL query + // FIXME: need better access to literal types - float result = 0.0f; + float value = 0.0f; - SLV2Strings prop - = slv2_port_get_value(p, id, "lv2:minimum"); + SLV2Strings result = slv2_port_get_value(p, port, "lv2:minimum"); - if (prop && slv2_strings_size(prop) == 1) - result = atof(slv2_strings_get_at(prop, 0)); + if (result && slv2_strings_size(result) == 1) + value = atof(slv2_strings_get_at(result, 0)); - slv2_strings_free(prop); + slv2_strings_free(result); - return result; + return value; } float slv2_port_get_maximum_value(SLV2Plugin p, - SLV2PortID id) + SLV2Port port) { - // FIXME: do casting properly in the SPARQL query + // FIXME: need better access to literal types - float result = 0.0f; + float value = 0.0f; - SLV2Strings prop - = slv2_port_get_value(p, id, "lv2:maximum"); + SLV2Strings result = slv2_port_get_value(p, port, "lv2:maximum"); - if (prop && slv2_strings_size(prop) == 1) - result = atof(slv2_strings_get_at(prop, 0)); + if (result && slv2_strings_size(result) == 1) + value = atof(slv2_strings_get_at(result, 0)); - slv2_strings_free(prop); + slv2_strings_free(result); - return result; + return value; } SLV2Strings slv2_port_get_properties(SLV2Plugin p, - SLV2PortID id) + SLV2Port port) { - return slv2_port_get_value(p, id, "lv2:portProperty"); + return slv2_port_get_value(p, port, "lv2:portProperty"); } SLV2Strings slv2_port_get_hints(SLV2Plugin p, - SLV2PortID id) + SLV2Port port) { - return slv2_port_get_value(p, id, "lv2:portHint"); + return slv2_port_get_value(p, port, "lv2:portHint"); } diff --git a/src/private_types.h b/src/private_types.h index 027d68d..12f74cb 100644 --- a/src/private_types.h +++ b/src/private_types.h @@ -25,13 +25,21 @@ extern "C" { #include <stdbool.h> #include <stddef.h> -#include <raptor.h> -#include <slv2/lv2.h> +#include <librdf.h> +#include <slv2/pluginlist.h> -/** The URI of the lv2.ttl file. +/** Reference to a port on some plugin. */ -extern raptor_uri* slv2_ontology_uri; +struct _Port { + uint32_t index; ///< LV2 index + char* symbol; ///< LV2 symbol + //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); /** Record of an installed/available plugin. @@ -40,28 +48,42 @@ extern raptor_uri* slv2_ontology_uri; * paths of relevant files, the actual data therein isn't loaded into memory. */ struct _Plugin { - char* plugin_uri; - char* bundle_url; // Bundle directory plugin was loaded from + int deletable; + struct _Model* model; + librdf_uri* plugin_uri; +// char* bundle_url; // Bundle directory plugin was loaded from + char* binary_uri; // lv2:binary raptor_sequence* data_uris; // rdfs::seeAlso - char* lib_uri; // lv2:binary + raptor_sequence* ports; + librdf_storage* storage; + librdf_model* rdf; }; - -/** Pimpl portion of SLV2Instance */ -struct _InstanceImpl { - void* lib_handle; -}; +SLV2Plugin slv2_plugin_new(SLV2Model model, librdf_uri* uri, const char* binary_uri); +void slv2_plugin_load(SLV2Plugin p); -/** List of references to plugins available for loading (private type) */ +/** List of references to plugins available for loading */ struct _PluginList { size_t num_plugins; struct _Plugin** plugins; }; +/** Pimpl portion of SLV2Instance */ +struct _InstanceImpl { + void* lib_handle; +}; -/** An ordered, indexable collection of strings. */ -//typedef raptor_sequence* SLV2Strings; + +/** Model of LV2 (RDF) data loaded from bundles. + */ +struct _Model { + librdf_world* world; + librdf_storage* storage; + librdf_model* model; + librdf_parser* parser; + SLV2Plugins plugins; +}; #ifdef __cplusplus diff --git a/src/query.c b/src/query.c index df0f6d4..7127a8d 100644 --- a/src/query.c +++ b/src/query.c @@ -20,40 +20,20 @@ #include <string.h> #include <stdlib.h> #include <assert.h> -#include <rasqal.h> +#include <librdf.h> #include <slv2/plugin.h> -#include <slv2/library.h> #include <slv2/util.h> #include <slv2/stringlist.h> #include "private_types.h" -char* -slv2_query_header(SLV2Plugin p) -{ - const char* const plugin_uri = slv2_plugin_get_uri(p); - //SLV2Strings files = slv2_plugin_get_data_uris(p); - - char* query_string = slv2_strjoin( - "PREFIX rdf: <http://www.w3.org/1999/02/22-rdf-syntax-ns#>\n" - "PREFIX rdfs: <http://www.w3.org/2000/01/rdf-schema#>\n" - "PREFIX doap: <http://usefulinc.com/ns/doap#>\n" - "PREFIX lv2: <http://lv2plug.in/ontology#>\n" - "PREFIX plugin: <", plugin_uri, ">\n", NULL); - - /*for (int i=0; i < slv2_strings_size(files); ++i) { - const char* file_uri = slv2_strings_get_at(files, i); - slv2_strappend(&query_string, "PREFIX data: <"); - slv2_strappend(&query_string, file_uri); - slv2_strappend(&query_string, ">\n"); - }*/ - - slv2_strappend(&query_string, "\n"); - - return query_string; -} - +static const char* slv2_query_prefixes = + "PREFIX rdf: <http://www.w3.org/1999/02/22-rdf-syntax-ns#>\n" + "PREFIX rdfs: <http://www.w3.org/2000/01/rdf-schema#>\n" + "PREFIX doap: <http://usefulinc.com/ns/doap#>\n" + "PREFIX lv2: <http://lv2plug.in/ontology#>\n"; +#if 0 char* slv2_query_lang_filter(const char* variable) { @@ -69,26 +49,48 @@ slv2_query_lang_filter(const char* variable) return result; } - +#endif SLV2Strings -slv2_query_get_variable_bindings(rasqal_query_results* results, +slv2_query_get_variable_bindings(librdf_query_results* results, const char* variable) { SLV2Strings result = NULL; - if (rasqal_query_results_get_bindings_count(results) > 0) + if (librdf_query_results_get_bindings_count(results) > 0) result = slv2_strings_new(); - while (!rasqal_query_results_finished(results)) { - - rasqal_literal* literal = - rasqal_query_results_get_binding_value_by_name(results, (const unsigned char*)variable); - assert(literal != NULL); - - raptor_sequence_push(result, strdup((const char*)rasqal_literal_as_string(literal))); - - rasqal_query_results_next(results); + while (!librdf_query_results_finished(results)) { + + librdf_node* node = + librdf_query_results_get_binding_value_by_name(results, variable); + + char* str = NULL; + + switch (librdf_node_get_type(node)) { + case LIBRDF_NODE_TYPE_RESOURCE: + str = strdup((const char*)librdf_uri_as_string(librdf_node_get_uri(node))); + break; + case LIBRDF_NODE_TYPE_LITERAL: + str = strdup((const char*)librdf_node_get_literal_value(node)); + break; + case LIBRDF_NODE_TYPE_BLANK: + str = strdup((const char*)librdf_node_get_blank_identifier(node)); + break; + case LIBRDF_NODE_TYPE_UNKNOWN: + default: + fprintf(stderr, "Unknown variable binding type for ?%s\n", variable); + break; + } + + if (str) { + //printf("?%s = %s\n", variable, str); + raptor_sequence_push(result, str); + } + + librdf_free_node(node); + + librdf_query_results_next(results); } return result; @@ -96,70 +98,61 @@ slv2_query_get_variable_bindings(rasqal_query_results* results, size_t -slv2_query_count_bindings(rasqal_query_results* results) +slv2_query_count_bindings(librdf_query_results* results) { size_t count = 0; - while (!rasqal_query_results_finished(results)) { + while (!librdf_query_results_finished(results)) { ++count; - rasqal_query_results_next(results); + librdf_query_results_next(results); } return count; } -rasqal_query_results* -slv2_plugin_query(SLV2Plugin plugin, +librdf_query_results* +slv2_plugin_query(SLV2Plugin plugin, const char* sparql_str) { - raptor_uri* base_uri = raptor_new_uri((unsigned char*)slv2_plugin_get_uri(plugin)); + if (!plugin->rdf) + slv2_plugin_load(plugin); + + librdf_uri* base_uri = plugin->plugin_uri; + + char* query_str = slv2_strjoin(slv2_query_prefixes, sparql_str, NULL); - rasqal_query *rq = rasqal_new_query("sparql", NULL); + //printf("******** Query \n%s********\n", query_str); + + librdf_query *rq = librdf_new_query(plugin->model->world, "sparql", NULL, + (const unsigned char*)query_str, base_uri); if (!rq) { - fprintf(stderr, "ERROR: Could not create Rasqal query\n"); + fprintf(stderr, "ERROR: Could not create query\n"); return NULL; } - - char* header = slv2_query_header(plugin); - char* query_str = slv2_strjoin(header, sparql_str, NULL); - - //printf("Query: \n%s\n\n", query_str); - - rasqal_query_prepare(rq, (unsigned char*)query_str, base_uri); // Add LV2 ontology to query sources - rasqal_query_add_data_graph(rq, slv2_ontology_uri, - NULL, RASQAL_DATA_GRAPH_BACKGROUND); + //librdf_query_add_data_graph(rq, slv2_ontology_uri, + // NULL, RASQAL_DATA_GRAPH_BACKGROUND); // Add all plugin data files to query sources - for (unsigned i=0; i < slv2_strings_size(plugin->data_uris); ++i) { + /*for (unsigned i=0; i < slv2_strings_size(plugin->data_uris); ++i) { const char* file_uri_str = slv2_strings_get_at(plugin->data_uris, i); raptor_uri* file_uri = raptor_new_uri((const unsigned char*)file_uri_str); - rasqal_query_add_data_graph(rq, file_uri, + librdf_query_add_data_graph(rq, file_uri, NULL, RASQAL_DATA_GRAPH_BACKGROUND); raptor_free_uri(file_uri); - } + }*/ - rasqal_query_results* results = rasqal_query_execute(rq); + librdf_query_results* results = librdf_query_execute(rq, plugin->rdf); - rasqal_free_query(rq); - raptor_free_uri(base_uri); + librdf_free_query(rq); - free(header); free(query_str); // FIXME: results leaked internally in places? return results; - - /* - SLV2Strings ret = slv2_query_get_variable_bindings(results, var_name); - - rasqal_free_query_results(results); - rasqal_free_query(rq); - - return ret;*/ } @@ -169,9 +162,9 @@ slv2_plugin_simple_query(SLV2Plugin plugin, const char* sparql_str, const char* variable) { - rasqal_query_results* results = slv2_plugin_query(plugin, sparql_str); + librdf_query_results* results = slv2_plugin_query(plugin, sparql_str); SLV2Strings ret = slv2_query_get_variable_bindings(results, variable); - rasqal_free_query_results(results); + librdf_free_query_results(results); return ret; } @@ -186,11 +179,11 @@ unsigned slv2_plugin_query_count(SLV2Plugin plugin, const char* sparql_str) { - rasqal_query_results* results = slv2_plugin_query(plugin, sparql_str); + librdf_query_results* results = slv2_plugin_query(plugin, sparql_str); if (results) { unsigned ret = slv2_query_count_bindings(results); - rasqal_free_query_results(results); + librdf_free_query_results(results); return ret; } else { return 0; @@ -202,34 +195,29 @@ size_t slv2_query_count_results(SLV2Plugin p, const char* query) { - char* header = slv2_query_header(p); - char* query_str = slv2_strjoin(header, query, NULL); + char* query_str = slv2_strjoin(slv2_query_prefixes, query, NULL); assert(p); assert(query_str); - rasqal_query *rq = rasqal_new_query("sparql", NULL); + librdf_query *rq = librdf_new_query(p->model->world, "sparql", NULL, + (unsigned char*)query_str, NULL); //printf("Query: \n%s\n\n", query_str); - rasqal_query_prepare(rq, (unsigned char*)query_str, NULL); - // Add LV2 ontology to query sources - rasqal_query_add_data_graph(rq, slv2_ontology_uri, - NULL, RASQAL_DATA_GRAPH_BACKGROUND); + //librdf_query_add_data_graph(rq, slv2_ontology_uri, + // NULL, RASQAL_DATA_GRAPH_BACKGROUND); - rasqal_query_results* results = rasqal_query_execute(rq); + librdf_query_results* results = librdf_query_execute(rq, p->model->model); assert(results); size_t count = slv2_query_count_bindings(results); - rasqal_free_query_results(results); - rasqal_free_query(rq); - - rasqal_finish(); + librdf_free_query_results(results); + librdf_free_query(rq); free(query_str); - free(header); return count; } diff --git a/utils/Makefile.am b/utils/Makefile.am index 09935fc..2cca154 100644 --- a/utils/Makefile.am +++ b/utils/Makefile.am @@ -1,4 +1,4 @@ -AM_CFLAGS = -I$(top_srcdir) @RASQAL_CFLAGS@ +AM_CFLAGS = -I$(top_srcdir) @REDLAND_CFLAGS@ bin_PROGRAMS = lv2_list lv2_inspect diff --git a/utils/lv2_inspect.c b/utils/lv2_inspect.c index a993dee..593cc00 100644 --- a/utils/lv2_inspect.c +++ b/utils/lv2_inspect.c @@ -20,18 +20,20 @@ #include <stdio.h> #include <slv2/slv2.h> +// FIXME: remove +#include "../src/private_types.h" void print_port(SLV2Plugin p, uint32_t index) { - SLV2PortID id = slv2_port_by_index(index); + SLV2Port port = slv2_plugin_get_port_by_index(p, index); char* str = NULL; SLV2PortClass cl = SLV2_UNKNOWN_PORT_CLASS; printf("\n\tPort %d:\n", index); - cl = slv2_port_get_class(p, id); + cl = slv2_port_get_class(p, port); printf("\t\tClass: "); switch (cl) { case SLV2_CONTROL_INPUT: @@ -58,22 +60,33 @@ print_port(SLV2Plugin p, uint32_t index) } printf("\n"); - str = slv2_port_get_symbol(p, id); + str = slv2_port_get_symbol(p, port); printf("\t\tSymbol: %s\n", str); free(str); - str = slv2_port_get_name(p, id); + str = slv2_port_get_name(p, port); printf("\t\tName: %s\n", str); free(str); if (cl == SLV2_CONTROL_INPUT || cl == SLV2_CONTROL_OUTPUT) { - printf("\t\tMinimum: %f\n", slv2_port_get_minimum_value(p, id)); - printf("\t\tMaximum: %f\n", slv2_port_get_maximum_value(p, id)); - printf("\t\tDefault: %f\n", slv2_port_get_default_value(p, id)); + printf("\t\tMinimum: %f\n", slv2_port_get_minimum_value(p, port)); + printf("\t\tMaximum: %f\n", slv2_port_get_maximum_value(p, port)); + printf("\t\tDefault: %f\n", slv2_port_get_default_value(p, port)); } -} + printf("\t\tProperties:\n"); + SLV2Strings properties = slv2_port_get_properties(p, port); + for (unsigned i=0; i < slv2_strings_size(properties); ++i) + printf("\t\t\t%s\n", slv2_strings_get_at(properties, i)); + slv2_strings_free(properties); + + printf("\t\tHints:\n"); + SLV2Strings hints = slv2_port_get_hints(p, port); + for (unsigned i=0; i < slv2_strings_size(hints); ++i) + printf("\t\t\t%s\n", slv2_strings_get_at(hints, i)); + slv2_strings_free(hints); +} void print_plugin(SLV2Plugin p) @@ -82,10 +95,12 @@ print_plugin(SLV2Plugin p) printf("<%s>\n", slv2_plugin_get_uri(p)); - printf("\tData URIs:\n"); + 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)); @@ -111,6 +126,9 @@ print_plugin(SLV2Plugin p) slv2_strings_free(v); uint32_t num_ports = slv2_plugin_get_num_ports(p); + + printf("\n\t# Ports: %d\n", num_ports); + for (uint32_t i=0; i < num_ports; ++i) print_port(p, i); } @@ -120,27 +138,27 @@ print_plugin(SLV2Plugin p) int main(int argc, char** argv) { - slv2_init(); + SLV2Model model = slv2_model_new(); + slv2_model_load_all(model); if (argc != 2) { fprintf(stderr, "Usage: %s PLUGIN_URI\n", argv[0]); return -1; } - SLV2Plugins plugins = slv2_plugins_new(); - slv2_plugins_load_all(plugins); + SLV2Plugins plugins = slv2_model_get_all_plugins(model); SLV2Plugin p = slv2_plugins_get_by_uri(plugins, argv[1]); - if (!p) { + if (p) { + print_plugin(p); + } else { fprintf(stderr, "Plugin not found.\n"); return -1; } - print_plugin(p); - slv2_plugins_free(plugins); - slv2_finish(); + slv2_model_free(model); - return 0; + return (p != NULL ? 0 : -1); } diff --git a/utils/lv2_list.c b/utils/lv2_list.c index cab660a..5c2a1dd 100644 --- a/utils/lv2_list.c +++ b/utils/lv2_list.c @@ -33,15 +33,15 @@ list_plugins(SLV2Plugins list) int main()//int argc, char** argv) { - slv2_init(); + SLV2Model model = slv2_model_new(); + slv2_model_load_all(model); - SLV2Plugins plugins = slv2_plugins_new(); - slv2_plugins_load_all(plugins); + SLV2Plugins plugins = slv2_model_get_all_plugins(model); list_plugins(plugins); slv2_plugins_free(plugins); - slv2_finish(); + slv2_model_free(model); return 0; } |