annotate rdf/RDFImporter.cpp @ 1879:652c5360e682

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