Mercurial > hg > piper-cpp
changeset 229:d54dab075247
Implement RDF type lookup
author | Chris Cannam <cannam@all-day-breakfast.com> |
---|---|
date | Mon, 12 Jun 2017 10:20:25 +0100 |
parents | 691b0daad1c6 |
children | d96981c06e62 |
files | .travis.yml vamp-server/test.sh vamp-support/RdfTypes.h |
diffstat | 3 files changed, 155 insertions(+), 15 deletions(-) [+] |
line wrap: on
line diff
--- a/.travis.yml Mon Jun 12 10:19:52 2017 +0100 +++ b/.travis.yml Mon Jun 12 10:20:25 2017 +0100 @@ -17,7 +17,7 @@ - qt5-default before_install: - - ( cd ../ ; git clone https://github.com/piper-audio/piper ) + - ( cd ../ ; git clone -b output-type-uri https://github.com/piper-audio/piper ) - ( cd ../ ; hg clone https://code.soundsoftware.ac.uk/hg/vamp-plugin-sdk ) - ( cd ../ ; git clone https://github.com/sandstorm-io/capnproto ) - ( cd ../capnproto/c++ ; ./setup-autotools.sh && autoreconf -i )
--- a/vamp-server/test.sh Mon Jun 12 10:19:52 2017 +0100 +++ b/vamp-server/test.sh Mon Jun 12 10:20:25 2017 +0100 @@ -80,7 +80,7 @@ # Expected output, apart from the plugin list which seems a bit # fragile to check here cat > "$expected" <<EOF -{"id": 6, "jsonrpc": "2.0", "method": "load", "result": {"defaultConfiguration": {"channelCount": 1, "framing": {"blockSize": 1024, "stepSize": 1024}, "parameterValues": {"sensitivity": 40, "threshold": 3}}, "handle": 1, "staticData": {"basic": {"description": "Detect percussive note onsets by identifying broadband energy rises", "identifier": "percussiononsets", "name": "Simple Percussion Onset Detector"}, "basicOutputInfo": [{"description": "Percussive note onset locations", "identifier": "onsets", "name": "Onsets"}, {"description": "Broadband energy rise detection function", "identifier": "detectionfunction", "name": "Detection Function"}], "category": ["Time", "Onsets"], "inputDomain": "TimeDomain", "key": "vamp-example-plugins:percussiononsets", "maker": "Vamp SDK Example Plugins", "maxChannelCount": 1, "minChannelCount": 1, "parameters": [{"basic": {"description": "Energy rise within a frequency bin necessary to count toward broadband total", "identifier": "threshold", "name": "Energy rise threshold"}, "defaultValue": 3, "extents": {"max": 20, "min": 0}, "unit": "dB", "valueNames": []}, {"basic": {"description": "Sensitivity of peak detector applied to broadband detection function", "identifier": "sensitivity", "name": "Sensitivity"}, "defaultValue": 40, "extents": {"max": 100, "min": 0}, "unit": "%", "valueNames": []}], "programs": [], "rights": "Code copyright 2006 Queen Mary, University of London, after Dan Barry et al 2005. Freely redistributable (BSD license)", "staticOutputInfo": {}, "version": 2}}} +{"id": 6, "jsonrpc": "2.0", "method": "load", "result": {"defaultConfiguration": {"channelCount": 1, "framing": {"blockSize": 1024, "stepSize": 1024}, "parameterValues": {"sensitivity": 40, "threshold": 3}}, "handle": 1, "staticData": {"basic": {"description": "Detect percussive note onsets by identifying broadband energy rises", "identifier": "percussiononsets", "name": "Simple Percussion Onset Detector"}, "basicOutputInfo": [{"description": "Percussive note onset locations", "identifier": "onsets", "name": "Onsets"}, {"description": "Broadband energy rise detection function", "identifier": "detectionfunction", "name": "Detection Function"}], "category": ["Time", "Onsets"], "inputDomain": "TimeDomain", "key": "vamp-example-plugins:percussiononsets", "maker": "Vamp SDK Example Plugins", "maxChannelCount": 1, "minChannelCount": 1, "parameters": [{"basic": {"description": "Energy rise within a frequency bin necessary to count toward broadband total", "identifier": "threshold", "name": "Energy rise threshold"}, "defaultValue": 3, "extents": {"max": 20, "min": 0}, "unit": "dB", "valueNames": []}, {"basic": {"description": "Sensitivity of peak detector applied to broadband detection function", "identifier": "sensitivity", "name": "Sensitivity"}, "defaultValue": 40, "extents": {"max": 100, "min": 0}, "unit": "%", "valueNames": []}], "programs": [], "rights": "Code copyright 2006 Queen Mary, University of London, after Dan Barry et al 2005. Freely redistributable (BSD license)", "staticOutputInfo": {"detectionfunction": {"typeURI": "http://purl.org/ontology/af/OnsetDetectionFunction"}, "onsets": {"typeURI": "http://purl.org/ontology/af/Onset"}}, "version": 2}}} {"error": {"code": 0, "message": "error in process request: plugin has not been configured"}, "jsonrpc": "2.0", "method": "process"} {"id": "weevil", "jsonrpc": "2.0", "method": "configure", "result": {"framing": {"blockSize": 8, "stepSize": 8}, "handle": 1, "outputList": [{"basic": {"description": "Percussive note onset locations", "identifier": "onsets", "name": "Onsets"}, "configured": {"binCount": 0, "binNames": [], "hasDuration": false, "sampleRate": 44100, "sampleType": "VariableSampleRate", "unit": ""}, "static": {}}, {"basic": {"description": "Broadband energy rise detection function", "identifier": "detectionfunction", "name": "Detection Function"}, "configured": {"binCount": 1, "binNames": [""], "hasDuration": false, "quantizeStep": 1, "sampleRate": 86.1328125, "sampleType": "FixedSampleRate", "unit": ""}, "static": {}}]}} {"error": {"code": 0, "message": "error in configure request: unknown plugin handle supplied to configure"}, "id": 9, "jsonrpc": "2.0", "method": "configure"}
--- a/vamp-support/RdfTypes.h Mon Jun 12 10:19:52 2017 +0100 +++ b/vamp-support/RdfTypes.h Mon Jun 12 10:20:25 2017 +0100 @@ -47,6 +47,10 @@ namespace piper_vamp { +//!!! todo: better (+ optional) error reporting; check whether file +//!!! exists before parsing it to avoid spurious error messages; +//!!! refactoring + class RdfTypes { public: @@ -98,9 +102,16 @@ (reader, (const uint8_t *)filename.c_str()); bool success = (rv == SERD_SUCCESS); if (!success) { - std::cerr << "Failed to import RDF from " << filename << std::endl; - } else { - std::cerr << "Imported RDF from " << filename << std::endl; + // We are asking Serd to parse the file without having + // checked whether it actually exists or not (in order to + // avoid duplicating ugly platform/encoding-specific stuff + // in this file). So don't bleat if the file is simply not + // found, but only if there's a real parse error + if (rv != SERD_ERR_NOT_FOUND && + rv != SERD_ERR_UNKNOWN) { + std::cerr << "Failed to import RDF from " << filename + << ": " << serd_strerror(rv) << std::endl; + } } serd_reader_free(reader); serd_env_free(env); @@ -117,7 +128,7 @@ if (li == std::string::npos) return {}; auto withoutSuffix = library.substr(0, li); - std::vector<std::string> suffixes { "ttl", "TTL", "n3", "N3" }; + std::vector<std::string> suffixes { "n3", "N3", "ttl", "TTL" }; std::vector<std::string> candidates; for (auto suffix : suffixes) { @@ -128,9 +139,12 @@ } void - loadStaticOutputInfoFromModel(SordModel *model, std::string pluginKey, + loadStaticOutputInfoFromModel(SordModel *model, + std::string pluginKey, StaticOutputInfo &info) { + // we want to find a graph like + // // :plugin a vamp:Plugin // :plugin vamp:identifier "pluginId" // :library vamp:available_plugin :plugin @@ -141,18 +155,144 @@ // :output1 vamp:computes_event_type :event // :output2 vamp:computes_feature :feature // :output3 vamp:computes_signal_type :signal - // and where pluginKey == libraryId + ":" + pluginId - - // Although, since we know we just loaded an RDF file - // associated with one particular plugin library, we could in - // theory skip the library bits. We'd still have most of the - // rest though since each library can have multiple plugins - // with output names that could conflict + // + // in which pluginKey == libraryId + ":" + pluginId std::string libraryId, pluginId; decomposePluginKey(pluginKey, libraryId, pluginId); - //!!! + typedef const uint8_t *S; + + SordNode *a = sord_new_uri + (m_world, S("http://www.w3.org/1999/02/22-rdf-syntax-ns#type")); + + SordNode *pluginType = sord_new_uri + (m_world, S("http://purl.org/ontology/vamp/Plugin")); + + SordNode *identProp = sord_new_uri + (m_world, S("http://purl.org/ontology/vamp/identifier")); + SordNode *availProp = sord_new_uri + (m_world, S("http://purl.org/ontology/vamp/available_plugin")); + SordNode *outputProp = sord_new_uri + (m_world, S("http://purl.org/ontology/vamp/output")); + + SordNode *computesEventProp = sord_new_uri + (m_world, S("http://purl.org/ontology/vamp/computes_event_type")); + SordNode *computesFeatureProp = sord_new_uri + (m_world, S("http://purl.org/ontology/vamp/computes_feature")); + SordNode *computesSignalProp = sord_new_uri + (m_world, S("http://purl.org/ontology/vamp/computes_signal_type")); + + SordIter *pluginItr = 0; + + for (pluginItr = sord_search(model, 0, a, pluginType, 0); + !sord_iter_end(pluginItr); + sord_iter_next(pluginItr)) { + + const SordNode *pluginNode = + sord_iter_get_node(pluginItr, SORD_SUBJECT); + + SordNode *pluginIdNode = + sord_get(model, pluginNode, identProp, 0, 0); + + if (!pluginIdNode || + sord_node_get_type(pluginIdNode) != SORD_LITERAL || + (const char *)sord_node_get_string(pluginIdNode) != pluginId) { + // This is a plugin node, but it's not the plugin node + // we're looking for. (We have to check both the type + // property, vamp:Plugin, and the identifier, + // vamp:identifier, because the identifier is just a + // string and it's possible it could be used for an + // output or parameter rather than just a plugin.) + continue; + } + + SordNode *libraryNode = + sord_get(model, 0, availProp, pluginNode, 0); + + if (!libraryNode) { + std::cerr << "Plugin is not listed as being in a library, " + << "skipping library id check" << std::endl; + } else { + SordNode *libIdNode = + sord_get(model, libraryNode, identProp, 0, 0); + if (!libIdNode || + sord_node_get_type(libIdNode) != SORD_LITERAL || + (const char *)sord_node_get_string(libIdNode) != libraryId) { + std::cerr << "Ignoring plugin in wrong library" << std::endl; + continue; + } + } + + SordIter *outputItr = 0; + + for (outputItr = sord_search(model, pluginNode, outputProp, 0, 0); + !sord_iter_end(outputItr); + sord_iter_next(outputItr)) { + + const SordNode *outputNode = + sord_iter_get_node(outputItr, SORD_OBJECT); + + SordNode *outputIdNode = + sord_get(model, outputNode, identProp, 0, 0); + + if (!outputIdNode || + sord_node_get_type(outputIdNode) != SORD_LITERAL || + !sord_node_get_string(outputIdNode)) { + std::cerr << "Ignoring output with no id" << std::endl; + continue; + } + + std::string outputId = + (const char *)sord_node_get_string(outputIdNode); + + SordIter *propItr = 0; + + for (propItr = sord_search(model, outputNode, 0, 0, 0); + !sord_iter_end(propItr); + sord_iter_next(propItr)) { + + const SordNode *propNode = + sord_iter_get_node(propItr, SORD_PREDICATE); + + if (sord_node_equals(propNode, computesEventProp) || + sord_node_equals(propNode, computesFeatureProp) || + sord_node_equals(propNode, computesSignalProp)) { + + const SordNode *computesNode = + sord_iter_get_node(propItr, SORD_OBJECT); + + if (sord_node_get_type(computesNode) != SORD_URI || + !sord_node_get_string(computesNode)) { + std::cerr << "Ignoring non-URI computes node" + << std::endl; + continue; + } + + std::string typeURI = + (const char *)sord_node_get_string(computesNode); + + std::cerr << "Found type <" << typeURI + << "> for output \"" << outputId + << "\" of plugin \"" << pluginId + << "\" in library " << libraryId + << std::endl; + + StaticOutputDescriptor desc; + desc.typeURI = typeURI; + info[outputId] = desc; + + break; // only interested in one "computes" property + } + } + + sord_iter_free(propItr); + } + + sord_iter_free(outputItr); + } + + sord_iter_free(pluginItr); } bool decomposePluginKey(std::string pluginKey,