annotate rdf/RDFImporter.cpp @ 1008:d9e0e59a1581

When using an aggregate model to pass data to a transform, zero-pad the shorter input to the duration of the longer rather than truncating the longer. (This is better behaviour for e.g. MATCH, and in any case the code was previously truncating incorrectly and ending up with garbage data at the end.)
author Chris Cannam
date Fri, 14 Nov 2014 13:51:33 +0000
parents 06579b8ffb7b
children b14064bd1f97
rev   line source
Chris@439 1 /* -*- c-basic-offset: 4 indent-tabs-mode: nil -*- vi:set ts=8 sts=4 sw=4: */
Chris@439 2
Chris@439 3 /*
Chris@439 4 Sonic Visualiser
Chris@439 5 An audio file viewer and annotation editor.
Chris@439 6 Centre for Digital Music, Queen Mary, University of London.
Chris@727 7 This file copyright 2008-2012 QMUL.
Chris@439 8
Chris@439 9 This program is free software; you can redistribute it and/or
Chris@439 10 modify it under the terms of the GNU General Public License as
Chris@439 11 published by the Free Software Foundation; either version 2 of the
Chris@439 12 License, or (at your option) any later version. See the file
Chris@439 13 COPYING included with this distribution for more information.
Chris@439 14 */
Chris@439 15
Chris@439 16 #include "RDFImporter.h"
Chris@439 17
Chris@439 18 #include <map>
Chris@439 19 #include <vector>
Chris@439 20
Chris@439 21 #include <iostream>
Chris@439 22 #include <cmath>
Chris@439 23
Chris@439 24 #include "base/ProgressReporter.h"
Chris@439 25 #include "base/RealTime.h"
Chris@439 26
Chris@439 27 #include "data/model/SparseOneDimensionalModel.h"
Chris@439 28 #include "data/model/SparseTimeValueModel.h"
Chris@439 29 #include "data/model/EditableDenseThreeDimensionalModel.h"
Chris@449 30 #include "data/model/NoteModel.h"
Chris@510 31 #include "data/model/TextModel.h"
Chris@449 32 #include "data/model/RegionModel.h"
Chris@499 33 #include "data/model/WaveFileModel.h"
Chris@499 34
Chris@499 35 #include "data/fileio/FileSource.h"
Chris@520 36 #include "data/fileio/CachedFile.h"
Chris@581 37 #include "data/fileio/FileFinder.h"
Chris@522 38
Chris@726 39 #include <dataquay/BasicStore.h>
Chris@726 40 #include <dataquay/PropertyObject.h>
Chris@726 41
Chris@726 42 using Dataquay::Uri;
Chris@726 43 using Dataquay::Node;
Chris@726 44 using Dataquay::Nodes;
Chris@726 45 using Dataquay::Triple;
Chris@726 46 using Dataquay::Triples;
Chris@726 47 using Dataquay::BasicStore;
Chris@726 48 using Dataquay::PropertyObject;
Chris@726 49
Chris@439 50 class RDFImporterImpl
Chris@439 51 {
Chris@439 52 public:
Chris@439 53 RDFImporterImpl(QString url, int sampleRate);
Chris@439 54 virtual ~RDFImporterImpl();
Chris@490 55
Chris@490 56 void setSampleRate(int sampleRate) { m_sampleRate = sampleRate; }
Chris@439 57
Chris@439 58 bool isOK();
Chris@439 59 QString getErrorString() const;
Chris@439 60
Chris@439 61 std::vector<Model *> getDataModels(ProgressReporter *);
Chris@439 62
Chris@439 63 protected:
Chris@726 64 BasicStore *m_store;
Chris@730 65 Uri expand(QString s) { return m_store->expand(s); }
Chris@726 66
Chris@439 67 QString m_uristring;
Chris@439 68 QString m_errorString;
Chris@499 69 std::map<QString, Model *> m_audioModelMap;
Chris@439 70 int m_sampleRate;
Chris@439 71
Chris@617 72 std::map<Model *, std::map<QString, float> > m_labelValueMap;
Chris@617 73
Chris@499 74 void getDataModelsAudio(std::vector<Model *> &, ProgressReporter *);
Chris@440 75 void getDataModelsSparse(std::vector<Model *> &, ProgressReporter *);
Chris@440 76 void getDataModelsDense(std::vector<Model *> &, ProgressReporter *);
Chris@440 77
Chris@493 78 void getDenseModelTitle(Model *, QString, QString);
Chris@493 79
Chris@440 80 void getDenseFeatureProperties(QString featureUri,
Chris@440 81 int &sampleRate, int &windowLength,
Chris@440 82 int &hopSize, int &width, int &height);
Chris@440 83
Chris@449 84 void fillModel(Model *, long, long, bool, std::vector<float> &, QString);
Chris@439 85 };
Chris@439 86
Chris@439 87 QString
Chris@439 88 RDFImporter::getKnownExtensions()
Chris@439 89 {
Chris@439 90 return "*.rdf *.n3 *.ttl";
Chris@439 91 }
Chris@439 92
Chris@439 93 RDFImporter::RDFImporter(QString url, int sampleRate) :
Chris@439 94 m_d(new RDFImporterImpl(url, sampleRate))
Chris@439 95 {
Chris@439 96 }
Chris@439 97
Chris@439 98 RDFImporter::~RDFImporter()
Chris@439 99 {
Chris@439 100 delete m_d;
Chris@439 101 }
Chris@439 102
Chris@490 103 void
Chris@490 104 RDFImporter::setSampleRate(int sampleRate)
Chris@490 105 {
Chris@490 106 m_d->setSampleRate(sampleRate);
Chris@490 107 }
Chris@490 108
Chris@439 109 bool
Chris@439 110 RDFImporter::isOK()
Chris@439 111 {
Chris@439 112 return m_d->isOK();
Chris@439 113 }
Chris@439 114
Chris@439 115 QString
Chris@439 116 RDFImporter::getErrorString() const
Chris@439 117 {
Chris@439 118 return m_d->getErrorString();
Chris@439 119 }
Chris@439 120
Chris@439 121 std::vector<Model *>
Chris@439 122 RDFImporter::getDataModels(ProgressReporter *r)
Chris@439 123 {
Chris@439 124 return m_d->getDataModels(r);
Chris@439 125 }
Chris@439 126
Chris@439 127 RDFImporterImpl::RDFImporterImpl(QString uri, int sampleRate) :
Chris@726 128 m_store(new BasicStore),
Chris@439 129 m_uristring(uri),
Chris@439 130 m_sampleRate(sampleRate)
Chris@439 131 {
Chris@726 132 //!!! retrieve data if remote... then
Chris@726 133
Chris@726 134 m_store->addPrefix("mo", Uri("http://purl.org/ontology/mo/"));
Chris@726 135 m_store->addPrefix("af", Uri("http://purl.org/ontology/af/"));
Chris@726 136 m_store->addPrefix("dc", Uri("http://purl.org/dc/elements/1.1/"));
Chris@726 137 m_store->addPrefix("tl", Uri("http://purl.org/NET/c4dm/timeline.owl#"));
Chris@726 138 m_store->addPrefix("event", Uri("http://purl.org/NET/c4dm/event.owl#"));
Chris@726 139 m_store->addPrefix("rdfs", Uri("http://www.w3.org/2000/01/rdf-schema#"));
Chris@727 140
Chris@738 141 try {
Chris@738 142 QUrl url;
Chris@738 143 if (uri.startsWith("file:")) {
Chris@738 144 url = QUrl(uri);
Chris@738 145 } else {
Chris@738 146 url = QUrl::fromLocalFile(uri);
Chris@738 147 }
Chris@738 148 m_store->import(url, BasicStore::ImportIgnoreDuplicates);
Chris@738 149 } catch (std::exception &e) {
Chris@738 150 m_errorString = e.what();
Chris@736 151 }
Chris@439 152 }
Chris@439 153
Chris@439 154 RDFImporterImpl::~RDFImporterImpl()
Chris@439 155 {
Chris@726 156 delete m_store;
Chris@439 157 }
Chris@439 158
Chris@439 159 bool
Chris@439 160 RDFImporterImpl::isOK()
Chris@439 161 {
Chris@439 162 return (m_errorString == "");
Chris@439 163 }
Chris@439 164
Chris@439 165 QString
Chris@439 166 RDFImporterImpl::getErrorString() const
Chris@439 167 {
Chris@439 168 return m_errorString;
Chris@439 169 }
Chris@439 170
Chris@439 171 std::vector<Model *>
Chris@439 172 RDFImporterImpl::getDataModels(ProgressReporter *reporter)
Chris@439 173 {
Chris@439 174 std::vector<Model *> models;
Chris@439 175
Chris@499 176 getDataModelsAudio(models, reporter);
Chris@499 177
Chris@490 178 if (m_sampleRate == 0) {
Chris@616 179 m_errorString = QString("Invalid audio data model (is audio file format supported?)");
Chris@843 180 cerr << m_errorString << endl;
Chris@490 181 return models;
Chris@490 182 }
Chris@490 183
Chris@508 184 QString error;
Chris@508 185
Chris@522 186 if (m_errorString != "") {
Chris@522 187 error = m_errorString;
Chris@522 188 }
Chris@508 189 m_errorString = "";
Chris@508 190
Chris@440 191 getDataModelsDense(models, reporter);
Chris@440 192
Chris@522 193 if (m_errorString != "") {
Chris@522 194 error = m_errorString;
Chris@522 195 }
Chris@440 196 m_errorString = "";
Chris@440 197
Chris@440 198 getDataModelsSparse(models, reporter);
Chris@440 199
Chris@522 200 if (m_errorString == "" && error != "") {
Chris@522 201 m_errorString = error;
Chris@522 202 }
Chris@440 203
Chris@440 204 return models;
Chris@440 205 }
Chris@440 206
Chris@440 207 void
Chris@499 208 RDFImporterImpl::getDataModelsAudio(std::vector<Model *> &models,
Chris@499 209 ProgressReporter *reporter)
Chris@499 210 {
Chris@726 211 Nodes sigs = m_store->match
Chris@730 212 (Triple(Node(), Uri("a"), expand("mo:Signal"))).subjects();
Chris@499 213
Chris@726 214 foreach (Node sig, sigs) {
Chris@726 215
Chris@730 216 Node file = m_store->complete(Triple(Node(), expand("mo:encodes"), sig));
Chris@726 217 if (file == Node()) {
Chris@730 218 file = m_store->complete(Triple(sig, expand("mo:available_as"), Node()));
Chris@726 219 }
Chris@726 220 if (file == Node()) {
Chris@843 221 cerr << "RDFImporterImpl::getDataModelsAudio: ERROR: No source for signal " << sig << endl;
Chris@726 222 continue;
Chris@726 223 }
Chris@499 224
Chris@726 225 QString signal = sig.value;
Chris@726 226 QString source = file.value;
Chris@589 227
Chris@726 228 SVDEBUG << "NOTE: Seeking signal source \"" << source
Chris@726 229 << "\"..." << endl;
Chris@616 230
Chris@522 231 FileSource *fs = new FileSource(source, reporter);
Chris@616 232 if (fs->isAvailable()) {
Chris@690 233 SVDEBUG << "NOTE: Source is available: Local filename is \""
Chris@726 234 << fs->getLocalFilename()
Chris@726 235 << "\"..." << endl;
Chris@616 236 }
Chris@616 237
Chris@522 238 #ifdef NO_SV_GUI
Chris@522 239 if (!fs->isAvailable()) {
Chris@522 240 m_errorString = QString("Signal source \"%1\" is not available").arg(source);
Chris@522 241 delete fs;
Chris@522 242 continue;
Chris@522 243 }
Chris@522 244 #else
Chris@522 245 if (!fs->isAvailable()) {
Chris@726 246 SVDEBUG << "NOTE: Signal source \"" << source
Chris@726 247 << "\" is not available, using file finder..." << endl;
Chris@522 248 FileFinder *ff = FileFinder::getInstance();
Chris@581 249 if (ff) {
Chris@581 250 QString path = ff->find(FileFinder::AudioFile,
Chris@581 251 fs->getLocation(),
Chris@581 252 m_uristring);
Chris@581 253 if (path != "") {
Chris@844 254 cerr << "File finder returns: \"" << path
Chris@843 255 << "\"" << endl;
Chris@522 256 delete fs;
Chris@581 257 fs = new FileSource(path, reporter);
Chris@581 258 if (!fs->isAvailable()) {
Chris@581 259 delete fs;
Chris@581 260 m_errorString = QString("Signal source \"%1\" is not available").arg(source);
Chris@581 261 continue;
Chris@581 262 }
Chris@522 263 }
Chris@499 264 }
Chris@522 265 }
Chris@522 266 #endif
Chris@522 267
Chris@522 268 if (reporter) {
Chris@522 269 reporter->setMessage(RDFImporter::tr("Importing audio referenced in RDF..."));
Chris@522 270 }
Chris@522 271 fs->waitForData();
Chris@522 272 WaveFileModel *newModel = new WaveFileModel(*fs, m_sampleRate);
Chris@522 273 if (newModel->isOK()) {
Chris@843 274 cerr << "Successfully created wave file model from source at \"" << source << "\"" << endl;
Chris@522 275 models.push_back(newModel);
Chris@522 276 m_audioModelMap[signal] = newModel;
Chris@522 277 if (m_sampleRate == 0) {
Chris@522 278 m_sampleRate = newModel->getSampleRate();
Chris@499 279 }
Chris@508 280 } else {
Chris@522 281 m_errorString = QString("Failed to create wave file model from source at \"%1\"").arg(source);
Chris@522 282 delete newModel;
Chris@499 283 }
Chris@522 284 delete fs;
Chris@499 285 }
Chris@499 286 }
Chris@499 287
Chris@499 288 void
Chris@440 289 RDFImporterImpl::getDataModelsDense(std::vector<Model *> &models,
Chris@440 290 ProgressReporter *reporter)
Chris@440 291 {
Chris@499 292 if (reporter) {
Chris@499 293 reporter->setMessage(RDFImporter::tr("Importing dense signal data from RDF..."));
Chris@499 294 }
Chris@499 295
Chris@726 296 Nodes sigFeatures = m_store->match
Chris@730 297 (Triple(Node(), expand("af:signal_feature"), Node())).objects();
Chris@440 298
Chris@726 299 foreach (Node sf, sigFeatures) {
Chris@440 300
Chris@726 301 if (sf.type != Node::URI && sf.type != Node::Blank) continue;
Chris@726 302
Chris@730 303 Node t = m_store->complete(Triple(sf, expand("a"), Node()));
Chris@730 304 Node v = m_store->complete(Triple(sf, expand("af:value"), Node()));
Chris@440 305
Chris@726 306 QString feature = sf.value;
Chris@726 307 QString type = t.value;
Chris@726 308 QString value = v.value;
Chris@726 309
Chris@726 310 if (type == "" || value == "") continue;
Chris@440 311
Chris@440 312 int sampleRate = 0;
Chris@440 313 int windowLength = 0;
Chris@440 314 int hopSize = 0;
Chris@440 315 int width = 0;
Chris@440 316 int height = 0;
Chris@440 317 getDenseFeatureProperties
Chris@440 318 (feature, sampleRate, windowLength, hopSize, width, height);
Chris@440 319
Chris@440 320 if (sampleRate != 0 && sampleRate != m_sampleRate) {
Chris@440 321 cerr << "WARNING: Sample rate in dense feature description does not match our underlying rate -- using rate from feature description" << endl;
Chris@440 322 }
Chris@440 323 if (sampleRate == 0) sampleRate = m_sampleRate;
Chris@440 324
Chris@440 325 if (hopSize == 0) {
Chris@440 326 cerr << "WARNING: Dense feature description does not specify a hop size -- assuming 1" << endl;
Chris@440 327 hopSize = 1;
Chris@440 328 }
Chris@440 329
Chris@440 330 if (height == 0) {
Chris@440 331 cerr << "WARNING: Dense feature description does not specify feature signal dimensions -- assuming one-dimensional (height = 1)" << endl;
Chris@440 332 height = 1;
Chris@440 333 }
Chris@440 334
Chris@440 335 QStringList values = value.split(' ', QString::SkipEmptyParts);
Chris@440 336
Chris@440 337 if (values.empty()) {
Chris@440 338 cerr << "WARNING: Dense feature description does not specify any values!" << endl;
Chris@440 339 continue;
Chris@440 340 }
Chris@440 341
Chris@440 342 if (height == 1) {
Chris@440 343
Chris@440 344 SparseTimeValueModel *m = new SparseTimeValueModel
Chris@440 345 (sampleRate, hopSize, false);
Chris@440 346
Chris@440 347 for (int j = 0; j < values.size(); ++j) {
Chris@440 348 float f = values[j].toFloat();
Chris@440 349 SparseTimeValueModel::Point point(j * hopSize, f, "");
Chris@440 350 m->addPoint(point);
Chris@440 351 }
Chris@493 352
Chris@493 353 getDenseModelTitle(m, feature, type);
Chris@440 354
Chris@558 355 m->setRDFTypeURI(type);
Chris@558 356
Chris@440 357 models.push_back(m);
Chris@440 358
Chris@440 359 } else {
Chris@440 360
Chris@440 361 EditableDenseThreeDimensionalModel *m =
Chris@535 362 new EditableDenseThreeDimensionalModel
Chris@535 363 (sampleRate, hopSize, height,
Chris@535 364 EditableDenseThreeDimensionalModel::NoCompression, false);
Chris@440 365
Chris@440 366 EditableDenseThreeDimensionalModel::Column column;
Chris@440 367
Chris@440 368 int x = 0;
Chris@440 369
Chris@440 370 for (int j = 0; j < values.size(); ++j) {
Chris@440 371 if (j % height == 0 && !column.empty()) {
Chris@440 372 m->setColumn(x++, column);
Chris@440 373 column.clear();
Chris@440 374 }
Chris@440 375 column.push_back(values[j].toFloat());
Chris@440 376 }
Chris@440 377
Chris@440 378 if (!column.empty()) {
Chris@440 379 m->setColumn(x++, column);
Chris@440 380 }
Chris@440 381
Chris@493 382 getDenseModelTitle(m, feature, type);
Chris@493 383
Chris@558 384 m->setRDFTypeURI(type);
Chris@558 385
Chris@440 386 models.push_back(m);
Chris@440 387 }
Chris@440 388 }
Chris@440 389 }
Chris@440 390
Chris@440 391 void
Chris@493 392 RDFImporterImpl::getDenseModelTitle(Model *m,
Chris@493 393 QString featureUri,
Chris@493 394 QString featureTypeUri)
Chris@493 395 {
Chris@730 396 Node n = m_store->complete
Chris@730 397 (Triple(Uri(featureUri), expand("dc:title"), Node()));
Chris@493 398
Chris@726 399 if (n.type == Node::Literal && n.value != "") {
Chris@726 400 SVDEBUG << "RDFImporterImpl::getDenseModelTitle: Title (from signal) \"" << n.value << "\"" << endl;
Chris@726 401 m->setObjectName(n.value);
Chris@493 402 return;
Chris@493 403 }
Chris@493 404
Chris@730 405 n = m_store->complete
Chris@730 406 (Triple(Uri(featureTypeUri), expand("dc:title"), Node()));
Chris@726 407
Chris@726 408 if (n.type == Node::Literal && n.value != "") {
Chris@726 409 SVDEBUG << "RDFImporterImpl::getDenseModelTitle: Title (from signal type) \"" << n.value << "\"" << endl;
Chris@726 410 m->setObjectName(n.value);
Chris@493 411 return;
Chris@493 412 }
Chris@493 413
Chris@690 414 SVDEBUG << "RDFImporterImpl::getDenseModelTitle: No title available for feature <" << featureUri << ">" << endl;
Chris@493 415 }
Chris@493 416
Chris@493 417 void
Chris@440 418 RDFImporterImpl::getDenseFeatureProperties(QString featureUri,
Chris@440 419 int &sampleRate, int &windowLength,
Chris@440 420 int &hopSize, int &width, int &height)
Chris@440 421 {
Chris@730 422 Node dim = m_store->complete
Chris@730 423 (Triple(Uri(featureUri), expand("af:dimensions"), Node()));
Chris@489 424
Chris@726 425 cerr << "Dimensions = \"" << dim.value << "\"" << endl;
Chris@440 426
Chris@726 427 if (dim.type == Node::Literal && dim.value != "") {
Chris@726 428 QStringList dl = dim.value.split(" ");
Chris@726 429 if (dl.empty()) dl.push_back(dim.value);
Chris@440 430 if (dl.size() > 0) height = dl[0].toInt();
Chris@440 431 if (dl.size() > 1) width = dl[1].toInt();
Chris@440 432 }
Chris@726 433
Chris@726 434 // Looking for rate, hop, window from:
Chris@726 435 //
Chris@726 436 // ?feature mo:time ?time .
Chris@726 437 // ?time a tl:Interval .
Chris@726 438 // ?time tl:onTimeLine ?timeline .
Chris@726 439 // ?map tl:rangeTimeLine ?timeline .
Chris@726 440 // ?map tl:sampleRate ?rate .
Chris@726 441 // ?map tl:hopSize ?hop .
Chris@726 442 // ?map tl:windowLength ?window .
Chris@440 443
Chris@730 444 Node interval = m_store->complete(Triple(Uri(featureUri), expand("mo:time"), Node()));
Chris@440 445
Chris@730 446 if (!m_store->contains(Triple(interval, expand("a"), expand("tl:Interval")))) {
Chris@726 447 cerr << "RDFImporterImpl::getDenseFeatureProperties: Feature time node "
Chris@726 448 << interval << " is not a tl:Interval" << endl;
Chris@726 449 return;
Chris@440 450 }
Chris@440 451
Chris@730 452 Node tl = m_store->complete(Triple(interval, expand("tl:onTimeLine"), Node()));
Chris@726 453
Chris@726 454 if (tl == Node()) {
Chris@726 455 cerr << "RDFImporterImpl::getDenseFeatureProperties: Interval node "
Chris@726 456 << interval << " lacks tl:onTimeLine property" << endl;
Chris@726 457 return;
Chris@440 458 }
Chris@440 459
Chris@730 460 Node map = m_store->complete(Triple(Node(), expand("tl:rangeTimeLine"), tl));
Chris@726 461
Chris@726 462 if (map == Node()) {
Chris@726 463 cerr << "RDFImporterImpl::getDenseFeatureProperties: No map for "
Chris@726 464 << "timeline node " << tl << endl;
Chris@726 465 }
Chris@726 466
Chris@726 467 PropertyObject po(m_store, "tl:", map);
Chris@726 468
Chris@726 469 if (po.hasProperty("sampleRate")) {
Chris@726 470 sampleRate = po.getProperty("sampleRate").toInt();
Chris@726 471 }
Chris@726 472 if (po.hasProperty("hopSize")) {
Chris@726 473 hopSize = po.getProperty("hopSize").toInt();
Chris@726 474 }
Chris@726 475 if (po.hasProperty("windowLength")) {
Chris@726 476 windowLength = po.getProperty("windowLength").toInt();
Chris@440 477 }
Chris@440 478
Chris@440 479 cerr << "sr = " << sampleRate << ", hop = " << hopSize << ", win = " << windowLength << endl;
Chris@440 480 }
Chris@440 481
Chris@440 482 void
Chris@440 483 RDFImporterImpl::getDataModelsSparse(std::vector<Model *> &models,
Chris@440 484 ProgressReporter *reporter)
Chris@440 485 {
Chris@499 486 if (reporter) {
Chris@499 487 reporter->setMessage(RDFImporter::tr("Importing event data from RDF..."));
Chris@499 488 }
Chris@499 489
Chris@726 490 /*
Chris@726 491 This function is only used for sparse data (for dense data we
Chris@726 492 would be in getDataModelsDense instead).
Chris@489 493
Chris@726 494 Our query is intended to retrieve every thing that has a time,
Chris@726 495 and every feature type and value associated with a thing that
Chris@726 496 has a time.
Chris@439 497
Chris@726 498 We will then need to refine this big bag of results into a set
Chris@726 499 of data models.
Chris@439 500
Chris@726 501 Results that have different source signals should go into
Chris@726 502 different models.
Chris@439 503
Chris@726 504 Results that have different feature types should go into
Chris@726 505 different models.
Chris@726 506 */
Chris@439 507
Chris@726 508 Nodes sigs = m_store->match
Chris@730 509 (Triple(Node(), expand("a"), expand("mo:Signal"))).subjects();
Chris@449 510
Chris@616 511 // Map from timeline uri to event type to dimensionality to
Chris@449 512 // presence of duration to model ptr. Whee!
Chris@449 513 std::map<QString, std::map<QString, std::map<int, std::map<bool, Model *> > > >
Chris@449 514 modelMap;
Chris@449 515
Chris@726 516 foreach (Node sig, sigs) {
Chris@726 517
Chris@730 518 Node interval = m_store->complete(Triple(sig, expand("mo:time"), Node()));
Chris@726 519 if (interval == Node()) continue;
Chris@439 520
Chris@730 521 Node tl = m_store->complete(Triple(interval, expand("tl:onTimeLine"), Node()));
Chris@726 522 if (tl == Node()) continue;
Chris@499 523
Chris@730 524 Nodes times = m_store->match(Triple(Node(), expand("tl:onTimeLine"), tl)).subjects();
Chris@449 525
Chris@726 526 foreach (Node tn, times) {
Chris@726 527
Chris@730 528 Nodes timedThings = m_store->match(Triple(Node(), expand("event:time"), tn)).subjects();
Chris@439 529
Chris@726 530 foreach (Node thing, timedThings) {
Chris@726 531
Chris@730 532 Node typ = m_store->complete(Triple(thing, expand("a"), Node()));
Chris@726 533 if (typ == Node()) continue;
Chris@439 534
Chris@730 535 Node valu = m_store->complete(Triple(thing, expand("af:feature"), Node()));
Chris@510 536
Chris@726 537 QString source = sig.value;
Chris@726 538 QString timeline = tl.value;
Chris@726 539 QString type = typ.value;
Chris@726 540 QString thinguri = thing.value;
Chris@510 541
Chris@726 542 /*
Chris@726 543 For sparse data, the determining factors in deciding
Chris@726 544 what model to use are: Do the features have values?
Chris@726 545 and Do the features have duration?
Chris@449 546
Chris@726 547 We can run through the results and check off whether
Chris@726 548 we find values and duration for each of the
Chris@726 549 source+type keys, and then run through the
Chris@726 550 source+type keys pushing each of the results into a
Chris@726 551 suitable model.
Chris@439 552
Chris@726 553 Unfortunately, at this point we do not yet have any
Chris@726 554 actual timing data (time/duration) -- just the time
Chris@726 555 URI.
Chris@449 556
Chris@726 557 What we _could_ do is to create one of each type of
Chris@726 558 model at the start, for each of the source+type
Chris@726 559 keys, and then push each feature into the relevant
Chris@726 560 model depending on what we find out about it. Then
Chris@726 561 return only non-empty models.
Chris@726 562 */
Chris@439 563
Chris@726 564 QString label = "";
Chris@726 565 bool text = (type.contains("Text") || type.contains("text")); // Ha, ha
Chris@726 566 bool note = (type.contains("Note") || type.contains("note")); // Guffaw
Chris@449 567
Chris@726 568 if (text) {
Chris@730 569 label = m_store->complete(Triple(thing, expand("af:text"), Node())).value;
Chris@726 570 }
Chris@726 571
Chris@726 572 if (label == "") {
Chris@730 573 label = m_store->complete(Triple(thing, expand("rdfs:label"), Node())).value;
Chris@726 574 }
Chris@449 575
Chris@726 576 RealTime time;
Chris@726 577 RealTime duration;
Chris@726 578
Chris@930 579 // bool haveTime = false;
Chris@726 580 bool haveDuration = false;
Chris@726 581
Chris@730 582 Node at = m_store->complete(Triple(tn, expand("tl:at"), Node()));
Chris@726 583
Chris@726 584 if (at != Node()) {
Chris@726 585 time = RealTime::fromXsdDuration(at.value.toStdString());
Chris@930 586 // haveTime = true;
Chris@726 587 } else {
Chris@726 588 //!!! NB we're using rather old terminology for these things, apparently:
Chris@726 589 // beginsAt -> start
Chris@726 590 // onTimeLine -> timeline
Chris@726 591
Chris@730 592 Node start = m_store->complete(Triple(tn, expand("tl:beginsAt"), Node()));
Chris@730 593 Node dur = m_store->complete(Triple(tn, expand("tl:duration"), Node()));
Chris@726 594 if (start != Node() && dur != Node()) {
Chris@726 595 time = RealTime::fromXsdDuration
Chris@726 596 (start.value.toStdString());
Chris@726 597 duration = RealTime::fromXsdDuration
Chris@726 598 (dur.value.toStdString());
Chris@930 599 // haveTime = haveDuration = true;
Chris@726 600 }
Chris@726 601 }
Chris@726 602
Chris@726 603 QString valuestring = valu.value;
Chris@726 604 std::vector<float> values;
Chris@726 605
Chris@726 606 if (valuestring != "") {
Chris@726 607 QStringList vsl = valuestring.split(" ", QString::SkipEmptyParts);
Chris@726 608 for (int j = 0; j < vsl.size(); ++j) {
Chris@726 609 bool success = false;
Chris@726 610 float v = vsl[j].toFloat(&success);
Chris@726 611 if (success) values.push_back(v);
Chris@726 612 }
Chris@726 613 }
Chris@726 614
Chris@726 615 int dimensions = 1;
Chris@726 616 if (values.size() == 1) dimensions = 2;
Chris@726 617 else if (values.size() > 1) dimensions = 3;
Chris@726 618
Chris@726 619 Model *model = 0;
Chris@726 620
Chris@726 621 if (modelMap[timeline][type][dimensions].find(haveDuration) ==
Chris@726 622 modelMap[timeline][type][dimensions].end()) {
Chris@449 623
Chris@449 624 /*
Chris@690 625 SVDEBUG << "Creating new model: source = " << source << ", type = " << type << ", dimensions = "
Chris@449 626 << dimensions << ", haveDuration = " << haveDuration
Chris@449 627 << ", time = " << time << ", duration = " << duration
Chris@687 628 << endl;
Chris@449 629 */
Chris@449 630
Chris@726 631 if (!haveDuration) {
Chris@449 632
Chris@726 633 if (dimensions == 1) {
Chris@726 634 if (text) {
Chris@726 635 model = new TextModel(m_sampleRate, 1, false);
Chris@726 636 } else {
Chris@726 637 model = new SparseOneDimensionalModel(m_sampleRate, 1, false);
Chris@726 638 }
Chris@726 639 } else if (dimensions == 2) {
Chris@726 640 if (text) {
Chris@726 641 model = new TextModel(m_sampleRate, 1, false);
Chris@726 642 } else {
Chris@726 643 model = new SparseTimeValueModel(m_sampleRate, 1, false);
Chris@726 644 }
Chris@726 645 } else {
Chris@726 646 // We don't have a three-dimensional sparse model,
Chris@726 647 // so use a note model. We do have some logic (in
Chris@726 648 // extractStructure below) for guessing whether
Chris@726 649 // this should after all have been a dense model,
Chris@726 650 // but it's hard to apply it because we don't have
Chris@726 651 // all the necessary timing data yet... hmm
Chris@726 652 model = new NoteModel(m_sampleRate, 1, false);
Chris@726 653 }
Chris@449 654
Chris@726 655 } else { // haveDuration
Chris@510 656
Chris@726 657 if (note || (dimensions > 2)) {
Chris@726 658 model = new NoteModel(m_sampleRate, 1, false);
Chris@726 659 } else {
Chris@726 660 // If our units are frequency or midi pitch, we
Chris@726 661 // should be using a note model... hm
Chris@726 662 model = new RegionModel(m_sampleRate, 1, false);
Chris@726 663 }
Chris@510 664 }
Chris@449 665
Chris@726 666 model->setRDFTypeURI(type);
Chris@449 667
Chris@726 668 if (m_audioModelMap.find(source) != m_audioModelMap.end()) {
Chris@843 669 cerr << "source model for " << model << " is " << m_audioModelMap[source] << endl;
Chris@726 670 model->setSourceModel(m_audioModelMap[source]);
Chris@510 671 }
Chris@449 672
Chris@730 673 QString title = m_store->complete
Chris@730 674 (Triple(typ, expand("dc:title"), Node())).value;
Chris@726 675 if (title == "") {
Chris@726 676 // take it from the end of the event type
Chris@726 677 title = type;
Chris@726 678 title.replace(QRegExp("^.*[/#]"), "");
Chris@726 679 }
Chris@726 680 model->setObjectName(title);
Chris@449 681
Chris@726 682 modelMap[timeline][type][dimensions][haveDuration] = model;
Chris@726 683 models.push_back(model);
Chris@449 684 }
Chris@449 685
Chris@726 686 model = modelMap[timeline][type][dimensions][haveDuration];
Chris@449 687
Chris@726 688 if (model) {
Chris@726 689 long ftime = RealTime::realTime2Frame(time, m_sampleRate);
Chris@726 690 long fduration = RealTime::realTime2Frame(duration, m_sampleRate);
Chris@726 691 fillModel(model, ftime, fduration, haveDuration, values, label);
Chris@449 692 }
Chris@449 693 }
Chris@439 694 }
Chris@439 695 }
Chris@439 696 }
Chris@439 697
Chris@439 698 void
Chris@449 699 RDFImporterImpl::fillModel(Model *model,
Chris@449 700 long ftime,
Chris@449 701 long fduration,
Chris@449 702 bool haveDuration,
Chris@449 703 std::vector<float> &values,
Chris@449 704 QString label)
Chris@449 705 {
Chris@690 706 // SVDEBUG << "RDFImporterImpl::fillModel: adding point at frame " << ftime << endl;
Chris@492 707
Chris@449 708 SparseOneDimensionalModel *sodm =
Chris@449 709 dynamic_cast<SparseOneDimensionalModel *>(model);
Chris@449 710 if (sodm) {
Chris@449 711 SparseOneDimensionalModel::Point point(ftime, label);
Chris@449 712 sodm->addPoint(point);
Chris@449 713 return;
Chris@449 714 }
Chris@449 715
Chris@510 716 TextModel *tm =
Chris@510 717 dynamic_cast<TextModel *>(model);
Chris@510 718 if (tm) {
Chris@510 719 TextModel::Point point
Chris@510 720 (ftime,
Chris@510 721 values.empty() ? 0.5f : values[0] < 0.f ? 0.f : values[0] > 1.f ? 1.f : values[0], // I was young and feckless once too
Chris@510 722 label);
Chris@510 723 tm->addPoint(point);
Chris@510 724 return;
Chris@510 725 }
Chris@510 726
Chris@449 727 SparseTimeValueModel *stvm =
Chris@449 728 dynamic_cast<SparseTimeValueModel *>(model);
Chris@449 729 if (stvm) {
Chris@449 730 SparseTimeValueModel::Point point
Chris@449 731 (ftime, values.empty() ? 0.f : values[0], label);
Chris@449 732 stvm->addPoint(point);
Chris@449 733 return;
Chris@449 734 }
Chris@449 735
Chris@449 736 NoteModel *nm =
Chris@449 737 dynamic_cast<NoteModel *>(model);
Chris@449 738 if (nm) {
Chris@449 739 if (haveDuration) {
Chris@449 740 float value = 0.f, level = 1.f;
Chris@449 741 if (!values.empty()) {
Chris@449 742 value = values[0];
Chris@449 743 if (values.size() > 1) {
Chris@449 744 level = values[1];
Chris@449 745 }
Chris@449 746 }
Chris@449 747 NoteModel::Point point(ftime, value, fduration, level, label);
Chris@449 748 nm->addPoint(point);
Chris@449 749 } else {
Chris@449 750 float value = 0.f, duration = 1.f, level = 1.f;
Chris@449 751 if (!values.empty()) {
Chris@449 752 value = values[0];
Chris@449 753 if (values.size() > 1) {
Chris@449 754 duration = values[1];
Chris@449 755 if (values.size() > 2) {
Chris@449 756 level = values[2];
Chris@449 757 }
Chris@449 758 }
Chris@449 759 }
Chris@449 760 NoteModel::Point point(ftime, value, duration, level, label);
Chris@449 761 nm->addPoint(point);
Chris@449 762 }
Chris@449 763 return;
Chris@449 764 }
Chris@449 765
Chris@449 766 RegionModel *rm =
Chris@449 767 dynamic_cast<RegionModel *>(model);
Chris@449 768 if (rm) {
Chris@617 769 float value = 0.f;
Chris@617 770 if (values.empty()) {
Chris@617 771 // no values? map each unique label to a distinct value
Chris@617 772 if (m_labelValueMap[model].find(label) == m_labelValueMap[model].end()) {
Chris@617 773 m_labelValueMap[model][label] = rm->getValueMaximum() + 1.f;
Chris@617 774 }
Chris@617 775 value = m_labelValueMap[model][label];
Chris@617 776 } else {
Chris@617 777 value = values[0];
Chris@617 778 }
Chris@449 779 if (haveDuration) {
Chris@617 780 RegionModel::Point point(ftime, value, fduration, label);
Chris@449 781 rm->addPoint(point);
Chris@449 782 } else {
Chris@449 783 // This won't actually happen -- we only create region models
Chris@449 784 // if we do have duration -- but just for completeness
Chris@617 785 float duration = 1.f;
Chris@449 786 if (!values.empty()) {
Chris@449 787 value = values[0];
Chris@449 788 if (values.size() > 1) {
Chris@449 789 duration = values[1];
Chris@449 790 }
Chris@449 791 }
Chris@449 792 RegionModel::Point point(ftime, value, duration, label);
Chris@449 793 rm->addPoint(point);
Chris@449 794 }
Chris@449 795 return;
Chris@449 796 }
Chris@449 797
Chris@843 798 cerr << "WARNING: RDFImporterImpl::fillModel: Unknown or unexpected model type" << endl;
Chris@449 799 return;
Chris@449 800 }
Chris@449 801
Chris@490 802 RDFImporter::RDFDocumentType
Chris@490 803 RDFImporter::identifyDocumentType(QString url)
Chris@490 804 {
Chris@490 805 bool haveAudio = false;
Chris@490 806 bool haveAnnotations = false;
Chris@726 807 bool haveRDF = false;
Chris@449 808
Chris@726 809 BasicStore *store = 0;
Chris@726 810
Chris@726 811 // This is not expected to return anything useful, but if it does
Chris@726 812 // anything at all then we know we have RDF
Chris@726 813 try {
Chris@738 814 //!!! non-local document?
Chris@726 815 store = BasicStore::load(QUrl(url));
Chris@730 816 Triple t = store->matchOnce(Triple());
Chris@726 817 if (t != Triple()) haveRDF = true;
Chris@738 818 } catch (std::exception &e) {
Chris@738 819 // nothing; haveRDF will be false so the next bit catches it
Chris@726 820 }
Chris@726 821
Chris@726 822 if (!haveRDF) {
Chris@726 823 delete store;
Chris@499 824 return NotRDF;
Chris@499 825 }
Chris@499 826
Chris@726 827 store->addPrefix("mo", Uri("http://purl.org/ontology/mo/"));
Chris@726 828 store->addPrefix("event", Uri("http://purl.org/NET/c4dm/event.owl#"));
Chris@726 829 store->addPrefix("af", Uri("http://purl.org/ontology/af/"));
Chris@726 830
Chris@588 831 // "MO-conformant" structure for audio files
Chris@588 832
Chris@730 833 Node n = store->complete(Triple(Node(), Uri("a"), store->expand("mo:AudioFile")));
Chris@726 834 if (n != Node() && n.type == Node::URI) {
Chris@588 835
Chris@490 836 haveAudio = true;
Chris@588 837
Chris@588 838 } else {
Chris@588 839
Chris@588 840 // Sonic Annotator v0.2 and below used to write this structure
Chris@588 841 // (which is not properly in conformance with the Music
Chris@588 842 // Ontology)
Chris@588 843
Chris@730 844 Nodes sigs = store->match(Triple(Node(), Uri("a"), store->expand("mo:Signal"))).subjects();
Chris@726 845 foreach (Node sig, sigs) {
Chris@730 846 Node aa = store->complete(Triple(sig, store->expand("mo:available_as"), Node()));
Chris@726 847 if (aa != Node()) {
Chris@726 848 haveAudio = true;
Chris@726 849 break;
Chris@726 850 }
Chris@588 851 }
Chris@490 852 }
Chris@490 853
Chris@690 854 SVDEBUG << "NOTE: RDFImporter::identifyDocumentType: haveAudio = "
Chris@687 855 << haveAudio << endl;
Chris@616 856
Chris@736 857 // can't call complete() with two Nothing nodes
Chris@736 858 n = store->matchOnce(Triple(Node(), store->expand("event:time"), Node())).c;
Chris@726 859 if (n != Node()) {
Chris@490 860 haveAnnotations = true;
Chris@490 861 }
Chris@490 862
Chris@490 863 if (!haveAnnotations) {
Chris@736 864 // can't call complete() with two Nothing nodes
Chris@736 865 n = store->matchOnce(Triple(Node(), store->expand("af:signal_feature"), Node())).c;
Chris@726 866 if (n != Node()) {
Chris@490 867 haveAnnotations = true;
Chris@490 868 }
Chris@490 869 }
Chris@490 870
Chris@690 871 SVDEBUG << "NOTE: RDFImporter::identifyDocumentType: haveAnnotations = "
Chris@687 872 << haveAnnotations << endl;
Chris@616 873
Chris@726 874 delete store;
Chris@542 875
Chris@490 876 if (haveAudio) {
Chris@490 877 if (haveAnnotations) {
Chris@490 878 return AudioRefAndAnnotations;
Chris@490 879 } else {
Chris@490 880 return AudioRef;
Chris@490 881 }
Chris@490 882 } else {
Chris@490 883 if (haveAnnotations) {
Chris@490 884 return Annotations;
Chris@490 885 } else {
Chris@499 886 return OtherRDFDocument;
Chris@490 887 }
Chris@490 888 }
Chris@492 889
Chris@542 890 return OtherRDFDocument;
Chris@490 891 }
Chris@490 892