annotate rdf/RDFFeatureWriter.cpp @ 1008:d9e0e59a1581

When using an aggregate model to pass data to a transform, zero-pad the shorter input to the duration of the longer rather than truncating the longer. (This is better behaviour for e.g. MATCH, and in any case the code was previously truncating incorrectly and ending up with garbage data at the end.)
author Chris Cannam
date Fri, 14 Nov 2014 13:51:33 +0000
parents e25dc8d57565
children d74ebd2d2c49
rev   line source
Chris@498 1 /* -*- c-basic-offset: 4 indent-tabs-mode: nil -*- vi:set ts=8 sts=4 sw=4: */
Chris@498 2
Chris@498 3 /*
Chris@498 4 Sonic Annotator
Chris@498 5 A utility for batch feature extraction from audio files.
Chris@498 6 Mark Levy, Chris Sutton and Chris Cannam, Queen Mary, University of London.
Chris@498 7 Copyright 2007-2008 QMUL.
Chris@498 8
Chris@498 9 This program is free software; you can redistribute it and/or
Chris@498 10 modify it under the terms of the GNU General Public License as
Chris@498 11 published by the Free Software Foundation; either version 2 of the
Chris@498 12 License, or (at your option) any later version. See the file
Chris@498 13 COPYING included with this distribution for more information.
Chris@498 14 */
Chris@498 15
Chris@498 16 #include <fstream>
Chris@498 17
Chris@599 18 #include "base/Exceptions.h"
Chris@599 19
Chris@498 20 #include "RDFFeatureWriter.h"
Chris@498 21 #include "RDFTransformFactory.h"
Chris@597 22 #include "PluginRDFIndexer.h"
Chris@498 23
Chris@498 24 #include <QTextStream>
Chris@498 25 #include <QUrl>
Chris@508 26 #include <QFileInfo>
Chris@498 27 #include <QRegExp>
Chris@498 28
Chris@498 29 using namespace std;
Chris@498 30 using Vamp::Plugin;
Chris@498 31 using Vamp::PluginBase;
Chris@498 32
Chris@498 33 RDFFeatureWriter::RDFFeatureWriter() :
Chris@498 34 FileFeatureWriter(SupportOneFilePerTrackTransform |
Chris@498 35 SupportOneFilePerTrack |
Chris@997 36 SupportOneFileTotal |
Chris@997 37 SupportStdOut,
Chris@498 38 "n3"),
Chris@498 39 m_plain(false),
Chris@597 40 m_network(false),
Chris@597 41 m_networkRetrieved(false),
Chris@498 42 m_count(0)
Chris@498 43 {
Chris@498 44 }
Chris@498 45
Chris@498 46 RDFFeatureWriter::~RDFFeatureWriter()
Chris@498 47 {
Chris@498 48 }
Chris@498 49
Chris@998 50 string
Chris@998 51 RDFFeatureWriter::getDescription() const
Chris@998 52 {
Chris@998 53 return "Write output in Audio Features Ontology RDF/Turtle format.";
Chris@998 54 }
Chris@998 55
Chris@498 56 RDFFeatureWriter::ParameterList
Chris@498 57 RDFFeatureWriter::getSupportedParameters() const
Chris@498 58 {
Chris@498 59 ParameterList pl = FileFeatureWriter::getSupportedParameters();
Chris@498 60 Parameter p;
Chris@498 61
Chris@498 62 p.name = "plain";
Chris@498 63 p.description = "Use \"plain\" RDF even if transform metadata is available.";
Chris@498 64 p.hasArg = false;
Chris@498 65 pl.push_back(p);
Chris@498 66
Chris@586 67 p.name = "audiofile-uri";
Chris@586 68 p.description = "Link the output RDF to the given audio file URI instead of its actual location.";
Chris@498 69 p.hasArg = true;
Chris@498 70 pl.push_back(p);
Chris@594 71
Chris@594 72 p.name = "track-uri";
Chris@594 73 p.description = "Link the output RDF to the given track URI.";
Chris@594 74 p.hasArg = true;
Chris@594 75 pl.push_back(p);
Chris@594 76
Chris@594 77 p.name = "maker-uri";
Chris@594 78 p.description = "Link the track in the output RDF to the given foaf:maker URI.";
Chris@594 79 p.hasArg = true;
Chris@594 80 pl.push_back(p);
Chris@597 81
Chris@597 82 p.name = "network";
Chris@597 83 p.description = "Attempt to retrieve RDF descriptions of plugins from network, if not available locally";
Chris@597 84 p.hasArg = false;
Chris@597 85 pl.push_back(p);
Chris@498 86
Chris@498 87 return pl;
Chris@498 88 }
Chris@498 89
Chris@498 90 void
Chris@498 91 RDFFeatureWriter::setParameters(map<string, string> &params)
Chris@498 92 {
Chris@498 93 FileFeatureWriter::setParameters(params);
Chris@498 94
Chris@498 95 for (map<string, string>::iterator i = params.begin();
Chris@498 96 i != params.end(); ++i) {
Chris@498 97 if (i->first == "plain") {
Chris@498 98 m_plain = true;
Chris@498 99 }
Chris@586 100 if (i->first == "audiofile-uri") {
Chris@594 101 m_userAudioFileUri = i->second.c_str();
Chris@594 102 }
Chris@594 103 if (i->first == "track-uri") {
Chris@594 104 m_userTrackUri = i->second.c_str();
Chris@594 105 }
Chris@594 106 if (i->first == "maker-uri") {
Chris@594 107 m_userMakerUri = i->second.c_str();
Chris@498 108 }
Chris@597 109 if (i->first == "network") {
Chris@597 110 m_network = true;
Chris@597 111 }
Chris@498 112 }
Chris@498 113 }
Chris@498 114
Chris@504 115 void
Chris@504 116 RDFFeatureWriter::setTrackMetadata(QString trackId,
Chris@504 117 TrackMetadata metadata)
Chris@504 118 {
Chris@686 119 // cerr << "setTrackMetadata: title = " << metadata.title << ", maker = " << metadata.maker << endl;
Chris@504 120 m_metadata[trackId] = metadata;
Chris@504 121 }
Chris@504 122
Chris@504 123 void
Chris@510 124 RDFFeatureWriter::setFixedEventTypeURI(QString uri)
Chris@510 125 {
Chris@510 126 m_fixedEventTypeURI = uri;
Chris@510 127 }
Chris@510 128
Chris@510 129 void
Chris@504 130 RDFFeatureWriter::write(QString trackId,
Chris@504 131 const Transform &transform,
Chris@504 132 const Plugin::OutputDescriptor& output,
Chris@504 133 const Plugin::FeatureList& features,
Chris@504 134 std::string summaryType)
Chris@498 135 {
Chris@498 136 QString pluginId = transform.getPluginIdentifier();
Chris@498 137
Chris@498 138 if (m_rdfDescriptions.find(pluginId) == m_rdfDescriptions.end()) {
Chris@498 139
Chris@597 140 if (m_network && !m_networkRetrieved) {
Chris@597 141 PluginRDFIndexer::getInstance()->indexConfiguredURLs();
Chris@597 142 m_networkRetrieved = true;
Chris@597 143 }
Chris@597 144
Chris@498 145 m_rdfDescriptions[pluginId] = PluginRDFDescription(pluginId);
Chris@498 146
Chris@498 147 if (m_rdfDescriptions[pluginId].haveDescription()) {
Chris@718 148 cerr << "NOTE: Have RDF description for plugin ID \""
Chris@686 149 << pluginId << "\"" << endl;
Chris@498 150 } else {
Chris@718 151 cerr << "NOTE: No RDF description for plugin ID \""
Chris@686 152 << pluginId << "\"" << endl;
Chris@597 153 if (!m_network) {
Chris@597 154 cerr << " Consider using the --rdf-network option to retrieve plugin descriptions" << endl;
Chris@597 155 cerr << " from the network where possible." << endl;
Chris@597 156 }
Chris@498 157 }
Chris@498 158 }
Chris@498 159
Chris@498 160 // Need to select appropriate output file for our track/transform
Chris@498 161 // combination
Chris@498 162
Chris@498 163 QTextStream *stream = getOutputStream(trackId, transform.getIdentifier());
Chris@512 164 if (!stream) {
Chris@605 165 throw FailedToOpenOutputStream(trackId, transform.getIdentifier());
Chris@512 166 }
Chris@498 167
Chris@498 168 if (m_startedStreamTransforms.find(stream) ==
Chris@498 169 m_startedStreamTransforms.end()) {
Chris@594 170 // cerr << "This stream is new, writing prefixes" << endl;
Chris@498 171 writePrefixes(stream);
Chris@498 172 if (m_singleFileName == "" && !m_stdout) {
Chris@498 173 writeSignalDescription(stream, trackId);
Chris@498 174 }
Chris@498 175 }
Chris@498 176
Chris@498 177 if (m_startedStreamTransforms[stream].find(transform) ==
Chris@498 178 m_startedStreamTransforms[stream].end()) {
Chris@498 179 m_startedStreamTransforms[stream].insert(transform);
Chris@498 180 writeLocalFeatureTypes
Chris@730 181 (stream, transform, output, m_rdfDescriptions[pluginId],
Chris@730 182 summaryType);
Chris@498 183 }
Chris@498 184
Chris@498 185 if (m_singleFileName != "" || m_stdout) {
Chris@498 186 if (m_startedTrackIds.find(trackId) == m_startedTrackIds.end()) {
Chris@498 187 writeSignalDescription(stream, trackId);
Chris@498 188 m_startedTrackIds.insert(trackId);
Chris@498 189 }
Chris@498 190 }
Chris@498 191
Chris@498 192 QString timelineURI = m_trackTimelineURIs[trackId];
Chris@498 193
Chris@498 194 if (timelineURI == "") {
Chris@498 195 cerr << "RDFFeatureWriter: INTERNAL ERROR: writing features without having established a timeline URI!" << endl;
Chris@498 196 exit(1);
Chris@498 197 }
Chris@498 198
Chris@498 199 if (summaryType != "") {
Chris@498 200
Chris@498 201 writeSparseRDF(stream, transform, output, features,
Chris@498 202 m_rdfDescriptions[pluginId], timelineURI);
Chris@498 203
Chris@498 204 } else if (m_rdfDescriptions[pluginId].haveDescription() &&
Chris@498 205 m_rdfDescriptions[pluginId].getOutputDisposition
Chris@498 206 (output.identifier.c_str()) ==
Chris@498 207 PluginRDFDescription::OutputDense) {
Chris@498 208
Chris@498 209 QString signalURI = m_trackSignalURIs[trackId];
Chris@498 210
Chris@498 211 if (signalURI == "") {
Chris@498 212 cerr << "RDFFeatureWriter: INTERNAL ERROR: writing dense features without having established a signal URI!" << endl;
Chris@498 213 exit(1);
Chris@498 214 }
Chris@498 215
Chris@498 216 writeDenseRDF(stream, transform, output, features,
Chris@498 217 m_rdfDescriptions[pluginId], signalURI, timelineURI);
Chris@498 218
Chris@507 219 } else if (!m_plain &&
Chris@507 220 m_rdfDescriptions[pluginId].haveDescription() &&
Chris@507 221 m_rdfDescriptions[pluginId].getOutputDisposition
Chris@507 222 (output.identifier.c_str()) ==
Chris@507 223 PluginRDFDescription::OutputTrackLevel &&
Chris@507 224 m_rdfDescriptions[pluginId].getOutputFeatureAttributeURI
Chris@507 225 (output.identifier.c_str()) != "") {
Chris@507 226
Chris@507 227 QString signalURI = m_trackSignalURIs[trackId];
Chris@507 228
Chris@507 229 if (signalURI == "") {
Chris@507 230 cerr << "RDFFeatureWriter: INTERNAL ERROR: writing track-level features without having established a signal URI!" << endl;
Chris@507 231 exit(1);
Chris@507 232 }
Chris@507 233
Chris@507 234 writeTrackLevelRDF(stream, transform, output, features,
Chris@507 235 m_rdfDescriptions[pluginId], signalURI);
Chris@507 236
Chris@498 237 } else {
Chris@498 238
Chris@498 239 writeSparseRDF(stream, transform, output, features,
Chris@498 240 m_rdfDescriptions[pluginId], timelineURI);
Chris@498 241 }
Chris@498 242 }
Chris@498 243
Chris@498 244 void
Chris@498 245 RDFFeatureWriter::writePrefixes(QTextStream *sptr)
Chris@498 246 {
Chris@498 247 QTextStream &stream = *sptr;
Chris@498 248
Chris@498 249 stream << "@prefix dc: <http://purl.org/dc/elements/1.1/> .\n"
Chris@498 250 << "@prefix mo: <http://purl.org/ontology/mo/> .\n"
Chris@498 251 << "@prefix af: <http://purl.org/ontology/af/> .\n"
Chris@504 252 << "@prefix foaf: <http://xmlns.com/foaf/0.1/> . \n"
Chris@498 253 << "@prefix event: <http://purl.org/NET/c4dm/event.owl#> .\n"
Chris@498 254 << "@prefix rdf: <http://www.w3.org/1999/02/22-rdf-syntax-ns#> .\n"
Chris@498 255 << "@prefix rdfs: <http://www.w3.org/2000/01/rdf-schema#> .\n"
Chris@498 256 << "@prefix xsd: <http://www.w3.org/2001/XMLSchema#> .\n"
Chris@498 257 << "@prefix tl: <http://purl.org/NET/c4dm/timeline.owl#> .\n"
Chris@498 258 << "@prefix vamp: <http://purl.org/ontology/vamp/> .\n"
Chris@498 259 << "@prefix : <#> .\n\n";
Chris@498 260 }
Chris@498 261
Chris@498 262 void
Chris@590 263 RDFFeatureWriter::reviewFileForAppending(QString filename)
Chris@590 264 {
Chris@590 265 // Appending to an RDF file is tricky, because we need to ensure
Chris@590 266 // that our URIs differ from any already in the file. This is a
Chris@590 267 // dirty grubby low-rent way of doing that. This function is
Chris@590 268 // called by FileFeatureWriter::getOutputFile when in append mode.
Chris@590 269
Chris@843 270 // cerr << "reviewFileForAppending(" << filename << ")" << endl;
Chris@590 271
Chris@590 272 QFile file(filename);
Chris@590 273
Chris@590 274 // just return, don't report failure -- function that called us will do that
Chris@590 275 if (!file.open(QIODevice::ReadOnly)) return;
Chris@590 276
Chris@590 277 QTextStream in(&file);
Chris@590 278
Chris@590 279 QRegExp localObjectUriWithDigits(":[^ ]+_([0-9]+) a ");
Chris@590 280
Chris@590 281 while (!in.atEnd()) {
Chris@590 282 QString line = in.readLine();
Chris@590 283 if (line.length() > 120) { // probably data
Chris@590 284 continue;
Chris@590 285 }
Chris@590 286 if (localObjectUriWithDigits.indexIn(line) > -1) {
Chris@590 287 QString numeric = localObjectUriWithDigits.cap(1);
Chris@590 288 int number = numeric.toInt();
Chris@590 289 if (number >= m_count) m_count = number + 1;
Chris@590 290 }
Chris@590 291 }
Chris@590 292
Chris@590 293 file.close();
Chris@590 294 }
Chris@590 295
Chris@590 296 void
Chris@498 297 RDFFeatureWriter::writeSignalDescription(QTextStream *sptr,
Chris@498 298 QString trackId)
Chris@498 299 {
Chris@690 300 // SVDEBUG << "RDFFeatureWriter::writeSignalDescription" << endl;
Chris@530 301
Chris@498 302 QTextStream &stream = *sptr;
Chris@498 303
Chris@498 304 /*
Chris@498 305 * Describe signal we're analysing (AudioFile, Signal, TimeLine, etc.)
Chris@498 306 */
Chris@498 307
Christophe@615 308 QUrl url(trackId, QUrl::StrictMode);
Chris@498 309 QString scheme = url.scheme().toLower();
Chris@498 310 bool local = (scheme == "" || scheme == "file" || scheme.length() == 1);
Chris@498 311
Chris@498 312 if (local) {
Chris@498 313 if (scheme == "") {
Chris@498 314 url.setScheme("file");
Chris@508 315 url.setPath(QFileInfo(url.path()).absoluteFilePath());
Chris@498 316 } else if (scheme.length() == 1) { // DOS drive letter!
Chris@498 317 url.setScheme("file");
Chris@498 318 url.setPath(scheme + ":" + url.path());
Chris@498 319 }
Chris@498 320 }
Chris@498 321
Chris@590 322 // Note reviewFileForAppending above (when opening in append mode)
Chris@498 323
Chris@602 324 unsigned long signalCount = m_count++;
Chris@498 325
Chris@498 326 if (m_trackSignalURIs.find(trackId) == m_trackSignalURIs.end()) {
Chris@498 327 m_trackSignalURIs[trackId] = QString(":signal_%1").arg(signalCount);
Chris@498 328 }
Chris@498 329 QString signalURI = m_trackSignalURIs[trackId];
Chris@498 330
Chris@585 331 if (m_trackTrackURIs.find(trackId) == m_trackTrackURIs.end()) {
Chris@585 332 m_trackTrackURIs[trackId] = QString(":track_%1").arg(signalCount);
Chris@585 333 }
Chris@585 334 QString trackURI = m_trackTrackURIs[trackId];
Chris@594 335
Chris@594 336 bool userSpecifiedTrack = false;
Chris@594 337 if (m_userTrackUri != "") {
Chris@594 338 trackURI = "<" + m_userTrackUri + ">";
Chris@594 339 m_trackTrackURIs[trackId] = trackURI;
Chris@594 340 userSpecifiedTrack = true;
Chris@594 341 }
Chris@585 342
Chris@498 343 if (m_trackTimelineURIs.find(trackId) == m_trackTimelineURIs.end()) {
Chris@498 344 m_trackTimelineURIs[trackId] = QString(":signal_timeline_%1").arg(signalCount);
Chris@498 345 }
Chris@498 346 QString timelineURI = m_trackTimelineURIs[trackId];
Chris@498 347
Chris@586 348 QString afURI = url.toEncoded().data();
Chris@594 349 if (m_userAudioFileUri != "") afURI = m_userAudioFileUri;
Chris@586 350
Chris@594 351 bool wantTrack = (userSpecifiedTrack ||
Chris@594 352 (m_userMakerUri != "") ||
Chris@594 353 (m_metadata.find(trackId) != m_metadata.end()));
Chris@594 354
Chris@594 355 // cerr << "wantTrack = " << wantTrack << " (userSpecifiedTrack = "
Chris@686 356 // << userSpecifiedTrack << ", m_userMakerUri = " << m_userMakerUri << ", have metadata = " << (m_metadata.find(trackId) != m_metadata.end()) << ")" << endl;
Chris@594 357
Chris@594 358 if (wantTrack) {
Chris@594 359 // We only write a Track at all if we have some title/artist
Chris@594 360 // metadata to put in it, or if the user has requested a
Chris@594 361 // specific track URI. Otherwise we can't be sure that what
Chris@594 362 // we have is a Track, in the publication sense -- it may just
Chris@594 363 // be a fragment, a test file, whatever. Since we'd have no
Chris@594 364 // metadata to associate with our Track, the only effect of
Chris@594 365 // including a Track would be to assert that this was one,
Chris@594 366 // which is the one thing we wouldn't know...
Chris@594 367 TrackMetadata tm;
Chris@594 368 if (m_metadata.find(trackId) != m_metadata.end()) {
Chris@594 369 tm = m_metadata[trackId];
Chris@585 370 }
Chris@594 371 stream << trackURI << " a mo:Track ";
Chris@594 372 if (tm.title != "") {
Chris@594 373 stream << ";\n dc:title \"\"\"" << tm.title << "\"\"\" ";
Chris@594 374 }
Chris@594 375 if (m_userMakerUri != "") {
Chris@594 376 stream << ";\n foaf:maker <" << m_userMakerUri << "> ";
Chris@594 377 } else if (tm.maker != "") {
Chris@594 378 stream << ";\n foaf:maker [ a mo:MusicArtist; foaf:name \"\"\"" << tm.maker << "\"\"\" ] ";
Chris@594 379 }
Chris@594 380 if (afURI != "") {
Chris@594 381 stream << ";\n mo:available_as <" << afURI << "> ";
Chris@594 382 }
Chris@594 383 stream << ".\n\n";
Chris@585 384 }
Chris@585 385
Chris@594 386 if (afURI != "") {
Chris@586 387 stream << "<" << afURI << "> a mo:AudioFile ;\n";
Chris@585 388 stream << " mo:encodes " << signalURI << ".\n\n";
Chris@500 389 }
Chris@500 390
Chris@500 391 stream << signalURI << " a mo:Signal ;\n";
Chris@500 392
Chris@500 393 stream << " mo:time [\n"
Chris@498 394 << " a tl:Interval ;\n"
Chris@498 395 << " tl:onTimeLine "
Chris@498 396 << timelineURI << "\n ] .\n\n";
Chris@585 397
Chris@736 398 stream << timelineURI << " a tl:Timeline .\n\n";
Chris@498 399 }
Chris@498 400
Chris@498 401 void
Chris@498 402 RDFFeatureWriter::writeLocalFeatureTypes(QTextStream *sptr,
Chris@498 403 const Transform &transform,
Chris@498 404 const Plugin::OutputDescriptor &od,
Chris@730 405 PluginRDFDescription &desc,
Chris@730 406 std::string summaryType)
Chris@498 407 {
Chris@498 408 QString outputId = od.identifier.c_str();
Chris@498 409 QTextStream &stream = *sptr;
Chris@498 410
Chris@507 411 // There is no "needFeatureType" for track-level outputs, because
Chris@507 412 // we can't meaningfully write a feature at all if we don't know
Chris@507 413 // what property to use for it. If the output is track level but
Chris@507 414 // there is no feature type given, we have to revert to events.
Chris@507 415
Chris@498 416 bool needEventType = false;
Chris@498 417 bool needSignalType = false;
Chris@498 418
Chris@498 419 //!!! bin names, extents and so on can be written out using e.g. vamp:bin_names ( "a" "b" "c" )
Chris@498 420
Chris@730 421 if (summaryType == "" &&
Chris@730 422 desc.getOutputDisposition(outputId) ==
Chris@498 423 PluginRDFDescription::OutputDense) {
Chris@498 424
Chris@498 425 // no feature events, so may need signal type but won't need
Chris@498 426 // event type
Chris@498 427
Chris@498 428 if (m_plain) {
Chris@498 429
Chris@498 430 needSignalType = true;
Chris@498 431
Chris@498 432 } else if (desc.getOutputSignalTypeURI(outputId) == "") {
Chris@498 433
Chris@498 434 needSignalType = true;
Chris@498 435 }
Chris@498 436
Chris@507 437 } else if (desc.getOutputDisposition(outputId) ==
Chris@507 438 PluginRDFDescription::OutputTrackLevel) {
Chris@507 439
Chris@507 440 // see note above -- need to generate an event type if no
Chris@507 441 // feature type given, or if in plain mode
Chris@507 442
Chris@507 443 if (m_plain) {
Chris@507 444
Chris@507 445 needEventType = true;
Chris@507 446
Chris@507 447 } else if (desc.getOutputFeatureAttributeURI(outputId) == "") {
Chris@507 448
Chris@507 449 if (desc.getOutputEventTypeURI(outputId) == "") {
Chris@507 450
Chris@507 451 needEventType = true;
Chris@507 452 }
Chris@507 453 }
Chris@507 454
Chris@498 455 } else {
Chris@498 456
Chris@498 457 // may need event type but won't need signal type
Chris@498 458
Chris@498 459 if (m_plain) {
Chris@498 460
Chris@498 461 needEventType = true;
Chris@498 462
Chris@498 463 } else if (desc.getOutputEventTypeURI(outputId) == "") {
Chris@498 464
Chris@498 465 needEventType = true;
Chris@498 466 }
Chris@498 467 }
Chris@498 468
Chris@498 469 QString transformUri;
Chris@498 470 if (m_transformURIs.find(transform) != m_transformURIs.end()) {
Chris@498 471 transformUri = m_transformURIs[transform];
Chris@498 472 } else {
Chris@498 473 transformUri = QString(":transform_%1_%2").arg(m_count++).arg(outputId);
Chris@498 474 m_transformURIs[transform] = transformUri;
Chris@498 475 }
Chris@498 476
Chris@500 477 if (transform.getIdentifier() != "") {
Chris@508 478 stream << endl
Chris@508 479 << RDFTransformFactory::writeTransformToRDF(transform, transformUri)
Chris@500 480 << endl;
Chris@500 481 }
Chris@498 482
Chris@510 483 if (needEventType && m_fixedEventTypeURI == "") {
Chris@498 484
Chris@498 485 QString uri;
Chris@734 486 if (m_syntheticEventTypeURIs.find(transform) !=
Chris@498 487 m_syntheticEventTypeURIs.end()) {
Chris@498 488 uri = m_syntheticEventTypeURIs[transform];
Chris@498 489 } else {
Chris@498 490 uri = QString(":event_type_%1").arg(m_count++);
Chris@498 491 m_syntheticEventTypeURIs[transform] = uri;
Chris@498 492 }
Chris@498 493
Chris@498 494 stream << uri
Chris@498 495 << " rdfs:subClassOf event:Event ;" << endl
Chris@498 496 << " dc:title \"" << od.name.c_str() << "\" ;" << endl
Chris@498 497 << " dc:format \"" << od.unit.c_str() << "\" ;" << endl
Chris@498 498 << " dc:description \"" << od.description.c_str() << "\" ."
Chris@498 499 << endl << endl;
Chris@498 500 }
Chris@498 501
Chris@498 502 if (needSignalType) {
Chris@498 503
Chris@498 504 QString uri;
Chris@498 505 if (m_syntheticSignalTypeURIs.find(transform) !=
Chris@498 506 m_syntheticSignalTypeURIs.end()) {
Chris@498 507 uri = m_syntheticSignalTypeURIs[transform];
Chris@498 508 } else {
Chris@498 509 uri = QString(":signal_type_%1").arg(m_count++);
Chris@498 510 m_syntheticSignalTypeURIs[transform] = uri;
Chris@498 511 }
Chris@498 512
Chris@498 513 stream << uri
Chris@498 514 << " rdfs:subClassOf af:Signal ;" << endl
Chris@498 515 << " dc:title \"" << od.name.c_str() << "\" ;" << endl
Chris@498 516 << " dc:format \"" << od.unit.c_str() << "\" ;" << endl
Chris@498 517 << " dc:description \"" << od.description.c_str() << "\" ."
Chris@498 518 << endl << endl;
Chris@498 519 }
Chris@498 520 }
Chris@498 521
Chris@498 522 void
Chris@498 523 RDFFeatureWriter::writeSparseRDF(QTextStream *sptr,
Chris@498 524 const Transform &transform,
Chris@498 525 const Plugin::OutputDescriptor& od,
Chris@498 526 const Plugin::FeatureList& featureList,
Chris@498 527 PluginRDFDescription &desc,
Chris@498 528 QString timelineURI)
Chris@498 529 {
Chris@690 530 // SVDEBUG << "RDFFeatureWriter::writeSparseRDF: have " << featureList.size() << " features" << endl;
Chris@512 531
Chris@498 532 if (featureList.empty()) return;
Chris@498 533 QTextStream &stream = *sptr;
Chris@498 534
Chris@498 535 bool plain = (m_plain || !desc.haveDescription());
Chris@498 536
Chris@498 537 QString outputId = od.identifier.c_str();
Chris@498 538
Chris@498 539 // iterate through FeatureLists
Chris@498 540
Chris@930 541 for (int i = 0; i < (int)featureList.size(); ++i) {
Chris@498 542
Chris@498 543 const Plugin::Feature &feature = featureList[i];
Chris@602 544 unsigned long featureNumber = m_count++;
Chris@498 545
Chris@498 546 stream << ":event_" << featureNumber << " a ";
Chris@498 547
Chris@510 548 if (m_fixedEventTypeURI != "") {
Chris@510 549 stream << m_fixedEventTypeURI << " ;\n";
Chris@510 550 } else {
Chris@510 551 QString eventTypeURI = desc.getOutputEventTypeURI(outputId);
Chris@510 552 if (plain || eventTypeURI == "") {
Chris@510 553 if (m_syntheticEventTypeURIs.find(transform) !=
Chris@510 554 m_syntheticEventTypeURIs.end()) {
Chris@510 555 stream << m_syntheticEventTypeURIs[transform] << " ;\n";
Chris@510 556 } else {
Chris@510 557 stream << ":event_type_" << outputId << " ;\n";
Chris@510 558 }
Chris@498 559 } else {
Chris@510 560 stream << "<" << eventTypeURI << "> ;\n";
Chris@498 561 }
Chris@498 562 }
Chris@498 563
Chris@498 564 QString timestamp = feature.timestamp.toString().c_str();
Chris@498 565 timestamp.replace(QRegExp("^ +"), "");
Chris@498 566
Chris@498 567 if (feature.hasDuration && feature.duration > Vamp::RealTime::zeroTime) {
Chris@498 568
Chris@498 569 QString duration = feature.duration.toString().c_str();
Chris@498 570 duration.replace(QRegExp("^ +"), "");
Chris@498 571
Chris@498 572 stream << " event:time [ \n"
Chris@498 573 << " a tl:Interval ;\n"
Chris@498 574 << " tl:onTimeLine " << timelineURI << " ;\n"
Chris@498 575 << " tl:beginsAt \"PT" << timestamp
Chris@498 576 << "S\"^^xsd:duration ;\n"
Chris@498 577 << " tl:duration \"PT" << duration
Chris@498 578 << "S\"^^xsd:duration ;\n"
Chris@498 579 << " ] ";
Chris@498 580
Chris@498 581 } else {
Chris@498 582
Chris@498 583 stream << " event:time [ \n"
Chris@498 584 << " a tl:Instant ;\n" //location of the event in time
Chris@498 585 << " tl:onTimeLine " << timelineURI << " ;\n"
Chris@498 586 << " tl:at \"PT" << timestamp
Chris@498 587 << "S\"^^xsd:duration ;\n ] ";
Chris@498 588 }
Chris@498 589
Chris@500 590 if (transform.getIdentifier() != "") {
Chris@500 591 stream << ";\n";
Chris@500 592 stream << " vamp:computed_by " << m_transformURIs[transform] << " ";
Chris@500 593 }
Chris@498 594
Chris@498 595 if (feature.label.length() > 0) {
Chris@498 596 stream << ";\n";
Chris@510 597 stream << " rdfs:label \"\"\"" << feature.label.c_str() << "\"\"\" ";
Chris@498 598 }
Chris@498 599
Chris@498 600 if (!feature.values.empty()) {
Chris@498 601 stream << ";\n";
Chris@498 602 //!!! named bins?
Chris@498 603 stream << " af:feature \"" << feature.values[0];
Chris@930 604 for (int j = 1; j < (int)feature.values.size(); ++j) {
Chris@498 605 stream << " " << feature.values[j];
Chris@498 606 }
Chris@498 607 stream << "\" ";
Chris@498 608 }
Chris@498 609
Chris@498 610 stream << ".\n";
Chris@498 611 }
Chris@498 612 }
Chris@498 613
Chris@498 614 void
Chris@507 615 RDFFeatureWriter::writeTrackLevelRDF(QTextStream *sptr,
Chris@930 616 const Transform &,
Chris@507 617 const Plugin::OutputDescriptor& od,
Chris@507 618 const Plugin::FeatureList& featureList,
Chris@507 619 PluginRDFDescription &desc,
Chris@507 620 QString signalURI)
Chris@507 621 {
Chris@507 622 if (featureList.empty()) return;
Chris@507 623 QTextStream &stream = *sptr;
Chris@507 624
Chris@930 625 // bool plain = (m_plain || !desc.haveDescription());
Chris@507 626
Chris@507 627 QString outputId = od.identifier.c_str();
Chris@507 628 QString featureUri = desc.getOutputFeatureAttributeURI(outputId);
Chris@507 629
Chris@507 630 if (featureUri == "") {
Chris@690 631 SVDEBUG << "RDFFeatureWriter::writeTrackLevelRDF: ERROR: No feature URI available -- this function should not have been called!" << endl;
Chris@507 632 return;
Chris@507 633 }
Chris@507 634
Chris@930 635 for (int i = 0; i < (int)featureList.size(); ++i) {
Chris@507 636
Chris@507 637 const Plugin::Feature &feature = featureList[i];
Chris@507 638
Chris@507 639 if (feature.values.empty()) {
Chris@507 640
Chris@507 641 if (feature.label == "") continue;
Chris@507 642
Chris@508 643 stream << signalURI << " " << featureUri << " \"\"\""
Chris@508 644 << feature.label.c_str() << "\"\"\" .\n";
Chris@507 645
Chris@507 646 } else {
Chris@507 647
Chris@507 648 stream << signalURI << " " << featureUri << " \""
Chris@507 649 << feature.values[0] << "\"^^xsd:float .\n";
Chris@507 650 }
Chris@507 651 }
Chris@507 652 }
Chris@507 653
Chris@507 654 void
Chris@498 655 RDFFeatureWriter::writeDenseRDF(QTextStream *sptr,
Chris@498 656 const Transform &transform,
Chris@498 657 const Plugin::OutputDescriptor& od,
Chris@498 658 const Plugin::FeatureList& featureList,
Chris@498 659 PluginRDFDescription &desc,
Chris@498 660 QString signalURI,
Chris@498 661 QString timelineURI)
Chris@498 662 {
Chris@498 663 if (featureList.empty()) return;
Chris@498 664
Chris@498 665 StringTransformPair sp(signalURI, transform);
Chris@498 666
Chris@498 667 if (m_openDenseFeatures.find(sp) == m_openDenseFeatures.end()) {
Chris@498 668
Chris@498 669 StreamBuffer b(sptr, "");
Chris@498 670 m_openDenseFeatures[sp] = b;
Chris@498 671
Chris@498 672 QString &str(m_openDenseFeatures[sp].second);
Chris@498 673 QTextStream stream(&str);
Chris@498 674
Chris@498 675 bool plain = (m_plain || !desc.haveDescription());
Chris@498 676 QString outputId = od.identifier.c_str();
Chris@498 677
Chris@602 678 unsigned long featureNumber = m_count++;
Chris@498 679
Chris@498 680 // need to write out feature timeline map -- for this we need
Chris@498 681 // the sample rate, window length and hop size from the
Chris@498 682 // transform
Chris@498 683
Chris@498 684 stream << "\n:feature_timeline_" << featureNumber << " a tl:DiscreteTimeLine .\n\n";
Chris@498 685
Chris@995 686 float sampleRate;
Chris@995 687 int stepSize, blockSize;
Chris@498 688
Chris@995 689 // If the output is FixedSampleRate, we need to draw the
Chris@995 690 // sample rate and step size from the output descriptor;
Chris@995 691 // otherwise they come from the transform
Chris@498 692
Chris@995 693 if (od.sampleType == Plugin::OutputDescriptor::FixedSampleRate) {
Chris@995 694
Chris@995 695 sampleRate = od.sampleRate;
Chris@995 696 stepSize = 1;
Chris@995 697 blockSize = 1;
Chris@995 698
Chris@995 699 } else {
Chris@995 700
Chris@995 701 sampleRate = transform.getSampleRate();
Chris@995 702 if (sampleRate == 0.f) {
Chris@995 703 cerr << "RDFFeatureWriter: INTERNAL ERROR: writing dense features without having set the sample rate properly!" << endl;
Chris@995 704 return;
Chris@995 705 }
Chris@995 706
Chris@995 707 stepSize = transform.getStepSize();
Chris@995 708 if (stepSize == 0) {
Chris@995 709 cerr << "RDFFeatureWriter: INTERNAL ERROR: writing dense features without having set the step size properly!" << endl;
Chris@995 710 return;
Chris@995 711 }
Chris@995 712
Chris@995 713 blockSize = transform.getBlockSize();
Chris@995 714 if (blockSize == 0) {
Chris@995 715 cerr << "RDFFeatureWriter: INTERNAL ERROR: writing dense features without having set the block size properly!" << endl;
Chris@995 716 return;
Chris@995 717 }
Chris@498 718 }
Chris@498 719
Chris@498 720 stream << ":feature_timeline_map_" << featureNumber
Chris@498 721 << " a tl:UniformSamplingWindowingMap ;\n"
Chris@498 722 << " tl:rangeTimeLine :feature_timeline_" << featureNumber << " ;\n"
Chris@498 723 << " tl:domainTimeLine " << timelineURI << " ;\n"
Chris@995 724 << " tl:sampleRate \"" << sampleRate << "\"^^xsd:float ;\n"
Chris@498 725 << " tl:windowLength \"" << blockSize << "\"^^xsd:int ;\n"
Chris@498 726 << " tl:hopSize \"" << stepSize << "\"^^xsd:int .\n\n";
Chris@498 727
Chris@498 728 stream << signalURI << " af:signal_feature :feature_"
Chris@498 729 << featureNumber << " ." << endl << endl;
Chris@498 730
Chris@498 731 stream << ":feature_" << featureNumber << " a ";
Chris@498 732
Chris@498 733 QString signalTypeURI = desc.getOutputSignalTypeURI(outputId);
Chris@498 734 if (plain || signalTypeURI == "") {
Chris@498 735 if (m_syntheticSignalTypeURIs.find(transform) !=
Chris@498 736 m_syntheticSignalTypeURIs.end()) {
Chris@498 737 stream << m_syntheticSignalTypeURIs[transform] << " ;\n";
Chris@498 738 } else {
Chris@498 739 stream << ":signal_type_" << outputId << " ;\n";
Chris@498 740 }
Chris@498 741 } else {
Chris@587 742 stream << "<" << signalTypeURI << "> ;\n";
Chris@498 743 }
Chris@498 744
Chris@498 745 stream << " mo:time ["
Chris@498 746 << "\n a tl:Interval ;"
Chris@498 747 << "\n tl:onTimeLine :feature_timeline_" << featureNumber << " ;";
Chris@498 748
Chris@498 749 RealTime startrt = transform.getStartTime();
Chris@498 750 RealTime durationrt = transform.getDuration();
Chris@498 751
Chris@498 752 int start = RealTime::realTime2Frame(startrt, sampleRate) / stepSize;
Chris@498 753 int duration = RealTime::realTime2Frame(durationrt, sampleRate) / stepSize;
Chris@498 754
Chris@498 755 if (start != 0) {
Chris@498 756 stream << "\n tl:start \"" << start << "\"^^xsd:int ;";
Chris@498 757 }
Chris@498 758 if (duration != 0) {
Chris@498 759 stream << "\n tl:duration \"" << duration << "\"^^xsd:int ;";
Chris@498 760 }
Chris@498 761
Chris@498 762 stream << "\n ] ;\n";
Chris@498 763
Chris@584 764 if (transform.getIdentifier() != "") {
Chris@584 765 stream << " vamp:computed_by " << m_transformURIs[transform] << " ;\n";
Chris@584 766 }
Chris@584 767
Chris@498 768 if (od.hasFixedBinCount) {
Chris@498 769 // We only know the height, so write the width as zero
Chris@498 770 stream << " af:dimensions \"" << od.binCount << " 0\" ;\n";
Chris@498 771 }
Chris@498 772
Chris@498 773 stream << " af:value \"";
Chris@498 774 }
Chris@498 775
Chris@498 776 QString &str = m_openDenseFeatures[sp].second;
Chris@498 777 QTextStream stream(&str);
Chris@498 778
Chris@930 779 for (int i = 0; i < (int)featureList.size(); ++i) {
Chris@498 780
Chris@498 781 const Plugin::Feature &feature = featureList[i];
Chris@498 782
Chris@930 783 for (int j = 0; j < (int)feature.values.size(); ++j) {
Chris@498 784 stream << feature.values[j] << " ";
Chris@498 785 }
Chris@498 786 }
Chris@498 787 }
Chris@498 788
Chris@498 789 void RDFFeatureWriter::finish()
Chris@498 790 {
Chris@690 791 // SVDEBUG << "RDFFeatureWriter::finish()" << endl;
Chris@498 792
Chris@498 793 // close any open dense feature literals
Chris@498 794
Chris@498 795 for (map<StringTransformPair, StreamBuffer>::iterator i =
Chris@498 796 m_openDenseFeatures.begin();
Chris@498 797 i != m_openDenseFeatures.end(); ++i) {
Chris@690 798 // SVDEBUG << "closing a stream" << endl;
Chris@498 799 StreamBuffer &b = i->second;
Chris@498 800 *(b.first) << b.second << "\" ." << endl;
Chris@498 801 }
Chris@498 802
Chris@498 803 m_openDenseFeatures.clear();
Chris@530 804 m_startedStreamTransforms.clear();
Chris@530 805
Chris@530 806 FileFeatureWriter::finish();
Chris@498 807 }
Chris@498 808
Chris@498 809