alo@223
|
1 /* -*- c-basic-offset: 4 indent-tabs-mode: nil -*- vi:set ts=8 sts=4 sw=4: */
|
alo@223
|
2
|
alo@223
|
3 /*
|
alo@223
|
4 Sonic Annotator
|
alo@223
|
5 A utility for batch feature extraction from audio files.
|
alo@223
|
6 Mark Levy, Chris Sutton and Chris Cannam, Queen Mary, University of London.
|
alo@223
|
7 Copyright 2007-2014 QMUL.
|
alo@223
|
8
|
alo@223
|
9 This program is free software; you can redistribute it and/or
|
alo@223
|
10 modify it under the terms of the GNU General Public License as
|
alo@223
|
11 published by the Free Software Foundation; either version 2 of the
|
alo@223
|
12 License, or (at your option) any later version. See the file
|
alo@223
|
13 COPYING included with this distribution for more information.
|
alo@223
|
14 */
|
alo@223
|
15
|
alo@223
|
16 #include "JsonLDFeatureWriter.h"
|
alo@223
|
17
|
alo@223
|
18 using namespace std;
|
alo@223
|
19 using Vamp::Plugin;
|
alo@223
|
20 using Vamp::PluginBase;
|
alo@223
|
21
|
alo@223
|
22 #include "base/Exceptions.h"
|
alo@223
|
23 #include "rdf/PluginRDFIndexer.h"
|
alo@223
|
24
|
alo@223
|
25 #include <QFileInfo>
|
alo@223
|
26 #include <QTextCodec>
|
alo@223
|
27 #include <QUuid>
|
alo@223
|
28
|
alo@223
|
29 #include "version.h"
|
alo@223
|
30
|
alo@223
|
31 JsonLDFeatureWriter::JsonLDFeatureWriter() :
|
alo@223
|
32 FileFeatureWriter(SupportOneFilePerTrackTransform |
|
alo@223
|
33 SupportOneFilePerTrack |
|
alo@223
|
34 SupportOneFileTotal |
|
alo@223
|
35 SupportStdOut,
|
alo@223
|
36 "json"),
|
alo@223
|
37 m_network(false),
|
alo@223
|
38 m_networkRetrieved(false),
|
alo@223
|
39 m_n(1),
|
alo@223
|
40 m_m(1),
|
alo@223
|
41 m_digits(6)
|
alo@223
|
42 {
|
alo@223
|
43 }
|
alo@223
|
44
|
alo@223
|
45 JsonLDFeatureWriter::~JsonLDFeatureWriter()
|
alo@223
|
46 {
|
alo@223
|
47 }
|
alo@223
|
48
|
alo@223
|
49 string
|
alo@223
|
50 JsonLDFeatureWriter::getDescription() const
|
alo@223
|
51 {
|
alo@223
|
52 return "Write features to JSON files in JSON-LD format. WARNING: This is a provisional implementation! The output format may change in future releases to comply more effectively with the specification. Please report any problems you find with the current implementation.";
|
alo@223
|
53 }
|
alo@223
|
54
|
alo@223
|
55 JsonLDFeatureWriter::ParameterList
|
alo@223
|
56 JsonLDFeatureWriter::getSupportedParameters() const
|
alo@223
|
57 {
|
alo@223
|
58 ParameterList pl = FileFeatureWriter::getSupportedParameters();
|
alo@223
|
59 Parameter p;
|
alo@223
|
60
|
alo@223
|
61 p.name = "digits";
|
alo@223
|
62 p.description = "Specify the number of significant digits to use when printing transform outputs. Outputs are represented internally using single-precision floating-point, so digits beyond the 8th or 9th place are usually meaningless. The default is 6.";
|
alo@223
|
63 p.hasArg = true;
|
alo@223
|
64 pl.push_back(p);
|
alo@223
|
65
|
alo@223
|
66 p.name = "network";
|
alo@223
|
67 p.description = "Attempt to retrieve RDF descriptions of plugins from network, if not available locally.";
|
alo@223
|
68 p.hasArg = false;
|
alo@223
|
69 pl.push_back(p);
|
alo@223
|
70
|
alo@223
|
71 return pl;
|
alo@223
|
72 }
|
alo@223
|
73
|
alo@223
|
74 void
|
alo@223
|
75 JsonLDFeatureWriter::setParameters(map<string, string> ¶ms)
|
alo@223
|
76 {
|
alo@223
|
77 FileFeatureWriter::setParameters(params);
|
alo@223
|
78
|
alo@223
|
79 for (map<string, string>::iterator i = params.begin();
|
alo@223
|
80 i != params.end(); ++i) {
|
alo@223
|
81 if (i->first == "network") {
|
alo@223
|
82 m_network = true;
|
alo@223
|
83 } else if (i->first == "digits") {
|
alo@223
|
84 int digits = atoi(i->second.c_str());
|
alo@223
|
85 if (digits <= 0 || digits > 100) {
|
alo@223
|
86 cerr << "JsonLDFeatureWriter: ERROR: Invalid or out-of-range value for number of significant digits: " << i->second << endl;
|
alo@223
|
87 cerr << "JsonLDFeatureWriter: NOTE: Continuing with default settings" << endl;
|
alo@223
|
88 } else {
|
alo@223
|
89 m_digits = digits;
|
alo@223
|
90 }
|
alo@223
|
91 }
|
alo@223
|
92 }
|
alo@223
|
93 }
|
alo@223
|
94
|
alo@223
|
95 void
|
alo@223
|
96 JsonLDFeatureWriter::setTrackMetadata(QString trackId, TrackMetadata metadata)
|
alo@223
|
97 {
|
alo@223
|
98 m_trackMetadata[trackId] = metadata;
|
alo@223
|
99 }
|
alo@223
|
100
|
alo@223
|
101 static double
|
alo@223
|
102 realTime2Sec(const Vamp::RealTime &r)
|
alo@223
|
103 {
|
alo@223
|
104 return r / Vamp::RealTime(1, 0);
|
alo@223
|
105 }
|
alo@223
|
106
|
alo@223
|
107 void
|
alo@223
|
108 JsonLDFeatureWriter::write(QString trackId,
|
alo@223
|
109 const Transform &transform,
|
alo@223
|
110 const Plugin::OutputDescriptor& ,
|
alo@223
|
111 const Plugin::FeatureList& features,
|
alo@223
|
112 std::string /* summaryType */)
|
alo@223
|
113 {
|
alo@223
|
114 QString transformId = transform.getIdentifier();
|
alo@223
|
115
|
alo@223
|
116 QTextStream *sptr = getOutputStream
|
alo@223
|
117 (trackId, transformId, QTextCodec::codecForName("UTF-8"));
|
alo@223
|
118 if (!sptr) {
|
alo@223
|
119 throw FailedToOpenOutputStream(trackId, transformId);
|
alo@223
|
120 }
|
alo@223
|
121
|
alo@223
|
122 DataId did(trackId, transform);
|
alo@223
|
123
|
alo@223
|
124 if (m_data.find(did) == m_data.end()) {
|
alo@223
|
125 identifyTask(transform);
|
alo@223
|
126 m_streamTracks[sptr].insert(trackId);
|
alo@223
|
127 m_streamTasks[sptr].insert(m_tasks[transformId]);
|
alo@223
|
128 m_streamData[sptr].insert(did);
|
alo@223
|
129 }
|
alo@223
|
130
|
alo@223
|
131 if (m_trackTimelineGuids.find(trackId) == m_trackTimelineGuids.end()) {
|
alo@223
|
132 QUuid uuid = QUuid::createUuid();
|
alo@223
|
133 m_trackTimelineGuids[trackId] = QString(uuid.toString().replace("{", "").replace("}", ""));
|
alo@223
|
134 }
|
alo@223
|
135
|
alo@223
|
136 QString d = m_data[did];
|
alo@223
|
137
|
alo@223
|
138 for (int i = 0; i < int(features.size()); ++i) {
|
alo@223
|
139
|
alo@223
|
140 if (d != "") {
|
alo@223
|
141 d += ",\n";
|
alo@223
|
142 }
|
alo@223
|
143
|
alo@223
|
144 d += "\t\t\t{ ";
|
alo@223
|
145
|
alo@223
|
146 Plugin::Feature f(features[i]);
|
alo@223
|
147
|
alo@223
|
148 QString timestr = f.timestamp.toString().c_str();
|
alo@223
|
149 timestr.replace(QRegExp("^ +"), "");
|
alo@223
|
150
|
alo@223
|
151 QString durstr = "0.0";
|
alo@223
|
152 if (f.hasDuration) {
|
alo@223
|
153 durstr = f.duration.toString().c_str();
|
alo@223
|
154 durstr.replace(QRegExp("^ +"), "");
|
alo@223
|
155 d += " \"@type\": \"tl:Interval\", ";
|
alo@223
|
156 }
|
alo@223
|
157 else{
|
alo@223
|
158 d += " \"@type\": \"tl:Instant\", ";
|
alo@223
|
159 }
|
alo@223
|
160
|
alo@223
|
161 d += QString("\"tl:at\": %1 ")
|
alo@223
|
162 .arg(timestr);
|
alo@223
|
163
|
alo@223
|
164 d += QString(", \"tl:timeline\": \"%1\" ")
|
alo@223
|
165 .arg(m_trackTimelineGuids[trackId]);
|
alo@223
|
166
|
alo@223
|
167 if (f.hasDuration) {
|
alo@223
|
168 d += QString(", \"tl:duration\": %2")
|
alo@223
|
169 .arg(durstr);
|
alo@223
|
170 }
|
alo@223
|
171
|
alo@223
|
172 if (f.label != "") {
|
alo@223
|
173 if (f.values.empty()) {
|
alo@223
|
174 d += QString(", \"afo:value\": \"%2\"").arg(f.label.c_str());
|
alo@223
|
175 } else {
|
alo@223
|
176 d += QString(", \"rdfs:label\": \"%2\"").arg(f.label.c_str());
|
alo@223
|
177 }
|
alo@223
|
178 }
|
alo@223
|
179
|
alo@223
|
180 if (!f.values.empty()) {
|
alo@223
|
181 d += QString(", \"afo:value\": ");
|
alo@223
|
182 if (f.values.size() > 1) {
|
alo@223
|
183 d += "[ ";
|
alo@223
|
184 }
|
alo@223
|
185 for (int j = 0; j < int(f.values.size()); ++j) {
|
alo@223
|
186 if (isnan(f.values[j])) {
|
alo@223
|
187 d += "\"NaN\"";
|
alo@223
|
188 } else if (isinf(f.values[j])) {
|
alo@223
|
189 d += "\"Inf\"";
|
alo@223
|
190 } else {
|
alo@223
|
191 d += QString("%1").arg(f.values[j], 0, 'g', m_digits);
|
alo@223
|
192 }
|
alo@223
|
193 if (j + 1 < int(f.values.size())) {
|
alo@223
|
194 d += ", ";
|
alo@223
|
195 }
|
alo@223
|
196 }
|
alo@223
|
197 if (f.values.size() > 1) {
|
alo@223
|
198 d += " ]";
|
alo@223
|
199 }
|
alo@223
|
200 }
|
alo@223
|
201
|
alo@223
|
202 d += " }";
|
alo@223
|
203 }
|
alo@223
|
204
|
alo@223
|
205 m_data[did] = d;
|
alo@223
|
206 }
|
alo@223
|
207
|
alo@223
|
208 void
|
alo@223
|
209 JsonLDFeatureWriter::setNofM(int n, int m)
|
alo@223
|
210 {
|
alo@223
|
211 if (m_singleFileName != "" || m_stdout) {
|
alo@223
|
212 m_n = n;
|
alo@223
|
213 m_m = m;
|
alo@223
|
214 } else {
|
alo@223
|
215 m_n = 1;
|
alo@223
|
216 m_m = 1;
|
alo@223
|
217 }
|
alo@223
|
218 }
|
alo@223
|
219
|
alo@223
|
220 void
|
alo@223
|
221 JsonLDFeatureWriter::finish()
|
alo@223
|
222 {
|
alo@223
|
223 for (FileStreamMap::const_iterator stri = m_streams.begin();
|
alo@223
|
224 stri != m_streams.end(); ++stri) {
|
alo@223
|
225
|
alo@223
|
226 QTextStream *sptr = stri->second;
|
alo@223
|
227 QTextStream &stream = *sptr;
|
alo@223
|
228
|
alo@223
|
229 bool firstInStream = true;
|
alo@223
|
230
|
alo@223
|
231 for (TrackIds::const_iterator tri = m_streamTracks[sptr].begin();
|
alo@223
|
232 tri != m_streamTracks[sptr].end(); ++tri) {
|
alo@223
|
233
|
alo@223
|
234 TrackId trackId = *tri;
|
alo@223
|
235
|
alo@223
|
236 if (firstInStream) {
|
alo@223
|
237 if (m_streamTracks[sptr].size() > 1 || (m_m > 1 && m_n == 1)) {
|
alo@223
|
238 stream << "[\n";
|
alo@223
|
239 }
|
alo@223
|
240 }
|
alo@223
|
241
|
alo@223
|
242 if (!firstInStream || (m_m > 1 && m_n > 1)) {
|
alo@223
|
243 stream << ",\n";
|
alo@223
|
244 }
|
alo@223
|
245
|
alo@223
|
246 stream << "{\n" << writeContext();
|
alo@223
|
247 stream << "\t\"@type\": \"mo:Track\",\n"
|
alo@223
|
248 << QString("\t\"mo:available_as\": \"%1\"").arg(QFileInfo(trackId).filePath());
|
alo@223
|
249
|
alo@223
|
250 if (m_trackMetadata.find(trackId) != m_trackMetadata.end()) {
|
alo@223
|
251
|
alo@223
|
252 if (m_trackMetadata[trackId].title != "") {
|
alo@223
|
253 stream << QString(",\n\t\"dc:title\": \"%1\"")
|
alo@223
|
254 .arg(m_trackMetadata[trackId].title);
|
alo@223
|
255 }
|
alo@223
|
256
|
alo@223
|
257 if (m_trackMetadata[trackId].maker != "") {
|
alo@223
|
258 stream << QString(",\n\t\"mo:artist\": { "
|
alo@223
|
259 "\t\t\"@type\": \"mo:MusicArtist\",\n"
|
alo@223
|
260 "\t\t\"foaf:name\": \"%1\" "
|
alo@223
|
261 "\t}")
|
alo@223
|
262 .arg(m_trackMetadata[trackId].maker);
|
alo@223
|
263 }
|
alo@223
|
264
|
alo@223
|
265 QString durstr = m_trackMetadata[trackId].duration.toString().c_str();
|
alo@223
|
266 durstr.replace(QRegExp("^ +"), "");
|
alo@223
|
267 stream << QString(",\n\t\"mo:encodes\": {\n"
|
alo@223
|
268 "\t\t\"@type\": \"mo:Signal\",\n"
|
alo@223
|
269 "\t\t\"mo:time\": {\n "
|
alo@223
|
270 "\t\t\t\"@type\": \"tl:Interval\",\n"
|
alo@223
|
271 "\t\t\t\"tl:duration\": \"PT%1S\",\n"
|
alo@223
|
272 "\t\t\t\"tl:timeline\": { \"@type\": \"tl:Timeline\", \"@id\": \"%2\" } "
|
alo@223
|
273 "\n\t\t}").arg(durstr).arg(m_trackTimelineGuids[trackId]);
|
alo@223
|
274 }
|
alo@223
|
275
|
alo@223
|
276 stream << "\n\t},\n";
|
alo@223
|
277 stream << "\t\"afo:features\": [\n";
|
alo@223
|
278
|
alo@223
|
279 bool firstInTrack = true;
|
alo@223
|
280
|
alo@223
|
281 for (Tasks::const_iterator ti = m_streamTasks[sptr].begin();
|
alo@223
|
282 ti != m_streamTasks[sptr].end(); ++ti) {
|
alo@223
|
283
|
alo@223
|
284 Task task = *ti;
|
alo@223
|
285
|
alo@223
|
286 for (DataIds::const_iterator di = m_streamData[sptr].begin();
|
alo@223
|
287 di != m_streamData[sptr].end(); ++di) {
|
alo@223
|
288
|
alo@223
|
289 DataId did = *di;
|
alo@223
|
290
|
alo@223
|
291 QString trackId = did.first;
|
alo@223
|
292 Transform transform = did.second;
|
alo@223
|
293
|
alo@223
|
294 if (m_tasks[transform.getIdentifier()] != task) continue;
|
alo@223
|
295
|
alo@223
|
296 QString data = m_data[did];
|
alo@223
|
297
|
alo@223
|
298 if (!firstInTrack) {
|
alo@223
|
299 stream << ",\n";
|
alo@223
|
300 }
|
alo@223
|
301
|
alo@223
|
302 stream << QString
|
alo@223
|
303 ("\t{\n"
|
alo@223
|
304 "\t\t\"@type\": \"afv:%1\",\n"
|
alo@223
|
305 "\t\t\"afo:computed_by\": {\n"
|
alo@223
|
306 "%2\t\t},\n"
|
alo@223
|
307 "\t\t\"afo:values\": [\n")
|
alo@223
|
308 .arg(transform.getOutput().replace(0, 1, transform.getOutput().at(0).toUpper()))
|
alo@223
|
309 .arg(writeTransformToObjectContents(transform));
|
alo@223
|
310
|
alo@223
|
311 stream << data;
|
alo@223
|
312
|
alo@223
|
313 stream << "\n\t\t]\n\t}";
|
alo@223
|
314 firstInTrack = false;
|
alo@223
|
315 }
|
alo@223
|
316 }
|
alo@223
|
317
|
alo@223
|
318 stream << "\n\t]";
|
alo@223
|
319
|
alo@223
|
320 stream << "\n}";
|
alo@223
|
321 firstInStream = false;
|
alo@223
|
322 }
|
alo@223
|
323
|
alo@223
|
324 if (!firstInStream) {
|
alo@223
|
325 if (m_streamTracks[sptr].size() > 1 || (m_m > 1 && m_n == m_m)) {
|
alo@223
|
326 stream << "\n\t]";
|
alo@223
|
327 }
|
alo@223
|
328 stream << "\n";
|
alo@223
|
329 }
|
alo@223
|
330 }
|
alo@223
|
331
|
alo@223
|
332 m_streamTracks.clear();
|
alo@223
|
333 m_streamTasks.clear();
|
alo@223
|
334 m_streamData.clear();
|
alo@223
|
335 m_data.clear();
|
alo@223
|
336
|
alo@223
|
337 FileFeatureWriter::finish();
|
alo@223
|
338 }
|
alo@223
|
339
|
alo@223
|
340 void
|
alo@223
|
341 JsonLDFeatureWriter::loadRDFDescription(const Transform &transform)
|
alo@223
|
342 {
|
alo@223
|
343 QString pluginId = transform.getPluginIdentifier();
|
alo@223
|
344 if (m_rdfDescriptions.find(pluginId) != m_rdfDescriptions.end()) return;
|
alo@223
|
345
|
alo@223
|
346 if (m_network && !m_networkRetrieved) {
|
alo@223
|
347 PluginRDFIndexer::getInstance()->indexConfiguredURLs();
|
alo@223
|
348 m_networkRetrieved = true;
|
alo@223
|
349 }
|
alo@223
|
350
|
alo@223
|
351 m_rdfDescriptions[pluginId] = PluginRDFDescription(pluginId);
|
alo@223
|
352
|
alo@223
|
353 if (m_rdfDescriptions[pluginId].haveDescription()) {
|
alo@223
|
354 cerr << "NOTE: Have RDF description for plugin ID \""
|
alo@223
|
355 << pluginId << "\"" << endl;
|
alo@223
|
356 } else {
|
alo@223
|
357 cerr << "NOTE: No RDF description for plugin ID \""
|
alo@223
|
358 << pluginId << "\"" << endl;
|
alo@223
|
359 if (!m_network) {
|
Chris@277
|
360 cerr << " Consider using the --jsld-network option to retrieve plugin descriptions" << endl;
|
alo@223
|
361 cerr << " from the network where possible." << endl;
|
alo@223
|
362 }
|
alo@223
|
363 }
|
alo@223
|
364 }
|
alo@223
|
365
|
alo@223
|
366 void
|
alo@223
|
367 JsonLDFeatureWriter::identifyTask(const Transform &transform)
|
alo@223
|
368 {
|
alo@223
|
369 QString transformId = transform.getIdentifier();
|
alo@223
|
370 if (m_tasks.find(transformId) != m_tasks.end()) return;
|
alo@223
|
371
|
alo@223
|
372 loadRDFDescription(transform);
|
alo@223
|
373
|
alo@223
|
374 Task task = UnknownTask;
|
alo@223
|
375
|
alo@223
|
376 QString pluginId = transform.getPluginIdentifier();
|
alo@223
|
377 QString outputId = transform.getOutput();
|
alo@223
|
378
|
alo@223
|
379 const PluginRDFDescription &desc = m_rdfDescriptions[pluginId];
|
alo@223
|
380
|
alo@223
|
381 if (desc.haveDescription()) {
|
alo@223
|
382
|
alo@223
|
383 PluginRDFDescription::OutputDisposition disp =
|
alo@223
|
384 desc.getOutputDisposition(outputId);
|
alo@223
|
385
|
alo@223
|
386 QString af = "http://purl.org/ontology/af/";
|
alo@223
|
387
|
alo@223
|
388 if (disp == PluginRDFDescription::OutputSparse) {
|
alo@223
|
389
|
alo@223
|
390 QString eventUri = desc.getOutputEventTypeURI(outputId);
|
alo@223
|
391
|
alo@223
|
392 //!!! todo: allow user to prod writer for task type
|
alo@223
|
393
|
alo@223
|
394 if (eventUri == af + "Note") {
|
alo@223
|
395 task = NoteTask;
|
alo@223
|
396 } else if (eventUri == af + "Beat") {
|
alo@223
|
397 task = BeatTask;
|
alo@223
|
398 } else if (eventUri == af + "ChordSegment") {
|
alo@223
|
399 task = ChordTask;
|
alo@223
|
400 } else if (eventUri == af + "KeyChange") {
|
alo@223
|
401 task = KeyTask;
|
alo@223
|
402 } else if (eventUri == af + "KeySegment") {
|
alo@223
|
403 task = KeyTask;
|
alo@223
|
404 } else if (eventUri == af + "Onset") {
|
alo@223
|
405 task = OnsetTask;
|
alo@223
|
406 } else if (eventUri == af + "NonTonalOnset") {
|
alo@223
|
407 task = OnsetTask;
|
alo@223
|
408 } else if (eventUri == af + "Segment") {
|
alo@223
|
409 task = SegmentTask;
|
alo@223
|
410 } else if (eventUri == af + "SpeechSegment") {
|
alo@223
|
411 task = SegmentTask;
|
alo@223
|
412 } else if (eventUri == af + "StructuralSegment") {
|
alo@223
|
413 task = SegmentTask;
|
alo@223
|
414 } else {
|
alo@223
|
415 cerr << "WARNING: Unsupported event type URI <"
|
alo@223
|
416 << eventUri << ">, proceeding with UnknownTask type"
|
alo@223
|
417 << endl;
|
alo@223
|
418 }
|
alo@223
|
419
|
alo@223
|
420 } else {
|
alo@223
|
421
|
Chris@277
|
422 cerr << "WARNING: Cannot currently write dense or track-level outputs to JSON-LD format (only sparse ones). Will proceed using UnknownTask type, but this probably isn't going to work" << endl;
|
alo@223
|
423 }
|
alo@223
|
424 }
|
alo@223
|
425
|
alo@223
|
426 m_tasks[transformId] = task;
|
alo@223
|
427 }
|
alo@223
|
428
|
alo@223
|
429 QString
|
alo@223
|
430 JsonLDFeatureWriter::getTaskKey(Task task)
|
alo@223
|
431 {
|
alo@223
|
432 switch (task) {
|
alo@223
|
433 case UnknownTask: return "unknown";
|
alo@223
|
434 case BeatTask: return "beat";
|
alo@223
|
435 case OnsetTask: return "onset";
|
alo@223
|
436 case ChordTask: return "chord";
|
alo@223
|
437 case SegmentTask: return "segment";
|
alo@223
|
438 case KeyTask: return "key";
|
alo@223
|
439 case NoteTask: return "note";
|
alo@223
|
440 case MelodyTask: return "melody";
|
alo@223
|
441 case PitchTask: return "pitch";
|
alo@223
|
442 }
|
alo@223
|
443 return "unknown";
|
alo@223
|
444 }
|
alo@223
|
445
|
alo@223
|
446 QString
|
alo@223
|
447 JsonLDFeatureWriter::writeTransformToObjectContents(const Transform &t)
|
alo@223
|
448 {
|
alo@223
|
449 QString json;
|
alo@223
|
450 QString stpl("\t\t\t\"%1\": \"%2\",\n");
|
alo@223
|
451 QString ntpl("\t\t\t\"%1\": %2,\n");
|
alo@223
|
452
|
alo@223
|
453 json += stpl.arg("@type").arg("vamp:Transform");
|
alo@223
|
454 json += stpl.arg("vamp:plugin_id").arg(t.getPluginIdentifier());
|
alo@223
|
455 json += stpl.arg("vamp:output_id").arg(t.getOutput());
|
alo@223
|
456
|
alo@223
|
457 if (t.getSummaryType() != Transform::NoSummary) {
|
alo@223
|
458 json += stpl.arg("vamp:summary_type")
|
alo@223
|
459 .arg(Transform::summaryTypeToString(t.getSummaryType()));
|
alo@223
|
460 }
|
alo@223
|
461
|
alo@223
|
462 if (t.getPluginVersion() != QString()) {
|
alo@223
|
463 json += stpl.arg("vamp:plugin_version").arg(t.getPluginVersion());
|
alo@223
|
464 }
|
alo@223
|
465
|
alo@223
|
466 if (t.getProgram() != QString()) {
|
alo@223
|
467 json += stpl.arg("vamp:program").arg(t.getProgram());
|
alo@223
|
468 }
|
alo@223
|
469
|
alo@223
|
470 if (t.getStepSize() != 0) {
|
alo@223
|
471 json += ntpl.arg("vamp:step_size").arg(t.getStepSize());
|
alo@223
|
472 }
|
alo@223
|
473
|
alo@223
|
474 if (t.getBlockSize() != 0) {
|
alo@223
|
475 json += ntpl.arg("vamp:block_size").arg(t.getBlockSize());
|
alo@223
|
476 }
|
alo@223
|
477
|
alo@223
|
478 if (t.getWindowType() != HanningWindow) {
|
alo@223
|
479 json += stpl.arg("vamp:window_type")
|
alo@223
|
480 .arg(Window<float>::getNameForType(t.getWindowType()).c_str());
|
alo@223
|
481 }
|
alo@223
|
482
|
alo@223
|
483 if (t.getStartTime() != RealTime::zeroTime) {
|
alo@223
|
484 json += ntpl.arg("tl:start")
|
alo@223
|
485 .arg(t.getStartTime().toDouble(), 0, 'g', 9);
|
alo@223
|
486 }
|
alo@223
|
487
|
alo@223
|
488 if (t.getDuration() != RealTime::zeroTime) {
|
alo@223
|
489 json += ntpl.arg("tl:duration")
|
alo@223
|
490 .arg(t.getDuration().toDouble(), 0, 'g', 9);
|
alo@223
|
491 }
|
alo@223
|
492
|
alo@223
|
493 if (t.getSampleRate() != 0) {
|
alo@223
|
494 json += ntpl.arg("vamp:sample_rate").arg(t.getSampleRate());
|
alo@223
|
495 }
|
alo@223
|
496
|
alo@223
|
497 if (!t.getParameters().empty()) {
|
alo@223
|
498 json += QString("\t\t\t\"vamp:parameter_binding\": [\n");
|
alo@223
|
499 Transform::ParameterMap parameters = t.getParameters();
|
alo@223
|
500 for (Transform::ParameterMap::const_iterator i = parameters.begin();
|
alo@223
|
501 i != parameters.end(); ++i) {
|
alo@223
|
502 if (i != parameters.begin()) {
|
alo@223
|
503 json += ",\n";
|
alo@223
|
504 }
|
alo@223
|
505 QString name = i->first;
|
alo@223
|
506 float value = i->second;
|
alo@223
|
507 json += QString("\t\t\t\t{\n");
|
alo@223
|
508 json += QString("\t\t\t\t\t\"@type\": \"vamp:Parameter\",\n");
|
alo@223
|
509 json += QString("\t\t\t\t\t\"vamp:identifier\": \"%1\",\n").arg(name);
|
alo@223
|
510 json += QString("\t\t\t\t\t\"vamp:value\": %1").arg(value, 0, 'g', 8);
|
alo@223
|
511 json += QString("\n\t\t\t\t}");
|
alo@223
|
512 }
|
alo@223
|
513 json += QString("\n\t\t\t],\n");
|
alo@223
|
514 }
|
alo@223
|
515
|
alo@223
|
516 // no trailing comma on final property:
|
alo@223
|
517 json += QString("\t\t\t\"vamp:transform_id\": \"%1\",\n").arg(t.getIdentifier());
|
alo@223
|
518 json += QString("\t\t\t\"afo:implemented_in\": {\n");
|
alo@223
|
519 json += QString("\t\t\t\t\"@type\": \"afo:SoftwareAgent\",\n");
|
alo@223
|
520 json += QString("\t\t\t\t\"afo:name\": \"Sonic Annotator\",\n");
|
alo@223
|
521 json += QString("\t\t\t\t\"afo:version\": \"%1\" \n").arg(RUNNER_VERSION);
|
alo@223
|
522 json += QString("\t\t\t}\n");
|
alo@223
|
523
|
alo@223
|
524 return json;
|
alo@223
|
525 }
|
alo@223
|
526
|
alo@223
|
527 QString
|
alo@223
|
528 JsonLDFeatureWriter::writeContext() {
|
alo@223
|
529 QString context;
|
alo@223
|
530 context += QString("\t\"@context\": {\n");
|
alo@223
|
531 context += QString("\t\t\"foaf\": \"http://xmlns.com/foaf/0.1/\",\n");
|
alo@223
|
532 context += QString("\t\t\"afo\": \"http://sovarr.c4dm.eecs.qmul.ac.uk/af/ontology/1.1#\",\n");
|
alo@223
|
533 context += QString("\t\t\"afv\": \"http://sovarr.c4dm.eecs.qmul.ac.uk/af/vocabulary/1.1#\",\n");
|
alo@223
|
534 context += QString("\t\t\"mo\": \"http://purl.org/ontology/mo/\",\n");
|
alo@223
|
535 context += QString("\t\t\"dc\": \"http://purl.org/dc/elements/1.1/\",\n");
|
alo@223
|
536 context += QString("\t\t\"tl\": \"http://purl.org/NET/c4dm/timeline.owl#\",\n");
|
alo@223
|
537 context += QString("\t\t\"vamp\": \"http://purl.org/ontology/vamp/\"\n");
|
alo@223
|
538 context += QString("\t},\n");
|
alo@223
|
539 return context;
|
alo@223
|
540 }
|