annotate rdf/RDFImporter.cpp @ 588:d04b8674b710

* Try to identify the properly conformant audio file structure written out by Sonic Annotator (but we still don't actually import it yet)
author Chris Cannam
date Wed, 13 May 2009 13:30:08 +0000
parents 2e0c987a12bd
children a03aafaacb5a
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@439 7 This file copyright 2008 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 "SimpleSPARQLQuery.h"
Chris@439 25
Chris@439 26 #include "base/ProgressReporter.h"
Chris@439 27 #include "base/RealTime.h"
Chris@439 28
Chris@439 29 #include "data/model/SparseOneDimensionalModel.h"
Chris@439 30 #include "data/model/SparseTimeValueModel.h"
Chris@439 31 #include "data/model/EditableDenseThreeDimensionalModel.h"
Chris@449 32 #include "data/model/NoteModel.h"
Chris@510 33 #include "data/model/TextModel.h"
Chris@449 34 #include "data/model/RegionModel.h"
Chris@499 35 #include "data/model/WaveFileModel.h"
Chris@499 36
Chris@499 37 #include "data/fileio/FileSource.h"
Chris@520 38 #include "data/fileio/CachedFile.h"
Chris@581 39 #include "data/fileio/FileFinder.h"
Chris@522 40
Chris@439 41 using std::cerr;
Chris@439 42 using std::endl;
Chris@439 43
Chris@439 44 class RDFImporterImpl
Chris@439 45 {
Chris@439 46 public:
Chris@439 47 RDFImporterImpl(QString url, int sampleRate);
Chris@439 48 virtual ~RDFImporterImpl();
Chris@490 49
Chris@490 50 void setSampleRate(int sampleRate) { m_sampleRate = sampleRate; }
Chris@439 51
Chris@439 52 bool isOK();
Chris@439 53 QString getErrorString() const;
Chris@439 54
Chris@439 55 std::vector<Model *> getDataModels(ProgressReporter *);
Chris@439 56
Chris@439 57 protected:
Chris@439 58 QString m_uristring;
Chris@439 59 QString m_errorString;
Chris@499 60 std::map<QString, Model *> m_audioModelMap;
Chris@439 61 int m_sampleRate;
Chris@439 62
Chris@520 63 static bool m_prefixesLoaded;
Chris@520 64 static void loadPrefixes(ProgressReporter *reporter);
Chris@520 65
Chris@499 66 void getDataModelsAudio(std::vector<Model *> &, ProgressReporter *);
Chris@440 67 void getDataModelsSparse(std::vector<Model *> &, ProgressReporter *);
Chris@440 68 void getDataModelsDense(std::vector<Model *> &, ProgressReporter *);
Chris@440 69
Chris@493 70 void getDenseModelTitle(Model *, QString, QString);
Chris@493 71
Chris@440 72 void getDenseFeatureProperties(QString featureUri,
Chris@440 73 int &sampleRate, int &windowLength,
Chris@440 74 int &hopSize, int &width, int &height);
Chris@440 75
Chris@449 76 void fillModel(Model *, long, long, bool, std::vector<float> &, QString);
Chris@439 77 };
Chris@439 78
Chris@520 79 bool RDFImporterImpl::m_prefixesLoaded = false;
Chris@439 80
Chris@439 81 QString
Chris@439 82 RDFImporter::getKnownExtensions()
Chris@439 83 {
Chris@439 84 return "*.rdf *.n3 *.ttl";
Chris@439 85 }
Chris@439 86
Chris@439 87 RDFImporter::RDFImporter(QString url, int sampleRate) :
Chris@439 88 m_d(new RDFImporterImpl(url, sampleRate))
Chris@439 89 {
Chris@439 90 }
Chris@439 91
Chris@439 92 RDFImporter::~RDFImporter()
Chris@439 93 {
Chris@439 94 delete m_d;
Chris@439 95 }
Chris@439 96
Chris@490 97 void
Chris@490 98 RDFImporter::setSampleRate(int sampleRate)
Chris@490 99 {
Chris@490 100 m_d->setSampleRate(sampleRate);
Chris@490 101 }
Chris@490 102
Chris@439 103 bool
Chris@439 104 RDFImporter::isOK()
Chris@439 105 {
Chris@439 106 return m_d->isOK();
Chris@439 107 }
Chris@439 108
Chris@439 109 QString
Chris@439 110 RDFImporter::getErrorString() const
Chris@439 111 {
Chris@439 112 return m_d->getErrorString();
Chris@439 113 }
Chris@439 114
Chris@439 115 std::vector<Model *>
Chris@439 116 RDFImporter::getDataModels(ProgressReporter *r)
Chris@439 117 {
Chris@439 118 return m_d->getDataModels(r);
Chris@439 119 }
Chris@439 120
Chris@439 121 RDFImporterImpl::RDFImporterImpl(QString uri, int sampleRate) :
Chris@439 122 m_uristring(uri),
Chris@439 123 m_sampleRate(sampleRate)
Chris@439 124 {
Chris@439 125 }
Chris@439 126
Chris@439 127 RDFImporterImpl::~RDFImporterImpl()
Chris@439 128 {
Chris@492 129 SimpleSPARQLQuery::closeSingleSource(m_uristring);
Chris@439 130 }
Chris@439 131
Chris@439 132 bool
Chris@439 133 RDFImporterImpl::isOK()
Chris@439 134 {
Chris@439 135 return (m_errorString == "");
Chris@439 136 }
Chris@439 137
Chris@439 138 QString
Chris@439 139 RDFImporterImpl::getErrorString() const
Chris@439 140 {
Chris@439 141 return m_errorString;
Chris@439 142 }
Chris@439 143
Chris@439 144 std::vector<Model *>
Chris@439 145 RDFImporterImpl::getDataModels(ProgressReporter *reporter)
Chris@439 146 {
Chris@520 147 loadPrefixes(reporter);
Chris@520 148
Chris@439 149 std::vector<Model *> models;
Chris@439 150
Chris@499 151 getDataModelsAudio(models, reporter);
Chris@499 152
Chris@490 153 if (m_sampleRate == 0) {
Chris@490 154 std::cerr << "RDFImporter::getDataModels: invalid sample rate" << std::endl;
Chris@490 155 return models;
Chris@490 156 }
Chris@490 157
Chris@508 158 QString error;
Chris@508 159
Chris@522 160 if (m_errorString != "") {
Chris@522 161 error = m_errorString;
Chris@522 162 }
Chris@508 163 m_errorString = "";
Chris@508 164
Chris@440 165 getDataModelsDense(models, reporter);
Chris@440 166
Chris@522 167 if (m_errorString != "") {
Chris@522 168 error = m_errorString;
Chris@522 169 }
Chris@440 170 m_errorString = "";
Chris@440 171
Chris@440 172 getDataModelsSparse(models, reporter);
Chris@440 173
Chris@522 174 if (m_errorString == "" && error != "") {
Chris@522 175 m_errorString = error;
Chris@522 176 }
Chris@440 177
Chris@440 178 return models;
Chris@440 179 }
Chris@440 180
Chris@440 181 void
Chris@499 182 RDFImporterImpl::getDataModelsAudio(std::vector<Model *> &models,
Chris@499 183 ProgressReporter *reporter)
Chris@499 184 {
Chris@499 185 SimpleSPARQLQuery query = SimpleSPARQLQuery
Chris@499 186 (SimpleSPARQLQuery::QueryFromSingleSource,
Chris@499 187 QString
Chris@499 188 (
Chris@499 189 " PREFIX mo: <http://purl.org/ontology/mo/> "
Chris@499 190 " SELECT ?signal ?source FROM <%1> "
Chris@499 191 " WHERE { ?signal a mo:Signal ; mo:available_as ?source } "
Chris@499 192 )
Chris@499 193 .arg(m_uristring));
Chris@499 194
Chris@499 195 SimpleSPARQLQuery::ResultList results = query.execute();
Chris@499 196
Chris@499 197 for (int i = 0; i < results.size(); ++i) {
Chris@499 198
Chris@499 199 QString signal = results[i]["signal"].value;
Chris@499 200 QString source = results[i]["source"].value;
Chris@499 201
Chris@522 202 FileSource *fs = new FileSource(source, reporter);
Chris@522 203 #ifdef NO_SV_GUI
Chris@522 204 if (!fs->isAvailable()) {
Chris@522 205 m_errorString = QString("Signal source \"%1\" is not available").arg(source);
Chris@522 206 delete fs;
Chris@522 207 continue;
Chris@522 208 }
Chris@522 209 #else
Chris@522 210 if (!fs->isAvailable()) {
Chris@522 211 FileFinder *ff = FileFinder::getInstance();
Chris@581 212 if (ff) {
Chris@581 213 QString path = ff->find(FileFinder::AudioFile,
Chris@581 214 fs->getLocation(),
Chris@581 215 m_uristring);
Chris@581 216 if (path != "") {
Chris@522 217 delete fs;
Chris@581 218 fs = new FileSource(path, reporter);
Chris@581 219 if (!fs->isAvailable()) {
Chris@581 220 delete fs;
Chris@581 221 m_errorString = QString("Signal source \"%1\" is not available").arg(source);
Chris@581 222 continue;
Chris@581 223 }
Chris@522 224 }
Chris@499 225 }
Chris@522 226 }
Chris@522 227 #endif
Chris@522 228
Chris@522 229 if (reporter) {
Chris@522 230 reporter->setMessage(RDFImporter::tr("Importing audio referenced in RDF..."));
Chris@522 231 }
Chris@522 232 fs->waitForData();
Chris@522 233 WaveFileModel *newModel = new WaveFileModel(*fs, m_sampleRate);
Chris@522 234 if (newModel->isOK()) {
Chris@522 235 std::cerr << "Successfully created wave file model from source at \"" << source.toStdString() << "\"" << std::endl;
Chris@522 236 models.push_back(newModel);
Chris@522 237 m_audioModelMap[signal] = newModel;
Chris@522 238 if (m_sampleRate == 0) {
Chris@522 239 m_sampleRate = newModel->getSampleRate();
Chris@499 240 }
Chris@508 241 } else {
Chris@522 242 m_errorString = QString("Failed to create wave file model from source at \"%1\"").arg(source);
Chris@522 243 delete newModel;
Chris@499 244 }
Chris@522 245 delete fs;
Chris@499 246 }
Chris@499 247 }
Chris@499 248
Chris@499 249 void
Chris@440 250 RDFImporterImpl::getDataModelsDense(std::vector<Model *> &models,
Chris@440 251 ProgressReporter *reporter)
Chris@440 252 {
Chris@499 253 if (reporter) {
Chris@499 254 reporter->setMessage(RDFImporter::tr("Importing dense signal data from RDF..."));
Chris@499 255 }
Chris@499 256
Chris@440 257 SimpleSPARQLQuery query = SimpleSPARQLQuery
Chris@489 258 (SimpleSPARQLQuery::QueryFromSingleSource,
Chris@480 259 QString
Chris@440 260 (
Chris@440 261 " PREFIX mo: <http://purl.org/ontology/mo/>"
Chris@440 262 " PREFIX af: <http://purl.org/ontology/af/>"
Chris@440 263
Chris@493 264 " SELECT ?feature ?feature_signal_type ?value "
Chris@440 265 " FROM <%1> "
Chris@440 266
Chris@440 267 " WHERE { "
Chris@440 268
Chris@493 269 " ?signal af:signal_feature ?feature . "
Chris@440 270
Chris@440 271 " ?feature a ?feature_signal_type ; "
Chris@440 272 " af:value ?value . "
Chris@440 273
Chris@440 274 " } "
Chris@440 275 )
Chris@440 276 .arg(m_uristring));
Chris@440 277
Chris@440 278 SimpleSPARQLQuery::ResultList results = query.execute();
Chris@440 279
Chris@440 280 if (!query.isOK()) {
Chris@440 281 m_errorString = query.getErrorString();
Chris@440 282 return;
Chris@440 283 }
Chris@440 284
Chris@440 285 if (query.wasCancelled()) {
Chris@440 286 m_errorString = "Query cancelled";
Chris@440 287 return;
Chris@440 288 }
Chris@440 289
Chris@440 290 for (int i = 0; i < results.size(); ++i) {
Chris@440 291
Chris@440 292 QString feature = results[i]["feature"].value;
Chris@440 293 QString type = results[i]["feature_signal_type"].value;
Chris@440 294 QString value = results[i]["value"].value;
Chris@440 295
Chris@440 296 int sampleRate = 0;
Chris@440 297 int windowLength = 0;
Chris@440 298 int hopSize = 0;
Chris@440 299 int width = 0;
Chris@440 300 int height = 0;
Chris@440 301 getDenseFeatureProperties
Chris@440 302 (feature, sampleRate, windowLength, hopSize, width, height);
Chris@440 303
Chris@440 304 if (sampleRate != 0 && sampleRate != m_sampleRate) {
Chris@440 305 cerr << "WARNING: Sample rate in dense feature description does not match our underlying rate -- using rate from feature description" << endl;
Chris@440 306 }
Chris@440 307 if (sampleRate == 0) sampleRate = m_sampleRate;
Chris@440 308
Chris@440 309 if (hopSize == 0) {
Chris@440 310 cerr << "WARNING: Dense feature description does not specify a hop size -- assuming 1" << endl;
Chris@440 311 hopSize = 1;
Chris@440 312 }
Chris@440 313
Chris@440 314 if (height == 0) {
Chris@440 315 cerr << "WARNING: Dense feature description does not specify feature signal dimensions -- assuming one-dimensional (height = 1)" << endl;
Chris@440 316 height = 1;
Chris@440 317 }
Chris@440 318
Chris@440 319 QStringList values = value.split(' ', QString::SkipEmptyParts);
Chris@440 320
Chris@440 321 if (values.empty()) {
Chris@440 322 cerr << "WARNING: Dense feature description does not specify any values!" << endl;
Chris@440 323 continue;
Chris@440 324 }
Chris@440 325
Chris@440 326 if (height == 1) {
Chris@440 327
Chris@440 328 SparseTimeValueModel *m = new SparseTimeValueModel
Chris@440 329 (sampleRate, hopSize, false);
Chris@440 330
Chris@440 331 for (int j = 0; j < values.size(); ++j) {
Chris@440 332 float f = values[j].toFloat();
Chris@440 333 SparseTimeValueModel::Point point(j * hopSize, f, "");
Chris@440 334 m->addPoint(point);
Chris@440 335 }
Chris@493 336
Chris@493 337 getDenseModelTitle(m, feature, type);
Chris@440 338
Chris@558 339 m->setRDFTypeURI(type);
Chris@558 340
Chris@440 341 models.push_back(m);
Chris@440 342
Chris@440 343 } else {
Chris@440 344
Chris@440 345 EditableDenseThreeDimensionalModel *m =
Chris@535 346 new EditableDenseThreeDimensionalModel
Chris@535 347 (sampleRate, hopSize, height,
Chris@535 348 EditableDenseThreeDimensionalModel::NoCompression, false);
Chris@440 349
Chris@440 350 EditableDenseThreeDimensionalModel::Column column;
Chris@440 351
Chris@440 352 int x = 0;
Chris@440 353
Chris@440 354 for (int j = 0; j < values.size(); ++j) {
Chris@440 355 if (j % height == 0 && !column.empty()) {
Chris@440 356 m->setColumn(x++, column);
Chris@440 357 column.clear();
Chris@440 358 }
Chris@440 359 column.push_back(values[j].toFloat());
Chris@440 360 }
Chris@440 361
Chris@440 362 if (!column.empty()) {
Chris@440 363 m->setColumn(x++, column);
Chris@440 364 }
Chris@440 365
Chris@493 366 getDenseModelTitle(m, feature, type);
Chris@493 367
Chris@558 368 m->setRDFTypeURI(type);
Chris@558 369
Chris@440 370 models.push_back(m);
Chris@440 371 }
Chris@440 372 }
Chris@440 373 }
Chris@440 374
Chris@440 375 void
Chris@493 376 RDFImporterImpl::getDenseModelTitle(Model *m,
Chris@493 377 QString featureUri,
Chris@493 378 QString featureTypeUri)
Chris@493 379 {
Chris@493 380 QString titleQuery = QString
Chris@493 381 (
Chris@493 382 " PREFIX dc: <http://purl.org/dc/elements/1.1/> "
Chris@493 383 " SELECT ?title "
Chris@493 384 " FROM <%1> "
Chris@493 385 " WHERE { "
Chris@493 386 " <%2> dc:title ?title . "
Chris@493 387 " } "
Chris@493 388 ).arg(m_uristring);
Chris@493 389
Chris@493 390 SimpleSPARQLQuery::Value v;
Chris@493 391
Chris@493 392 v = SimpleSPARQLQuery::singleResultQuery
Chris@493 393 (SimpleSPARQLQuery::QueryFromSingleSource,
Chris@493 394 titleQuery.arg(featureUri),
Chris@493 395 "title");
Chris@493 396
Chris@493 397 if (v.value != "") {
Chris@493 398 std::cerr << "RDFImporterImpl::getDenseModelTitle: Title (from signal) \"" << v.value.toStdString() << "\"" << std::endl;
Chris@493 399 m->setObjectName(v.value);
Chris@493 400 return;
Chris@493 401 }
Chris@493 402
Chris@493 403 v = SimpleSPARQLQuery::singleResultQuery
Chris@493 404 (SimpleSPARQLQuery::QueryFromSingleSource,
Chris@493 405 titleQuery.arg(featureTypeUri),
Chris@493 406 "title");
Chris@493 407
Chris@493 408 if (v.value != "") {
Chris@493 409 std::cerr << "RDFImporterImpl::getDenseModelTitle: Title (from signal type) \"" << v.value.toStdString() << "\"" << std::endl;
Chris@493 410 m->setObjectName(v.value);
Chris@493 411 return;
Chris@493 412 }
Chris@493 413
Chris@493 414 std::cerr << "RDFImporterImpl::getDenseModelTitle: No title available for feature <" << featureUri.toStdString() << ">" << std::endl;
Chris@493 415 }
Chris@493 416
Chris@493 417 void
Chris@440 418 RDFImporterImpl::getDenseFeatureProperties(QString featureUri,
Chris@440 419 int &sampleRate, int &windowLength,
Chris@440 420 int &hopSize, int &width, int &height)
Chris@440 421 {
Chris@489 422 SimpleSPARQLQuery::QueryType s = SimpleSPARQLQuery::QueryFromSingleSource;
Chris@489 423
Chris@440 424 QString dimensionsQuery
Chris@440 425 (
Chris@440 426 " PREFIX mo: <http://purl.org/ontology/mo/>"
Chris@440 427 " PREFIX af: <http://purl.org/ontology/af/>"
Chris@440 428
Chris@440 429 " SELECT ?dimensions "
Chris@440 430 " FROM <%1> "
Chris@440 431
Chris@440 432 " WHERE { "
Chris@440 433
Chris@440 434 " <%2> af:dimensions ?dimensions . "
Chris@440 435
Chris@440 436 " } "
Chris@440 437 );
Chris@440 438
Chris@440 439 SimpleSPARQLQuery::Value dimensionsValue =
Chris@489 440 SimpleSPARQLQuery::singleResultQuery
Chris@489 441 (s, dimensionsQuery.arg(m_uristring).arg(featureUri), "dimensions");
Chris@440 442
Chris@440 443 cerr << "Dimensions = \"" << dimensionsValue.value.toStdString() << "\""
Chris@440 444 << endl;
Chris@440 445
Chris@440 446 if (dimensionsValue.value != "") {
Chris@440 447 QStringList dl = dimensionsValue.value.split(" ");
Chris@440 448 if (dl.empty()) dl.push_back(dimensionsValue.value);
Chris@440 449 if (dl.size() > 0) height = dl[0].toInt();
Chris@440 450 if (dl.size() > 1) width = dl[1].toInt();
Chris@440 451 }
Chris@440 452
Chris@440 453 QString queryTemplate
Chris@440 454 (
Chris@440 455 " PREFIX mo: <http://purl.org/ontology/mo/>"
Chris@440 456 " PREFIX af: <http://purl.org/ontology/af/>"
Chris@440 457 " PREFIX tl: <http://purl.org/NET/c4dm/timeline.owl#>"
Chris@440 458
Chris@440 459 " SELECT ?%3 "
Chris@440 460 " FROM <%1> "
Chris@440 461
Chris@440 462 " WHERE { "
Chris@440 463
Chris@440 464 " <%2> mo:time ?time . "
Chris@440 465
Chris@440 466 " ?time a tl:Interval ; "
Chris@440 467 " tl:onTimeLine ?timeline . "
Chris@440 468
Chris@440 469 " ?map tl:rangeTimeLine ?timeline . "
Chris@440 470
Chris@440 471 " ?map tl:%3 ?%3 . "
Chris@440 472
Chris@440 473 " } "
Chris@440 474 );
Chris@440 475
Chris@440 476 // Another laborious workaround for rasqal's failure to handle
Chris@440 477 // multiple optionals properly
Chris@440 478
Chris@440 479 SimpleSPARQLQuery::Value srValue =
Chris@489 480 SimpleSPARQLQuery::singleResultQuery(s,
Chris@480 481 queryTemplate
Chris@440 482 .arg(m_uristring).arg(featureUri)
Chris@440 483 .arg("sampleRate"),
Chris@440 484 "sampleRate");
Chris@440 485 if (srValue.value != "") {
Chris@440 486 sampleRate = srValue.value.toInt();
Chris@440 487 }
Chris@440 488
Chris@440 489 SimpleSPARQLQuery::Value hopValue =
Chris@489 490 SimpleSPARQLQuery::singleResultQuery(s,
Chris@480 491 queryTemplate
Chris@440 492 .arg(m_uristring).arg(featureUri)
Chris@440 493 .arg("hopSize"),
Chris@440 494 "hopSize");
Chris@440 495 if (srValue.value != "") {
Chris@440 496 hopSize = hopValue.value.toInt();
Chris@440 497 }
Chris@440 498
Chris@440 499 SimpleSPARQLQuery::Value winValue =
Chris@489 500 SimpleSPARQLQuery::singleResultQuery(s,
Chris@480 501 queryTemplate
Chris@440 502 .arg(m_uristring).arg(featureUri)
Chris@440 503 .arg("windowLength"),
Chris@440 504 "windowLength");
Chris@440 505 if (winValue.value != "") {
Chris@440 506 windowLength = winValue.value.toInt();
Chris@440 507 }
Chris@440 508
Chris@440 509 cerr << "sr = " << sampleRate << ", hop = " << hopSize << ", win = " << windowLength << endl;
Chris@440 510 }
Chris@440 511
Chris@440 512 void
Chris@440 513 RDFImporterImpl::getDataModelsSparse(std::vector<Model *> &models,
Chris@440 514 ProgressReporter *reporter)
Chris@440 515 {
Chris@499 516 if (reporter) {
Chris@499 517 reporter->setMessage(RDFImporter::tr("Importing event data from RDF..."));
Chris@499 518 }
Chris@499 519
Chris@489 520 SimpleSPARQLQuery::QueryType s = SimpleSPARQLQuery::QueryFromSingleSource;
Chris@489 521
Chris@439 522 // Our query is intended to retrieve every thing that has a time,
Chris@439 523 // and every feature type and value associated with a thing that
Chris@439 524 // has a time.
Chris@439 525
Chris@439 526 // We will then need to refine this big bag of results into a set
Chris@439 527 // of data models.
Chris@439 528
Chris@439 529 // Results that have different source signals should go into
Chris@439 530 // different models.
Chris@439 531
Chris@439 532 // Results that have different feature types should go into
Chris@439 533 // different models.
Chris@439 534
Chris@439 535 // Results that are sparse should go into different models from
Chris@439 536 // those that are dense (we need to examine the timestamps to
Chris@439 537 // establish this -- if the timestamps are regular, the results
Chris@439 538 // are dense -- so we can't do it as we go along, only after
Chris@439 539 // collecting all results).
Chris@439 540
Chris@439 541 // Timed things that have features associated with them should not
Chris@439 542 // appear directly in any model -- their features should appear
Chris@439 543 // instead -- and these should be different models from those used
Chris@439 544 // for timed things that do not have features.
Chris@439 545
Chris@439 546 // As we load the results, we'll push them into a partially
Chris@439 547 // structured container that maps from source signal (URI as
Chris@439 548 // string) -> feature type (likewise) -> time -> list of values.
Chris@439 549 // If the source signal or feature type is unavailable, the empty
Chris@439 550 // string will do.
Chris@439 551
Chris@449 552 QString prefixes = QString(
Chris@439 553 " PREFIX event: <http://purl.org/NET/c4dm/event.owl#>"
Chris@449 554 " PREFIX tl: <http://purl.org/NET/c4dm/timeline.owl#>"
Chris@439 555 " PREFIX mo: <http://purl.org/ontology/mo/>"
Chris@439 556 " PREFIX af: <http://purl.org/ontology/af/>"
Chris@449 557 " PREFIX rdfs: <http://www.w3.org/2000/01/rdf-schema#>"
Chris@449 558 );
Chris@439 559
Chris@449 560 QString queryString = prefixes + QString(
Chris@449 561
Chris@499 562 " SELECT ?signal ?timed_thing ?event_type ?value"
Chris@439 563 " FROM <%1>"
Chris@439 564
Chris@439 565 " WHERE {"
Chris@440 566
Chris@440 567 " ?signal a mo:Signal ."
Chris@440 568
Chris@439 569 " ?signal mo:time ?interval ."
Chris@449 570 " ?interval tl:onTimeLine ?tl ."
Chris@449 571 " ?time tl:onTimeLine ?tl ."
Chris@449 572 " ?timed_thing event:time ?time ."
Chris@440 573 " ?timed_thing a ?event_type ."
Chris@440 574
Chris@439 575 " OPTIONAL {"
Chris@440 576 " ?timed_thing af:feature ?value"
Chris@439 577 " }"
Chris@439 578 " }"
Chris@439 579
Chris@439 580 ).arg(m_uristring);
Chris@439 581
Chris@449 582 QString timeQueryString = prefixes + QString(
Chris@449 583
Chris@449 584 " SELECT ?time FROM <%1> "
Chris@449 585 " WHERE { "
Chris@449 586 " <%2> event:time ?t . "
Chris@449 587 " ?t tl:at ?time . "
Chris@449 588 " } "
Chris@449 589
Chris@449 590 ).arg(m_uristring);
Chris@449 591
Chris@449 592 QString rangeQueryString = prefixes + QString(
Chris@449 593
Chris@449 594 " SELECT ?time ?duration FROM <%1> "
Chris@449 595 " WHERE { "
Chris@449 596 " <%2> event:time ?t . "
Chris@449 597 " ?t tl:beginsAt ?time . "
Chris@449 598 " ?t tl:duration ?duration . "
Chris@449 599 " } "
Chris@449 600
Chris@449 601 ).arg(m_uristring);
Chris@449 602
Chris@449 603 QString labelQueryString = prefixes + QString(
Chris@449 604
Chris@449 605 " SELECT ?label FROM <%1> "
Chris@449 606 " WHERE { "
Chris@449 607 " <%2> rdfs:label ?label . "
Chris@449 608 " } "
Chris@449 609
Chris@449 610 ).arg(m_uristring);
Chris@449 611
Chris@510 612 QString textQueryString = prefixes + QString(
Chris@510 613
Chris@510 614 " SELECT ?label FROM <%1> "
Chris@510 615 " WHERE { "
Chris@510 616 " <%2> af:text ?label . "
Chris@510 617 " } "
Chris@510 618
Chris@510 619 ).arg(m_uristring);
Chris@510 620
Chris@489 621 SimpleSPARQLQuery query(s, queryString);
Chris@439 622 query.setProgressReporter(reporter);
Chris@439 623
Chris@500 624 // cerr << "Query will be: " << queryString.toStdString() << endl;
Chris@439 625
Chris@439 626 SimpleSPARQLQuery::ResultList results = query.execute();
Chris@439 627
Chris@439 628 if (!query.isOK()) {
Chris@439 629 m_errorString = query.getErrorString();
Chris@440 630 return;
Chris@439 631 }
Chris@439 632
Chris@439 633 if (query.wasCancelled()) {
Chris@439 634 m_errorString = "Query cancelled";
Chris@440 635 return;
Chris@439 636 }
Chris@439 637
Chris@449 638 /*
Chris@449 639 This function is now only used for sparse data (for dense data
Chris@449 640 we would be in getDataModelsDense instead).
Chris@449 641
Chris@449 642 For sparse data, the determining factors in deciding what model
Chris@449 643 to use are: Do the features have values? and Do the features
Chris@449 644 have duration?
Chris@449 645
Chris@449 646 We can run through the results and check off whether we find
Chris@449 647 values and duration for each of the source+type keys, and then
Chris@449 648 run through the source+type keys pushing each of the results
Chris@449 649 into a suitable model.
Chris@449 650
Chris@449 651 Unfortunately, at this point we do not yet have any actual
Chris@449 652 timing data (time/duration) -- just the time URI.
Chris@449 653
Chris@449 654 What we _could_ do is to create one of each type of model at the
Chris@449 655 start, for each of the source+type keys, and then push each
Chris@449 656 feature into the relevant model depending on what we find out
Chris@449 657 about it. Then return only non-empty models.
Chris@449 658 */
Chris@449 659
Chris@449 660 // Map from signal source to event type to dimensionality to
Chris@449 661 // presence of duration to model ptr. Whee!
Chris@449 662 std::map<QString, std::map<QString, std::map<int, std::map<bool, Model *> > > >
Chris@449 663 modelMap;
Chris@449 664
Chris@439 665 for (int i = 0; i < results.size(); ++i) {
Chris@439 666
Chris@499 667 if (i % 4 == 0) {
Chris@499 668 if (reporter) reporter->setProgress(i/4);
Chris@499 669 }
Chris@499 670
Chris@499 671 QString source = results[i]["signal"].value;
Chris@449 672 QString type = results[i]["event_type"].value;
Chris@449 673 QString thinguri = results[i]["timed_thing"].value;
Chris@449 674
Chris@449 675 RealTime time;
Chris@449 676 RealTime duration;
Chris@439 677
Chris@449 678 bool haveTime = false;
Chris@449 679 bool haveDuration = false;
Chris@439 680
Chris@510 681 QString label = "";
Chris@510 682 bool text = (type.contains("Text") || type.contains("text")); // Ha, ha
Chris@510 683
Chris@510 684 if (text) {
Chris@510 685 label = SimpleSPARQLQuery::singleResultQuery
Chris@510 686 (s, textQueryString.arg(thinguri), "label").value;
Chris@510 687 }
Chris@510 688
Chris@510 689 if (label == "") {
Chris@510 690 label = SimpleSPARQLQuery::singleResultQuery
Chris@510 691 (s, labelQueryString.arg(thinguri), "label").value;
Chris@510 692 }
Chris@449 693
Chris@489 694 SimpleSPARQLQuery rangeQuery(s, rangeQueryString.arg(thinguri));
Chris@450 695 SimpleSPARQLQuery::ResultList rangeResults = rangeQuery.execute();
Chris@450 696 if (!rangeResults.empty()) {
Chris@450 697 // std::cerr << rangeResults.size() << " range results" << std::endl;
Chris@450 698 time = RealTime::fromXsdDuration
Chris@450 699 (rangeResults[0]["time"].value.toStdString());
Chris@450 700 duration = RealTime::fromXsdDuration
Chris@450 701 (rangeResults[0]["duration"].value.toStdString());
Chris@450 702 // std::cerr << "duration string " << rangeResults[0]["duration"].value.toStdString() << std::endl;
Chris@449 703 haveTime = true;
Chris@450 704 haveDuration = true;
Chris@449 705 } else {
Chris@450 706 QString timestring = SimpleSPARQLQuery::singleResultQuery
Chris@489 707 (s, timeQueryString.arg(thinguri), "time").value;
Chris@520 708 // std::cerr << "timestring = " << timestring.toStdString() << std::endl;
Chris@450 709 if (timestring != "") {
Chris@450 710 time = RealTime::fromXsdDuration(timestring.toStdString());
Chris@449 711 haveTime = true;
Chris@449 712 }
Chris@449 713 }
Chris@439 714
Chris@439 715 QString valuestring = results[i]["value"].value;
Chris@449 716 std::vector<float> values;
Chris@449 717
Chris@439 718 if (valuestring != "") {
Chris@449 719 QStringList vsl = valuestring.split(" ", QString::SkipEmptyParts);
Chris@449 720 for (int j = 0; j < vsl.size(); ++j) {
Chris@449 721 bool success = false;
Chris@449 722 float v = vsl[j].toFloat(&success);
Chris@449 723 if (success) values.push_back(v);
Chris@449 724 }
Chris@439 725 }
Chris@439 726
Chris@449 727 int dimensions = 1;
Chris@449 728 if (values.size() == 1) dimensions = 2;
Chris@449 729 else if (values.size() > 1) dimensions = 3;
Chris@449 730
Chris@449 731 Model *model = 0;
Chris@449 732
Chris@449 733 if (modelMap[source][type][dimensions].find(haveDuration) ==
Chris@449 734 modelMap[source][type][dimensions].end()) {
Chris@449 735
Chris@449 736 /*
Chris@449 737 std::cerr << "Creating new model: source = " << source.toStdString()
Chris@449 738 << ", type = " << type.toStdString() << ", dimensions = "
Chris@449 739 << dimensions << ", haveDuration = " << haveDuration
Chris@449 740 << ", time = " << time << ", duration = " << duration
Chris@449 741 << std::endl;
Chris@449 742 */
Chris@449 743
Chris@449 744 if (!haveDuration) {
Chris@449 745
Chris@449 746 if (dimensions == 1) {
Chris@449 747
Chris@510 748 if (text) {
Chris@510 749
Chris@510 750 model = new TextModel(m_sampleRate, 1, false);
Chris@510 751
Chris@510 752 } else {
Chris@510 753
Chris@510 754 model = new SparseOneDimensionalModel(m_sampleRate, 1, false);
Chris@510 755 }
Chris@449 756
Chris@449 757 } else if (dimensions == 2) {
Chris@449 758
Chris@510 759 if (text) {
Chris@510 760
Chris@510 761 model = new TextModel(m_sampleRate, 1, false);
Chris@510 762
Chris@510 763 } else {
Chris@510 764
Chris@510 765 model = new SparseTimeValueModel(m_sampleRate, 1, false);
Chris@510 766 }
Chris@449 767
Chris@449 768 } else {
Chris@449 769
Chris@449 770 // We don't have a three-dimensional sparse model,
Chris@449 771 // so use a note model. We do have some logic (in
Chris@449 772 // extractStructure below) for guessing whether
Chris@449 773 // this should after all have been a dense model,
Chris@449 774 // but it's hard to apply it because we don't have
Chris@449 775 // all the necessary timing data yet... hmm
Chris@449 776
Chris@449 777 model = new NoteModel(m_sampleRate, 1, false);
Chris@449 778 }
Chris@449 779
Chris@449 780 } else { // haveDuration
Chris@449 781
Chris@449 782 if (dimensions == 1 || dimensions == 2) {
Chris@449 783
Chris@449 784 // If our units are frequency or midi pitch, we
Chris@449 785 // should be using a note model... hm
Chris@449 786
Chris@449 787 model = new RegionModel(m_sampleRate, 1, false);
Chris@449 788
Chris@449 789 } else {
Chris@449 790
Chris@449 791 // We don't have a three-dimensional sparse model,
Chris@449 792 // so use a note model. We do have some logic (in
Chris@449 793 // extractStructure below) for guessing whether
Chris@449 794 // this should after all have been a dense model,
Chris@449 795 // but it's hard to apply it because we don't have
Chris@449 796 // all the necessary timing data yet... hmm
Chris@449 797
Chris@449 798 model = new NoteModel(m_sampleRate, 1, false);
Chris@449 799 }
Chris@449 800 }
Chris@449 801
Chris@558 802 model->setRDFTypeURI(type);
Chris@558 803
Chris@499 804 if (m_audioModelMap.find(source) != m_audioModelMap.end()) {
Chris@499 805 std::cerr << "source model for " << model << " is " << m_audioModelMap[source] << std::endl;
Chris@499 806 model->setSourceModel(m_audioModelMap[source]);
Chris@499 807 }
Chris@499 808
Chris@493 809 QString titleQuery = QString
Chris@493 810 (
Chris@493 811 " PREFIX dc: <http://purl.org/dc/elements/1.1/> "
Chris@493 812 " SELECT ?title "
Chris@493 813 " FROM <%1> "
Chris@493 814 " WHERE { "
Chris@493 815 " <%2> dc:title ?title . "
Chris@493 816 " } "
Chris@493 817 ).arg(m_uristring).arg(type);
Chris@493 818 QString title = SimpleSPARQLQuery::singleResultQuery
Chris@493 819 (s, titleQuery, "title").value;
Chris@493 820 if (title != "") model->setObjectName(title);
Chris@493 821
Chris@449 822 modelMap[source][type][dimensions][haveDuration] = model;
Chris@449 823 models.push_back(model);
Chris@449 824 }
Chris@449 825
Chris@449 826 model = modelMap[source][type][dimensions][haveDuration];
Chris@449 827
Chris@449 828 if (model) {
Chris@449 829 long ftime = RealTime::realTime2Frame(time, m_sampleRate);
Chris@449 830 long fduration = RealTime::realTime2Frame(duration, m_sampleRate);
Chris@449 831 fillModel(model, ftime, fduration, haveDuration, values, label);
Chris@439 832 }
Chris@439 833 }
Chris@439 834 }
Chris@439 835
Chris@439 836 void
Chris@449 837 RDFImporterImpl::fillModel(Model *model,
Chris@449 838 long ftime,
Chris@449 839 long fduration,
Chris@449 840 bool haveDuration,
Chris@449 841 std::vector<float> &values,
Chris@449 842 QString label)
Chris@449 843 {
Chris@493 844 // std::cerr << "RDFImporterImpl::fillModel: adding point at frame " << ftime << std::endl;
Chris@492 845
Chris@449 846 SparseOneDimensionalModel *sodm =
Chris@449 847 dynamic_cast<SparseOneDimensionalModel *>(model);
Chris@449 848 if (sodm) {
Chris@449 849 SparseOneDimensionalModel::Point point(ftime, label);
Chris@449 850 sodm->addPoint(point);
Chris@449 851 return;
Chris@449 852 }
Chris@449 853
Chris@510 854 TextModel *tm =
Chris@510 855 dynamic_cast<TextModel *>(model);
Chris@510 856 if (tm) {
Chris@510 857 TextModel::Point point
Chris@510 858 (ftime,
Chris@510 859 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 860 label);
Chris@510 861 tm->addPoint(point);
Chris@510 862 return;
Chris@510 863 }
Chris@510 864
Chris@449 865 SparseTimeValueModel *stvm =
Chris@449 866 dynamic_cast<SparseTimeValueModel *>(model);
Chris@449 867 if (stvm) {
Chris@449 868 SparseTimeValueModel::Point point
Chris@449 869 (ftime, values.empty() ? 0.f : values[0], label);
Chris@449 870 stvm->addPoint(point);
Chris@449 871 return;
Chris@449 872 }
Chris@449 873
Chris@449 874 NoteModel *nm =
Chris@449 875 dynamic_cast<NoteModel *>(model);
Chris@449 876 if (nm) {
Chris@449 877 if (haveDuration) {
Chris@449 878 float value = 0.f, level = 1.f;
Chris@449 879 if (!values.empty()) {
Chris@449 880 value = values[0];
Chris@449 881 if (values.size() > 1) {
Chris@449 882 level = values[1];
Chris@449 883 }
Chris@449 884 }
Chris@449 885 NoteModel::Point point(ftime, value, fduration, level, label);
Chris@449 886 nm->addPoint(point);
Chris@449 887 } else {
Chris@449 888 float value = 0.f, duration = 1.f, level = 1.f;
Chris@449 889 if (!values.empty()) {
Chris@449 890 value = values[0];
Chris@449 891 if (values.size() > 1) {
Chris@449 892 duration = values[1];
Chris@449 893 if (values.size() > 2) {
Chris@449 894 level = values[2];
Chris@449 895 }
Chris@449 896 }
Chris@449 897 }
Chris@449 898 NoteModel::Point point(ftime, value, duration, level, label);
Chris@449 899 nm->addPoint(point);
Chris@449 900 }
Chris@449 901 return;
Chris@449 902 }
Chris@449 903
Chris@449 904 RegionModel *rm =
Chris@449 905 dynamic_cast<RegionModel *>(model);
Chris@449 906 if (rm) {
Chris@449 907 if (haveDuration) {
Chris@449 908 RegionModel::Point point
Chris@449 909 (ftime, values.empty() ? 0.f : values[0], fduration, label);
Chris@449 910 rm->addPoint(point);
Chris@449 911 } else {
Chris@449 912 // This won't actually happen -- we only create region models
Chris@449 913 // if we do have duration -- but just for completeness
Chris@449 914 float value = 0.f, duration = 1.f;
Chris@449 915 if (!values.empty()) {
Chris@449 916 value = values[0];
Chris@449 917 if (values.size() > 1) {
Chris@449 918 duration = values[1];
Chris@449 919 }
Chris@449 920 }
Chris@449 921 RegionModel::Point point(ftime, value, duration, label);
Chris@449 922 rm->addPoint(point);
Chris@449 923 }
Chris@449 924 return;
Chris@449 925 }
Chris@449 926
Chris@449 927 std::cerr << "WARNING: RDFImporterImpl::fillModel: Unknown or unexpected model type" << std::endl;
Chris@449 928 return;
Chris@449 929 }
Chris@449 930
Chris@490 931 RDFImporter::RDFDocumentType
Chris@490 932 RDFImporter::identifyDocumentType(QString url)
Chris@490 933 {
Chris@490 934 bool haveAudio = false;
Chris@490 935 bool haveAnnotations = false;
Chris@449 936
Chris@499 937 // This query is not expected to return any values, but if it
Chris@499 938 // executes successfully (leaving no error in the error string)
Chris@499 939 // then we know we have RDF
Chris@499 940 SimpleSPARQLQuery q(SimpleSPARQLQuery::QueryFromSingleSource,
Chris@499 941 QString(" SELECT ?x FROM <%1> WHERE { ?x <y> <z> } ")
Chris@499 942 .arg(url));
Chris@499 943
Chris@499 944 SimpleSPARQLQuery::ResultList r = q.execute();
Chris@499 945 if (!q.isOK()) {
Chris@542 946 SimpleSPARQLQuery::closeSingleSource(url);
Chris@499 947 return NotRDF;
Chris@499 948 }
Chris@499 949
Chris@588 950 // "MO-conformant" structure for audio files
Chris@588 951
Chris@490 952 SimpleSPARQLQuery::Value value =
Chris@490 953 SimpleSPARQLQuery::singleResultQuery
Chris@490 954 (SimpleSPARQLQuery::QueryFromSingleSource,
Chris@490 955 QString
Chris@490 956 (" PREFIX mo: <http://purl.org/ontology/mo/> "
Chris@490 957 " SELECT ?url FROM <%1> "
Chris@588 958 " WHERE { ?url a mo:AudioFile } "
Chris@490 959 ).arg(url),
Chris@490 960 "url");
Chris@490 961
Chris@490 962 if (value.type == SimpleSPARQLQuery::URIValue) {
Chris@588 963
Chris@490 964 haveAudio = true;
Chris@588 965
Chris@588 966 } else {
Chris@588 967
Chris@588 968 // Sonic Annotator v0.2 and below used to write this structure
Chris@588 969 // (which is not properly in conformance with the Music
Chris@588 970 // Ontology)
Chris@588 971
Chris@588 972 value =
Chris@588 973 SimpleSPARQLQuery::singleResultQuery
Chris@588 974 (SimpleSPARQLQuery::QueryFromSingleSource,
Chris@588 975 QString
Chris@588 976 (" PREFIX mo: <http://purl.org/ontology/mo/> "
Chris@588 977 " SELECT ?url FROM <%1> "
Chris@588 978 " WHERE { ?signal a mo:Signal ; mo:available_as ?url } "
Chris@588 979 ).arg(url),
Chris@588 980 "url");
Chris@588 981
Chris@588 982 if (value.type == SimpleSPARQLQuery::URIValue) {
Chris@588 983 haveAudio = true;
Chris@588 984 }
Chris@490 985 }
Chris@490 986
Chris@490 987 value =
Chris@490 988 SimpleSPARQLQuery::singleResultQuery
Chris@490 989 (SimpleSPARQLQuery::QueryFromSingleSource,
Chris@490 990 QString
Chris@490 991 (" PREFIX event: <http://purl.org/NET/c4dm/event.owl#> "
Chris@490 992 " SELECT ?thing FROM <%1> "
Chris@490 993 " WHERE { ?thing event:time ?time } "
Chris@490 994 ).arg(url),
Chris@490 995 "thing");
Chris@490 996
Chris@490 997 if (value.type == SimpleSPARQLQuery::URIValue) {
Chris@490 998 haveAnnotations = true;
Chris@490 999 }
Chris@490 1000
Chris@490 1001 if (!haveAnnotations) {
Chris@490 1002
Chris@490 1003 value =
Chris@490 1004 SimpleSPARQLQuery::singleResultQuery
Chris@490 1005 (SimpleSPARQLQuery::QueryFromSingleSource,
Chris@490 1006 QString
Chris@490 1007 (" PREFIX af: <http://purl.org/ontology/af/> "
Chris@490 1008 " SELECT ?thing FROM <%1> "
Chris@490 1009 " WHERE { ?signal af:signal_feature ?thing } "
Chris@490 1010 ).arg(url),
Chris@490 1011 "thing");
Chris@490 1012
Chris@490 1013 if (value.type == SimpleSPARQLQuery::URIValue) {
Chris@490 1014 haveAnnotations = true;
Chris@490 1015 }
Chris@490 1016 }
Chris@490 1017
Chris@542 1018 SimpleSPARQLQuery::closeSingleSource(url);
Chris@542 1019
Chris@490 1020 if (haveAudio) {
Chris@490 1021 if (haveAnnotations) {
Chris@490 1022 return AudioRefAndAnnotations;
Chris@490 1023 } else {
Chris@490 1024 return AudioRef;
Chris@490 1025 }
Chris@490 1026 } else {
Chris@490 1027 if (haveAnnotations) {
Chris@490 1028 return Annotations;
Chris@490 1029 } else {
Chris@499 1030 return OtherRDFDocument;
Chris@490 1031 }
Chris@490 1032 }
Chris@492 1033
Chris@542 1034 return OtherRDFDocument;
Chris@490 1035 }
Chris@490 1036
Chris@520 1037 void
Chris@520 1038 RDFImporterImpl::loadPrefixes(ProgressReporter *reporter)
Chris@520 1039 {
Chris@520 1040 return;
Chris@520 1041 //!!!
Chris@520 1042 if (m_prefixesLoaded) return;
Chris@520 1043 const char *prefixes[] = {
Chris@520 1044 "http://purl.org/NET/c4dm/event.owl",
Chris@520 1045 "http://purl.org/NET/c4dm/timeline.owl",
Chris@520 1046 "http://purl.org/ontology/mo/",
Chris@520 1047 "http://purl.org/ontology/af/",
Chris@520 1048 "http://www.w3.org/2000/01/rdf-schema",
Chris@520 1049 "http://purl.org/dc/elements/1.1/",
Chris@520 1050 };
Chris@520 1051 for (size_t i = 0; i < sizeof(prefixes)/sizeof(prefixes[0]); ++i) {
Chris@520 1052 CachedFile cf(prefixes[i], reporter, "application/rdf+xml");
Chris@520 1053 if (!cf.isOK()) continue;
Chris@520 1054 SimpleSPARQLQuery::addSourceToModel
Chris@520 1055 (QUrl::fromLocalFile(cf.getLocalFilename()).toString());
Chris@520 1056 }
Chris@520 1057 m_prefixesLoaded = true;
Chris@520 1058 }