annotate rdf/RDFImporter.cpp @ 1671:82d03c9661f9 single-point

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