annotate rdf/RDFImporter.cpp @ 1455:ec9e65fcf749

The use of the begin/end pairs here just seems to cause too many rows to be deleted (from the visual representation, not the underlying model). Things apparently work better if we just modify the underlying model and let the change signals percolate back up again. To that end, update the change handlers so as to cover their proper ranges with dataChanged signals.
author Chris Cannam
date Mon, 23 Apr 2018 16:03:35 +0100
parents b9faee02afa5
children 85e9b7b31a8d
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@440 350 SparseTimeValueModel::Point point(j * hopSize, f, "");
Chris@440 351 m->addPoint(point);
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@726 620 Model *model = 0;
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@449 712 SparseOneDimensionalModel::Point point(ftime, label);
Chris@449 713 sodm->addPoint(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@510 720 TextModel::Point point
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@510 724 tm->addPoint(point);
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@449 731 SparseTimeValueModel::Point point
Chris@449 732 (ftime, values.empty() ? 0.f : values[0], label);
Chris@449 733 stvm->addPoint(point);
Chris@449 734 return;
Chris@449 735 }
Chris@449 736
Chris@449 737 NoteModel *nm =
Chris@449 738 dynamic_cast<NoteModel *>(model);
Chris@449 739 if (nm) {
Chris@449 740 if (haveDuration) {
Chris@449 741 float value = 0.f, level = 1.f;
Chris@449 742 if (!values.empty()) {
Chris@449 743 value = values[0];
Chris@449 744 if (values.size() > 1) {
Chris@449 745 level = values[1];
Chris@449 746 }
Chris@449 747 }
Chris@449 748 NoteModel::Point point(ftime, value, fduration, level, label);
Chris@449 749 nm->addPoint(point);
Chris@449 750 } else {
Chris@449 751 float value = 0.f, duration = 1.f, level = 1.f;
Chris@449 752 if (!values.empty()) {
Chris@449 753 value = values[0];
Chris@449 754 if (values.size() > 1) {
Chris@449 755 duration = values[1];
Chris@449 756 if (values.size() > 2) {
Chris@449 757 level = values[2];
Chris@449 758 }
Chris@449 759 }
Chris@449 760 }
Chris@1039 761 NoteModel::Point point(ftime, value, sv_frame_t(lrintf(duration)),
Chris@1039 762 level, label);
Chris@449 763 nm->addPoint(point);
Chris@449 764 }
Chris@449 765 return;
Chris@449 766 }
Chris@449 767
Chris@449 768 RegionModel *rm =
Chris@449 769 dynamic_cast<RegionModel *>(model);
Chris@449 770 if (rm) {
Chris@617 771 float value = 0.f;
Chris@617 772 if (values.empty()) {
Chris@617 773 // no values? map each unique label to a distinct value
Chris@617 774 if (m_labelValueMap[model].find(label) == m_labelValueMap[model].end()) {
Chris@617 775 m_labelValueMap[model][label] = rm->getValueMaximum() + 1.f;
Chris@617 776 }
Chris@617 777 value = m_labelValueMap[model][label];
Chris@617 778 } else {
Chris@617 779 value = values[0];
Chris@617 780 }
Chris@449 781 if (haveDuration) {
Chris@617 782 RegionModel::Point point(ftime, value, fduration, label);
Chris@449 783 rm->addPoint(point);
Chris@449 784 } else {
Chris@449 785 // This won't actually happen -- we only create region models
Chris@449 786 // if we do have duration -- but just for completeness
Chris@617 787 float duration = 1.f;
Chris@449 788 if (!values.empty()) {
Chris@449 789 value = values[0];
Chris@449 790 if (values.size() > 1) {
Chris@449 791 duration = values[1];
Chris@449 792 }
Chris@449 793 }
Chris@1039 794 RegionModel::Point point(ftime, value,
Chris@1039 795 sv_frame_t(lrintf(duration)), label);
Chris@449 796 rm->addPoint(point);
Chris@449 797 }
Chris@449 798 return;
Chris@449 799 }
Chris@449 800
Chris@843 801 cerr << "WARNING: RDFImporterImpl::fillModel: Unknown or unexpected model type" << endl;
Chris@449 802 return;
Chris@449 803 }
Chris@449 804
Chris@490 805 RDFImporter::RDFDocumentType
Chris@490 806 RDFImporter::identifyDocumentType(QString url)
Chris@490 807 {
Chris@490 808 bool haveAudio = false;
Chris@490 809 bool haveAnnotations = false;
Chris@726 810 bool haveRDF = false;
Chris@449 811
Chris@726 812 BasicStore *store = 0;
Chris@726 813
Chris@726 814 // This is not expected to return anything useful, but if it does
Chris@726 815 // anything at all then we know we have RDF
Chris@726 816 try {
Chris@738 817 //!!! non-local document?
Chris@726 818 store = BasicStore::load(QUrl(url));
Chris@730 819 Triple t = store->matchOnce(Triple());
Chris@726 820 if (t != Triple()) haveRDF = true;
Chris@738 821 } catch (std::exception &e) {
Chris@738 822 // nothing; haveRDF will be false so the next bit catches it
Chris@726 823 }
Chris@726 824
Chris@726 825 if (!haveRDF) {
Chris@726 826 delete store;
Chris@499 827 return NotRDF;
Chris@499 828 }
Chris@499 829
Chris@726 830 store->addPrefix("mo", Uri("http://purl.org/ontology/mo/"));
Chris@726 831 store->addPrefix("event", Uri("http://purl.org/NET/c4dm/event.owl#"));
Chris@726 832 store->addPrefix("af", Uri("http://purl.org/ontology/af/"));
Chris@726 833
Chris@588 834 // "MO-conformant" structure for audio files
Chris@588 835
Chris@730 836 Node n = store->complete(Triple(Node(), Uri("a"), store->expand("mo:AudioFile")));
Chris@726 837 if (n != Node() && n.type == Node::URI) {
Chris@588 838
Chris@490 839 haveAudio = true;
Chris@588 840
Chris@588 841 } else {
Chris@588 842
Chris@588 843 // Sonic Annotator v0.2 and below used to write this structure
Chris@588 844 // (which is not properly in conformance with the Music
Chris@588 845 // Ontology)
Chris@588 846
Chris@730 847 Nodes sigs = store->match(Triple(Node(), Uri("a"), store->expand("mo:Signal"))).subjects();
Chris@726 848 foreach (Node sig, sigs) {
Chris@730 849 Node aa = store->complete(Triple(sig, store->expand("mo:available_as"), Node()));
Chris@726 850 if (aa != Node()) {
Chris@726 851 haveAudio = true;
Chris@726 852 break;
Chris@726 853 }
Chris@588 854 }
Chris@490 855 }
Chris@490 856
Chris@690 857 SVDEBUG << "NOTE: RDFImporter::identifyDocumentType: haveAudio = "
Chris@687 858 << haveAudio << endl;
Chris@616 859
Chris@736 860 // can't call complete() with two Nothing nodes
Chris@736 861 n = store->matchOnce(Triple(Node(), store->expand("event:time"), Node())).c;
Chris@726 862 if (n != Node()) {
Chris@490 863 haveAnnotations = true;
Chris@490 864 }
Chris@490 865
Chris@490 866 if (!haveAnnotations) {
Chris@736 867 // can't call complete() with two Nothing nodes
Chris@736 868 n = store->matchOnce(Triple(Node(), store->expand("af:signal_feature"), Node())).c;
Chris@726 869 if (n != Node()) {
Chris@490 870 haveAnnotations = true;
Chris@490 871 }
Chris@490 872 }
Chris@490 873
Chris@690 874 SVDEBUG << "NOTE: RDFImporter::identifyDocumentType: haveAnnotations = "
Chris@687 875 << haveAnnotations << endl;
Chris@616 876
Chris@726 877 delete store;
Chris@542 878
Chris@490 879 if (haveAudio) {
Chris@490 880 if (haveAnnotations) {
Chris@490 881 return AudioRefAndAnnotations;
Chris@490 882 } else {
Chris@490 883 return AudioRef;
Chris@490 884 }
Chris@490 885 } else {
Chris@490 886 if (haveAnnotations) {
Chris@490 887 return Annotations;
Chris@490 888 } else {
Chris@499 889 return OtherRDFDocument;
Chris@490 890 }
Chris@490 891 }
Chris@492 892
Chris@542 893 return OtherRDFDocument;
Chris@490 894 }
Chris@490 895