annotate rdf/RDFFeatureWriter.cpp @ 1879:652c5360e682

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