annotate rdf/RDFFeatureWriter.cpp @ 661:a4faa1840384

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