Chris@439
|
1 /* -*- c-basic-offset: 4 indent-tabs-mode: nil -*- vi:set ts=8 sts=4 sw=4: */
|
Chris@439
|
2
|
Chris@439
|
3 /*
|
Chris@439
|
4 Sonic Visualiser
|
Chris@439
|
5 An audio file viewer and annotation editor.
|
Chris@439
|
6 Centre for Digital Music, Queen Mary, University of London.
|
Chris@727
|
7 This file copyright 2008-2012 QMUL.
|
Chris@439
|
8
|
Chris@439
|
9 This program is free software; you can redistribute it and/or
|
Chris@439
|
10 modify it under the terms of the GNU General Public License as
|
Chris@439
|
11 published by the Free Software Foundation; either version 2 of the
|
Chris@439
|
12 License, or (at your option) any later version. See the file
|
Chris@439
|
13 COPYING included with this distribution for more information.
|
Chris@439
|
14 */
|
Chris@439
|
15
|
Chris@439
|
16 #include "RDFTransformFactory.h"
|
Chris@439
|
17
|
Chris@439
|
18 #include <map>
|
Chris@439
|
19 #include <vector>
|
Chris@439
|
20
|
Chris@494
|
21 #include <QTextStream>
|
Chris@592
|
22 #include <QUrl>
|
Chris@494
|
23
|
Chris@439
|
24 #include <iostream>
|
Chris@439
|
25 #include <cmath>
|
Chris@439
|
26
|
Chris@439
|
27 #include "PluginRDFIndexer.h"
|
Chris@494
|
28 #include "PluginRDFDescription.h"
|
Chris@439
|
29 #include "base/ProgressReporter.h"
|
Chris@503
|
30 #include "plugin/PluginIdentifier.h"
|
Chris@439
|
31
|
Chris@439
|
32 #include "transform/TransformFactory.h"
|
Chris@439
|
33
|
Chris@727
|
34 #include <dataquay/BasicStore.h>
|
Chris@727
|
35 #include <dataquay/PropertyObject.h>
|
Chris@727
|
36
|
Chris@439
|
37 using std::cerr;
|
Chris@439
|
38 using std::endl;
|
Chris@439
|
39
|
Chris@727
|
40 using Dataquay::Uri;
|
Chris@727
|
41 using Dataquay::Node;
|
Chris@727
|
42 using Dataquay::Nodes;
|
Chris@727
|
43 using Dataquay::Triple;
|
Chris@727
|
44 using Dataquay::Triples;
|
Chris@727
|
45 using Dataquay::BasicStore;
|
Chris@727
|
46 using Dataquay::PropertyObject;
|
Chris@439
|
47
|
Chris@439
|
48
|
Chris@439
|
49 class RDFTransformFactoryImpl
|
Chris@439
|
50 {
|
Chris@439
|
51 public:
|
Chris@439
|
52 RDFTransformFactoryImpl(QString url);
|
Chris@439
|
53 virtual ~RDFTransformFactoryImpl();
|
Chris@439
|
54
|
Chris@493
|
55 bool isRDF();
|
Chris@439
|
56 bool isOK();
|
Chris@439
|
57 QString getErrorString() const;
|
Chris@439
|
58
|
Chris@439
|
59 std::vector<Transform> getTransforms(ProgressReporter *);
|
Chris@439
|
60
|
Chris@494
|
61 static QString writeTransformToRDF(const Transform &, QString);
|
Chris@494
|
62
|
Chris@439
|
63 protected:
|
Chris@727
|
64 BasicStore *m_store;
|
Chris@439
|
65 QString m_urlString;
|
Chris@439
|
66 QString m_errorString;
|
Chris@493
|
67 bool m_isRDF;
|
Chris@489
|
68 bool setOutput(Transform &, QString);
|
Chris@489
|
69 bool setParameters(Transform &, QString);
|
Chris@439
|
70 };
|
Chris@439
|
71
|
Chris@439
|
72
|
Chris@439
|
73 QString
|
Chris@439
|
74 RDFTransformFactory::getKnownExtensions()
|
Chris@439
|
75 {
|
Chris@439
|
76 return "*.rdf *.n3 *.ttl";
|
Chris@439
|
77 }
|
Chris@439
|
78
|
Chris@439
|
79 RDFTransformFactory::RDFTransformFactory(QString url) :
|
Chris@439
|
80 m_d(new RDFTransformFactoryImpl(url))
|
Chris@439
|
81 {
|
Chris@439
|
82 }
|
Chris@439
|
83
|
Chris@439
|
84 RDFTransformFactory::~RDFTransformFactory()
|
Chris@439
|
85 {
|
Chris@439
|
86 delete m_d;
|
Chris@439
|
87 }
|
Chris@439
|
88
|
Chris@439
|
89 bool
|
Chris@493
|
90 RDFTransformFactory::isRDF()
|
Chris@493
|
91 {
|
Chris@493
|
92 return m_d->isRDF();
|
Chris@493
|
93 }
|
Chris@493
|
94
|
Chris@493
|
95 bool
|
Chris@439
|
96 RDFTransformFactory::isOK()
|
Chris@439
|
97 {
|
Chris@439
|
98 return m_d->isOK();
|
Chris@439
|
99 }
|
Chris@439
|
100
|
Chris@439
|
101 QString
|
Chris@439
|
102 RDFTransformFactory::getErrorString() const
|
Chris@439
|
103 {
|
Chris@439
|
104 return m_d->getErrorString();
|
Chris@439
|
105 }
|
Chris@439
|
106
|
Chris@439
|
107 std::vector<Transform>
|
Chris@439
|
108 RDFTransformFactory::getTransforms(ProgressReporter *r)
|
Chris@439
|
109 {
|
Chris@439
|
110 return m_d->getTransforms(r);
|
Chris@439
|
111 }
|
Chris@439
|
112
|
Chris@494
|
113 QString
|
Chris@494
|
114 RDFTransformFactory::writeTransformToRDF(const Transform &t, QString f)
|
Chris@494
|
115 {
|
Chris@494
|
116 return RDFTransformFactoryImpl::writeTransformToRDF(t, f);
|
Chris@494
|
117 }
|
Chris@494
|
118
|
Chris@439
|
119 RDFTransformFactoryImpl::RDFTransformFactoryImpl(QString url) :
|
Chris@727
|
120 m_store(new BasicStore),
|
Chris@493
|
121 m_urlString(url),
|
Chris@493
|
122 m_isRDF(false)
|
Chris@439
|
123 {
|
Chris@727
|
124 //!!! retrieve data if remote... then
|
Chris@727
|
125 m_store->addPrefix("vamp", Uri("http://purl.org/ontology/vamp/"));
|
Chris@727
|
126 try {
|
Chris@727
|
127 m_store->import(QUrl::fromLocalFile(url), BasicStore::ImportIgnoreDuplicates);
|
Chris@727
|
128 m_isRDF = true;
|
Chris@727
|
129 } catch (...) { }
|
Chris@439
|
130 }
|
Chris@439
|
131
|
Chris@439
|
132 RDFTransformFactoryImpl::~RDFTransformFactoryImpl()
|
Chris@439
|
133 {
|
Chris@727
|
134 delete m_store;
|
Chris@439
|
135 }
|
Chris@439
|
136
|
Chris@439
|
137 bool
|
Chris@493
|
138 RDFTransformFactoryImpl::isRDF()
|
Chris@493
|
139 {
|
Chris@493
|
140 return m_isRDF;
|
Chris@493
|
141 }
|
Chris@493
|
142
|
Chris@493
|
143 bool
|
Chris@439
|
144 RDFTransformFactoryImpl::isOK()
|
Chris@439
|
145 {
|
Chris@439
|
146 return (m_errorString == "");
|
Chris@439
|
147 }
|
Chris@439
|
148
|
Chris@439
|
149 QString
|
Chris@439
|
150 RDFTransformFactoryImpl::getErrorString() const
|
Chris@439
|
151 {
|
Chris@439
|
152 return m_errorString;
|
Chris@439
|
153 }
|
Chris@439
|
154
|
Chris@439
|
155 std::vector<Transform>
|
Chris@439
|
156 RDFTransformFactoryImpl::getTransforms(ProgressReporter *reporter)
|
Chris@439
|
157 {
|
Chris@439
|
158 std::vector<Transform> transforms;
|
Chris@439
|
159
|
Chris@440
|
160 std::map<QString, Transform> uriTransformMap;
|
Chris@439
|
161
|
Chris@727
|
162 Nodes tnodes = m_store->match
|
Chris@727
|
163 (Triple(Node(), "a", m_store->expand("vamp:Transform"))).a();
|
Chris@439
|
164
|
Chris@728
|
165 PluginRDFIndexer *indexer = PluginRDFIndexer::getInstance();
|
Chris@728
|
166
|
Chris@727
|
167 foreach (Node tnode, tnodes) {
|
Chris@439
|
168
|
Chris@727
|
169 Node pnode = m_store->matchFirst
|
Chris@727
|
170 (Triple(tnode, "vamp:plugin", Node())).c;
|
Chris@440
|
171
|
Chris@728
|
172 if (pnode == Node()) {
|
Chris@728
|
173 cerr << "RDFTransformFactory: WARNING: No vamp:plugin for "
|
Chris@728
|
174 << "vamp:Transform node " << tnode
|
Chris@728
|
175 << ", skipping this transform" << endl;
|
Chris@728
|
176 continue;
|
Chris@728
|
177 }
|
Chris@440
|
178
|
Chris@728
|
179 QString transformUri = tnode.value;
|
Chris@728
|
180 QString pluginUri = pnode.value;
|
Chris@439
|
181
|
Chris@439
|
182 QString pluginId = indexer->getIdForPluginURI(pluginUri);
|
Chris@439
|
183 if (pluginId == "") {
|
Chris@439
|
184 cerr << "RDFTransformFactory: WARNING: Unknown plugin <"
|
Chris@686
|
185 << pluginUri << "> for transform <"
|
Chris@686
|
186 << transformUri << ">, skipping this transform"
|
Chris@440
|
187 << endl;
|
Chris@440
|
188 continue;
|
Chris@440
|
189 }
|
Chris@440
|
190
|
Chris@439
|
191 Transform transform;
|
Chris@439
|
192 transform.setPluginIdentifier(pluginId);
|
Chris@439
|
193
|
Chris@489
|
194 if (!setOutput(transform, transformUri)) {
|
Chris@439
|
195 return transforms;
|
Chris@439
|
196 }
|
Chris@439
|
197
|
Chris@489
|
198 if (!setParameters(transform, transformUri)) {
|
Chris@439
|
199 return transforms;
|
Chris@439
|
200 }
|
Chris@439
|
201
|
Chris@440
|
202 uriTransformMap[transformUri] = transform;
|
Chris@439
|
203
|
Chris@489
|
204 static const char *optionals[] = {
|
Chris@489
|
205 "program",
|
Chris@508
|
206 "summary_type",
|
Chris@489
|
207 "step_size",
|
Chris@489
|
208 "block_size",
|
Chris@489
|
209 "window_type",
|
Chris@489
|
210 "sample_rate",
|
Chris@489
|
211 "start",
|
Chris@489
|
212 "duration"
|
Chris@489
|
213 };
|
Chris@489
|
214
|
Chris@489
|
215 for (int j = 0; j < sizeof(optionals)/sizeof(optionals[0]); ++j) {
|
Chris@439
|
216
|
Chris@489
|
217 QString optional = optionals[j];
|
Chris@489
|
218
|
Chris@728
|
219 Node onode = m_store->matchFirst
|
Chris@728
|
220 (Triple(Uri(transformUri), optional, Node())).c;
|
Chris@440
|
221
|
Chris@728
|
222 if (onode.type != Node::Literal) continue;
|
Chris@440
|
223
|
Chris@728
|
224 if (optional == "program") {
|
Chris@728
|
225 transform.setProgram(onode.value);
|
Chris@728
|
226 } else if (optional == "summary_type") {
|
Chris@728
|
227 transform.setSummaryType
|
Chris@728
|
228 (transform.stringToSummaryType(onode.value));
|
Chris@728
|
229 } else if (optional == "step_size") {
|
Chris@728
|
230 transform.setStepSize(onode.value.toUInt());
|
Chris@728
|
231 } else if (optional == "block_size") {
|
Chris@728
|
232 transform.setBlockSize(onode.value.toUInt());
|
Chris@728
|
233 } else if (optional == "window_type") {
|
Chris@728
|
234 transform.setWindowType
|
Chris@728
|
235 (Window<float>::getTypeForName
|
Chris@728
|
236 (onode.value.toLower().toStdString()));
|
Chris@728
|
237 } else if (optional == "sample_rate") {
|
Chris@728
|
238 transform.setSampleRate(onode.value.toFloat());
|
Chris@728
|
239 } else if (optional == "start") {
|
Chris@728
|
240 transform.setStartTime
|
Chris@728
|
241 (RealTime::fromXsdDuration(onode.value.toStdString()));
|
Chris@728
|
242 } else if (optional == "duration") {
|
Chris@728
|
243 transform.setDuration
|
Chris@728
|
244 (RealTime::fromXsdDuration(onode.value.toStdString()));
|
Chris@728
|
245 } else {
|
Chris@728
|
246 cerr << "RDFTransformFactory: ERROR: Inconsistent optionals lists (unexpected optional \"" << optional << "\"" << endl;
|
Chris@440
|
247 }
|
Chris@440
|
248 }
|
Chris@440
|
249
|
Chris@690
|
250 SVDEBUG << "RDFTransformFactory: NOTE: Transform is: " << endl;
|
Chris@686
|
251 cerr << transform.toXmlString() << endl;
|
Chris@439
|
252
|
Chris@439
|
253 transforms.push_back(transform);
|
Chris@439
|
254 }
|
Chris@439
|
255
|
Chris@439
|
256 return transforms;
|
Chris@439
|
257 }
|
Chris@439
|
258
|
Chris@440
|
259 bool
|
Chris@440
|
260 RDFTransformFactoryImpl::setOutput(Transform &transform,
|
Chris@489
|
261 QString transformUri)
|
Chris@440
|
262 {
|
Chris@728
|
263 Node outputNode = m_store->matchFirst
|
Chris@728
|
264 (Triple(Uri(transformUri), "vamp:output", Node())).c;
|
Chris@728
|
265
|
Chris@728
|
266 if (outputNode == Node()) return true;
|
Chris@489
|
267
|
Chris@728
|
268 if (outputNode.type != Node::URI && outputNode.type != Node::Blank) {
|
Chris@728
|
269 m_errorString = QString("vamp:output for output of transform <%1> is not a URI or blank node").arg(transformUri);
|
Chris@728
|
270 return false;
|
Chris@489
|
271 }
|
Chris@728
|
272
|
Chris@728
|
273 // Now, outputNode might be the subject of a triple within m_store
|
Chris@728
|
274 // that tells us the vamp:identifier, or it might be the subject
|
Chris@728
|
275 // of a triple within the indexer that tells us it
|
Chris@728
|
276
|
Chris@728
|
277 Node identNode = m_store->matchFirst
|
Chris@728
|
278 (Triple(outputNode, "vamp:identifier", Node())).c;
|
Chris@728
|
279
|
Chris@728
|
280 if (identNode == Node()) {
|
Chris@728
|
281 PluginRDFIndexer *indexer = PluginRDFIndexer::getInstance();
|
Chris@728
|
282 const BasicStore *index = indexer->getIndex();
|
Chris@728
|
283 identNode = index->matchFirst
|
Chris@728
|
284 (Triple(outputNode, "vamp:identifier", Node())).c;
|
Chris@728
|
285 }
|
Chris@728
|
286
|
Chris@728
|
287 if (identNode == Node() || identNode.type != Node::Literal) {
|
Chris@494
|
288 m_errorString = QString("No vamp:identifier found for output of transform <%1>, or vamp:identifier is not a literal").arg(transformUri);
|
Chris@489
|
289 return false;
|
Chris@489
|
290 }
|
Chris@489
|
291
|
Chris@728
|
292 transform.setOutput(identNode.value);
|
Chris@440
|
293
|
Chris@440
|
294 return true;
|
Chris@440
|
295 }
|
Chris@440
|
296
|
Chris@440
|
297
|
Chris@440
|
298 bool
|
Chris@440
|
299 RDFTransformFactoryImpl::setParameters(Transform &transform,
|
Chris@489
|
300 QString transformUri)
|
Chris@440
|
301 {
|
Chris@440
|
302 SimpleSPARQLQuery paramQuery
|
Chris@489
|
303 (SimpleSPARQLQuery::QueryFromModel,
|
Chris@480
|
304 QString
|
Chris@440
|
305 (
|
Chris@440
|
306 " PREFIX vamp: <http://purl.org/ontology/vamp/> "
|
Chris@440
|
307
|
Chris@440
|
308 " SELECT ?param_id ?param_value "
|
Chris@440
|
309
|
Chris@440
|
310 " WHERE { "
|
Chris@489
|
311 " <%1> vamp:parameter_binding ?binding . "
|
Chris@440
|
312 " ?binding vamp:parameter ?param ; "
|
Chris@440
|
313 " vamp:value ?param_value . "
|
Chris@440
|
314 " ?param vamp:identifier ?param_id "
|
Chris@440
|
315 " } "
|
Chris@440
|
316 )
|
Chris@440
|
317 .arg(transformUri));
|
Chris@440
|
318
|
Chris@440
|
319 SimpleSPARQLQuery::ResultList paramResults = paramQuery.execute();
|
Chris@440
|
320
|
Chris@440
|
321 if (!paramQuery.isOK()) {
|
Chris@440
|
322 m_errorString = paramQuery.getErrorString();
|
Chris@440
|
323 return false;
|
Chris@440
|
324 }
|
Chris@440
|
325
|
Chris@440
|
326 if (paramQuery.wasCancelled()) {
|
Chris@440
|
327 m_errorString = "Query cancelled";
|
Chris@440
|
328 return false;
|
Chris@440
|
329 }
|
Chris@440
|
330
|
Chris@440
|
331 for (int j = 0; j < paramResults.size(); ++j) {
|
Chris@440
|
332
|
Chris@440
|
333 QString paramId = paramResults[j]["param_id"].value;
|
Chris@440
|
334 QString paramValue = paramResults[j]["param_value"].value;
|
Chris@440
|
335
|
Chris@440
|
336 if (paramId == "" || paramValue == "") continue;
|
Chris@440
|
337
|
Chris@440
|
338 transform.setParameter(paramId, paramValue.toFloat());
|
Chris@440
|
339 }
|
Chris@440
|
340
|
Chris@440
|
341 return true;
|
Chris@440
|
342 }
|
Chris@440
|
343
|
Chris@494
|
344 QString
|
Chris@494
|
345 RDFTransformFactoryImpl::writeTransformToRDF(const Transform &transform,
|
Chris@494
|
346 QString uri)
|
Chris@494
|
347 {
|
Chris@494
|
348 QString str;
|
Chris@494
|
349 QTextStream s(&str);
|
Chris@494
|
350
|
Chris@503
|
351 // assumes the usual prefixes are available; requires that uri be
|
Chris@503
|
352 // a local fragment (e.g. ":transform") rather than a uri enclosed
|
Chris@503
|
353 // in <>, so that we can suffix it if need be
|
Chris@494
|
354
|
Chris@494
|
355 QString pluginId = transform.getPluginIdentifier();
|
Chris@494
|
356 QString pluginUri = PluginRDFIndexer::getInstance()->getURIForPluginId(pluginId);
|
Chris@494
|
357
|
Chris@503
|
358 if (pluginUri != "") {
|
Chris@503
|
359 s << uri << " a vamp:Transform ;" << endl;
|
Chris@592
|
360 s << " vamp:plugin <" << QUrl(pluginUri).toEncoded().data() << "> ;" << endl;
|
Chris@503
|
361 } else {
|
Chris@686
|
362 std::cerr << "WARNING: RDFTransformFactory::writeTransformToRDF: No plugin URI available for plugin id \"" << pluginId << "\", writing synthetic plugin and library resources" << std::endl;
|
Chris@503
|
363 QString type, soname, label;
|
Chris@503
|
364 PluginIdentifier::parseIdentifier(pluginId, type, soname, label);
|
Chris@503
|
365 s << uri << "_plugin a vamp:Plugin ;" << endl;
|
Chris@503
|
366 s << " vamp:identifier \"" << label << "\" .\n" << endl;
|
Chris@503
|
367 s << uri << "_library a vamp:PluginLibrary ;" << endl;
|
Chris@503
|
368 s << " vamp:identifier \"" << soname << "\" ;" << endl;
|
Chris@503
|
369 s << " vamp:available_plugin " << uri << "_plugin .\n" << endl;
|
Chris@503
|
370 s << uri << " a vamp:Transform ;" << endl;
|
Chris@503
|
371 s << " vamp:plugin " << uri << "_plugin ;" << endl;
|
Chris@503
|
372 }
|
Chris@503
|
373
|
Chris@494
|
374 PluginRDFDescription description(pluginId);
|
Chris@503
|
375 QString outputId = transform.getOutput();
|
Chris@503
|
376 QString outputUri = description.getOutputUri(outputId);
|
Chris@494
|
377
|
Chris@494
|
378 if (transform.getOutput() != "" && outputUri == "") {
|
Chris@686
|
379 std::cerr << "WARNING: RDFTransformFactory::writeTransformToRDF: No output URI available for transform output id \"" << transform.getOutput() << "\", writing a synthetic output resource" << std::endl;
|
Chris@494
|
380 }
|
Chris@494
|
381
|
Chris@494
|
382 if (transform.getStepSize() != 0) {
|
Chris@494
|
383 s << " vamp:step_size \"" << transform.getStepSize() << "\"^^xsd:int ; " << endl;
|
Chris@494
|
384 }
|
Chris@494
|
385 if (transform.getBlockSize() != 0) {
|
Chris@494
|
386 s << " vamp:block_size \"" << transform.getBlockSize() << "\"^^xsd:int ; " << endl;
|
Chris@494
|
387 }
|
Chris@494
|
388 if (transform.getStartTime() != RealTime::zeroTime) {
|
Chris@494
|
389 s << " vamp:start \"" << transform.getStartTime().toXsdDuration().c_str() << "\"^^xsd:duration ; " << endl;
|
Chris@494
|
390 }
|
Chris@494
|
391 if (transform.getDuration() != RealTime::zeroTime) {
|
Chris@494
|
392 s << " vamp:duration \"" << transform.getDuration().toXsdDuration().c_str() << "\"^^xsd:duration ; " << endl;
|
Chris@494
|
393 }
|
Chris@494
|
394 if (transform.getSampleRate() != 0) {
|
Chris@494
|
395 s << " vamp:sample_rate \"" << transform.getSampleRate() << "\"^^xsd:float ; " << endl;
|
Chris@494
|
396 }
|
Chris@494
|
397
|
Chris@494
|
398 QString program = transform.getProgram();
|
Chris@494
|
399 if (program != "") {
|
Chris@494
|
400 s << " vamp:program \"\"\"" << program << "\"\"\" ;" << endl;
|
Chris@494
|
401 }
|
Chris@494
|
402
|
Chris@508
|
403 QString summary = transform.summaryTypeToString(transform.getSummaryType());
|
Chris@508
|
404 if (summary != "") {
|
Chris@508
|
405 s << " vamp:summary_type \"" << summary << "\" ;" << endl;
|
Chris@508
|
406 }
|
Chris@508
|
407
|
Chris@494
|
408 Transform::ParameterMap parameters = transform.getParameters();
|
Chris@494
|
409 for (Transform::ParameterMap::const_iterator i = parameters.begin();
|
Chris@494
|
410 i != parameters.end(); ++i) {
|
Chris@494
|
411 QString name = i->first;
|
Chris@494
|
412 float value = i->second;
|
Chris@494
|
413 s << " vamp:parameter_binding [" << endl;
|
Chris@494
|
414 s << " vamp:parameter [ vamp:identifier \"" << name << "\" ] ;" << endl;
|
Chris@494
|
415 s << " vamp:value \"" << value << "\"^^xsd:float ;" << endl;
|
Chris@494
|
416 s << " ] ;" << endl;
|
Chris@494
|
417 }
|
Chris@494
|
418
|
Chris@494
|
419 if (outputUri != "") {
|
Chris@592
|
420 s << " vamp:output <" << QUrl(outputUri).toEncoded().data() << "> ." << endl;
|
Chris@503
|
421 } else if (outputId != "") {
|
Chris@503
|
422 s << " vamp:output [ vamp:identifier \"" << outputId << "\" ] ." << endl;
|
Chris@494
|
423 } else {
|
Chris@494
|
424 s << " ." << endl;
|
Chris@494
|
425 }
|
Chris@494
|
426
|
Chris@494
|
427 return str;
|
Chris@494
|
428 }
|
Chris@494
|
429
|