annotate rdf/RDFFeatureWriter.cpp @ 507:0944d13689b2

* Implement proper RDF feature writing for track level features, using the feature attribute URI given in the plugin description RDF (if there is one)
author Chris Cannam
date Fri, 05 Dec 2008 14:19:04 +0000
parents 3376dc26dece
children 1b8c748fd7ea
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@498 21 #include "RDFFeatureWriter.h"
Chris@498 22 #include "RDFTransformFactory.h"
Chris@498 23
Chris@498 24 #include <QTextStream>
Chris@498 25 #include <QUrl>
Chris@498 26 #include <QRegExp>
Chris@498 27
Chris@498 28 using namespace std;
Chris@498 29 using Vamp::Plugin;
Chris@498 30 using Vamp::PluginBase;
Chris@498 31
Chris@498 32 RDFFeatureWriter::RDFFeatureWriter() :
Chris@498 33 FileFeatureWriter(SupportOneFilePerTrackTransform |
Chris@498 34 SupportOneFilePerTrack |
Chris@498 35 SupportOneFileTotal,
Chris@498 36 "n3"),
Chris@498 37 m_plain(false),
Chris@498 38 m_count(0)
Chris@498 39 {
Chris@498 40 }
Chris@498 41
Chris@498 42 RDFFeatureWriter::~RDFFeatureWriter()
Chris@498 43 {
Chris@498 44 }
Chris@498 45
Chris@498 46 RDFFeatureWriter::ParameterList
Chris@498 47 RDFFeatureWriter::getSupportedParameters() const
Chris@498 48 {
Chris@498 49 ParameterList pl = FileFeatureWriter::getSupportedParameters();
Chris@498 50 Parameter p;
Chris@498 51
Chris@498 52 p.name = "plain";
Chris@498 53 p.description = "Use \"plain\" RDF even if transform metadata is available.";
Chris@498 54 p.hasArg = false;
Chris@498 55 pl.push_back(p);
Chris@498 56
Chris@498 57 p.name = "signal-uri";
Chris@498 58 p.description = "Link the output RDF to the given signal URI.";
Chris@498 59 p.hasArg = true;
Chris@498 60 pl.push_back(p);
Chris@498 61
Chris@498 62 return pl;
Chris@498 63 }
Chris@498 64
Chris@498 65 void
Chris@498 66 RDFFeatureWriter::setParameters(map<string, string> &params)
Chris@498 67 {
Chris@498 68 FileFeatureWriter::setParameters(params);
Chris@498 69
Chris@498 70 for (map<string, string>::iterator i = params.begin();
Chris@498 71 i != params.end(); ++i) {
Chris@498 72 if (i->first == "plain") {
Chris@498 73 m_plain = true;
Chris@498 74 }
Chris@498 75 if (i->first == "signal-uri") {
Chris@498 76 m_suri = i->second.c_str();
Chris@498 77 }
Chris@498 78 }
Chris@498 79 }
Chris@498 80
Chris@504 81 void
Chris@504 82 RDFFeatureWriter::setTrackMetadata(QString trackId,
Chris@504 83 TrackMetadata metadata)
Chris@504 84 {
Chris@504 85 m_metadata[trackId] = metadata;
Chris@504 86 }
Chris@504 87
Chris@504 88 void
Chris@504 89 RDFFeatureWriter::write(QString trackId,
Chris@504 90 const Transform &transform,
Chris@504 91 const Plugin::OutputDescriptor& output,
Chris@504 92 const Plugin::FeatureList& features,
Chris@504 93 std::string summaryType)
Chris@498 94 {
Chris@498 95 QString pluginId = transform.getPluginIdentifier();
Chris@498 96
Chris@498 97 if (m_rdfDescriptions.find(pluginId) == m_rdfDescriptions.end()) {
Chris@498 98
Chris@498 99 m_rdfDescriptions[pluginId] = PluginRDFDescription(pluginId);
Chris@498 100
Chris@498 101 if (m_rdfDescriptions[pluginId].haveDescription()) {
Chris@498 102 cerr << "NOTE: Have RDF description for plugin ID \""
Chris@498 103 << pluginId.toStdString() << "\"" << endl;
Chris@498 104 } else {
Chris@498 105 cerr << "NOTE: Do not have RDF description for plugin ID \""
Chris@498 106 << pluginId.toStdString() << "\"" << endl;
Chris@498 107 }
Chris@498 108 }
Chris@498 109
Chris@498 110 // Need to select appropriate output file for our track/transform
Chris@498 111 // combination
Chris@498 112
Chris@498 113 QTextStream *stream = getOutputStream(trackId, transform.getIdentifier());
Chris@498 114 if (!stream) return; //!!! this is probably better handled with an exception
Chris@498 115
Chris@498 116 if (m_startedStreamTransforms.find(stream) ==
Chris@498 117 m_startedStreamTransforms.end()) {
Chris@498 118 cerr << "This stream is new, writing prefixes" << endl;
Chris@498 119 writePrefixes(stream);
Chris@498 120 if (m_singleFileName == "" && !m_stdout) {
Chris@498 121 writeSignalDescription(stream, trackId);
Chris@498 122 }
Chris@498 123 }
Chris@498 124
Chris@498 125 if (m_startedStreamTransforms[stream].find(transform) ==
Chris@498 126 m_startedStreamTransforms[stream].end()) {
Chris@498 127 m_startedStreamTransforms[stream].insert(transform);
Chris@498 128 writeLocalFeatureTypes
Chris@498 129 (stream, transform, output, m_rdfDescriptions[pluginId]);
Chris@498 130 }
Chris@498 131
Chris@498 132 if (m_singleFileName != "" || m_stdout) {
Chris@498 133 if (m_startedTrackIds.find(trackId) == m_startedTrackIds.end()) {
Chris@498 134 writeSignalDescription(stream, trackId);
Chris@498 135 m_startedTrackIds.insert(trackId);
Chris@498 136 }
Chris@498 137 }
Chris@498 138
Chris@498 139 QString timelineURI = m_trackTimelineURIs[trackId];
Chris@498 140
Chris@498 141 if (timelineURI == "") {
Chris@498 142 cerr << "RDFFeatureWriter: INTERNAL ERROR: writing features without having established a timeline URI!" << endl;
Chris@498 143 exit(1);
Chris@498 144 }
Chris@498 145
Chris@498 146 if (summaryType != "") {
Chris@498 147
Chris@498 148 writeSparseRDF(stream, transform, output, features,
Chris@498 149 m_rdfDescriptions[pluginId], timelineURI);
Chris@498 150
Chris@498 151 } else if (m_rdfDescriptions[pluginId].haveDescription() &&
Chris@498 152 m_rdfDescriptions[pluginId].getOutputDisposition
Chris@498 153 (output.identifier.c_str()) ==
Chris@498 154 PluginRDFDescription::OutputDense) {
Chris@498 155
Chris@498 156 QString signalURI = m_trackSignalURIs[trackId];
Chris@498 157
Chris@498 158 if (signalURI == "") {
Chris@498 159 cerr << "RDFFeatureWriter: INTERNAL ERROR: writing dense features without having established a signal URI!" << endl;
Chris@498 160 exit(1);
Chris@498 161 }
Chris@498 162
Chris@498 163 writeDenseRDF(stream, transform, output, features,
Chris@498 164 m_rdfDescriptions[pluginId], signalURI, timelineURI);
Chris@498 165
Chris@507 166 } else if (!m_plain &&
Chris@507 167 m_rdfDescriptions[pluginId].haveDescription() &&
Chris@507 168 m_rdfDescriptions[pluginId].getOutputDisposition
Chris@507 169 (output.identifier.c_str()) ==
Chris@507 170 PluginRDFDescription::OutputTrackLevel &&
Chris@507 171 m_rdfDescriptions[pluginId].getOutputFeatureAttributeURI
Chris@507 172 (output.identifier.c_str()) != "") {
Chris@507 173
Chris@507 174 QString signalURI = m_trackSignalURIs[trackId];
Chris@507 175
Chris@507 176 if (signalURI == "") {
Chris@507 177 cerr << "RDFFeatureWriter: INTERNAL ERROR: writing track-level features without having established a signal URI!" << endl;
Chris@507 178 exit(1);
Chris@507 179 }
Chris@507 180
Chris@507 181 writeTrackLevelRDF(stream, transform, output, features,
Chris@507 182 m_rdfDescriptions[pluginId], signalURI);
Chris@507 183
Chris@498 184 } else {
Chris@498 185
Chris@498 186 writeSparseRDF(stream, transform, output, features,
Chris@498 187 m_rdfDescriptions[pluginId], timelineURI);
Chris@498 188 }
Chris@498 189 }
Chris@498 190
Chris@498 191 void
Chris@498 192 RDFFeatureWriter::writePrefixes(QTextStream *sptr)
Chris@498 193 {
Chris@498 194 QTextStream &stream = *sptr;
Chris@498 195
Chris@498 196 stream << "@prefix dc: <http://purl.org/dc/elements/1.1/> .\n"
Chris@498 197 << "@prefix mo: <http://purl.org/ontology/mo/> .\n"
Chris@498 198 << "@prefix af: <http://purl.org/ontology/af/> .\n"
Chris@504 199 << "@prefix foaf: <http://xmlns.com/foaf/0.1/> . \n"
Chris@498 200 << "@prefix event: <http://purl.org/NET/c4dm/event.owl#> .\n"
Chris@498 201 << "@prefix rdf: <http://www.w3.org/1999/02/22-rdf-syntax-ns#> .\n"
Chris@498 202 << "@prefix rdfs: <http://www.w3.org/2000/01/rdf-schema#> .\n"
Chris@498 203 << "@prefix xsd: <http://www.w3.org/2001/XMLSchema#> .\n"
Chris@498 204 << "@prefix tl: <http://purl.org/NET/c4dm/timeline.owl#> .\n"
Chris@498 205 << "@prefix vamp: <http://purl.org/ontology/vamp/> .\n"
Chris@498 206 << "@prefix : <#> .\n\n";
Chris@498 207 }
Chris@498 208
Chris@498 209 void
Chris@498 210 RDFFeatureWriter::writeSignalDescription(QTextStream *sptr,
Chris@498 211 QString trackId)
Chris@498 212 {
Chris@498 213 QTextStream &stream = *sptr;
Chris@498 214
Chris@498 215 /*
Chris@498 216 * Describe signal we're analysing (AudioFile, Signal, TimeLine, etc.)
Chris@498 217 */
Chris@498 218
Chris@498 219 QUrl url(trackId);
Chris@498 220 QString scheme = url.scheme().toLower();
Chris@498 221 bool local = (scheme == "" || scheme == "file" || scheme.length() == 1);
Chris@498 222
Chris@498 223 if (local) {
Chris@498 224 if (scheme == "") {
Chris@498 225 url.setScheme("file");
Chris@498 226 } else if (scheme.length() == 1) { // DOS drive letter!
Chris@498 227 url.setScheme("file");
Chris@498 228 url.setPath(scheme + ":" + url.path());
Chris@498 229 }
Chris@498 230 }
Chris@498 231
Chris@498 232 //!!! FIX: If we are appending, we need to start counting after
Chris@498 233 //all of the existing counts that are already in the file!
Chris@498 234
Chris@498 235 uint64_t signalCount = m_count++;
Chris@498 236
Chris@498 237 if (m_trackSignalURIs.find(trackId) == m_trackSignalURIs.end()) {
Chris@498 238 m_trackSignalURIs[trackId] = QString(":signal_%1").arg(signalCount);
Chris@498 239 }
Chris@498 240
Chris@498 241 if (m_suri != NULL) {
Chris@498 242 m_trackSignalURIs[trackId] = "<" + m_suri + ">";
Chris@498 243 }
Chris@498 244 QString signalURI = m_trackSignalURIs[trackId];
Chris@498 245
Chris@498 246 if (m_trackTimelineURIs.find(trackId) == m_trackTimelineURIs.end()) {
Chris@498 247 m_trackTimelineURIs[trackId] = QString(":signal_timeline_%1").arg(signalCount);
Chris@498 248 }
Chris@498 249 QString timelineURI = m_trackTimelineURIs[trackId];
Chris@498 250
Chris@500 251 if (trackId != "") {
Chris@500 252 stream << "\n<" << url.toEncoded().data() << "> a mo:AudioFile .\n\n";
Chris@500 253 }
Chris@500 254
Chris@500 255 stream << signalURI << " a mo:Signal ;\n";
Chris@500 256
Chris@500 257 if (trackId != "") {
Chris@500 258 stream << " mo:available_as <" << url.toEncoded().data()
Chris@500 259 << "> ;\n";
Chris@500 260 }
Chris@500 261
Chris@504 262 if (m_metadata.find(trackId) != m_metadata.end()) {
Chris@504 263 TrackMetadata tm = m_metadata[trackId];
Chris@504 264 if (tm.title != "") {
Chris@504 265 stream << " dc:title \"\"\"" << tm.title << "\"\"\" ;\n";
Chris@504 266 }
Chris@504 267 if (tm.maker != "") {
Chris@507 268 stream << " foaf:maker [ a mo:MusicArtist; foaf:name \"\"\"" << tm.maker << "\"\"\" ] ;\n";
Chris@504 269 }
Chris@504 270 }
Chris@504 271
Chris@500 272 stream << " mo:time [\n"
Chris@498 273 << " a tl:Interval ;\n"
Chris@498 274 << " tl:onTimeLine "
Chris@498 275 << timelineURI << "\n ] .\n\n";
Chris@498 276 }
Chris@498 277
Chris@498 278 void
Chris@498 279 RDFFeatureWriter::writeLocalFeatureTypes(QTextStream *sptr,
Chris@498 280 const Transform &transform,
Chris@498 281 const Plugin::OutputDescriptor &od,
Chris@498 282 PluginRDFDescription &desc)
Chris@498 283 {
Chris@498 284 QString outputId = od.identifier.c_str();
Chris@498 285 QTextStream &stream = *sptr;
Chris@498 286
Chris@507 287 // There is no "needFeatureType" for track-level outputs, because
Chris@507 288 // we can't meaningfully write a feature at all if we don't know
Chris@507 289 // what property to use for it. If the output is track level but
Chris@507 290 // there is no feature type given, we have to revert to events.
Chris@507 291
Chris@498 292 bool needEventType = false;
Chris@498 293 bool needSignalType = false;
Chris@498 294
Chris@498 295 //!!! bin names, extents and so on can be written out using e.g. vamp:bin_names ( "a" "b" "c" )
Chris@498 296
Chris@498 297 if (desc.getOutputDisposition(outputId) ==
Chris@498 298 PluginRDFDescription::OutputDense) {
Chris@498 299
Chris@498 300 // no feature events, so may need signal type but won't need
Chris@498 301 // event type
Chris@498 302
Chris@498 303 if (m_plain) {
Chris@498 304
Chris@498 305 needSignalType = true;
Chris@498 306
Chris@498 307 } else if (desc.getOutputSignalTypeURI(outputId) == "") {
Chris@498 308
Chris@498 309 needSignalType = true;
Chris@498 310 }
Chris@498 311
Chris@507 312 } else if (desc.getOutputDisposition(outputId) ==
Chris@507 313 PluginRDFDescription::OutputTrackLevel) {
Chris@507 314
Chris@507 315 // see note above -- need to generate an event type if no
Chris@507 316 // feature type given, or if in plain mode
Chris@507 317
Chris@507 318 cerr << "Note: track level output" << endl;
Chris@507 319
Chris@507 320 if (m_plain) {
Chris@507 321
Chris@507 322 needEventType = true;
Chris@507 323
Chris@507 324 } else if (desc.getOutputFeatureAttributeURI(outputId) == "") {
Chris@507 325
Chris@507 326 if (desc.getOutputEventTypeURI(outputId) == "") {
Chris@507 327
Chris@507 328 needEventType = true;
Chris@507 329 }
Chris@507 330 }
Chris@507 331
Chris@498 332 } else {
Chris@498 333
Chris@498 334 // may need event type but won't need signal type
Chris@498 335
Chris@498 336 if (m_plain) {
Chris@498 337
Chris@498 338 needEventType = true;
Chris@498 339
Chris@498 340 } else if (desc.getOutputEventTypeURI(outputId) == "") {
Chris@498 341
Chris@498 342 needEventType = true;
Chris@498 343 }
Chris@498 344 }
Chris@498 345
Chris@498 346 QString transformUri;
Chris@498 347 if (m_transformURIs.find(transform) != m_transformURIs.end()) {
Chris@498 348 transformUri = m_transformURIs[transform];
Chris@498 349 } else {
Chris@498 350 transformUri = QString(":transform_%1_%2").arg(m_count++).arg(outputId);
Chris@498 351 m_transformURIs[transform] = transformUri;
Chris@498 352 }
Chris@498 353
Chris@500 354 if (transform.getIdentifier() != "") {
Chris@500 355 stream << RDFTransformFactory::writeTransformToRDF(transform, transformUri)
Chris@500 356 << endl;
Chris@500 357 }
Chris@498 358
Chris@498 359 if (needEventType) {
Chris@498 360
Chris@498 361 QString uri;
Chris@498 362 if (m_syntheticEventTypeURIs.find(transform) !=
Chris@498 363 m_syntheticEventTypeURIs.end()) {
Chris@498 364 uri = m_syntheticEventTypeURIs[transform];
Chris@498 365 } else {
Chris@498 366 uri = QString(":event_type_%1").arg(m_count++);
Chris@498 367 m_syntheticEventTypeURIs[transform] = uri;
Chris@498 368 }
Chris@498 369
Chris@498 370 stream << uri
Chris@498 371 << " rdfs:subClassOf event:Event ;" << endl
Chris@498 372 << " dc:title \"" << od.name.c_str() << "\" ;" << endl
Chris@498 373 << " dc:format \"" << od.unit.c_str() << "\" ;" << endl
Chris@498 374 << " dc:description \"" << od.description.c_str() << "\" ."
Chris@498 375 << endl << endl;
Chris@498 376 }
Chris@498 377
Chris@498 378 if (needSignalType) {
Chris@498 379
Chris@498 380 QString uri;
Chris@498 381 if (m_syntheticSignalTypeURIs.find(transform) !=
Chris@498 382 m_syntheticSignalTypeURIs.end()) {
Chris@498 383 uri = m_syntheticSignalTypeURIs[transform];
Chris@498 384 } else {
Chris@498 385 uri = QString(":signal_type_%1").arg(m_count++);
Chris@498 386 m_syntheticSignalTypeURIs[transform] = uri;
Chris@498 387 }
Chris@498 388
Chris@498 389 stream << uri
Chris@498 390 << " rdfs:subClassOf af:Signal ;" << endl
Chris@498 391 << " dc:title \"" << od.name.c_str() << "\" ;" << endl
Chris@498 392 << " dc:format \"" << od.unit.c_str() << "\" ;" << endl
Chris@498 393 << " dc:description \"" << od.description.c_str() << "\" ."
Chris@498 394 << endl << endl;
Chris@498 395 }
Chris@498 396 }
Chris@498 397
Chris@498 398 void
Chris@498 399 RDFFeatureWriter::writeSparseRDF(QTextStream *sptr,
Chris@498 400 const Transform &transform,
Chris@498 401 const Plugin::OutputDescriptor& od,
Chris@498 402 const Plugin::FeatureList& featureList,
Chris@498 403 PluginRDFDescription &desc,
Chris@498 404 QString timelineURI)
Chris@498 405 {
Chris@498 406 if (featureList.empty()) return;
Chris@498 407 QTextStream &stream = *sptr;
Chris@498 408
Chris@498 409 bool plain = (m_plain || !desc.haveDescription());
Chris@498 410
Chris@498 411 QString outputId = od.identifier.c_str();
Chris@498 412
Chris@498 413 // iterate through FeatureLists
Chris@498 414
Chris@498 415 for (int i = 0; i < featureList.size(); ++i) {
Chris@498 416
Chris@498 417 const Plugin::Feature &feature = featureList[i];
Chris@498 418 uint64_t featureNumber = m_count++;
Chris@498 419
Chris@498 420 stream << ":event_" << featureNumber << " a ";
Chris@498 421
Chris@498 422 QString eventTypeURI = desc.getOutputEventTypeURI(outputId);
Chris@498 423 if (plain || eventTypeURI == "") {
Chris@498 424 if (m_syntheticEventTypeURIs.find(transform) !=
Chris@498 425 m_syntheticEventTypeURIs.end()) {
Chris@498 426 stream << m_syntheticEventTypeURIs[transform] << " ;\n";
Chris@498 427 } else {
Chris@498 428 stream << ":event_type_" << outputId << " ;\n";
Chris@498 429 }
Chris@498 430 } else {
Chris@498 431 stream << "<" << eventTypeURI << "> ;\n";
Chris@498 432 }
Chris@498 433
Chris@498 434 QString timestamp = feature.timestamp.toString().c_str();
Chris@498 435 timestamp.replace(QRegExp("^ +"), "");
Chris@498 436
Chris@498 437 if (feature.hasDuration && feature.duration > Vamp::RealTime::zeroTime) {
Chris@498 438
Chris@498 439 QString duration = feature.duration.toString().c_str();
Chris@498 440 duration.replace(QRegExp("^ +"), "");
Chris@498 441
Chris@498 442 stream << " event:time [ \n"
Chris@498 443 << " a tl:Interval ;\n"
Chris@498 444 << " tl:onTimeLine " << timelineURI << " ;\n"
Chris@498 445 << " tl:beginsAt \"PT" << timestamp
Chris@498 446 << "S\"^^xsd:duration ;\n"
Chris@498 447 << " tl:duration \"PT" << duration
Chris@498 448 << "S\"^^xsd:duration ;\n"
Chris@498 449 << " ] ";
Chris@498 450
Chris@498 451 } else {
Chris@498 452
Chris@498 453 stream << " event:time [ \n"
Chris@498 454 << " a tl:Instant ;\n" //location of the event in time
Chris@498 455 << " tl:onTimeLine " << timelineURI << " ;\n"
Chris@498 456 << " tl:at \"PT" << timestamp
Chris@498 457 << "S\"^^xsd:duration ;\n ] ";
Chris@498 458 }
Chris@498 459
Chris@500 460 if (transform.getIdentifier() != "") {
Chris@500 461 stream << ";\n";
Chris@500 462 stream << " vamp:computed_by " << m_transformURIs[transform] << " ";
Chris@500 463 }
Chris@498 464
Chris@498 465 if (feature.label.length() > 0) {
Chris@498 466 stream << ";\n";
Chris@498 467 stream << " rdfs:label \"" << feature.label.c_str() << "\" ";
Chris@498 468 }
Chris@498 469
Chris@498 470 if (!feature.values.empty()) {
Chris@498 471 stream << ";\n";
Chris@498 472 //!!! named bins?
Chris@498 473 stream << " af:feature \"" << feature.values[0];
Chris@498 474 for (int j = 1; j < feature.values.size(); ++j) {
Chris@498 475 stream << " " << feature.values[j];
Chris@498 476 }
Chris@498 477 stream << "\" ";
Chris@498 478 }
Chris@498 479
Chris@498 480 stream << ".\n";
Chris@498 481 }
Chris@498 482 }
Chris@498 483
Chris@498 484 void
Chris@507 485 RDFFeatureWriter::writeTrackLevelRDF(QTextStream *sptr,
Chris@507 486 const Transform &transform,
Chris@507 487 const Plugin::OutputDescriptor& od,
Chris@507 488 const Plugin::FeatureList& featureList,
Chris@507 489 PluginRDFDescription &desc,
Chris@507 490 QString signalURI)
Chris@507 491 {
Chris@507 492 if (featureList.empty()) return;
Chris@507 493 QTextStream &stream = *sptr;
Chris@507 494
Chris@507 495 bool plain = (m_plain || !desc.haveDescription());
Chris@507 496
Chris@507 497 QString outputId = od.identifier.c_str();
Chris@507 498 QString featureUri = desc.getOutputFeatureAttributeURI(outputId);
Chris@507 499
Chris@507 500 if (featureUri == "") {
Chris@507 501 cerr << "RDFFeatureWriter::writeTrackLevelRDF: ERROR: No feature URI available -- this function should not have been called!" << endl;
Chris@507 502 return;
Chris@507 503 }
Chris@507 504
Chris@507 505 for (int i = 0; i < featureList.size(); ++i) {
Chris@507 506
Chris@507 507 const Plugin::Feature &feature = featureList[i];
Chris@507 508
Chris@507 509 if (feature.values.empty()) {
Chris@507 510
Chris@507 511 if (feature.label == "") continue;
Chris@507 512
Chris@507 513 stream << signalURI << " " << featureUri << " \""
Chris@507 514 << feature.label.c_str() << "\" .\n";
Chris@507 515
Chris@507 516 } else {
Chris@507 517
Chris@507 518 stream << signalURI << " " << featureUri << " \""
Chris@507 519 << feature.values[0] << "\"^^xsd:float .\n";
Chris@507 520 }
Chris@507 521 }
Chris@507 522 }
Chris@507 523
Chris@507 524 void
Chris@498 525 RDFFeatureWriter::writeDenseRDF(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 signalURI,
Chris@498 531 QString timelineURI)
Chris@498 532 {
Chris@498 533 if (featureList.empty()) return;
Chris@498 534
Chris@498 535 StringTransformPair sp(signalURI, transform);
Chris@498 536
Chris@498 537 if (m_openDenseFeatures.find(sp) == m_openDenseFeatures.end()) {
Chris@498 538
Chris@498 539 StreamBuffer b(sptr, "");
Chris@498 540 m_openDenseFeatures[sp] = b;
Chris@498 541
Chris@498 542 QString &str(m_openDenseFeatures[sp].second);
Chris@498 543 QTextStream stream(&str);
Chris@498 544
Chris@498 545 bool plain = (m_plain || !desc.haveDescription());
Chris@498 546 QString outputId = od.identifier.c_str();
Chris@498 547
Chris@498 548 uint64_t featureNumber = m_count++;
Chris@498 549
Chris@498 550 // need to write out feature timeline map -- for this we need
Chris@498 551 // the sample rate, window length and hop size from the
Chris@498 552 // transform
Chris@498 553
Chris@498 554 stream << "\n:feature_timeline_" << featureNumber << " a tl:DiscreteTimeLine .\n\n";
Chris@498 555
Chris@498 556 size_t stepSize = transform.getStepSize();
Chris@498 557 if (stepSize == 0) {
Chris@498 558 cerr << "RDFFeatureWriter: INTERNAL ERROR: writing dense features without having set the step size properly!" << endl;
Chris@498 559 return;
Chris@498 560 }
Chris@498 561
Chris@498 562 size_t blockSize = transform.getBlockSize();
Chris@498 563 if (blockSize == 0) {
Chris@498 564 cerr << "RDFFeatureWriter: INTERNAL ERROR: writing dense features without having set the block size properly!" << endl;
Chris@498 565 return;
Chris@498 566 }
Chris@498 567
Chris@498 568 float sampleRate = transform.getSampleRate();
Chris@498 569 if (sampleRate == 0.f) {
Chris@498 570 cerr << "RDFFeatureWriter: INTERNAL ERROR: writing dense features without having set the sample rate properly!" << endl;
Chris@498 571 return;
Chris@498 572 }
Chris@498 573
Chris@498 574 stream << ":feature_timeline_map_" << featureNumber
Chris@498 575 << " a tl:UniformSamplingWindowingMap ;\n"
Chris@498 576 << " tl:rangeTimeLine :feature_timeline_" << featureNumber << " ;\n"
Chris@498 577 << " tl:domainTimeLine " << timelineURI << " ;\n"
Chris@498 578 << " tl:sampleRate \"" << int(sampleRate) << "\"^^xsd:int ;\n"
Chris@498 579 << " tl:windowLength \"" << blockSize << "\"^^xsd:int ;\n"
Chris@498 580 << " tl:hopSize \"" << stepSize << "\"^^xsd:int .\n\n";
Chris@498 581
Chris@498 582 stream << signalURI << " af:signal_feature :feature_"
Chris@498 583 << featureNumber << " ." << endl << endl;
Chris@498 584
Chris@498 585 stream << ":feature_" << featureNumber << " a ";
Chris@498 586
Chris@498 587 QString signalTypeURI = desc.getOutputSignalTypeURI(outputId);
Chris@498 588 if (plain || signalTypeURI == "") {
Chris@498 589 if (m_syntheticSignalTypeURIs.find(transform) !=
Chris@498 590 m_syntheticSignalTypeURIs.end()) {
Chris@498 591 stream << m_syntheticSignalTypeURIs[transform] << " ;\n";
Chris@498 592 } else {
Chris@498 593 stream << ":signal_type_" << outputId << " ;\n";
Chris@498 594 }
Chris@498 595 } else {
Chris@498 596 stream << signalTypeURI << " ;\n";
Chris@498 597 }
Chris@498 598
Chris@498 599 stream << " mo:time ["
Chris@498 600 << "\n a tl:Interval ;"
Chris@498 601 << "\n tl:onTimeLine :feature_timeline_" << featureNumber << " ;";
Chris@498 602
Chris@498 603 RealTime startrt = transform.getStartTime();
Chris@498 604 RealTime durationrt = transform.getDuration();
Chris@498 605
Chris@498 606 int start = RealTime::realTime2Frame(startrt, sampleRate) / stepSize;
Chris@498 607 int duration = RealTime::realTime2Frame(durationrt, sampleRate) / stepSize;
Chris@498 608
Chris@498 609 if (start != 0) {
Chris@498 610 stream << "\n tl:start \"" << start << "\"^^xsd:int ;";
Chris@498 611 }
Chris@498 612 if (duration != 0) {
Chris@498 613 stream << "\n tl:duration \"" << duration << "\"^^xsd:int ;";
Chris@498 614 }
Chris@498 615
Chris@498 616 stream << "\n ] ;\n";
Chris@498 617
Chris@498 618 if (od.hasFixedBinCount) {
Chris@498 619 // We only know the height, so write the width as zero
Chris@498 620 stream << " af:dimensions \"" << od.binCount << " 0\" ;\n";
Chris@498 621 }
Chris@498 622
Chris@498 623 stream << " af:value \"";
Chris@498 624 }
Chris@498 625
Chris@498 626 QString &str = m_openDenseFeatures[sp].second;
Chris@498 627 QTextStream stream(&str);
Chris@498 628
Chris@498 629 for (int i = 0; i < featureList.size(); ++i) {
Chris@498 630
Chris@498 631 const Plugin::Feature &feature = featureList[i];
Chris@498 632
Chris@498 633 for (int j = 0; j < feature.values.size(); ++j) {
Chris@498 634 stream << feature.values[j] << " ";
Chris@498 635 }
Chris@498 636 }
Chris@498 637 }
Chris@498 638
Chris@498 639 void RDFFeatureWriter::finish()
Chris@498 640 {
Chris@498 641 // cerr << "RDFFeatureWriter::finish()" << endl;
Chris@498 642
Chris@498 643 // close any open dense feature literals
Chris@498 644
Chris@498 645 for (map<StringTransformPair, StreamBuffer>::iterator i =
Chris@498 646 m_openDenseFeatures.begin();
Chris@498 647 i != m_openDenseFeatures.end(); ++i) {
Chris@498 648 cerr << "closing a stream" << endl;
Chris@498 649 StreamBuffer &b = i->second;
Chris@498 650 *(b.first) << b.second << "\" ." << endl;
Chris@498 651 }
Chris@498 652
Chris@498 653 m_openDenseFeatures.clear();
Chris@498 654 }
Chris@498 655
Chris@498 656