annotate rdf/RDFImporter.cpp @ 458:f60360209e5c

* Fix race condition in FFTFileCache when reading from the same FFT model from multiple threads (e.g. when applying more than one plugin at once)
author Chris Cannam
date Wed, 15 Oct 2008 12:08:02 +0000
parents d8a2c28ba9f6
children 3ffce691c9bf
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@449 33 #include "data/model/RegionModel.h"
Chris@439 34
Chris@439 35 using std::cerr;
Chris@439 36 using std::endl;
Chris@439 37
Chris@439 38 class RDFImporterImpl
Chris@439 39 {
Chris@439 40 public:
Chris@439 41 RDFImporterImpl(QString url, int sampleRate);
Chris@439 42 virtual ~RDFImporterImpl();
Chris@439 43
Chris@439 44 bool isOK();
Chris@439 45 QString getErrorString() const;
Chris@439 46
Chris@439 47 std::vector<Model *> getDataModels(ProgressReporter *);
Chris@439 48
Chris@439 49 protected:
Chris@439 50 QString m_uristring;
Chris@439 51 QString m_errorString;
Chris@439 52 int m_sampleRate;
Chris@439 53
Chris@440 54 void getDataModelsSparse(std::vector<Model *> &, ProgressReporter *);
Chris@440 55 void getDataModelsDense(std::vector<Model *> &, ProgressReporter *);
Chris@440 56
Chris@440 57 void getDenseFeatureProperties(QString featureUri,
Chris@440 58 int &sampleRate, int &windowLength,
Chris@440 59 int &hopSize, int &width, int &height);
Chris@440 60
Chris@449 61
Chris@449 62 void fillModel(Model *, long, long, bool, std::vector<float> &, QString);
Chris@449 63
Chris@449 64 /*
Chris@449 65
Chris@449 66 typedef std::vector<std::pair<RealTime, float> > DurationValueList;
Chris@449 67 typedef std::map<RealTime, DurationValueList> TimeDurationValueMap;
Chris@449 68 typedef std::map<QString, TimeDurationValueMap> TypeTimeDurationValueMap;
Chris@449 69 typedef std::map<QString, TypeTimeDurationValueMap> SourceTypeTimeDurationValueMap;
Chris@449 70
Chris@449 71 void extractStructure(const TimeDurationValueMap &map, bool &sparse,
Chris@439 72 int &minValueCount, int &maxValueCount);
Chris@439 73
Chris@449 74 void fillModel(SparseOneDimensionalModel *, const TimeDurationValueMap &);
Chris@449 75 void fillModel(SparseTimeValueModel *, const TimeDurationValueMap &);
Chris@449 76 void fillModel(EditableDenseThreeDimensionalModel *, const TimeDurationValueMap &);
Chris@449 77 */
Chris@439 78 };
Chris@439 79
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@439 97 bool
Chris@439 98 RDFImporter::isOK()
Chris@439 99 {
Chris@439 100 return m_d->isOK();
Chris@439 101 }
Chris@439 102
Chris@439 103 QString
Chris@439 104 RDFImporter::getErrorString() const
Chris@439 105 {
Chris@439 106 return m_d->getErrorString();
Chris@439 107 }
Chris@439 108
Chris@439 109 std::vector<Model *>
Chris@439 110 RDFImporter::getDataModels(ProgressReporter *r)
Chris@439 111 {
Chris@439 112 return m_d->getDataModels(r);
Chris@439 113 }
Chris@439 114
Chris@439 115 RDFImporterImpl::RDFImporterImpl(QString uri, int sampleRate) :
Chris@439 116 m_uristring(uri),
Chris@439 117 m_sampleRate(sampleRate)
Chris@439 118 {
Chris@439 119 }
Chris@439 120
Chris@439 121 RDFImporterImpl::~RDFImporterImpl()
Chris@439 122 {
Chris@439 123 }
Chris@439 124
Chris@439 125 bool
Chris@439 126 RDFImporterImpl::isOK()
Chris@439 127 {
Chris@439 128 return (m_errorString == "");
Chris@439 129 }
Chris@439 130
Chris@439 131 QString
Chris@439 132 RDFImporterImpl::getErrorString() const
Chris@439 133 {
Chris@439 134 return m_errorString;
Chris@439 135 }
Chris@439 136
Chris@439 137 std::vector<Model *>
Chris@439 138 RDFImporterImpl::getDataModels(ProgressReporter *reporter)
Chris@439 139 {
Chris@439 140 std::vector<Model *> models;
Chris@439 141
Chris@440 142 getDataModelsDense(models, reporter);
Chris@440 143
Chris@440 144 QString error;
Chris@440 145 if (!isOK()) error = m_errorString;
Chris@440 146 m_errorString = "";
Chris@440 147
Chris@440 148 getDataModelsSparse(models, reporter);
Chris@440 149
Chris@440 150 if (isOK()) m_errorString = error;
Chris@440 151
Chris@440 152 return models;
Chris@440 153 }
Chris@440 154
Chris@440 155 void
Chris@440 156 RDFImporterImpl::getDataModelsDense(std::vector<Model *> &models,
Chris@440 157 ProgressReporter *reporter)
Chris@440 158 {
Chris@440 159 SimpleSPARQLQuery query = SimpleSPARQLQuery
Chris@440 160 (QString
Chris@440 161 (
Chris@440 162 " PREFIX mo: <http://purl.org/ontology/mo/>"
Chris@440 163 " PREFIX af: <http://purl.org/ontology/af/>"
Chris@440 164
Chris@440 165 " SELECT ?feature ?signal_source ?feature_signal_type ?value "
Chris@440 166 " FROM <%1> "
Chris@440 167
Chris@440 168 " WHERE { "
Chris@440 169
Chris@440 170 " ?signal a mo:Signal ; "
Chris@440 171 " mo:available_as ?signal_source ; "
Chris@440 172 " af:signal_feature ?feature . "
Chris@440 173
Chris@440 174 " ?feature a ?feature_signal_type ; "
Chris@440 175 " af:value ?value . "
Chris@440 176
Chris@440 177 " } "
Chris@440 178 )
Chris@440 179 .arg(m_uristring));
Chris@440 180
Chris@440 181 SimpleSPARQLQuery::ResultList results = query.execute();
Chris@440 182
Chris@440 183 if (!query.isOK()) {
Chris@440 184 m_errorString = query.getErrorString();
Chris@440 185 return;
Chris@440 186 }
Chris@440 187
Chris@440 188 if (query.wasCancelled()) {
Chris@440 189 m_errorString = "Query cancelled";
Chris@440 190 return;
Chris@440 191 }
Chris@440 192
Chris@440 193 for (int i = 0; i < results.size(); ++i) {
Chris@440 194
Chris@440 195 QString feature = results[i]["feature"].value;
Chris@440 196 QString source = results[i]["signal_source"].value;
Chris@440 197 QString type = results[i]["feature_signal_type"].value;
Chris@440 198 QString value = results[i]["value"].value;
Chris@440 199
Chris@440 200 int sampleRate = 0;
Chris@440 201 int windowLength = 0;
Chris@440 202 int hopSize = 0;
Chris@440 203 int width = 0;
Chris@440 204 int height = 0;
Chris@440 205 getDenseFeatureProperties
Chris@440 206 (feature, sampleRate, windowLength, hopSize, width, height);
Chris@440 207
Chris@440 208 if (sampleRate != 0 && sampleRate != m_sampleRate) {
Chris@440 209 cerr << "WARNING: Sample rate in dense feature description does not match our underlying rate -- using rate from feature description" << endl;
Chris@440 210 }
Chris@440 211 if (sampleRate == 0) sampleRate = m_sampleRate;
Chris@440 212
Chris@440 213 if (hopSize == 0) {
Chris@440 214 cerr << "WARNING: Dense feature description does not specify a hop size -- assuming 1" << endl;
Chris@440 215 hopSize = 1;
Chris@440 216 }
Chris@440 217
Chris@440 218 if (height == 0) {
Chris@440 219 cerr << "WARNING: Dense feature description does not specify feature signal dimensions -- assuming one-dimensional (height = 1)" << endl;
Chris@440 220 height = 1;
Chris@440 221 }
Chris@440 222
Chris@440 223 QStringList values = value.split(' ', QString::SkipEmptyParts);
Chris@440 224
Chris@440 225 if (values.empty()) {
Chris@440 226 cerr << "WARNING: Dense feature description does not specify any values!" << endl;
Chris@440 227 continue;
Chris@440 228 }
Chris@440 229
Chris@440 230 if (height == 1) {
Chris@440 231
Chris@440 232 SparseTimeValueModel *m = new SparseTimeValueModel
Chris@440 233 (sampleRate, hopSize, false);
Chris@440 234
Chris@440 235 for (int j = 0; j < values.size(); ++j) {
Chris@440 236 float f = values[j].toFloat();
Chris@440 237 SparseTimeValueModel::Point point(j * hopSize, f, "");
Chris@440 238 m->addPoint(point);
Chris@440 239 }
Chris@440 240
Chris@440 241 models.push_back(m);
Chris@440 242
Chris@440 243 } else {
Chris@440 244
Chris@440 245 EditableDenseThreeDimensionalModel *m =
Chris@440 246 new EditableDenseThreeDimensionalModel(sampleRate, hopSize,
Chris@440 247 height, false);
Chris@440 248
Chris@440 249 EditableDenseThreeDimensionalModel::Column column;
Chris@440 250
Chris@440 251 int x = 0;
Chris@440 252
Chris@440 253 for (int j = 0; j < values.size(); ++j) {
Chris@440 254 if (j % height == 0 && !column.empty()) {
Chris@440 255 m->setColumn(x++, column);
Chris@440 256 column.clear();
Chris@440 257 }
Chris@440 258 column.push_back(values[j].toFloat());
Chris@440 259 }
Chris@440 260
Chris@440 261 if (!column.empty()) {
Chris@440 262 m->setColumn(x++, column);
Chris@440 263 }
Chris@440 264
Chris@440 265 models.push_back(m);
Chris@440 266 }
Chris@440 267 }
Chris@440 268 }
Chris@440 269
Chris@440 270 void
Chris@440 271 RDFImporterImpl::getDenseFeatureProperties(QString featureUri,
Chris@440 272 int &sampleRate, int &windowLength,
Chris@440 273 int &hopSize, int &width, int &height)
Chris@440 274 {
Chris@440 275 QString dimensionsQuery
Chris@440 276 (
Chris@440 277 " PREFIX mo: <http://purl.org/ontology/mo/>"
Chris@440 278 " PREFIX af: <http://purl.org/ontology/af/>"
Chris@440 279
Chris@440 280 " SELECT ?dimensions "
Chris@440 281 " FROM <%1> "
Chris@440 282
Chris@440 283 " WHERE { "
Chris@440 284
Chris@440 285 " <%2> af:dimensions ?dimensions . "
Chris@440 286
Chris@440 287 " } "
Chris@440 288 );
Chris@440 289
Chris@440 290 SimpleSPARQLQuery::Value dimensionsValue =
Chris@440 291 SimpleSPARQLQuery::singleResultQuery(dimensionsQuery
Chris@440 292 .arg(m_uristring).arg(featureUri),
Chris@440 293 "dimensions");
Chris@440 294
Chris@440 295 cerr << "Dimensions = \"" << dimensionsValue.value.toStdString() << "\""
Chris@440 296 << endl;
Chris@440 297
Chris@440 298 if (dimensionsValue.value != "") {
Chris@440 299 QStringList dl = dimensionsValue.value.split(" ");
Chris@440 300 if (dl.empty()) dl.push_back(dimensionsValue.value);
Chris@440 301 if (dl.size() > 0) height = dl[0].toInt();
Chris@440 302 if (dl.size() > 1) width = dl[1].toInt();
Chris@440 303 }
Chris@440 304
Chris@440 305 QString queryTemplate
Chris@440 306 (
Chris@440 307 " PREFIX mo: <http://purl.org/ontology/mo/>"
Chris@440 308 " PREFIX af: <http://purl.org/ontology/af/>"
Chris@440 309 " PREFIX tl: <http://purl.org/NET/c4dm/timeline.owl#>"
Chris@440 310
Chris@440 311 " SELECT ?%3 "
Chris@440 312 " FROM <%1> "
Chris@440 313
Chris@440 314 " WHERE { "
Chris@440 315
Chris@440 316 " <%2> mo:time ?time . "
Chris@440 317
Chris@440 318 " ?time a tl:Interval ; "
Chris@440 319 " tl:onTimeLine ?timeline . "
Chris@440 320
Chris@440 321 " ?map tl:rangeTimeLine ?timeline . "
Chris@440 322
Chris@440 323 " ?map tl:%3 ?%3 . "
Chris@440 324
Chris@440 325 " } "
Chris@440 326 );
Chris@440 327
Chris@440 328 // Another laborious workaround for rasqal's failure to handle
Chris@440 329 // multiple optionals properly
Chris@440 330
Chris@440 331 SimpleSPARQLQuery::Value srValue =
Chris@440 332 SimpleSPARQLQuery::singleResultQuery(queryTemplate
Chris@440 333 .arg(m_uristring).arg(featureUri)
Chris@440 334 .arg("sampleRate"),
Chris@440 335 "sampleRate");
Chris@440 336 if (srValue.value != "") {
Chris@440 337 sampleRate = srValue.value.toInt();
Chris@440 338 }
Chris@440 339
Chris@440 340 SimpleSPARQLQuery::Value hopValue =
Chris@440 341 SimpleSPARQLQuery::singleResultQuery(queryTemplate
Chris@440 342 .arg(m_uristring).arg(featureUri)
Chris@440 343 .arg("hopSize"),
Chris@440 344 "hopSize");
Chris@440 345 if (srValue.value != "") {
Chris@440 346 hopSize = hopValue.value.toInt();
Chris@440 347 }
Chris@440 348
Chris@440 349 SimpleSPARQLQuery::Value winValue =
Chris@440 350 SimpleSPARQLQuery::singleResultQuery(queryTemplate
Chris@440 351 .arg(m_uristring).arg(featureUri)
Chris@440 352 .arg("windowLength"),
Chris@440 353 "windowLength");
Chris@440 354 if (winValue.value != "") {
Chris@440 355 windowLength = winValue.value.toInt();
Chris@440 356 }
Chris@440 357
Chris@440 358 cerr << "sr = " << sampleRate << ", hop = " << hopSize << ", win = " << windowLength << endl;
Chris@440 359 }
Chris@440 360
Chris@440 361 void
Chris@440 362 RDFImporterImpl::getDataModelsSparse(std::vector<Model *> &models,
Chris@440 363 ProgressReporter *reporter)
Chris@440 364 {
Chris@439 365 // Our query is intended to retrieve every thing that has a time,
Chris@439 366 // and every feature type and value associated with a thing that
Chris@439 367 // has a time.
Chris@439 368
Chris@439 369 // We will then need to refine this big bag of results into a set
Chris@439 370 // of data models.
Chris@439 371
Chris@439 372 // Results that have different source signals should go into
Chris@439 373 // different models.
Chris@439 374
Chris@439 375 // Results that have different feature types should go into
Chris@439 376 // different models.
Chris@439 377
Chris@439 378 // Results that are sparse should go into different models from
Chris@439 379 // those that are dense (we need to examine the timestamps to
Chris@439 380 // establish this -- if the timestamps are regular, the results
Chris@439 381 // are dense -- so we can't do it as we go along, only after
Chris@439 382 // collecting all results).
Chris@439 383
Chris@439 384 // Timed things that have features associated with them should not
Chris@439 385 // appear directly in any model -- their features should appear
Chris@439 386 // instead -- and these should be different models from those used
Chris@439 387 // for timed things that do not have features.
Chris@439 388
Chris@439 389 // As we load the results, we'll push them into a partially
Chris@439 390 // structured container that maps from source signal (URI as
Chris@439 391 // string) -> feature type (likewise) -> time -> list of values.
Chris@439 392 // If the source signal or feature type is unavailable, the empty
Chris@439 393 // string will do.
Chris@439 394
Chris@449 395 // SourceTypeTimeDurationValueMap m;
Chris@439 396
Chris@449 397 QString prefixes = QString(
Chris@439 398 " PREFIX event: <http://purl.org/NET/c4dm/event.owl#>"
Chris@449 399 " PREFIX tl: <http://purl.org/NET/c4dm/timeline.owl#>"
Chris@439 400 " PREFIX mo: <http://purl.org/ontology/mo/>"
Chris@439 401 " PREFIX af: <http://purl.org/ontology/af/>"
Chris@449 402 " PREFIX rdfs: <http://www.w3.org/2000/01/rdf-schema#>"
Chris@449 403 );
Chris@439 404
Chris@449 405 QString queryString = prefixes + QString(
Chris@449 406
Chris@449 407 " SELECT ?signal_source ?timed_thing ?event_type ?value"
Chris@439 408 " FROM <%1>"
Chris@439 409
Chris@439 410 " WHERE {"
Chris@440 411
Chris@440 412 " ?signal mo:available_as ?signal_source ."
Chris@440 413 " ?signal a mo:Signal ."
Chris@440 414
Chris@439 415 " ?signal mo:time ?interval ."
Chris@449 416 " ?interval tl:onTimeLine ?tl ."
Chris@449 417 " ?time tl:onTimeLine ?tl ."
Chris@449 418 " ?timed_thing event:time ?time ."
Chris@440 419 " ?timed_thing a ?event_type ."
Chris@440 420
Chris@439 421 " OPTIONAL {"
Chris@440 422 " ?timed_thing af:feature ?value"
Chris@439 423 " }"
Chris@439 424 " }"
Chris@439 425
Chris@439 426 ).arg(m_uristring);
Chris@439 427
Chris@449 428 QString timeQueryString = prefixes + QString(
Chris@449 429
Chris@449 430 " SELECT ?time FROM <%1> "
Chris@449 431 " WHERE { "
Chris@449 432 " <%2> event:time ?t . "
Chris@449 433 " ?t tl:at ?time . "
Chris@449 434 " } "
Chris@449 435
Chris@449 436 ).arg(m_uristring);
Chris@449 437
Chris@449 438 QString rangeQueryString = prefixes + QString(
Chris@449 439
Chris@449 440 " SELECT ?time ?duration FROM <%1> "
Chris@449 441 " WHERE { "
Chris@449 442 " <%2> event:time ?t . "
Chris@449 443 " ?t tl:beginsAt ?time . "
Chris@449 444 " ?t tl:duration ?duration . "
Chris@449 445 " } "
Chris@449 446
Chris@449 447 ).arg(m_uristring);
Chris@449 448
Chris@449 449 QString labelQueryString = prefixes + QString(
Chris@449 450
Chris@449 451 " SELECT ?label FROM <%1> "
Chris@449 452 " WHERE { "
Chris@449 453 " <%2> rdfs:label ?label . "
Chris@449 454 " } "
Chris@449 455
Chris@449 456 ).arg(m_uristring);
Chris@449 457
Chris@439 458 SimpleSPARQLQuery query(queryString);
Chris@439 459 query.setProgressReporter(reporter);
Chris@439 460
Chris@439 461 cerr << "Query will be: " << queryString.toStdString() << endl;
Chris@439 462
Chris@439 463 SimpleSPARQLQuery::ResultList results = query.execute();
Chris@439 464
Chris@439 465 if (!query.isOK()) {
Chris@439 466 m_errorString = query.getErrorString();
Chris@440 467 return;
Chris@439 468 }
Chris@439 469
Chris@439 470 if (query.wasCancelled()) {
Chris@439 471 m_errorString = "Query cancelled";
Chris@440 472 return;
Chris@439 473 }
Chris@439 474
Chris@449 475
Chris@449 476
Chris@449 477 /*
Chris@449 478
Chris@449 479 This function is now only used for sparse data (for dense data
Chris@449 480 we would be in getDataModelsDense instead).
Chris@449 481
Chris@449 482 For sparse data, the determining factors in deciding what model
Chris@449 483 to use are: Do the features have values? and Do the features
Chris@449 484 have duration?
Chris@449 485
Chris@449 486 We can run through the results and check off whether we find
Chris@449 487 values and duration for each of the source+type keys, and then
Chris@449 488 run through the source+type keys pushing each of the results
Chris@449 489 into a suitable model.
Chris@449 490
Chris@449 491 Unfortunately, at this point we do not yet have any actual
Chris@449 492 timing data (time/duration) -- just the time URI.
Chris@449 493
Chris@449 494 What we _could_ do is to create one of each type of model at the
Chris@449 495 start, for each of the source+type keys, and then push each
Chris@449 496 feature into the relevant model depending on what we find out
Chris@449 497 about it. Then return only non-empty models.
Chris@449 498
Chris@449 499
Chris@449 500 */
Chris@449 501
Chris@449 502 // Map from signal source to event type to dimensionality to
Chris@449 503 // presence of duration to model ptr. Whee!
Chris@449 504 std::map<QString, std::map<QString, std::map<int, std::map<bool, Model *> > > >
Chris@449 505 modelMap;
Chris@449 506
Chris@439 507 for (int i = 0; i < results.size(); ++i) {
Chris@439 508
Chris@440 509 QString source = results[i]["signal_source"].value;
Chris@449 510 QString type = results[i]["event_type"].value;
Chris@449 511 QString thinguri = results[i]["timed_thing"].value;
Chris@449 512
Chris@449 513 RealTime time;
Chris@449 514 RealTime duration;
Chris@439 515
Chris@449 516 bool haveTime = false;
Chris@449 517 bool haveDuration = false;
Chris@439 518
Chris@449 519 QString label = SimpleSPARQLQuery::singleResultQuery
Chris@449 520 (labelQueryString.arg(thinguri), "label").value;
Chris@449 521
Chris@450 522 SimpleSPARQLQuery rangeQuery(rangeQueryString.arg(thinguri));
Chris@450 523 SimpleSPARQLQuery::ResultList rangeResults = rangeQuery.execute();
Chris@450 524 if (!rangeResults.empty()) {
Chris@450 525 // std::cerr << rangeResults.size() << " range results" << std::endl;
Chris@450 526 time = RealTime::fromXsdDuration
Chris@450 527 (rangeResults[0]["time"].value.toStdString());
Chris@450 528 duration = RealTime::fromXsdDuration
Chris@450 529 (rangeResults[0]["duration"].value.toStdString());
Chris@450 530 // std::cerr << "duration string " << rangeResults[0]["duration"].value.toStdString() << std::endl;
Chris@449 531 haveTime = true;
Chris@450 532 haveDuration = true;
Chris@449 533 } else {
Chris@450 534 QString timestring = SimpleSPARQLQuery::singleResultQuery
Chris@450 535 (timeQueryString.arg(thinguri), "time").value;
Chris@450 536 if (timestring != "") {
Chris@450 537 time = RealTime::fromXsdDuration(timestring.toStdString());
Chris@449 538 haveTime = true;
Chris@449 539 }
Chris@449 540 }
Chris@439 541
Chris@439 542 QString valuestring = results[i]["value"].value;
Chris@449 543 std::vector<float> values;
Chris@449 544
Chris@439 545 if (valuestring != "") {
Chris@449 546 QStringList vsl = valuestring.split(" ", QString::SkipEmptyParts);
Chris@449 547 for (int j = 0; j < vsl.size(); ++j) {
Chris@449 548 bool success = false;
Chris@449 549 float v = vsl[j].toFloat(&success);
Chris@449 550 if (success) values.push_back(v);
Chris@449 551 }
Chris@439 552 }
Chris@439 553
Chris@449 554 int dimensions = 1;
Chris@449 555 if (values.size() == 1) dimensions = 2;
Chris@449 556 else if (values.size() > 1) dimensions = 3;
Chris@449 557
Chris@449 558 Model *model = 0;
Chris@449 559
Chris@449 560 if (modelMap[source][type][dimensions].find(haveDuration) ==
Chris@449 561 modelMap[source][type][dimensions].end()) {
Chris@449 562
Chris@449 563 /*
Chris@449 564 std::cerr << "Creating new model: source = " << source.toStdString()
Chris@449 565 << ", type = " << type.toStdString() << ", dimensions = "
Chris@449 566 << dimensions << ", haveDuration = " << haveDuration
Chris@449 567 << ", time = " << time << ", duration = " << duration
Chris@449 568 << std::endl;
Chris@449 569 */
Chris@449 570
Chris@449 571 if (!haveDuration) {
Chris@449 572
Chris@449 573 if (dimensions == 1) {
Chris@449 574
Chris@449 575 // std::cerr << "SparseOneDimensionalModel" << std::endl;
Chris@449 576 model = new SparseOneDimensionalModel(m_sampleRate, 1, false);
Chris@449 577
Chris@449 578 } else if (dimensions == 2) {
Chris@449 579
Chris@449 580 // std::cerr << "SparseTimeValueModel" << std::endl;
Chris@449 581 model = new SparseTimeValueModel(m_sampleRate, 1, false);
Chris@449 582
Chris@449 583 } else {
Chris@449 584
Chris@449 585 // We don't have a three-dimensional sparse model,
Chris@449 586 // so use a note model. We do have some logic (in
Chris@449 587 // extractStructure below) for guessing whether
Chris@449 588 // this should after all have been a dense model,
Chris@449 589 // but it's hard to apply it because we don't have
Chris@449 590 // all the necessary timing data yet... hmm
Chris@449 591
Chris@449 592 // std::cerr << "NoteModel" << std::endl;
Chris@449 593 model = new NoteModel(m_sampleRate, 1, false);
Chris@449 594 }
Chris@449 595
Chris@449 596 } else { // haveDuration
Chris@449 597
Chris@449 598 if (dimensions == 1 || dimensions == 2) {
Chris@449 599
Chris@449 600 // If our units are frequency or midi pitch, we
Chris@449 601 // should be using a note model... hm
Chris@449 602
Chris@449 603 // std::cerr << "RegionModel" << std::endl;
Chris@449 604 model = new RegionModel(m_sampleRate, 1, false);
Chris@449 605
Chris@449 606 } else {
Chris@449 607
Chris@449 608 // We don't have a three-dimensional sparse model,
Chris@449 609 // so use a note model. We do have some logic (in
Chris@449 610 // extractStructure below) for guessing whether
Chris@449 611 // this should after all have been a dense model,
Chris@449 612 // but it's hard to apply it because we don't have
Chris@449 613 // all the necessary timing data yet... hmm
Chris@449 614
Chris@449 615 // std::cerr << "NoteModel" << std::endl;
Chris@449 616 model = new NoteModel(m_sampleRate, 1, false);
Chris@449 617 }
Chris@449 618 }
Chris@449 619
Chris@449 620 modelMap[source][type][dimensions][haveDuration] = model;
Chris@449 621 models.push_back(model);
Chris@449 622 }
Chris@449 623
Chris@449 624 model = modelMap[source][type][dimensions][haveDuration];
Chris@449 625
Chris@449 626 if (model) {
Chris@449 627 long ftime = RealTime::realTime2Frame(time, m_sampleRate);
Chris@449 628 long fduration = RealTime::realTime2Frame(duration, m_sampleRate);
Chris@449 629 fillModel(model, ftime, fduration, haveDuration, values, label);
Chris@439 630 }
Chris@439 631 }
Chris@449 632
Chris@439 633
Chris@449 634 /*
Chris@449 635 for (SourceTypeTimeDurationValueMap::const_iterator mi = m.begin();
Chris@439 636 mi != m.end(); ++mi) {
Chris@439 637
Chris@439 638 QString source = mi->first;
Chris@439 639
Chris@449 640 for (TypeTimeDurationValueMap::const_iterator ttvi = mi->second.begin();
Chris@439 641 ttvi != mi->second.end(); ++ttvi) {
Chris@439 642
Chris@439 643 QString type = ttvi->first;
Chris@439 644
Chris@439 645 // Now we need to work out what sort of model to use for
Chris@439 646 // this source/type combination. Ultimately we'll
Chris@439 647 // hopefully be able to map directly from the type to the
Chris@439 648 // model on the basis of known structures for the types,
Chris@439 649 // but we also want to be able to handle untyped data
Chris@439 650 // according to its apparent structure so let's do that
Chris@439 651 // first.
Chris@439 652
Chris@439 653 bool sparse = false;
Chris@439 654 int minValueCount = 0, maxValueCount = 0;
Chris@439 655
Chris@439 656 extractStructure(ttvi->second, sparse, minValueCount, maxValueCount);
Chris@439 657
Chris@439 658 cerr << "For source \"" << source.toStdString() << "\", type \""
Chris@439 659 << type.toStdString() << "\" we have sparse = " << sparse
Chris@439 660 << ", min value count = " << minValueCount << ", max = "
Chris@439 661 << maxValueCount << endl;
Chris@439 662
Chris@439 663 // Model allocations:
Chris@439 664 //
Chris@439 665 // Sparse, no values: SparseOneDimensionalModel
Chris@439 666 //
Chris@439 667 // Sparse, always 1 value: SparseTimeValueModel
Chris@439 668 //
Chris@439 669 // Sparse, > 1 value: No standard model for this. If
Chris@439 670 // there are always 2 values, perhaps hack it into
Chris@439 671 // NoteModel for now? Or always use SparseTimeValueModel
Chris@439 672 // and discard all but the first value.
Chris@439 673 //
Chris@439 674 // Dense, no values: Meaningless; no suitable model
Chris@439 675 //
Chris@439 676 // Dense, > 0 values: EditableDenseThreeDimensionalModel
Chris@439 677 //
Chris@439 678 // These should just be our fallback positions; we want to
Chris@439 679 // be reading semantic data from the RDF in order to pick
Chris@439 680 // the right model directly
Chris@439 681
Chris@439 682 enum { SODM, STVM, EDTDM } modelType = SODM;
Chris@439 683
Chris@439 684 if (sparse) {
Chris@439 685 if (maxValueCount == 0) {
Chris@439 686 modelType = SODM;
Chris@439 687 } else if (minValueCount == 1 && maxValueCount == 1) {
Chris@439 688 modelType = STVM;
Chris@439 689 } else {
Chris@439 690 cerr << "WARNING: No suitable model available for sparse data with between " << minValueCount << " and " << maxValueCount << " values" << endl;
Chris@439 691 modelType = STVM;
Chris@439 692 }
Chris@439 693 } else {
Chris@439 694 if (maxValueCount == 0) {
Chris@439 695 cerr << "WARNING: Dense data set with no values is not meaningful, skipping" << endl;
Chris@439 696 continue;
Chris@439 697 } else {
Chris@439 698 modelType = EDTDM;
Chris@439 699 }
Chris@439 700 }
Chris@439 701
Chris@439 702 //!!! set model name &c
Chris@439 703
Chris@439 704 if (modelType == SODM) {
Chris@439 705
Chris@439 706 SparseOneDimensionalModel *model =
Chris@439 707 new SparseOneDimensionalModel(m_sampleRate, 1, false);
Chris@439 708
Chris@439 709 fillModel(model, ttvi->second);
Chris@439 710 models.push_back(model);
Chris@439 711
Chris@439 712 } else if (modelType == STVM) {
Chris@439 713
Chris@439 714 SparseTimeValueModel *model =
Chris@439 715 new SparseTimeValueModel(m_sampleRate, 1, false);
Chris@439 716
Chris@439 717 fillModel(model, ttvi->second);
Chris@439 718 models.push_back(model);
Chris@439 719
Chris@439 720 } else {
Chris@439 721
Chris@439 722 EditableDenseThreeDimensionalModel *model =
Chris@439 723 new EditableDenseThreeDimensionalModel(m_sampleRate, 1, 0,
Chris@439 724 false);
Chris@439 725
Chris@439 726 fillModel(model, ttvi->second);
Chris@439 727 models.push_back(model);
Chris@439 728 }
Chris@439 729 }
Chris@439 730 }
Chris@449 731 */
Chris@439 732 }
Chris@439 733
Chris@449 734 /*
Chris@439 735 void
Chris@449 736 RDFImporterImpl::extractStructure(const TimeDurationValueMap &tvm,
Chris@439 737 bool &sparse,
Chris@439 738 int &minValueCount,
Chris@439 739 int &maxValueCount)
Chris@439 740 {
Chris@439 741 // These are floats intentionally rather than RealTime --
Chris@439 742 // see logic for handling rounding error below
Chris@439 743 float firstTime = 0.f;
Chris@439 744 float timeStep = 0.f;
Chris@439 745 bool haveTimeStep = false;
Chris@439 746
Chris@449 747 for (TimeDurationValueMap::const_iterator tvi = tvm.begin(); tvi != tvm.end(); ++tvi) {
Chris@439 748
Chris@439 749 RealTime time = tvi->first;
Chris@439 750 int valueCount = tvi->second.size();
Chris@439 751
Chris@439 752 if (tvi == tvm.begin()) {
Chris@439 753
Chris@439 754 minValueCount = valueCount;
Chris@439 755 maxValueCount = valueCount;
Chris@439 756
Chris@439 757 firstTime = time.toDouble();
Chris@439 758
Chris@439 759 } else {
Chris@439 760
Chris@439 761 if (valueCount < minValueCount) minValueCount = valueCount;
Chris@439 762 if (valueCount > maxValueCount) maxValueCount = valueCount;
Chris@439 763
Chris@439 764 if (!haveTimeStep) {
Chris@439 765 timeStep = time.toDouble() - firstTime;
Chris@439 766 if (timeStep == 0.f) sparse = true;
Chris@439 767 haveTimeStep = true;
Chris@439 768 } else if (!sparse) {
Chris@439 769 // test whether this time is within
Chris@439 770 // rounding-error range of being an integer
Chris@439 771 // multiple of some constant away from the
Chris@439 772 // first time
Chris@439 773 float timeAsFloat = time.toDouble();
Chris@439 774 int count = int((timeAsFloat - firstTime) / timeStep + 0.5);
Chris@439 775 float expected = firstTime + (timeStep * count);
Chris@439 776 if (fabsf(expected - timeAsFloat) > 1e-6) {
Chris@439 777 cerr << "Event at " << timeAsFloat << " is not evenly spaced -- would expect it to be " << expected << " for a spacing of " << count << " * " << timeStep << endl;
Chris@439 778 sparse = true;
Chris@439 779 }
Chris@439 780 }
Chris@439 781 }
Chris@439 782 }
Chris@439 783 }
Chris@449 784 */
Chris@439 785
Chris@439 786 void
Chris@449 787 RDFImporterImpl::fillModel(Model *model,
Chris@449 788 long ftime,
Chris@449 789 long fduration,
Chris@449 790 bool haveDuration,
Chris@449 791 std::vector<float> &values,
Chris@449 792 QString label)
Chris@449 793 {
Chris@449 794 SparseOneDimensionalModel *sodm =
Chris@449 795 dynamic_cast<SparseOneDimensionalModel *>(model);
Chris@449 796 if (sodm) {
Chris@449 797 SparseOneDimensionalModel::Point point(ftime, label);
Chris@449 798 sodm->addPoint(point);
Chris@449 799 return;
Chris@449 800 }
Chris@449 801
Chris@449 802 SparseTimeValueModel *stvm =
Chris@449 803 dynamic_cast<SparseTimeValueModel *>(model);
Chris@449 804 if (stvm) {
Chris@449 805 SparseTimeValueModel::Point point
Chris@449 806 (ftime, values.empty() ? 0.f : values[0], label);
Chris@449 807 stvm->addPoint(point);
Chris@449 808 return;
Chris@449 809 }
Chris@449 810
Chris@449 811 NoteModel *nm =
Chris@449 812 dynamic_cast<NoteModel *>(model);
Chris@449 813 if (nm) {
Chris@449 814 if (haveDuration) {
Chris@449 815 float value = 0.f, level = 1.f;
Chris@449 816 if (!values.empty()) {
Chris@449 817 value = values[0];
Chris@449 818 if (values.size() > 1) {
Chris@449 819 level = values[1];
Chris@449 820 }
Chris@449 821 }
Chris@449 822 NoteModel::Point point(ftime, value, fduration, level, label);
Chris@449 823 nm->addPoint(point);
Chris@449 824 } else {
Chris@449 825 float value = 0.f, duration = 1.f, level = 1.f;
Chris@449 826 if (!values.empty()) {
Chris@449 827 value = values[0];
Chris@449 828 if (values.size() > 1) {
Chris@449 829 duration = values[1];
Chris@449 830 if (values.size() > 2) {
Chris@449 831 level = values[2];
Chris@449 832 }
Chris@449 833 }
Chris@449 834 }
Chris@449 835 NoteModel::Point point(ftime, value, duration, level, label);
Chris@449 836 nm->addPoint(point);
Chris@449 837 }
Chris@449 838 return;
Chris@449 839 }
Chris@449 840
Chris@449 841 RegionModel *rm =
Chris@449 842 dynamic_cast<RegionModel *>(model);
Chris@449 843 if (rm) {
Chris@449 844 if (haveDuration) {
Chris@449 845 RegionModel::Point point
Chris@449 846 (ftime, values.empty() ? 0.f : values[0], fduration, label);
Chris@449 847 rm->addPoint(point);
Chris@449 848 } else {
Chris@449 849 // This won't actually happen -- we only create region models
Chris@449 850 // if we do have duration -- but just for completeness
Chris@449 851 float value = 0.f, duration = 1.f;
Chris@449 852 if (!values.empty()) {
Chris@449 853 value = values[0];
Chris@449 854 if (values.size() > 1) {
Chris@449 855 duration = values[1];
Chris@449 856 }
Chris@449 857 }
Chris@449 858 RegionModel::Point point(ftime, value, duration, label);
Chris@449 859 rm->addPoint(point);
Chris@449 860 }
Chris@449 861 return;
Chris@449 862 }
Chris@449 863
Chris@449 864 std::cerr << "WARNING: RDFImporterImpl::fillModel: Unknown or unexpected model type" << std::endl;
Chris@449 865 return;
Chris@449 866 }
Chris@449 867
Chris@449 868
Chris@449 869 /*
Chris@449 870 void
Chris@439 871 RDFImporterImpl::fillModel(SparseOneDimensionalModel *model,
Chris@449 872 const TimeDurationValueMap &tvm)
Chris@439 873 {
Chris@439 874 //!!! labels &c not yet handled
Chris@439 875
Chris@449 876 for (TimeDurationValueMap::const_iterator tvi = tvm.begin();
Chris@439 877 tvi != tvm.end(); ++tvi) {
Chris@439 878
Chris@439 879 RealTime time = tvi->first;
Chris@439 880 long frame = RealTime::realTime2Frame(time, m_sampleRate);
Chris@439 881
Chris@439 882 SparseOneDimensionalModel::Point point(frame);
Chris@439 883
Chris@439 884 model->addPoint(point);
Chris@439 885 }
Chris@439 886 }
Chris@439 887
Chris@439 888 void
Chris@439 889 RDFImporterImpl::fillModel(SparseTimeValueModel *model,
Chris@449 890 const TimeDurationValueMap &tvm)
Chris@439 891 {
Chris@439 892 //!!! labels &c not yet handled
Chris@439 893
Chris@449 894 for (TimeDurationValueMap::const_iterator tvi = tvm.begin();
Chris@439 895 tvi != tvm.end(); ++tvi) {
Chris@439 896
Chris@439 897 RealTime time = tvi->first;
Chris@439 898 long frame = RealTime::realTime2Frame(time, m_sampleRate);
Chris@439 899
Chris@439 900 float value = 0.f;
Chris@449 901 if (!tvi->second.empty()) value = *tvi->second.begin()->second;
Chris@439 902
Chris@439 903 SparseTimeValueModel::Point point(frame, value, "");
Chris@439 904
Chris@439 905 model->addPoint(point);
Chris@439 906 }
Chris@439 907 }
Chris@439 908
Chris@439 909 void
Chris@439 910 RDFImporterImpl::fillModel(EditableDenseThreeDimensionalModel *model,
Chris@449 911 const TimeDurationValueMap &tvm)
Chris@439 912 {
Chris@439 913 //!!! labels &c not yet handled
Chris@439 914
Chris@439 915 //!!! start time offset not yet handled
Chris@439 916
Chris@439 917 size_t col = 0;
Chris@439 918
Chris@449 919 for (TimeDurationValueMap::const_iterator tvi = tvm.begin();
Chris@439 920 tvi != tvm.end(); ++tvi) {
Chris@439 921
Chris@449 922 model->setColumn(col++, tvi->second.second);
Chris@439 923 }
Chris@439 924 }
Chris@439 925
Chris@449 926 */