annotate rdf/RDFFeatureWriter.cpp @ 498:fdf5930b7ccc

* Bring FeatureWriter and RDFFeatureWriter into the fold (from Runner) so that we can use them to export features from SV as well
author Chris Cannam
date Fri, 28 Nov 2008 13:47:11 +0000
parents
children 83eae5239db6
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@498 81 void RDFFeatureWriter::write(QString trackId,
Chris@498 82 const Transform &transform,
Chris@498 83 const Plugin::OutputDescriptor& output,
Chris@498 84 const Plugin::FeatureList& features,
Chris@498 85 std::string summaryType)
Chris@498 86 {
Chris@498 87 QString pluginId = transform.getPluginIdentifier();
Chris@498 88
Chris@498 89 if (m_rdfDescriptions.find(pluginId) == m_rdfDescriptions.end()) {
Chris@498 90
Chris@498 91 m_rdfDescriptions[pluginId] = PluginRDFDescription(pluginId);
Chris@498 92
Chris@498 93 if (m_rdfDescriptions[pluginId].haveDescription()) {
Chris@498 94 cerr << "NOTE: Have RDF description for plugin ID \""
Chris@498 95 << pluginId.toStdString() << "\"" << endl;
Chris@498 96 } else {
Chris@498 97 cerr << "NOTE: Do not have RDF description for plugin ID \""
Chris@498 98 << pluginId.toStdString() << "\"" << endl;
Chris@498 99 }
Chris@498 100 }
Chris@498 101
Chris@498 102 // Need to select appropriate output file for our track/transform
Chris@498 103 // combination
Chris@498 104
Chris@498 105 QTextStream *stream = getOutputStream(trackId, transform.getIdentifier());
Chris@498 106 if (!stream) return; //!!! this is probably better handled with an exception
Chris@498 107
Chris@498 108 if (m_startedStreamTransforms.find(stream) ==
Chris@498 109 m_startedStreamTransforms.end()) {
Chris@498 110 cerr << "This stream is new, writing prefixes" << endl;
Chris@498 111 writePrefixes(stream);
Chris@498 112 if (m_singleFileName == "" && !m_stdout) {
Chris@498 113 writeSignalDescription(stream, trackId);
Chris@498 114 }
Chris@498 115 }
Chris@498 116
Chris@498 117 if (m_startedStreamTransforms[stream].find(transform) ==
Chris@498 118 m_startedStreamTransforms[stream].end()) {
Chris@498 119 m_startedStreamTransforms[stream].insert(transform);
Chris@498 120 writeLocalFeatureTypes
Chris@498 121 (stream, transform, output, m_rdfDescriptions[pluginId]);
Chris@498 122 }
Chris@498 123
Chris@498 124 if (m_singleFileName != "" || m_stdout) {
Chris@498 125 if (m_startedTrackIds.find(trackId) == m_startedTrackIds.end()) {
Chris@498 126 writeSignalDescription(stream, trackId);
Chris@498 127 m_startedTrackIds.insert(trackId);
Chris@498 128 }
Chris@498 129 }
Chris@498 130
Chris@498 131 QString timelineURI = m_trackTimelineURIs[trackId];
Chris@498 132
Chris@498 133 if (timelineURI == "") {
Chris@498 134 cerr << "RDFFeatureWriter: INTERNAL ERROR: writing features without having established a timeline URI!" << endl;
Chris@498 135 exit(1);
Chris@498 136 }
Chris@498 137
Chris@498 138 if (summaryType != "") {
Chris@498 139
Chris@498 140 writeSparseRDF(stream, transform, output, features,
Chris@498 141 m_rdfDescriptions[pluginId], timelineURI);
Chris@498 142
Chris@498 143 } else if (m_rdfDescriptions[pluginId].haveDescription() &&
Chris@498 144 m_rdfDescriptions[pluginId].getOutputDisposition
Chris@498 145 (output.identifier.c_str()) ==
Chris@498 146 PluginRDFDescription::OutputDense) {
Chris@498 147
Chris@498 148 QString signalURI = m_trackSignalURIs[trackId];
Chris@498 149
Chris@498 150 if (signalURI == "") {
Chris@498 151 cerr << "RDFFeatureWriter: INTERNAL ERROR: writing dense features without having established a signal URI!" << endl;
Chris@498 152 exit(1);
Chris@498 153 }
Chris@498 154
Chris@498 155 writeDenseRDF(stream, transform, output, features,
Chris@498 156 m_rdfDescriptions[pluginId], signalURI, timelineURI);
Chris@498 157
Chris@498 158 } else {
Chris@498 159
Chris@498 160 writeSparseRDF(stream, transform, output, features,
Chris@498 161 m_rdfDescriptions[pluginId], timelineURI);
Chris@498 162 }
Chris@498 163 }
Chris@498 164
Chris@498 165 void
Chris@498 166 RDFFeatureWriter::writePrefixes(QTextStream *sptr)
Chris@498 167 {
Chris@498 168 QTextStream &stream = *sptr;
Chris@498 169
Chris@498 170 stream << "@prefix dc: <http://purl.org/dc/elements/1.1/> .\n"
Chris@498 171 << "@prefix mo: <http://purl.org/ontology/mo/> .\n"
Chris@498 172 << "@prefix af: <http://purl.org/ontology/af/> .\n"
Chris@498 173 << "@prefix event: <http://purl.org/NET/c4dm/event.owl#> .\n"
Chris@498 174 << "@prefix rdf: <http://www.w3.org/1999/02/22-rdf-syntax-ns#> .\n"
Chris@498 175 << "@prefix rdfs: <http://www.w3.org/2000/01/rdf-schema#> .\n"
Chris@498 176 << "@prefix xsd: <http://www.w3.org/2001/XMLSchema#> .\n"
Chris@498 177 << "@prefix tl: <http://purl.org/NET/c4dm/timeline.owl#> .\n"
Chris@498 178 << "@prefix vamp: <http://purl.org/ontology/vamp/> .\n"
Chris@498 179 << "@prefix : <#> .\n\n";
Chris@498 180 }
Chris@498 181
Chris@498 182 void
Chris@498 183 RDFFeatureWriter::writeSignalDescription(QTextStream *sptr,
Chris@498 184 QString trackId)
Chris@498 185 {
Chris@498 186 QTextStream &stream = *sptr;
Chris@498 187
Chris@498 188 /*
Chris@498 189 * Describe signal we're analysing (AudioFile, Signal, TimeLine, etc.)
Chris@498 190 */
Chris@498 191
Chris@498 192 QUrl url(trackId);
Chris@498 193 QString scheme = url.scheme().toLower();
Chris@498 194 bool local = (scheme == "" || scheme == "file" || scheme.length() == 1);
Chris@498 195
Chris@498 196 if (local) {
Chris@498 197 if (scheme == "") {
Chris@498 198 url.setScheme("file");
Chris@498 199 } else if (scheme.length() == 1) { // DOS drive letter!
Chris@498 200 url.setScheme("file");
Chris@498 201 url.setPath(scheme + ":" + url.path());
Chris@498 202 }
Chris@498 203 }
Chris@498 204
Chris@498 205 //!!! FIX: If we are appending, we need to start counting after
Chris@498 206 //all of the existing counts that are already in the file!
Chris@498 207
Chris@498 208 uint64_t signalCount = m_count++;
Chris@498 209
Chris@498 210 if (m_trackSignalURIs.find(trackId) == m_trackSignalURIs.end()) {
Chris@498 211 m_trackSignalURIs[trackId] = QString(":signal_%1").arg(signalCount);
Chris@498 212 }
Chris@498 213
Chris@498 214 if (m_suri != NULL) {
Chris@498 215 m_trackSignalURIs[trackId] = "<" + m_suri + ">";
Chris@498 216 }
Chris@498 217 QString signalURI = m_trackSignalURIs[trackId];
Chris@498 218
Chris@498 219 if (m_trackTimelineURIs.find(trackId) == m_trackTimelineURIs.end()) {
Chris@498 220 m_trackTimelineURIs[trackId] = QString(":signal_timeline_%1").arg(signalCount);
Chris@498 221 }
Chris@498 222 QString timelineURI = m_trackTimelineURIs[trackId];
Chris@498 223
Chris@498 224 stream << "\n<" << url.toEncoded().data() << "> a mo:AudioFile .\n\n"
Chris@498 225 << signalURI << " a mo:Signal ;\n"
Chris@498 226 << " mo:available_as <" << url.toEncoded().data()
Chris@498 227 << "> ;\n"
Chris@498 228 << " mo:time [\n"
Chris@498 229 << " a tl:Interval ;\n"
Chris@498 230 << " tl:onTimeLine "
Chris@498 231 << timelineURI << "\n ] .\n\n";
Chris@498 232 }
Chris@498 233
Chris@498 234 void
Chris@498 235 RDFFeatureWriter::writeLocalFeatureTypes(QTextStream *sptr,
Chris@498 236 const Transform &transform,
Chris@498 237 const Plugin::OutputDescriptor &od,
Chris@498 238 PluginRDFDescription &desc)
Chris@498 239 {
Chris@498 240 QString outputId = od.identifier.c_str();
Chris@498 241 QTextStream &stream = *sptr;
Chris@498 242
Chris@498 243 bool needEventType = false;
Chris@498 244 bool needSignalType = false;
Chris@498 245
Chris@498 246 //!!! feature attribute type is not yet supported
Chris@498 247
Chris@498 248 //!!! bin names, extents and so on can be written out using e.g. vamp:bin_names ( "a" "b" "c" )
Chris@498 249
Chris@498 250 if (desc.getOutputDisposition(outputId) ==
Chris@498 251 PluginRDFDescription::OutputDense) {
Chris@498 252
Chris@498 253 // no feature events, so may need signal type but won't need
Chris@498 254 // event type
Chris@498 255
Chris@498 256 if (m_plain) {
Chris@498 257
Chris@498 258 needSignalType = true;
Chris@498 259
Chris@498 260 } else if (desc.getOutputSignalTypeURI(outputId) == "") {
Chris@498 261
Chris@498 262 needSignalType = true;
Chris@498 263 }
Chris@498 264
Chris@498 265 } else {
Chris@498 266
Chris@498 267 // may need event type but won't need signal type
Chris@498 268
Chris@498 269 if (m_plain) {
Chris@498 270
Chris@498 271 needEventType = true;
Chris@498 272
Chris@498 273 } else if (desc.getOutputEventTypeURI(outputId) == "") {
Chris@498 274
Chris@498 275 needEventType = true;
Chris@498 276 }
Chris@498 277 }
Chris@498 278
Chris@498 279 QString transformUri;
Chris@498 280 if (m_transformURIs.find(transform) != m_transformURIs.end()) {
Chris@498 281 transformUri = m_transformURIs[transform];
Chris@498 282 } else {
Chris@498 283 transformUri = QString(":transform_%1_%2").arg(m_count++).arg(outputId);
Chris@498 284 m_transformURIs[transform] = transformUri;
Chris@498 285 }
Chris@498 286
Chris@498 287 stream << RDFTransformFactory::writeTransformToRDF(transform, transformUri)
Chris@498 288 << endl;
Chris@498 289
Chris@498 290 if (needEventType) {
Chris@498 291
Chris@498 292 QString uri;
Chris@498 293 if (m_syntheticEventTypeURIs.find(transform) !=
Chris@498 294 m_syntheticEventTypeURIs.end()) {
Chris@498 295 uri = m_syntheticEventTypeURIs[transform];
Chris@498 296 } else {
Chris@498 297 uri = QString(":event_type_%1").arg(m_count++);
Chris@498 298 m_syntheticEventTypeURIs[transform] = uri;
Chris@498 299 }
Chris@498 300
Chris@498 301 stream << uri
Chris@498 302 << " rdfs:subClassOf event:Event ;" << endl
Chris@498 303 << " dc:title \"" << od.name.c_str() << "\" ;" << endl
Chris@498 304 << " dc:format \"" << od.unit.c_str() << "\" ;" << endl
Chris@498 305 << " dc:description \"" << od.description.c_str() << "\" ."
Chris@498 306 << endl << endl;
Chris@498 307 }
Chris@498 308
Chris@498 309 if (needSignalType) {
Chris@498 310
Chris@498 311 QString uri;
Chris@498 312 if (m_syntheticSignalTypeURIs.find(transform) !=
Chris@498 313 m_syntheticSignalTypeURIs.end()) {
Chris@498 314 uri = m_syntheticSignalTypeURIs[transform];
Chris@498 315 } else {
Chris@498 316 uri = QString(":signal_type_%1").arg(m_count++);
Chris@498 317 m_syntheticSignalTypeURIs[transform] = uri;
Chris@498 318 }
Chris@498 319
Chris@498 320 stream << uri
Chris@498 321 << " rdfs:subClassOf af:Signal ;" << endl
Chris@498 322 << " dc:title \"" << od.name.c_str() << "\" ;" << endl
Chris@498 323 << " dc:format \"" << od.unit.c_str() << "\" ;" << endl
Chris@498 324 << " dc:description \"" << od.description.c_str() << "\" ."
Chris@498 325 << endl << endl;
Chris@498 326 }
Chris@498 327 }
Chris@498 328
Chris@498 329 void
Chris@498 330 RDFFeatureWriter::writeSparseRDF(QTextStream *sptr,
Chris@498 331 const Transform &transform,
Chris@498 332 const Plugin::OutputDescriptor& od,
Chris@498 333 const Plugin::FeatureList& featureList,
Chris@498 334 PluginRDFDescription &desc,
Chris@498 335 QString timelineURI)
Chris@498 336 {
Chris@498 337 if (featureList.empty()) return;
Chris@498 338 QTextStream &stream = *sptr;
Chris@498 339
Chris@498 340 bool plain = (m_plain || !desc.haveDescription());
Chris@498 341
Chris@498 342 QString outputId = od.identifier.c_str();
Chris@498 343
Chris@498 344 // iterate through FeatureLists
Chris@498 345
Chris@498 346 for (int i = 0; i < featureList.size(); ++i) {
Chris@498 347
Chris@498 348 const Plugin::Feature &feature = featureList[i];
Chris@498 349 uint64_t featureNumber = m_count++;
Chris@498 350
Chris@498 351 stream << ":event_" << featureNumber << " a ";
Chris@498 352
Chris@498 353 QString eventTypeURI = desc.getOutputEventTypeURI(outputId);
Chris@498 354 if (plain || eventTypeURI == "") {
Chris@498 355 if (m_syntheticEventTypeURIs.find(transform) !=
Chris@498 356 m_syntheticEventTypeURIs.end()) {
Chris@498 357 stream << m_syntheticEventTypeURIs[transform] << " ;\n";
Chris@498 358 } else {
Chris@498 359 stream << ":event_type_" << outputId << " ;\n";
Chris@498 360 }
Chris@498 361 } else {
Chris@498 362 stream << "<" << eventTypeURI << "> ;\n";
Chris@498 363 }
Chris@498 364
Chris@498 365 QString timestamp = feature.timestamp.toString().c_str();
Chris@498 366 timestamp.replace(QRegExp("^ +"), "");
Chris@498 367
Chris@498 368 if (feature.hasDuration && feature.duration > Vamp::RealTime::zeroTime) {
Chris@498 369
Chris@498 370 QString duration = feature.duration.toString().c_str();
Chris@498 371 duration.replace(QRegExp("^ +"), "");
Chris@498 372
Chris@498 373 stream << " event:time [ \n"
Chris@498 374 << " a tl:Interval ;\n"
Chris@498 375 << " tl:onTimeLine " << timelineURI << " ;\n"
Chris@498 376 << " tl:beginsAt \"PT" << timestamp
Chris@498 377 << "S\"^^xsd:duration ;\n"
Chris@498 378 << " tl:duration \"PT" << duration
Chris@498 379 << "S\"^^xsd:duration ;\n"
Chris@498 380 << " ] ";
Chris@498 381
Chris@498 382 } else {
Chris@498 383
Chris@498 384 stream << " event:time [ \n"
Chris@498 385 << " a tl:Instant ;\n" //location of the event in time
Chris@498 386 << " tl:onTimeLine " << timelineURI << " ;\n"
Chris@498 387 << " tl:at \"PT" << timestamp
Chris@498 388 << "S\"^^xsd:duration ;\n ] ";
Chris@498 389 }
Chris@498 390
Chris@498 391 stream << ";\n";
Chris@498 392 stream << " vamp:computed_by " << m_transformURIs[transform] << " ";
Chris@498 393
Chris@498 394 if (feature.label.length() > 0) {
Chris@498 395 stream << ";\n";
Chris@498 396 stream << " rdfs:label \"" << feature.label.c_str() << "\" ";
Chris@498 397 }
Chris@498 398
Chris@498 399 if (!feature.values.empty()) {
Chris@498 400 stream << ";\n";
Chris@498 401 //!!! named bins?
Chris@498 402 stream << " af:feature \"" << feature.values[0];
Chris@498 403 for (int j = 1; j < feature.values.size(); ++j) {
Chris@498 404 stream << " " << feature.values[j];
Chris@498 405 }
Chris@498 406 stream << "\" ";
Chris@498 407 }
Chris@498 408
Chris@498 409 stream << ".\n";
Chris@498 410 }
Chris@498 411 }
Chris@498 412
Chris@498 413 void
Chris@498 414 RDFFeatureWriter::writeDenseRDF(QTextStream *sptr,
Chris@498 415 const Transform &transform,
Chris@498 416 const Plugin::OutputDescriptor& od,
Chris@498 417 const Plugin::FeatureList& featureList,
Chris@498 418 PluginRDFDescription &desc,
Chris@498 419 QString signalURI,
Chris@498 420 QString timelineURI)
Chris@498 421 {
Chris@498 422 if (featureList.empty()) return;
Chris@498 423
Chris@498 424 StringTransformPair sp(signalURI, transform);
Chris@498 425
Chris@498 426 if (m_openDenseFeatures.find(sp) == m_openDenseFeatures.end()) {
Chris@498 427
Chris@498 428 StreamBuffer b(sptr, "");
Chris@498 429 m_openDenseFeatures[sp] = b;
Chris@498 430
Chris@498 431 QString &str(m_openDenseFeatures[sp].second);
Chris@498 432 QTextStream stream(&str);
Chris@498 433
Chris@498 434 bool plain = (m_plain || !desc.haveDescription());
Chris@498 435 QString outputId = od.identifier.c_str();
Chris@498 436
Chris@498 437 uint64_t featureNumber = m_count++;
Chris@498 438
Chris@498 439 // need to write out feature timeline map -- for this we need
Chris@498 440 // the sample rate, window length and hop size from the
Chris@498 441 // transform
Chris@498 442
Chris@498 443 stream << "\n:feature_timeline_" << featureNumber << " a tl:DiscreteTimeLine .\n\n";
Chris@498 444
Chris@498 445 size_t stepSize = transform.getStepSize();
Chris@498 446 if (stepSize == 0) {
Chris@498 447 cerr << "RDFFeatureWriter: INTERNAL ERROR: writing dense features without having set the step size properly!" << endl;
Chris@498 448 return;
Chris@498 449 }
Chris@498 450
Chris@498 451 size_t blockSize = transform.getBlockSize();
Chris@498 452 if (blockSize == 0) {
Chris@498 453 cerr << "RDFFeatureWriter: INTERNAL ERROR: writing dense features without having set the block size properly!" << endl;
Chris@498 454 return;
Chris@498 455 }
Chris@498 456
Chris@498 457 float sampleRate = transform.getSampleRate();
Chris@498 458 if (sampleRate == 0.f) {
Chris@498 459 cerr << "RDFFeatureWriter: INTERNAL ERROR: writing dense features without having set the sample rate properly!" << endl;
Chris@498 460 return;
Chris@498 461 }
Chris@498 462
Chris@498 463 stream << ":feature_timeline_map_" << featureNumber
Chris@498 464 << " a tl:UniformSamplingWindowingMap ;\n"
Chris@498 465 << " tl:rangeTimeLine :feature_timeline_" << featureNumber << " ;\n"
Chris@498 466 << " tl:domainTimeLine " << timelineURI << " ;\n"
Chris@498 467 << " tl:sampleRate \"" << int(sampleRate) << "\"^^xsd:int ;\n"
Chris@498 468 << " tl:windowLength \"" << blockSize << "\"^^xsd:int ;\n"
Chris@498 469 << " tl:hopSize \"" << stepSize << "\"^^xsd:int .\n\n";
Chris@498 470
Chris@498 471 stream << signalURI << " af:signal_feature :feature_"
Chris@498 472 << featureNumber << " ." << endl << endl;
Chris@498 473
Chris@498 474 stream << ":feature_" << featureNumber << " a ";
Chris@498 475
Chris@498 476 QString signalTypeURI = desc.getOutputSignalTypeURI(outputId);
Chris@498 477 if (plain || signalTypeURI == "") {
Chris@498 478 if (m_syntheticSignalTypeURIs.find(transform) !=
Chris@498 479 m_syntheticSignalTypeURIs.end()) {
Chris@498 480 stream << m_syntheticSignalTypeURIs[transform] << " ;\n";
Chris@498 481 } else {
Chris@498 482 stream << ":signal_type_" << outputId << " ;\n";
Chris@498 483 }
Chris@498 484 } else {
Chris@498 485 stream << signalTypeURI << " ;\n";
Chris@498 486 }
Chris@498 487
Chris@498 488 stream << " mo:time ["
Chris@498 489 << "\n a tl:Interval ;"
Chris@498 490 << "\n tl:onTimeLine :feature_timeline_" << featureNumber << " ;";
Chris@498 491
Chris@498 492 RealTime startrt = transform.getStartTime();
Chris@498 493 RealTime durationrt = transform.getDuration();
Chris@498 494
Chris@498 495 int start = RealTime::realTime2Frame(startrt, sampleRate) / stepSize;
Chris@498 496 int duration = RealTime::realTime2Frame(durationrt, sampleRate) / stepSize;
Chris@498 497
Chris@498 498 if (start != 0) {
Chris@498 499 stream << "\n tl:start \"" << start << "\"^^xsd:int ;";
Chris@498 500 }
Chris@498 501 if (duration != 0) {
Chris@498 502 stream << "\n tl:duration \"" << duration << "\"^^xsd:int ;";
Chris@498 503 }
Chris@498 504
Chris@498 505 stream << "\n ] ;\n";
Chris@498 506
Chris@498 507 if (od.hasFixedBinCount) {
Chris@498 508 // We only know the height, so write the width as zero
Chris@498 509 stream << " af:dimensions \"" << od.binCount << " 0\" ;\n";
Chris@498 510 }
Chris@498 511
Chris@498 512 stream << " af:value \"";
Chris@498 513 }
Chris@498 514
Chris@498 515 QString &str = m_openDenseFeatures[sp].second;
Chris@498 516 QTextStream stream(&str);
Chris@498 517
Chris@498 518 for (int i = 0; i < featureList.size(); ++i) {
Chris@498 519
Chris@498 520 const Plugin::Feature &feature = featureList[i];
Chris@498 521
Chris@498 522 for (int j = 0; j < feature.values.size(); ++j) {
Chris@498 523 stream << feature.values[j] << " ";
Chris@498 524 }
Chris@498 525 }
Chris@498 526 }
Chris@498 527
Chris@498 528 void RDFFeatureWriter::finish()
Chris@498 529 {
Chris@498 530 // cerr << "RDFFeatureWriter::finish()" << endl;
Chris@498 531
Chris@498 532 // close any open dense feature literals
Chris@498 533
Chris@498 534 for (map<StringTransformPair, StreamBuffer>::iterator i =
Chris@498 535 m_openDenseFeatures.begin();
Chris@498 536 i != m_openDenseFeatures.end(); ++i) {
Chris@498 537 cerr << "closing a stream" << endl;
Chris@498 538 StreamBuffer &b = i->second;
Chris@498 539 *(b.first) << b.second << "\" ." << endl;
Chris@498 540 }
Chris@498 541
Chris@498 542 m_openDenseFeatures.clear();
Chris@498 543 }
Chris@498 544
Chris@498 545