annotate rdf/RDFFeatureWriter.cpp @ 808:67003fb58ba4

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