diff vamp-support/StaticOutputRdf.h @ 244:f548eb11ae01

Rename RdfTypes (which sounds like a set of type declarations) to StaticOutputRdf
author Chris Cannam <cannam@all-day-breakfast.com>
date Thu, 15 Jun 2017 09:09:07 +0100
parents
children b10018fcd5eb
line wrap: on
line diff
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/vamp-support/StaticOutputRdf.h	Thu Jun 15 09:09:07 2017 +0100
@@ -0,0 +1,314 @@
+/* -*- c-basic-offset: 4 indent-tabs-mode: nil -*-  vi:set ts=8 sts=4 sw=4: */
+
+/*
+    Piper C++
+
+    An API for audio analysis and feature extraction plugins.
+
+    Centre for Digital Music, Queen Mary, University of London.
+    Copyright 2006-2017 Chris Cannam and QMUL.
+  
+    Permission is hereby granted, free of charge, to any person
+    obtaining a copy of this software and associated documentation
+    files (the "Software"), to deal in the Software without
+    restriction, including without limitation the rights to use, copy,
+    modify, merge, publish, distribute, sublicense, and/or sell copies
+    of the Software, and to permit persons to whom the Software is
+    furnished to do so, subject to the following conditions:
+
+    The above copyright notice and this permission notice shall be
+    included in all copies or substantial portions of the Software.
+
+    THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+    EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+    MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+    NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR
+    ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF
+    CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+    WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+
+    Except as contained in this notice, the names of the Centre for
+    Digital Music; Queen Mary, University of London; and Chris Cannam
+    shall not be used in advertising or otherwise to promote the sale,
+    use or other dealings in this Software without prior written
+    authorization.
+*/
+
+#ifndef PIPER_STATIC_OUTPUT_RDF_H
+#define PIPER_STATIC_OUTPUT_RDF_H
+
+#include "StaticOutputDescriptor.h"
+
+#include <vamp-hostsdk/PluginLoader.h>
+
+#include <sord/sord.h>
+
+#include <mutex>
+
+namespace piper_vamp {
+
+//!!! todo: better (+ optional) error reporting; check whether file
+//!!! exists before parsing it to avoid spurious error messages;
+//!!! refactoring
+
+class StaticOutputRdf
+{
+public:
+    StaticOutputRdf() :
+        m_world(sord_world_new())
+    {}
+
+    ~StaticOutputRdf() {
+        sord_world_free(m_world);
+    }
+    
+    StaticOutputInfo loadStaticOutputInfo(Vamp::HostExt::PluginLoader::PluginKey
+                                          pluginKey) {
+
+        StaticOutputInfo info;
+        SordModel *model = sord_new(m_world, SORD_SPO|SORD_OPS|SORD_POS, false);
+        if (loadRdf(model, candidateRdfFilesFor(pluginKey))) {
+            loadStaticOutputInfoFromModel(model, pluginKey, info);
+        }
+        sord_free(model);
+        return info;
+    }
+
+private:
+    SordWorld *m_world;
+
+    bool loadRdf(SordModel *targetModel, std::vector<std::string> filenames) {
+        for (auto f: filenames) {
+            if (loadRdfFile(targetModel, f)) {
+                return true;
+            }
+        }
+        return false;
+    }
+
+    bool loadRdfFile(SordModel *targetModel, std::string filename) {
+        std::string base = "file://" + filename;
+        SerdURI bu;
+        if (serd_uri_parse((const uint8_t *)base.c_str(), &bu) !=
+            SERD_SUCCESS) {
+            std::cerr << "Failed to parse base URI " << base << std::endl;
+            return false;
+        }
+        SerdNode bn = serd_node_from_string(SERD_URI,
+                                            (const uint8_t *)base.c_str());
+        SerdEnv *env = serd_env_new(&bn);
+        SerdReader *reader = sord_new_reader(targetModel, env, SERD_TURTLE, 0);
+        SerdStatus rv = serd_reader_read_file
+            (reader, (const uint8_t *)filename.c_str());
+        bool success = (rv == SERD_SUCCESS);
+        if (!success) {
+            // 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);
+        return success;
+    }
+    
+    std::vector<std::string> candidateRdfFilesFor(Vamp::HostExt::
+                                                  PluginLoader::PluginKey key) {
+
+        std::string library = Vamp::HostExt::PluginLoader::getInstance()->
+            getLibraryPathForPlugin(key);
+	
+        auto li = library.rfind('.');
+        if (li == std::string::npos) return {};
+        auto withoutSuffix = library.substr(0, li);
+
+        std::vector<std::string> suffixes { "n3", "N3", "ttl", "TTL" };
+        std::vector<std::string> candidates;
+
+        for (auto suffix : suffixes) {
+            candidates.push_back(withoutSuffix + "." + suffix);
+        }
+
+        return candidates;
+    }
+
+    void
+    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
+        // :library vamp:identifier "libraryId"
+        // :plugin vamp:output :output1
+        // :plugin vamp:output :output2
+        // :plugin vamp:output :output3
+        // :output1 vamp:computes_event_type :event
+        // :output2 vamp:computes_feature :feature
+        // :output3 vamp:computes_signal_type :signal
+        //
+        // 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,
+                            std::string &libraryId,
+                            std::string &pluginId) {
+        auto i = pluginKey.find(':');
+        if (i == std::string::npos || i == 0 || i + 1 == pluginKey.length()) {
+            return false;
+        }
+        libraryId = pluginKey.substr(0, i);
+        pluginId = pluginKey.substr(i + 1);
+        return true;
+    }
+                            
+};
+
+}
+
+#endif