Mercurial > hg > piper-cpp
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 |