comparison 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
comparison
equal deleted inserted replaced
243:498f899a91b6 244:f548eb11ae01
1 /* -*- c-basic-offset: 4 indent-tabs-mode: nil -*- vi:set ts=8 sts=4 sw=4: */
2
3 /*
4 Piper C++
5
6 An API for audio analysis and feature extraction plugins.
7
8 Centre for Digital Music, Queen Mary, University of London.
9 Copyright 2006-2017 Chris Cannam and QMUL.
10
11 Permission is hereby granted, free of charge, to any person
12 obtaining a copy of this software and associated documentation
13 files (the "Software"), to deal in the Software without
14 restriction, including without limitation the rights to use, copy,
15 modify, merge, publish, distribute, sublicense, and/or sell copies
16 of the Software, and to permit persons to whom the Software is
17 furnished to do so, subject to the following conditions:
18
19 The above copyright notice and this permission notice shall be
20 included in all copies or substantial portions of the Software.
21
22 THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
23 EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
24 MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
25 NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR
26 ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF
27 CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
28 WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
29
30 Except as contained in this notice, the names of the Centre for
31 Digital Music; Queen Mary, University of London; and Chris Cannam
32 shall not be used in advertising or otherwise to promote the sale,
33 use or other dealings in this Software without prior written
34 authorization.
35 */
36
37 #ifndef PIPER_STATIC_OUTPUT_RDF_H
38 #define PIPER_STATIC_OUTPUT_RDF_H
39
40 #include "StaticOutputDescriptor.h"
41
42 #include <vamp-hostsdk/PluginLoader.h>
43
44 #include <sord/sord.h>
45
46 #include <mutex>
47
48 namespace piper_vamp {
49
50 //!!! todo: better (+ optional) error reporting; check whether file
51 //!!! exists before parsing it to avoid spurious error messages;
52 //!!! refactoring
53
54 class StaticOutputRdf
55 {
56 public:
57 StaticOutputRdf() :
58 m_world(sord_world_new())
59 {}
60
61 ~StaticOutputRdf() {
62 sord_world_free(m_world);
63 }
64
65 StaticOutputInfo loadStaticOutputInfo(Vamp::HostExt::PluginLoader::PluginKey
66 pluginKey) {
67
68 StaticOutputInfo info;
69 SordModel *model = sord_new(m_world, SORD_SPO|SORD_OPS|SORD_POS, false);
70 if (loadRdf(model, candidateRdfFilesFor(pluginKey))) {
71 loadStaticOutputInfoFromModel(model, pluginKey, info);
72 }
73 sord_free(model);
74 return info;
75 }
76
77 private:
78 SordWorld *m_world;
79
80 bool loadRdf(SordModel *targetModel, std::vector<std::string> filenames) {
81 for (auto f: filenames) {
82 if (loadRdfFile(targetModel, f)) {
83 return true;
84 }
85 }
86 return false;
87 }
88
89 bool loadRdfFile(SordModel *targetModel, std::string filename) {
90 std::string base = "file://" + filename;
91 SerdURI bu;
92 if (serd_uri_parse((const uint8_t *)base.c_str(), &bu) !=
93 SERD_SUCCESS) {
94 std::cerr << "Failed to parse base URI " << base << std::endl;
95 return false;
96 }
97 SerdNode bn = serd_node_from_string(SERD_URI,
98 (const uint8_t *)base.c_str());
99 SerdEnv *env = serd_env_new(&bn);
100 SerdReader *reader = sord_new_reader(targetModel, env, SERD_TURTLE, 0);
101 SerdStatus rv = serd_reader_read_file
102 (reader, (const uint8_t *)filename.c_str());
103 bool success = (rv == SERD_SUCCESS);
104 if (!success) {
105 // We are asking Serd to parse the file without having
106 // checked whether it actually exists or not (in order to
107 // avoid duplicating ugly platform/encoding-specific stuff
108 // in this file). So don't bleat if the file is simply not
109 // found, but only if there's a real parse error
110 if (rv != SERD_ERR_NOT_FOUND &&
111 rv != SERD_ERR_UNKNOWN) {
112 std::cerr << "Failed to import RDF from " << filename
113 << ": " << serd_strerror(rv) << std::endl;
114 }
115 }
116 serd_reader_free(reader);
117 serd_env_free(env);
118 return success;
119 }
120
121 std::vector<std::string> candidateRdfFilesFor(Vamp::HostExt::
122 PluginLoader::PluginKey key) {
123
124 std::string library = Vamp::HostExt::PluginLoader::getInstance()->
125 getLibraryPathForPlugin(key);
126
127 auto li = library.rfind('.');
128 if (li == std::string::npos) return {};
129 auto withoutSuffix = library.substr(0, li);
130
131 std::vector<std::string> suffixes { "n3", "N3", "ttl", "TTL" };
132 std::vector<std::string> candidates;
133
134 for (auto suffix : suffixes) {
135 candidates.push_back(withoutSuffix + "." + suffix);
136 }
137
138 return candidates;
139 }
140
141 void
142 loadStaticOutputInfoFromModel(SordModel *model,
143 std::string pluginKey,
144 StaticOutputInfo &info) {
145
146 // we want to find a graph like
147 //
148 // :plugin a vamp:Plugin
149 // :plugin vamp:identifier "pluginId"
150 // :library vamp:available_plugin :plugin
151 // :library vamp:identifier "libraryId"
152 // :plugin vamp:output :output1
153 // :plugin vamp:output :output2
154 // :plugin vamp:output :output3
155 // :output1 vamp:computes_event_type :event
156 // :output2 vamp:computes_feature :feature
157 // :output3 vamp:computes_signal_type :signal
158 //
159 // in which pluginKey == libraryId + ":" + pluginId
160
161 std::string libraryId, pluginId;
162 decomposePluginKey(pluginKey, libraryId, pluginId);
163
164 typedef const uint8_t *S;
165
166 SordNode *a = sord_new_uri
167 (m_world, S("http://www.w3.org/1999/02/22-rdf-syntax-ns#type"));
168
169 SordNode *pluginType = sord_new_uri
170 (m_world, S("http://purl.org/ontology/vamp/Plugin"));
171
172 SordNode *identProp = sord_new_uri
173 (m_world, S("http://purl.org/ontology/vamp/identifier"));
174 SordNode *availProp = sord_new_uri
175 (m_world, S("http://purl.org/ontology/vamp/available_plugin"));
176 SordNode *outputProp = sord_new_uri
177 (m_world, S("http://purl.org/ontology/vamp/output"));
178
179 SordNode *computesEventProp = sord_new_uri
180 (m_world, S("http://purl.org/ontology/vamp/computes_event_type"));
181 SordNode *computesFeatureProp = sord_new_uri
182 (m_world, S("http://purl.org/ontology/vamp/computes_feature"));
183 SordNode *computesSignalProp = sord_new_uri
184 (m_world, S("http://purl.org/ontology/vamp/computes_signal_type"));
185
186 SordIter *pluginItr = 0;
187
188 for (pluginItr = sord_search(model, 0, a, pluginType, 0);
189 !sord_iter_end(pluginItr);
190 sord_iter_next(pluginItr)) {
191
192 const SordNode *pluginNode =
193 sord_iter_get_node(pluginItr, SORD_SUBJECT);
194
195 SordNode *pluginIdNode =
196 sord_get(model, pluginNode, identProp, 0, 0);
197
198 if (!pluginIdNode ||
199 sord_node_get_type(pluginIdNode) != SORD_LITERAL ||
200 (const char *)sord_node_get_string(pluginIdNode) != pluginId) {
201 // This is a plugin node, but it's not the plugin node
202 // we're looking for. (We have to check both the type
203 // property, vamp:Plugin, and the identifier,
204 // vamp:identifier, because the identifier is just a
205 // string and it's possible it could be used for an
206 // output or parameter rather than just a plugin.)
207 continue;
208 }
209
210 SordNode *libraryNode =
211 sord_get(model, 0, availProp, pluginNode, 0);
212
213 if (!libraryNode) {
214 std::cerr << "Plugin is not listed as being in a library, "
215 << "skipping library id check" << std::endl;
216 } else {
217 SordNode *libIdNode =
218 sord_get(model, libraryNode, identProp, 0, 0);
219 if (!libIdNode ||
220 sord_node_get_type(libIdNode) != SORD_LITERAL ||
221 (const char *)sord_node_get_string(libIdNode) != libraryId) {
222 std::cerr << "Ignoring plugin in wrong library" << std::endl;
223 continue;
224 }
225 }
226
227 SordIter *outputItr = 0;
228
229 for (outputItr = sord_search(model, pluginNode, outputProp, 0, 0);
230 !sord_iter_end(outputItr);
231 sord_iter_next(outputItr)) {
232
233 const SordNode *outputNode =
234 sord_iter_get_node(outputItr, SORD_OBJECT);
235
236 SordNode *outputIdNode =
237 sord_get(model, outputNode, identProp, 0, 0);
238
239 if (!outputIdNode ||
240 sord_node_get_type(outputIdNode) != SORD_LITERAL ||
241 !sord_node_get_string(outputIdNode)) {
242 std::cerr << "Ignoring output with no id" << std::endl;
243 continue;
244 }
245
246 std::string outputId =
247 (const char *)sord_node_get_string(outputIdNode);
248
249 SordIter *propItr = 0;
250
251 for (propItr = sord_search(model, outputNode, 0, 0, 0);
252 !sord_iter_end(propItr);
253 sord_iter_next(propItr)) {
254
255 const SordNode *propNode =
256 sord_iter_get_node(propItr, SORD_PREDICATE);
257
258 if (sord_node_equals(propNode, computesEventProp) ||
259 sord_node_equals(propNode, computesFeatureProp) ||
260 sord_node_equals(propNode, computesSignalProp)) {
261
262 const SordNode *computesNode =
263 sord_iter_get_node(propItr, SORD_OBJECT);
264
265 if (sord_node_get_type(computesNode) != SORD_URI ||
266 !sord_node_get_string(computesNode)) {
267 std::cerr << "Ignoring non-URI computes node"
268 << std::endl;
269 continue;
270 }
271
272 std::string typeURI =
273 (const char *)sord_node_get_string(computesNode);
274
275 std::cerr << "Found type <" << typeURI
276 << "> for output \"" << outputId
277 << "\" of plugin \"" << pluginId
278 << "\" in library " << libraryId
279 << std::endl;
280
281 StaticOutputDescriptor desc;
282 desc.typeURI = typeURI;
283 info[outputId] = desc;
284
285 break; // only interested in one "computes" property
286 }
287 }
288
289 sord_iter_free(propItr);
290 }
291
292 sord_iter_free(outputItr);
293 }
294
295 sord_iter_free(pluginItr);
296 }
297
298 bool decomposePluginKey(std::string pluginKey,
299 std::string &libraryId,
300 std::string &pluginId) {
301 auto i = pluginKey.find(':');
302 if (i == std::string::npos || i == 0 || i + 1 == pluginKey.length()) {
303 return false;
304 }
305 libraryId = pluginKey.substr(0, i);
306 pluginId = pluginKey.substr(i + 1);
307 return true;
308 }
309
310 };
311
312 }
313
314 #endif