annotate framework/SVFileReader.cpp @ 771:1d6cca5a5621 pitch-align

Allow use of proper sparse models (i.e. retaining event time info) in alignment; use this to switch to note alignment, which is what we have most recently been doing in the external program. Not currently producing correct results, though
author Chris Cannam
date Fri, 29 May 2020 17:39:02 +0100
parents 3e930ea94db6
children
rev   line source
Chris@45 1 /* -*- c-basic-offset: 4 indent-tabs-mode: nil -*- vi:set ts=8 sts=4 sw=4: */
Chris@45 2
Chris@45 3 /*
Chris@45 4 Sonic Visualiser
Chris@45 5 An audio file viewer and annotation editor.
Chris@45 6 Centre for Digital Music, Queen Mary, University of London.
Chris@45 7 This file copyright 2006 Chris Cannam and QMUL.
Chris@45 8
Chris@45 9 This program is free software; you can redistribute it and/or
Chris@45 10 modify it under the terms of the GNU General Public License as
Chris@45 11 published by the Free Software Foundation; either version 2 of the
Chris@45 12 License, or (at your option) any later version. See the file
Chris@45 13 COPYING included with this distribution for more information.
Chris@45 14 */
Chris@45 15
Chris@45 16 #include "SVFileReader.h"
Chris@45 17
Chris@45 18 #include "layer/Layer.h"
Chris@45 19 #include "view/View.h"
Chris@45 20 #include "base/PlayParameters.h"
Chris@45 21 #include "base/PlayParameterRepository.h"
Chris@116 22 #include "base/Preferences.h"
Chris@45 23
Chris@45 24 #include "data/fileio/AudioFileReaderFactory.h"
Chris@45 25 #include "data/fileio/FileSource.h"
Chris@45 26
Chris@170 27 #include "data/fileio/FileFinder.h"
Chris@109 28
Chris@479 29 #include "data/model/ReadOnlyWaveFileModel.h"
Chris@45 30 #include "data/model/EditableDenseThreeDimensionalModel.h"
Chris@45 31 #include "data/model/SparseOneDimensionalModel.h"
Chris@45 32 #include "data/model/SparseTimeValueModel.h"
Chris@45 33 #include "data/model/NoteModel.h"
Chris@137 34 #include "data/model/RegionModel.h"
Chris@45 35 #include "data/model/TextModel.h"
Chris@45 36 #include "data/model/ImageModel.h"
Chris@710 37 #include "data/model/BoxModel.h"
Chris@111 38 #include "data/model/AlignmentModel.h"
Chris@629 39 #include "data/model/AggregateWaveModel.h"
Chris@45 40
Chris@106 41 #include "transform/TransformFactory.h"
Chris@72 42
Chris@45 43 #include "view/Pane.h"
Chris@45 44
Chris@109 45 #include "widgets/ProgressDialog.h"
Chris@109 46
Chris@45 47 #include "Document.h"
Chris@45 48
Chris@45 49 #include <QString>
Chris@45 50 #include <QMessageBox>
Chris@45 51 #include <QFileDialog>
Chris@45 52
Chris@45 53 #include <iostream>
Chris@45 54
Chris@45 55 SVFileReader::SVFileReader(Document *document,
Chris@595 56 SVFileReaderPaneCallback &callback,
Chris@45 57 QString location) :
Chris@45 58 m_document(document),
Chris@45 59 m_paneCallback(callback),
Chris@45 60 m_location(location),
Chris@636 61 m_currentPane(nullptr),
Chris@685 62 m_currentDataset(XmlExportable::NO_ID),
Chris@636 63 m_currentLayer(nullptr),
Chris@685 64 m_pendingDerivedModel(XmlExportable::NO_ID),
Chris@410 65 m_currentTransformChannel(0),
Chris@410 66 m_currentTransformIsNewStyle(true),
Chris@45 67 m_datasetSeparator(" "),
Chris@45 68 m_inRow(false),
Chris@45 69 m_inLayer(false),
Chris@45 70 m_inView(false),
Chris@410 71 m_inData(false),
Chris@410 72 m_inSelections(false),
Chris@45 73 m_rowNumber(0),
Chris@45 74 m_ok(false)
Chris@45 75 {
Chris@45 76 }
Chris@45 77
Chris@45 78 void
Chris@45 79 SVFileReader::parse(const QString &xmlData)
Chris@45 80 {
Chris@45 81 QXmlInputSource inputSource;
Chris@45 82 inputSource.setData(xmlData);
Chris@45 83 parse(inputSource);
Chris@45 84 }
Chris@45 85
Chris@45 86 void
Chris@45 87 SVFileReader::parse(QXmlInputSource &inputSource)
Chris@45 88 {
Chris@45 89 QXmlSimpleReader reader;
Chris@45 90 reader.setContentHandler(this);
Chris@45 91 reader.setErrorHandler(this);
Chris@45 92 m_ok = reader.parse(inputSource);
Chris@45 93 }
Chris@45 94
Chris@45 95 bool
Chris@45 96 SVFileReader::isOK()
Chris@45 97 {
Chris@45 98 return m_ok;
Chris@45 99 }
Chris@595 100
Chris@45 101 SVFileReader::~SVFileReader()
Chris@45 102 {
Chris@45 103 if (!m_awaitingDatasets.empty()) {
Chris@601 104 SVCERR << "WARNING: SV-XML: File ended with "
Chris@595 105 << m_awaitingDatasets.size() << " unfilled model dataset(s)"
Chris@595 106 << endl;
Chris@45 107 }
Chris@45 108
Chris@685 109 std::set<ModelId> unaddedModels;
Chris@45 110
Chris@685 111 for (auto i: m_models) {
Chris@685 112 if (m_addedModels.find(i.second) == m_addedModels.end()) {
Chris@685 113 unaddedModels.insert(i.second);
Chris@595 114 }
Chris@45 115 }
Chris@45 116
Chris@45 117 if (!unaddedModels.empty()) {
Chris@601 118 SVCERR << "WARNING: SV-XML: File contained "
Chris@685 119 << unaddedModels.size() << " unused models"
Chris@685 120 << endl;
Chris@685 121 for (auto m: unaddedModels) {
Chris@685 122 ModelById::release(m);
Chris@595 123 }
Chris@685 124 }
Chris@685 125
Chris@685 126 if (!m_paths.empty()) {
Chris@685 127 SVCERR << "WARNING: SV-XML: File contained "
Chris@685 128 << m_paths.size() << " unused paths"
Chris@685 129 << endl;
Chris@685 130 for (auto p: m_paths) {
Chris@685 131 delete p.second;
Chris@685 132 }
Chris@685 133 }
Chris@45 134 }
Chris@45 135
Chris@45 136 bool
Chris@45 137 SVFileReader::startElement(const QString &, const QString &,
Chris@595 138 const QString &qName,
Chris@595 139 const QXmlAttributes &attributes)
Chris@45 140 {
Chris@45 141 QString name = qName.toLower();
Chris@45 142
Chris@45 143 bool ok = false;
Chris@45 144
Chris@45 145 // Valid element names:
Chris@45 146 //
Chris@45 147 // sv
Chris@45 148 // data
Chris@45 149 // dataset
Chris@45 150 // display
Chris@45 151 // derivation
Chris@45 152 // playparameters
Chris@45 153 // layer
Chris@45 154 // model
Chris@45 155 // point
Chris@45 156 // row
Chris@45 157 // view
Chris@45 158 // window
Chris@72 159 // plugin
Chris@72 160 // transform
Chris@72 161 // selections
Chris@72 162 // selection
Chris@72 163 // measurement
Chris@45 164
Chris@45 165 if (name == "sv") {
Chris@45 166
Chris@595 167 // nothing needed
Chris@595 168 ok = true;
Chris@45 169
Chris@45 170 } else if (name == "data") {
Chris@45 171
Chris@595 172 // nothing needed
Chris@595 173 m_inData = true;
Chris@595 174 ok = true;
Chris@45 175
Chris@45 176 } else if (name == "display") {
Chris@45 177
Chris@595 178 // nothing needed
Chris@595 179 ok = true;
Chris@45 180
Chris@45 181 } else if (name == "window") {
Chris@45 182
Chris@595 183 ok = readWindow(attributes);
Chris@45 184
Chris@45 185 } else if (name == "model") {
Chris@45 186
Chris@595 187 ok = readModel(attributes);
Chris@45 188
Chris@45 189 } else if (name == "dataset") {
Chris@595 190
Chris@595 191 ok = readDatasetStart(attributes);
Chris@45 192
Chris@45 193 } else if (name == "bin") {
Chris@595 194
Chris@595 195 ok = addBinToDataset(attributes);
Chris@45 196
Chris@45 197 } else if (name == "point") {
Chris@595 198
Chris@595 199 ok = addPointToDataset(attributes);
Chris@45 200
Chris@45 201 } else if (name == "row") {
Chris@45 202
Chris@595 203 ok = addRowToDataset(attributes);
Chris@45 204
Chris@45 205 } else if (name == "layer") {
Chris@45 206
Chris@45 207 addUnaddedModels(); // all models must be specified before first layer
Chris@595 208 ok = readLayer(attributes);
Chris@45 209
Chris@45 210 } else if (name == "view") {
Chris@45 211
Chris@595 212 m_inView = true;
Chris@595 213 ok = readView(attributes);
Chris@45 214
Chris@45 215 } else if (name == "derivation") {
Chris@45 216
Chris@629 217 makeAggregateModels(); // must be done before derivations that use them
Chris@595 218 ok = readDerivation(attributes);
Chris@45 219
Chris@45 220 } else if (name == "playparameters") {
Chris@45 221
Chris@45 222 ok = readPlayParameters(attributes);
Chris@45 223
Chris@45 224 } else if (name == "plugin") {
Chris@45 225
Chris@595 226 ok = readPlugin(attributes);
Chris@45 227
Chris@45 228 } else if (name == "selections") {
Chris@45 229
Chris@595 230 m_inSelections = true;
Chris@595 231 ok = true;
Chris@45 232
Chris@45 233 } else if (name == "selection") {
Chris@45 234
Chris@595 235 ok = readSelection(attributes);
Chris@45 236
Chris@45 237 } else if (name == "measurement") {
Chris@45 238
Chris@45 239 ok = readMeasurement(attributes);
Chris@45 240
Chris@72 241 } else if (name == "transform") {
Chris@72 242
Chris@72 243 ok = readTransform(attributes);
Chris@72 244
Chris@72 245 } else if (name == "parameter") {
Chris@72 246
Chris@72 247 ok = readParameter(attributes);
Chris@72 248
Chris@45 249 } else {
Chris@601 250 SVCERR << "WARNING: SV-XML: Unexpected element \""
Chris@294 251 << name << "\"" << endl;
Chris@45 252 }
Chris@45 253
Chris@45 254 if (!ok) {
Chris@601 255 SVCERR << "WARNING: SV-XML: Failed to completely process element \""
Chris@595 256 << name << "\"" << endl;
Chris@45 257 }
Chris@45 258
Chris@45 259 return true;
Chris@45 260 }
Chris@45 261
Chris@45 262 bool
Chris@45 263 SVFileReader::characters(const QString &text)
Chris@45 264 {
Chris@45 265 bool ok = false;
Chris@45 266
Chris@45 267 if (m_inRow) {
Chris@595 268 ok = readRowData(text);
Chris@595 269 if (!ok) {
Chris@601 270 SVCERR << "WARNING: SV-XML: Failed to read row data content for row " << m_rowNumber << endl;
Chris@595 271 }
Chris@45 272 }
Chris@45 273
Chris@45 274 return true;
Chris@45 275 }
Chris@45 276
Chris@45 277 bool
Chris@45 278 SVFileReader::endElement(const QString &, const QString &,
Chris@595 279 const QString &qName)
Chris@45 280 {
Chris@45 281 QString name = qName.toLower();
Chris@45 282
Chris@45 283 if (name == "dataset") {
Chris@45 284
Chris@685 285 if (m_currentDataset != XmlExportable::NO_ID) {
Chris@595 286
Chris@595 287 bool foundInAwaiting = false;
Chris@45 288
Chris@685 289 for (auto i: m_awaitingDatasets) {
Chris@685 290 if (i.second == m_currentDataset) {
Chris@685 291 m_awaitingDatasets.erase(i.first);
Chris@595 292 foundInAwaiting = true;
Chris@595 293 break;
Chris@595 294 }
Chris@595 295 }
Chris@45 296
Chris@595 297 if (!foundInAwaiting) {
Chris@601 298 SVCERR << "WARNING: SV-XML: Dataset precedes model, or no model uses dataset" << endl;
Chris@595 299 }
Chris@595 300 }
Chris@45 301
Chris@685 302 m_currentDataset = XmlExportable::NO_ID;
Chris@45 303
Chris@45 304 } else if (name == "data") {
Chris@45 305
Chris@45 306 addUnaddedModels();
Chris@595 307 m_inData = false;
Chris@45 308
Chris@45 309 } else if (name == "derivation") {
Chris@45 310
Chris@685 311 if (m_currentDerivedModel.isNone()) {
Chris@685 312 if (m_pendingDerivedModel == XmlExportable::NO_ID) {
Chris@685 313 SVCERR << "WARNING: SV-XML: No valid output model id "
Chris@685 314 << "for derivation" << endl;
Chris@685 315 } else if (haveModel(m_pendingDerivedModel)) {
Chris@601 316 SVCERR << "WARNING: SV-XML: Derivation has existing model "
Chris@685 317 << m_pendingDerivedModel
Chris@685 318 << " as target, not regenerating" << endl;
Chris@45 319 } else {
Chris@78 320 QString message;
Chris@685 321 m_currentDerivedModel = m_models[m_pendingDerivedModel] =
Chris@72 322 m_document->addDerivedModel
Chris@72 323 (m_currentTransform,
Chris@72 324 ModelTransformer::Input(m_currentTransformSource,
Chris@78 325 m_currentTransformChannel),
Chris@78 326 message);
Chris@685 327 if (m_currentDerivedModel.isNone()) {
Chris@79 328 emit modelRegenerationFailed(tr("(derived model in SV-XML)"),
Chris@79 329 m_currentTransform.getIdentifier(),
Chris@79 330 message);
Chris@79 331 } else if (message != "") {
Chris@79 332 emit modelRegenerationWarning(tr("(derived model in SV-XML)"),
Chris@79 333 m_currentTransform.getIdentifier(),
Chris@79 334 message);
Chris@79 335 }
Chris@45 336 }
Chris@45 337 } else {
Chris@329 338 m_document->addAlreadyDerivedModel
Chris@72 339 (m_currentTransform,
Chris@72 340 ModelTransformer::Input(m_currentTransformSource,
Chris@72 341 m_currentTransformChannel),
Chris@72 342 m_currentDerivedModel);
Chris@45 343 }
Chris@45 344
Chris@45 345 m_addedModels.insert(m_currentDerivedModel);
Chris@685 346 m_currentDerivedModel = {};
Chris@685 347 m_pendingDerivedModel = XmlExportable::NO_ID;
Chris@685 348 m_currentTransformSource = {};
Chris@72 349 m_currentTransform = Transform();
Chris@72 350 m_currentTransformChannel = -1;
Chris@45 351
Chris@45 352 } else if (name == "row") {
Chris@595 353 m_inRow = false;
Chris@45 354 } else if (name == "layer") {
Chris@45 355 m_inLayer = false;
Chris@45 356 } else if (name == "view") {
Chris@595 357 m_inView = false;
Chris@45 358 } else if (name == "selections") {
Chris@595 359 m_inSelections = false;
Chris@45 360 } else if (name == "playparameters") {
Chris@686 361 m_currentPlayParameters = {};
Chris@45 362 }
Chris@45 363
Chris@45 364 return true;
Chris@45 365 }
Chris@45 366
Chris@45 367 bool
Chris@45 368 SVFileReader::error(const QXmlParseException &exception)
Chris@45 369 {
Chris@45 370 m_errorString =
Chris@595 371 QString("ERROR: SV-XML: %1 at line %2, column %3")
Chris@595 372 .arg(exception.message())
Chris@595 373 .arg(exception.lineNumber())
Chris@595 374 .arg(exception.columnNumber());
Chris@601 375 SVCERR << m_errorString << endl;
Chris@45 376 return QXmlDefaultHandler::error(exception);
Chris@45 377 }
Chris@45 378
Chris@45 379 bool
Chris@45 380 SVFileReader::fatalError(const QXmlParseException &exception)
Chris@45 381 {
Chris@45 382 m_errorString =
Chris@595 383 QString("FATAL ERROR: SV-XML: %1 at line %2, column %3")
Chris@595 384 .arg(exception.message())
Chris@595 385 .arg(exception.lineNumber())
Chris@595 386 .arg(exception.columnNumber());
Chris@601 387 SVCERR << m_errorString << endl;
Chris@45 388 return QXmlDefaultHandler::fatalError(exception);
Chris@45 389 }
Chris@45 390
Chris@45 391
Chris@595 392 #define READ_MANDATORY(TYPE, NAME, CONVERSION) \
Chris@45 393 TYPE NAME = attributes.value(#NAME).trimmed().CONVERSION(&ok); \
Chris@45 394 if (!ok) { \
Chris@601 395 SVCERR << "WARNING: SV-XML: Missing or invalid mandatory " #TYPE " attribute \"" #NAME "\"" << endl; \
Chris@595 396 return false; \
Chris@45 397 }
Chris@45 398
Chris@45 399 bool
Chris@576 400 SVFileReader::readWindow(const QXmlAttributes &)
Chris@45 401 {
Chris@576 402 // The window element contains window dimensions, which we used to
Chris@576 403 // read and size the window accordingly. This was a Bad Idea [tm]
Chris@576 404 // and we now do nothing instead. See #1769 Loading window
Chris@576 405 // dimensions from session file is a really bad idea
Chris@45 406 return true;
Chris@45 407 }
Chris@45 408
Chris@45 409 void
Chris@629 410 SVFileReader::makeAggregateModels()
Chris@629 411 {
Chris@685 412 std::map<ExportId, PendingAggregateRec> stillPending;
Chris@629 413
Chris@629 414 for (auto p: m_pendingAggregates) {
Chris@629 415
Chris@629 416 int id = p.first;
Chris@629 417 const PendingAggregateRec &rec = p.second;
Chris@629 418 bool skip = false;
Chris@629 419
Chris@629 420 AggregateWaveModel::ChannelSpecList specs;
Chris@685 421 for (ExportId componentId: rec.components) {
Chris@629 422 bool found = false;
Chris@629 423 if (m_models.find(componentId) != m_models.end()) {
Chris@685 424 ModelId modelId = m_models[componentId];
Chris@685 425 auto rs = ModelById::getAs<RangeSummarisableTimeValueModel>
Chris@685 426 (modelId);
Chris@629 427 if (rs) {
Chris@629 428 specs.push_back(AggregateWaveModel::ModelChannelSpec
Chris@685 429 (modelId, -1));
Chris@629 430 found = true;
Chris@685 431 } else {
Chris@685 432 SVDEBUG << "SVFileReader::makeAggregateModels: "
Chris@685 433 << "Component model id " << componentId
Chris@685 434 << "in aggregate model id " << id
Chris@685 435 << "does not appear to be convertible to "
Chris@685 436 << "RangeSummarisableTimeValueModel"
Chris@685 437 << endl;
Chris@629 438 }
Chris@629 439 }
Chris@629 440 if (!found) {
Chris@685 441 SVDEBUG << "SVFileReader::makeAggregateModels: "
Chris@629 442 << "Unknown component model id "
Chris@629 443 << componentId << " in aggregate model id " << id
Chris@629 444 << ", hoping we won't be needing it just yet"
Chris@629 445 << endl;
Chris@629 446 skip = true;
Chris@629 447 }
Chris@629 448 }
Chris@629 449
Chris@629 450 if (skip) {
Chris@629 451 stillPending[id] = rec;
Chris@629 452 } else {
Chris@685 453 auto model = std::make_shared<AggregateWaveModel>(specs);
Chris@629 454 model->setObjectName(rec.name);
Chris@685 455 m_models[id] = ModelById::add(model);
Chris@629 456
Chris@685 457 SVDEBUG << "SVFileReader::makeAggregateModels: created aggregate "
Chris@685 458 << "model id " << id << " with " << specs.size()
Chris@685 459 << " components" << endl;
Chris@629 460 }
Chris@629 461 }
Chris@629 462
Chris@629 463 m_pendingAggregates = stillPending;
Chris@629 464 }
Chris@629 465
Chris@629 466 void
Chris@45 467 SVFileReader::addUnaddedModels()
Chris@45 468 {
Chris@629 469 makeAggregateModels();
Chris@662 470
Chris@685 471 for (auto i: m_models) {
Chris@662 472
Chris@685 473 ModelId modelId = i.second;
Chris@685 474
Chris@685 475 if (m_addedModels.find(modelId) != m_addedModels.end()) {
Chris@662 476 // already added this one
Chris@662 477 continue;
Chris@45 478 }
Chris@685 479
Chris@693 480 m_document->addNonDerivedModel(modelId);
Chris@662 481
Chris@693 482 // make a note of all models that have been added to the
Chris@693 483 // document, so they don't get released by our own destructor
Chris@685 484 m_addedModels.insert(modelId);
Chris@45 485 }
Chris@45 486 }
Chris@45 487
Chris@45 488 bool
Chris@45 489 SVFileReader::readModel(const QXmlAttributes &attributes)
Chris@45 490 {
Chris@45 491 bool ok = false;
Chris@45 492
Chris@45 493 READ_MANDATORY(int, id, toInt);
Chris@45 494
Chris@45 495 if (haveModel(id)) {
Chris@601 496 SVCERR << "WARNING: SV-XML: Ignoring duplicate model id " << id
Chris@595 497 << endl;
Chris@595 498 return false;
Chris@45 499 }
Chris@45 500
Chris@45 501 QString name = attributes.value("name");
Chris@45 502
Chris@233 503 SVDEBUG << "SVFileReader::readModel: model name \"" << name << "\"" << endl;
Chris@45 504
Chris@436 505 READ_MANDATORY(double, sampleRate, toDouble);
Chris@45 506
Chris@45 507 QString type = attributes.value("type").trimmed();
Chris@362 508 bool isMainModel = (attributes.value("mainModel").trimmed() == "true");
Chris@45 509
Chris@45 510 if (type == "wavefile") {
Chris@595 511
Chris@636 512 WaveFileModel *model = nullptr;
Chris@45 513 FileFinder *ff = FileFinder::getInstance();
Chris@45 514 QString originalPath = attributes.value("file");
Chris@45 515 QString path = ff->find(FileFinder::AudioFile,
Chris@45 516 originalPath, m_location);
Chris@45 517
Chris@248 518 SVDEBUG << "Wave file originalPath = " << originalPath << ", path = "
Chris@248 519 << path << endl;
Chris@248 520
Chris@109 521 ProgressDialog dialog(tr("Opening file or URL..."), true, 2000);
Chris@109 522 FileSource file(path, &dialog);
Chris@45 523 file.waitForStatus();
Chris@45 524
Chris@45 525 if (!file.isOK()) {
Chris@601 526 SVCERR << "SVFileReader::readModel: Failed to retrieve file \"" << path << "\" for wave file model: " << file.getErrorString() << endl;
Chris@45 527 } else if (!file.isAvailable()) {
Chris@601 528 SVCERR << "SVFileReader::readModel: Failed to retrieve file \"" << path << "\" for wave file model: Source unavailable" << endl;
Chris@45 529 } else {
Chris@45 530
Chris@45 531 file.waitForData();
Chris@116 532
Chris@436 533 sv_samplerate_t rate = sampleRate;
Chris@116 534
Chris@360 535 if (Preferences::getInstance()->getFixedSampleRate() != 0) {
Chris@360 536 rate = Preferences::getInstance()->getFixedSampleRate();
Chris@362 537 } else if (rate == 0 &&
Chris@362 538 !isMainModel &&
Chris@360 539 Preferences::getInstance()->getResampleOnLoad()) {
Chris@685 540 auto mm = ModelById::getAs<WaveFileModel>
Chris@685 541 (m_document->getMainModel());
Chris@116 542 if (mm) rate = mm->getSampleRate();
Chris@116 543 }
Chris@116 544
Chris@479 545 model = new ReadOnlyWaveFileModel(file, rate);
Chris@45 546 if (!model->isOK()) {
Chris@45 547 delete model;
Chris@636 548 model = nullptr;
Chris@45 549 }
Chris@45 550 }
Chris@45 551
Chris@601 552 if (!model) {
Chris@601 553 m_document->setIncomplete(true);
Chris@601 554 return false;
Chris@601 555 }
Chris@45 556
Chris@45 557 model->setObjectName(name);
Chris@685 558
Chris@687 559 ModelId modelId = ModelById::add(std::shared_ptr<Model>(model));
Chris@685 560 m_models[id] = modelId;
Chris@685 561
Chris@595 562 if (isMainModel) {
Chris@685 563 m_document->setMainModel(modelId);
Chris@685 564 m_addedModels.insert(modelId);
Chris@595 565 }
Chris@595 566 // Derived models will be added when their derivation
Chris@595 567 // is found.
Chris@45 568
Chris@595 569 return true;
Chris@45 570
Chris@629 571 } else if (type == "aggregatewave") {
Chris@629 572
Chris@629 573 QString components = attributes.value("components");
Chris@629 574 QStringList componentIdStrings = components.split(",");
Chris@629 575 std::vector<int> componentIds;
Chris@685 576 for (auto cidStr: componentIdStrings) {
Chris@629 577 bool ok = false;
Chris@685 578 int cid = cidStr.toInt(&ok);
Chris@629 579 if (!ok) {
Chris@685 580 SVCERR << "SVFileReader::readModel: Failed to convert component model id from part \"" << cidStr << "\" in \"" << components << "\"" << endl;
Chris@629 581 } else {
Chris@685 582 componentIds.push_back(cid);
Chris@629 583 }
Chris@629 584 }
Chris@629 585 PendingAggregateRec rec { name, sampleRate, componentIds };
Chris@629 586 m_pendingAggregates[id] = rec;
Chris@629 587
Chris@629 588 // The aggregate model will be constructed from its pending
Chris@629 589 // record in makeAggregateModels; it can't happen here because
Chris@629 590 // the component models might not all have been observed yet
Chris@629 591 // (an unfortunate accident of the way the file is written)
Chris@629 592
Chris@629 593 return true;
Chris@629 594
Chris@45 595 } else if (type == "dense") {
Chris@595 596
Chris@595 597 READ_MANDATORY(int, dimensions, toInt);
Chris@595 598
Chris@595 599 // Currently the only dense model we support here is the dense
Chris@595 600 // 3d model. Dense time-value models are always file-backed
Chris@595 601 // waveform data, at this point, and they come in as wavefile
Chris@595 602 // models.
Chris@595 603
Chris@595 604 if (dimensions == 3) {
Chris@595 605
Chris@595 606 READ_MANDATORY(int, windowSize, toInt);
Chris@595 607 READ_MANDATORY(int, yBinCount, toInt);
Chris@595 608
Chris@685 609 auto model = std::make_shared<EditableDenseThreeDimensionalModel>
Chris@707 610 (sampleRate, windowSize, yBinCount);
Chris@685 611
Chris@685 612 model->setObjectName(name);
Chris@685 613 m_models[id] = ModelById::add(model);
Chris@595 614
Chris@595 615 float minimum = attributes.value("minimum").trimmed().toFloat(&ok);
Chris@595 616 if (ok) model->setMinimumLevel(minimum);
Chris@595 617
Chris@595 618 float maximum = attributes.value("maximum").trimmed().toFloat(&ok);
Chris@595 619 if (ok) model->setMaximumLevel(maximum);
Chris@45 620
Chris@595 621 int dataset = attributes.value("dataset").trimmed().toInt(&ok);
Chris@595 622 if (ok) m_awaitingDatasets[dataset] = id;
Chris@45 623
Chris@181 624 int startFrame = attributes.value("startFrame").trimmed().toInt(&ok);
Chris@181 625 if (ok) model->setStartFrame(startFrame);
Chris@181 626
Chris@595 627 return true;
Chris@45 628
Chris@595 629 } else {
Chris@45 630
Chris@601 631 SVCERR << "WARNING: SV-XML: Unexpected dense model dimension ("
Chris@595 632 << dimensions << ")" << endl;
Chris@595 633 }
Chris@45 634 } else if (type == "sparse") {
Chris@45 635
Chris@595 636 READ_MANDATORY(int, dimensions, toInt);
Chris@595 637
Chris@595 638 if (dimensions == 1) {
Chris@595 639
Chris@595 640 READ_MANDATORY(int, resolution, toInt);
Chris@685 641
Chris@45 642 if (attributes.value("subtype") == "image") {
Chris@45 643
Chris@45 644 bool notifyOnAdd = (attributes.value("notifyOnAdd") == "true");
Chris@685 645 auto model = std::make_shared<ImageModel>
Chris@685 646 (sampleRate, resolution, notifyOnAdd);
Chris@45 647 model->setObjectName(name);
Chris@685 648 m_models[id] = ModelById::add(model);
Chris@45 649
Chris@45 650 } else {
Chris@45 651
Chris@685 652 auto model = std::make_shared<SparseOneDimensionalModel>
Chris@45 653 (sampleRate, resolution);
Chris@45 654 model->setObjectName(name);
Chris@685 655 m_models[id] = ModelById::add(model);
Chris@45 656 }
Chris@45 657
Chris@595 658 int dataset = attributes.value("dataset").trimmed().toInt(&ok);
Chris@595 659 if (ok) m_awaitingDatasets[dataset] = id;
Chris@45 660
Chris@595 661 return true;
Chris@45 662
Chris@595 663 } else if (dimensions == 2 || dimensions == 3) {
Chris@595 664
Chris@595 665 READ_MANDATORY(int, resolution, toInt);
Chris@45 666
Chris@45 667 bool haveMinMax = true;
Chris@595 668 float minimum = attributes.value("minimum").trimmed().toFloat(&ok);
Chris@45 669 if (!ok) haveMinMax = false;
Chris@595 670 float maximum = attributes.value("maximum").trimmed().toFloat(&ok);
Chris@45 671 if (!ok) haveMinMax = false;
Chris@45 672
Chris@595 673 float valueQuantization =
Chris@595 674 attributes.value("valueQuantization").trimmed().toFloat(&ok);
Chris@45 675
Chris@595 676 bool notifyOnAdd = (attributes.value("notifyOnAdd") == "true");
Chris@45 677
Chris@45 678 QString units = attributes.value("units");
Chris@45 679
Chris@595 680 if (dimensions == 2) {
Chris@595 681 if (attributes.value("subtype") == "text") {
Chris@685 682 auto model = std::make_shared<TextModel>
Chris@595 683 (sampleRate, resolution, notifyOnAdd);
Chris@45 684 model->setObjectName(name);
Chris@685 685 m_models[id] = ModelById::add(model);
Chris@111 686 } else if (attributes.value("subtype") == "path") {
Chris@685 687 // Paths are no longer actually models
Chris@685 688 Path *path = new Path(sampleRate, resolution);
Chris@685 689 m_paths[id] = path;
Chris@710 690 } else if (attributes.value("subtype") == "box" ||
Chris@710 691 attributes.value("subtype") == "timefrequencybox") {
Chris@710 692 auto model = std::make_shared<BoxModel>
Chris@709 693 (sampleRate, resolution, notifyOnAdd);
Chris@710 694 model->setScaleUnits(units);
Chris@709 695 model->setObjectName(name);
Chris@709 696 m_models[id] = ModelById::add(model);
Chris@595 697 } else {
Chris@685 698 std::shared_ptr<SparseTimeValueModel> model;
Chris@45 699 if (haveMinMax) {
Chris@685 700 model = std::make_shared<SparseTimeValueModel>
Chris@685 701 (sampleRate, resolution, minimum, maximum,
Chris@685 702 notifyOnAdd);
Chris@45 703 } else {
Chris@685 704 model = std::make_shared<SparseTimeValueModel>
Chris@45 705 (sampleRate, resolution, notifyOnAdd);
Chris@45 706 }
Chris@45 707 model->setScaleUnits(units);
Chris@45 708 model->setObjectName(name);
Chris@685 709 m_models[id] = ModelById::add(model);
Chris@595 710 }
Chris@595 711 } else {
Chris@137 712 if (attributes.value("subtype") == "region") {
Chris@685 713 std::shared_ptr<RegionModel> model;
Chris@137 714 if (haveMinMax) {
Chris@685 715 model = std::make_shared<RegionModel>
Chris@685 716 (sampleRate, resolution, minimum, maximum,
Chris@685 717 notifyOnAdd);
Chris@137 718 } else {
Chris@685 719 model = std::make_shared<RegionModel>
Chris@137 720 (sampleRate, resolution, notifyOnAdd);
Chris@137 721 }
Chris@137 722 model->setValueQuantization(valueQuantization);
Chris@137 723 model->setScaleUnits(units);
Chris@137 724 model->setObjectName(name);
Chris@685 725 m_models[id] = ModelById::add(model);
Chris@343 726 } else if (attributes.value("subtype") == "flexinote") {
Chris@685 727 std::shared_ptr<NoteModel> model;
Chris@343 728 if (haveMinMax) {
Chris@685 729 model = std::make_shared<NoteModel>
Chris@646 730 (sampleRate, resolution, minimum, maximum,
Chris@646 731 notifyOnAdd,
Chris@646 732 NoteModel::FLEXI_NOTE);
Chris@343 733 } else {
Chris@685 734 model = std::make_shared<NoteModel>
Chris@646 735 (sampleRate, resolution, notifyOnAdd,
Chris@646 736 NoteModel::FLEXI_NOTE);
Chris@343 737 }
Chris@343 738 model->setValueQuantization(valueQuantization);
Chris@343 739 model->setScaleUnits(units);
Chris@343 740 model->setObjectName(name);
Chris@685 741 m_models[id] = ModelById::add(model);
Chris@45 742 } else {
Chris@137 743 // note models written out by SV 1.3 and earlier
Chris@137 744 // have no subtype, so we can't test that
Chris@685 745 std::shared_ptr<NoteModel> model;
Chris@137 746 if (haveMinMax) {
Chris@685 747 model = std::make_shared<NoteModel>
Chris@137 748 (sampleRate, resolution, minimum, maximum, notifyOnAdd);
Chris@137 749 } else {
Chris@685 750 model = std::make_shared<NoteModel>
Chris@137 751 (sampleRate, resolution, notifyOnAdd);
Chris@137 752 }
Chris@137 753 model->setValueQuantization(valueQuantization);
Chris@137 754 model->setScaleUnits(units);
Chris@137 755 model->setObjectName(name);
Chris@685 756 m_models[id] = ModelById::add(model);
Chris@45 757 }
Chris@137 758 }
Chris@45 759
Chris@595 760 int dataset = attributes.value("dataset").trimmed().toInt(&ok);
Chris@595 761 if (ok) m_awaitingDatasets[dataset] = id;
Chris@45 762
Chris@595 763 return true;
Chris@45 764
Chris@595 765 } else {
Chris@45 766
Chris@601 767 SVCERR << "WARNING: SV-XML: Unexpected sparse model dimension ("
Chris@595 768 << dimensions << ")" << endl;
Chris@595 769 }
Chris@111 770
Chris@111 771 } else if (type == "alignment") {
Chris@111 772
Chris@111 773 READ_MANDATORY(int, reference, toInt);
Chris@111 774 READ_MANDATORY(int, aligned, toInt);
Chris@111 775 READ_MANDATORY(int, path, toInt);
Chris@111 776
Chris@685 777 ModelId refModel, alignedModel;
Chris@685 778 Path *pathPtr = nullptr;
Chris@111 779
Chris@111 780 if (m_models.find(reference) != m_models.end()) {
Chris@111 781 refModel = m_models[reference];
Chris@111 782 } else {
Chris@601 783 SVCERR << "WARNING: SV-XML: Unknown reference model id "
Chris@111 784 << reference << " in alignment model id " << id
Chris@293 785 << endl;
Chris@111 786 }
Chris@111 787
Chris@111 788 if (m_models.find(aligned) != m_models.end()) {
Chris@111 789 alignedModel = m_models[aligned];
Chris@111 790 } else {
Chris@601 791 SVCERR << "WARNING: SV-XML: Unknown aligned model id "
Chris@111 792 << aligned << " in alignment model id " << id
Chris@293 793 << endl;
Chris@111 794 }
Chris@111 795
Chris@685 796 if (m_paths.find(path) != m_paths.end()) {
Chris@685 797 pathPtr = m_paths[path];
Chris@111 798 } else {
Chris@685 799 SVCERR << "WARNING: SV-XML: Unknown path id "
Chris@111 800 << path << " in alignment model id " << id
Chris@293 801 << endl;
Chris@111 802 }
Chris@111 803
Chris@685 804 if (!refModel.isNone() && !alignedModel.isNone() && pathPtr) {
Chris@685 805 auto model = std::make_shared<AlignmentModel>
Chris@685 806 (refModel, alignedModel, ModelId());
Chris@685 807 model->setPath(*pathPtr);
Chris@685 808 model->setObjectName(name);
Chris@685 809 m_models[id] = ModelById::add(model);
Chris@685 810 if (auto am = ModelById::get(alignedModel)) {
Chris@685 811 am->setAlignment(m_models[id]);
Chris@111 812 }
Chris@111 813 return true;
Chris@111 814 }
Chris@685 815
Chris@685 816 if (pathPtr) {
Chris@685 817 delete pathPtr;
Chris@685 818 m_paths.erase(path);
Chris@685 819 }
Chris@111 820
Chris@45 821 } else {
Chris@45 822
Chris@590 823 SVCERR << "WARNING: SV-XML: Unexpected model type \""
Chris@590 824 << type << "\" for model id " << id << endl;
Chris@45 825 }
Chris@45 826
Chris@45 827 return false;
Chris@45 828 }
Chris@45 829
Chris@45 830 bool
Chris@45 831 SVFileReader::readView(const QXmlAttributes &attributes)
Chris@45 832 {
Chris@45 833 QString type = attributes.value("type");
Chris@636 834 m_currentPane = nullptr;
Chris@45 835
Chris@45 836 if (type != "pane") {
Chris@601 837 SVCERR << "WARNING: SV-XML: Unexpected view type \""
Chris@595 838 << type << "\"" << endl;
Chris@595 839 return false;
Chris@45 840 }
Chris@45 841
Chris@45 842 m_currentPane = m_paneCallback.addPane();
Chris@45 843
Chris@630 844 SVDEBUG << "SVFileReader::addPane: pane is " << m_currentPane << endl;
Chris@342 845
Chris@45 846 if (!m_currentPane) {
Chris@601 847 SVCERR << "WARNING: SV-XML: Internal error: Failed to add pane!"
Chris@595 848 << endl;
Chris@595 849 return false;
Chris@45 850 }
Chris@45 851
Chris@45 852 bool ok = false;
Chris@45 853
Chris@45 854 View *view = m_currentPane;
Chris@45 855
Chris@45 856 // The view properties first
Chris@45 857
Chris@366 858 READ_MANDATORY(int, centre, toInt);
Chris@366 859 READ_MANDATORY(int, zoom, toInt);
Chris@45 860 READ_MANDATORY(int, followPan, toInt);
Chris@45 861 READ_MANDATORY(int, followZoom, toInt);
Chris@45 862 QString tracking = attributes.value("tracking");
Chris@45 863
Chris@624 864 ZoomLevel zoomLevel;
Chris@624 865 int deepZoom = attributes.value("deepZoom").trimmed().toInt(&ok);
Chris@624 866 if (ok && zoom == 1 && deepZoom > 1) {
Chris@624 867 zoomLevel = { ZoomLevel::PixelsPerFrame, deepZoom };
Chris@624 868 } else {
Chris@624 869 zoomLevel = { ZoomLevel::FramesPerPixel, zoom };
Chris@624 870 }
Chris@624 871
Chris@45 872 // Specify the follow modes before we set the actual values
Chris@45 873 view->setFollowGlobalPan(followPan);
Chris@45 874 view->setFollowGlobalZoom(followZoom);
Chris@45 875 view->setPlaybackFollow(tracking == "scroll" ? PlaybackScrollContinuous :
Chris@595 876 tracking == "page" ? PlaybackScrollPageWithCentre :
Chris@595 877 tracking == "daw" ? PlaybackScrollPage
Chris@595 878 : PlaybackIgnore);
Chris@45 879
Chris@45 880 // Then set these values
Chris@45 881 view->setCentreFrame(centre);
Chris@624 882 view->setZoomLevel(zoomLevel);
Chris@45 883
Chris@45 884 // And pane properties
Chris@45 885 READ_MANDATORY(int, centreLineVisible, toInt);
Chris@45 886 m_currentPane->setCentreLineVisible(centreLineVisible);
Chris@45 887
Chris@45 888 int height = attributes.value("height").toInt(&ok);
Chris@45 889 if (ok) {
Chris@595 890 m_currentPane->resize(m_currentPane->width(), height);
Chris@45 891 }
Chris@45 892
Chris@45 893 return true;
Chris@45 894 }
Chris@45 895
Chris@45 896 bool
Chris@45 897 SVFileReader::readLayer(const QXmlAttributes &attributes)
Chris@45 898 {
Chris@45 899 QString type = attributes.value("type");
Chris@45 900
Chris@45 901 int id;
Chris@45 902 bool ok = false;
Chris@45 903 id = attributes.value("id").trimmed().toInt(&ok);
Chris@45 904
Chris@45 905 if (!ok) {
Chris@601 906 SVCERR << "WARNING: SV-XML: No layer id for layer of type \""
Chris@595 907 << type
Chris@595 908 << "\"" << endl;
Chris@595 909 return false;
Chris@45 910 }
Chris@45 911
Chris@636 912 Layer *layer = nullptr;
Chris@45 913 bool isNewLayer = false;
Chris@45 914
Chris@45 915 // Layers are expected to be defined in layer elements in the data
Chris@45 916 // section, and referred to in layer elements in the view
Chris@45 917 // sections. So if we're in the data section, we expect this
Chris@45 918 // layer not to exist already; if we're in the view section, we
Chris@45 919 // expect it to exist.
Chris@45 920
Chris@45 921 if (m_inData) {
Chris@45 922
Chris@595 923 if (m_layers.find(id) != m_layers.end()) {
Chris@601 924 SVCERR << "WARNING: SV-XML: Ignoring duplicate layer id " << id
Chris@595 925 << " in data section" << endl;
Chris@595 926 return false;
Chris@595 927 }
Chris@45 928
Chris@595 929 layer = m_layers[id] = m_document->createLayer
Chris@595 930 (LayerFactory::getInstance()->getLayerTypeForName(type));
Chris@45 931
Chris@595 932 if (layer) {
Chris@595 933 m_layers[id] = layer;
Chris@595 934 isNewLayer = true;
Chris@595 935 }
Chris@45 936
Chris@45 937 } else {
Chris@45 938
Chris@595 939 if (!m_currentPane) {
Chris@601 940 SVCERR << "WARNING: SV-XML: No current pane for layer " << id
Chris@595 941 << " in view section" << endl;
Chris@595 942 return false;
Chris@595 943 }
Chris@45 944
Chris@595 945 if (m_layers.find(id) != m_layers.end()) {
Chris@595 946
Chris@595 947 layer = m_layers[id];
Chris@595 948
Chris@595 949 } else {
Chris@601 950 SVCERR << "WARNING: SV-XML: Layer id " << id
Chris@595 951 << " in view section has not been defined -- defining it here"
Chris@595 952 << endl;
Chris@45 953
Chris@595 954 layer = m_document->createLayer
Chris@595 955 (LayerFactory::getInstance()->getLayerTypeForName(type));
Chris@45 956
Chris@595 957 if (layer) {
Chris@595 958 m_layers[id] = layer;
Chris@595 959 isNewLayer = true;
Chris@595 960 }
Chris@595 961 }
Chris@45 962 }
Chris@595 963
Chris@45 964 if (!layer) {
Chris@601 965 SVCERR << "WARNING: SV-XML: Failed to add layer of type \""
Chris@595 966 << type
Chris@595 967 << "\"" << endl;
Chris@595 968 return false;
Chris@45 969 }
Chris@45 970
Chris@45 971 if (isNewLayer) {
Chris@45 972
Chris@595 973 QString name = attributes.value("name");
Chris@595 974 layer->setObjectName(name);
Chris@45 975
Chris@89 976 QString presentationName = attributes.value("presentationName");
Chris@89 977 layer->setPresentationName(presentationName);
Chris@89 978
Chris@595 979 int modelId;
Chris@595 980 bool modelOk = false;
Chris@595 981 modelId = attributes.value("model").trimmed().toInt(&modelOk);
Chris@45 982
Chris@595 983 if (modelOk) {
Chris@595 984 if (haveModel(modelId)) {
Chris@685 985 m_document->setModel(layer, m_models[modelId]);
Chris@595 986 } else {
Chris@601 987 SVCERR << "WARNING: SV-XML: Unknown model id " << modelId
Chris@685 988 << " in layer definition" << endl;
Chris@451 989 if (!layer->canExistWithoutModel()) {
Chris@451 990 // Don't add a layer with an unknown model id
Chris@451 991 // unless it explicitly supports this state
Chris@451 992 m_document->deleteLayer(layer);
Chris@636 993 m_layers[id] = layer = nullptr;
Chris@451 994 return false;
Chris@451 995 }
Chris@451 996 }
Chris@595 997 }
Chris@45 998
Chris@447 999 if (layer) layer->setProperties(attributes);
Chris@45 1000 }
Chris@45 1001
Chris@447 1002 if (!m_inData && m_currentPane && layer) {
Chris@45 1003
Chris@45 1004 QString visible = attributes.value("visible");
Chris@45 1005 bool dormant = (visible == "false");
Chris@45 1006
Chris@45 1007 // We need to do this both before and after adding the layer
Chris@45 1008 // to the view -- we need it to be dormant if appropriate
Chris@45 1009 // before it's actually added to the view so that any property
Chris@45 1010 // box gets the right state when it's added, but the add layer
Chris@45 1011 // command sets dormant to false because it assumes it may be
Chris@45 1012 // restoring a previously dormant layer, so we need to set it
Chris@45 1013 // again afterwards too. Hm
Chris@45 1014 layer->setLayerDormant(m_currentPane, dormant);
Chris@45 1015
Chris@595 1016 m_document->addLayerToView(m_currentPane, layer);
Chris@45 1017
Chris@45 1018 layer->setLayerDormant(m_currentPane, dormant);
Chris@45 1019 }
Chris@45 1020
Chris@45 1021 m_currentLayer = layer;
Chris@636 1022 m_inLayer = (layer != nullptr);
Chris@45 1023
Chris@45 1024 return true;
Chris@45 1025 }
Chris@45 1026
Chris@45 1027 bool
Chris@45 1028 SVFileReader::readDatasetStart(const QXmlAttributes &attributes)
Chris@45 1029 {
Chris@45 1030 bool ok = false;
Chris@45 1031
Chris@45 1032 READ_MANDATORY(int, id, toInt);
Chris@45 1033 READ_MANDATORY(int, dimensions, toInt);
Chris@45 1034
Chris@45 1035 if (m_awaitingDatasets.find(id) == m_awaitingDatasets.end()) {
Chris@601 1036 SVCERR << "WARNING: SV-XML: Unwanted dataset " << id << endl;
Chris@595 1037 return false;
Chris@45 1038 }
Chris@45 1039
Chris@685 1040 int awaitingId = m_awaitingDatasets[id];
Chris@685 1041
Chris@685 1042 ModelId modelId;
Chris@685 1043 Path *path = nullptr;
Chris@45 1044
Chris@685 1045 if (haveModel(awaitingId)) {
Chris@685 1046 modelId = m_models[awaitingId];
Chris@685 1047 } else if (m_paths.find(awaitingId) != m_paths.end()) {
Chris@685 1048 path = m_paths[awaitingId];
Chris@45 1049 } else {
Chris@685 1050 SVCERR << "WARNING: SV-XML: Internal error: Unknown model or path "
Chris@685 1051 << modelId << " awaiting dataset " << id << endl;
Chris@595 1052 return false;
Chris@45 1053 }
Chris@45 1054
Chris@45 1055 bool good = false;
Chris@45 1056
Chris@45 1057 switch (dimensions) {
Chris@45 1058 case 1:
Chris@685 1059 good =
Chris@685 1060 (ModelById::isa<SparseOneDimensionalModel>(modelId) ||
Chris@685 1061 ModelById::isa<ImageModel>(modelId));
Chris@595 1062 break;
Chris@45 1063
Chris@45 1064 case 2:
Chris@685 1065 good =
Chris@685 1066 (ModelById::isa<SparseTimeValueModel>(modelId) ||
Chris@685 1067 ModelById::isa<TextModel>(modelId) ||
Chris@710 1068 ModelById::isa<BoxModel>(modelId) ||
Chris@685 1069 path);
Chris@595 1070 break;
Chris@45 1071
Chris@45 1072 case 3:
Chris@685 1073 if (ModelById::isa<EditableDenseThreeDimensionalModel>(modelId)) {
Chris@685 1074 good = true;
Chris@595 1075 m_datasetSeparator = attributes.value("separator");
Chris@685 1076 } else {
Chris@685 1077 good =
Chris@685 1078 (ModelById::isa<NoteModel>(modelId) ||
Chris@685 1079 ModelById::isa<RegionModel>(modelId));
Chris@595 1080 }
Chris@595 1081 break;
Chris@45 1082 }
Chris@45 1083
Chris@45 1084 if (!good) {
Chris@601 1085 SVCERR << "WARNING: SV-XML: Model id " << modelId << " has wrong number of dimensions or inappropriate type for " << dimensions << "-D dataset " << id << endl;
Chris@685 1086 m_currentDataset = XmlExportable::NO_ID;
Chris@595 1087 return false;
Chris@45 1088 }
Chris@45 1089
Chris@685 1090 m_currentDataset = awaitingId;
Chris@45 1091 return true;
Chris@45 1092 }
Chris@45 1093
Chris@45 1094 bool
Chris@45 1095 SVFileReader::addPointToDataset(const QXmlAttributes &attributes)
Chris@45 1096 {
Chris@45 1097 bool ok = false;
Chris@45 1098
Chris@45 1099 READ_MANDATORY(int, frame, toInt);
Chris@45 1100
Chris@685 1101 if (m_paths.find(m_currentDataset) != m_paths.end()) {
Chris@685 1102 Path *path = m_paths[m_currentDataset];
Chris@685 1103 int mapframe = attributes.value("mapframe").trimmed().toInt(&ok);
Chris@685 1104 path->add(PathPoint(frame, mapframe));
Chris@685 1105 return ok;
Chris@685 1106 }
Chris@45 1107
Chris@685 1108 if (!haveModel(m_currentDataset)) {
Chris@685 1109 SVCERR << "WARNING: SV-XML: Point element found in non-point dataset"
Chris@685 1110 << endl;
Chris@685 1111 return false;
Chris@685 1112 }
Chris@685 1113
Chris@685 1114 ModelId modelId = m_models[m_currentDataset];
Chris@45 1115
Chris@685 1116 if (auto sodm = ModelById::getAs<SparseOneDimensionalModel>(modelId)) {
Chris@595 1117 QString label = attributes.value("label");
Chris@651 1118 sodm->add(Event(frame, label));
Chris@595 1119 return true;
Chris@45 1120 }
Chris@45 1121
Chris@685 1122 if (auto stvm = ModelById::getAs<SparseTimeValueModel>(modelId)) {
Chris@709 1123 float value = attributes.value("value").trimmed().toFloat(&ok);
Chris@595 1124 QString label = attributes.value("label");
Chris@649 1125 stvm->add(Event(frame, value, label));
Chris@595 1126 return ok;
Chris@45 1127 }
Chris@595 1128
Chris@685 1129 if (auto nm = ModelById::getAs<NoteModel>(modelId)) {
Chris@709 1130 float value = attributes.value("value").trimmed().toFloat(&ok);
Chris@709 1131 int duration = attributes.value("duration").trimmed().toInt(&ok);
Chris@595 1132 QString label = attributes.value("label");
Chris@61 1133 float level = attributes.value("level").trimmed().toFloat(&ok);
Chris@61 1134 if (!ok) { // level is optional
Chris@61 1135 level = 1.f;
Chris@61 1136 ok = true;
Chris@61 1137 }
Chris@646 1138 nm->add(Event(frame, value, duration, level, label));
Chris@595 1139 return ok;
Chris@343 1140 }
Chris@343 1141
Chris@685 1142 if (auto rm = ModelById::getAs<RegionModel>(modelId)) {
Chris@709 1143 float value = attributes.value("value").trimmed().toFloat(&ok);
Chris@709 1144 int duration = attributes.value("duration").trimmed().toInt(&ok);
Chris@595 1145 QString label = attributes.value("label");
Chris@648 1146 rm->add(Event(frame, value, duration, label));
Chris@595 1147 return ok;
Chris@137 1148 }
Chris@137 1149
Chris@685 1150 if (auto tm = ModelById::getAs<TextModel>(modelId)) {
Chris@709 1151 float height = attributes.value("height").trimmed().toFloat(&ok);
Chris@595 1152 QString label = attributes.value("label");
Chris@652 1153 tm->add(Event(frame, height, label));
Chris@595 1154 return ok;
Chris@45 1155 }
Chris@45 1156
Chris@710 1157 if (auto bm = ModelById::getAs<BoxModel>(modelId)) {
Chris@710 1158 float value = attributes.value("value").trimmed().toFloat(&ok);
Chris@710 1159 if (!ok) {
Chris@710 1160 value = attributes.value("frequency").trimmed().toFloat(&ok);
Chris@710 1161 if (bm->getScaleUnits() == "") {
Chris@710 1162 bm->setScaleUnits("Hz");
Chris@710 1163 }
Chris@710 1164 }
Chris@709 1165 float extent = attributes.value("extent").trimmed().toFloat(&ok);
Chris@709 1166 int duration = attributes.value("duration").trimmed().toInt(&ok);
Chris@709 1167 QString label = attributes.value("label");
Chris@710 1168 bm->add(Event(frame, value, duration, extent, label));
Chris@709 1169 return ok;
Chris@709 1170 }
Chris@709 1171
Chris@685 1172 if (auto im = ModelById::getAs<ImageModel>(modelId)) {
Chris@595 1173 QString image = attributes.value("image");
Chris@595 1174 QString label = attributes.value("label");
Chris@653 1175 im->add(Event(frame).withURI(image).withLabel(label));
Chris@595 1176 return ok;
Chris@45 1177 }
Chris@45 1178
Chris@685 1179 SVCERR << "WARNING: SV-XML: Point element found in non-point dataset"
Chris@685 1180 << endl;
Chris@45 1181
Chris@45 1182 return false;
Chris@45 1183 }
Chris@45 1184
Chris@45 1185 bool
Chris@45 1186 SVFileReader::addBinToDataset(const QXmlAttributes &attributes)
Chris@45 1187 {
Chris@685 1188 if (!haveModel(m_currentDataset)) {
Chris@685 1189 SVCERR << "WARNING: SV-XML: Bin definition found in incompatible dataset"
Chris@685 1190 << endl;
Chris@685 1191 return false;
Chris@685 1192 }
Chris@685 1193
Chris@685 1194 ModelId modelId = m_models[m_currentDataset];
Chris@45 1195
Chris@685 1196 if (auto dtdm = ModelById::getAs<EditableDenseThreeDimensionalModel>
Chris@685 1197 (modelId)) {
Chris@45 1198
Chris@595 1199 bool ok = false;
Chris@595 1200 int n = attributes.value("number").trimmed().toInt(&ok);
Chris@595 1201 if (!ok) {
Chris@601 1202 SVCERR << "WARNING: SV-XML: Missing or invalid bin number"
Chris@595 1203 << endl;
Chris@595 1204 return false;
Chris@595 1205 }
Chris@45 1206
Chris@595 1207 QString name = attributes.value("name");
Chris@595 1208 dtdm->setBinName(n, name);
Chris@595 1209 return true;
Chris@45 1210 }
Chris@45 1211
Chris@685 1212 SVCERR << "WARNING: SV-XML: Bin definition found in incompatible dataset"
Chris@685 1213 << endl;
Chris@45 1214
Chris@45 1215 return false;
Chris@45 1216 }
Chris@45 1217
Chris@45 1218
Chris@45 1219 bool
Chris@45 1220 SVFileReader::addRowToDataset(const QXmlAttributes &attributes)
Chris@45 1221 {
Chris@45 1222 m_inRow = false;
Chris@45 1223
Chris@45 1224 bool ok = false;
Chris@45 1225 m_rowNumber = attributes.value("n").trimmed().toInt(&ok);
Chris@45 1226 if (!ok) {
Chris@601 1227 SVCERR << "WARNING: SV-XML: Missing or invalid row number"
Chris@595 1228 << endl;
Chris@595 1229 return false;
Chris@45 1230 }
Chris@45 1231
Chris@45 1232 m_inRow = true;
Chris@45 1233
Chris@601 1234 // SVCERR << "SV-XML: In row " << m_rowNumber << endl;
Chris@45 1235
Chris@45 1236 return true;
Chris@45 1237 }
Chris@45 1238
Chris@45 1239 bool
Chris@45 1240 SVFileReader::readRowData(const QString &text)
Chris@45 1241 {
Chris@685 1242 if (!haveModel(m_currentDataset)) {
Chris@685 1243 SVCERR << "WARNING: SV-XML: Row data found in non-row dataset" << endl;
Chris@685 1244 return false;
Chris@685 1245 }
Chris@685 1246
Chris@685 1247 ModelId modelId = m_models[m_currentDataset];
Chris@45 1248 bool warned = false;
Chris@45 1249
Chris@685 1250 if (auto dtdm = ModelById::getAs<EditableDenseThreeDimensionalModel>
Chris@685 1251 (modelId)) {
Chris@685 1252
Chris@595 1253 QStringList data = text.split(m_datasetSeparator);
Chris@45 1254
Chris@595 1255 DenseThreeDimensionalModel::Column values;
Chris@45 1256
Chris@595 1257 for (QStringList::iterator i = data.begin(); i != data.end(); ++i) {
Chris@45 1258
Chris@595 1259 if (int(values.size()) == dtdm->getHeight()) {
Chris@595 1260 if (!warned) {
Chris@601 1261 SVCERR << "WARNING: SV-XML: Too many y-bins in 3-D dataset row "
Chris@595 1262 << m_rowNumber << endl;
Chris@595 1263 warned = true;
Chris@595 1264 }
Chris@595 1265 }
Chris@45 1266
Chris@595 1267 bool ok;
Chris@595 1268 float value = i->toFloat(&ok);
Chris@595 1269 if (!ok) {
Chris@601 1270 SVCERR << "WARNING: SV-XML: Bad floating-point value "
Chris@595 1271 << i->toLocal8Bit().data()
Chris@595 1272 << " in row data" << endl;
Chris@595 1273 } else {
Chris@595 1274 values.push_back(value);
Chris@595 1275 }
Chris@595 1276 }
Chris@45 1277
Chris@595 1278 dtdm->setColumn(m_rowNumber, values);
Chris@595 1279 return true;
Chris@45 1280 }
Chris@45 1281
Chris@601 1282 SVCERR << "WARNING: SV-XML: Row data found in non-row dataset" << endl;
Chris@45 1283 return false;
Chris@45 1284 }
Chris@45 1285
Chris@45 1286 bool
Chris@45 1287 SVFileReader::readDerivation(const QXmlAttributes &attributes)
Chris@45 1288 {
Chris@685 1289 int modelExportId = 0;
Chris@45 1290 bool modelOk = false;
Chris@685 1291 modelExportId = attributes.value("model").trimmed().toInt(&modelOk);
Chris@45 1292
Chris@45 1293 if (!modelOk) {
Chris@601 1294 SVCERR << "WARNING: SV-XML: No model id specified for derivation" << endl;
Chris@595 1295 return false;
Chris@45 1296 }
Chris@45 1297
Chris@685 1298 if (haveModel(modelExportId)) {
Chris@685 1299 m_currentDerivedModel = m_models[modelExportId];
Chris@45 1300 } else {
Chris@45 1301 // we'll regenerate the model when the derivation element ends
Chris@685 1302 m_currentDerivedModel = {};
Chris@45 1303 }
Chris@45 1304
Chris@685 1305 m_pendingDerivedModel = modelExportId;
Chris@45 1306
Chris@45 1307 int sourceId = 0;
Chris@45 1308 bool sourceOk = false;
Chris@45 1309 sourceId = attributes.value("source").trimmed().toInt(&sourceOk);
Chris@45 1310
Chris@45 1311 if (sourceOk && haveModel(sourceId)) {
Chris@72 1312 m_currentTransformSource = m_models[sourceId];
Chris@45 1313 } else {
Chris@585 1314 SVDEBUG << "NOTE: SV-XML: Can't find a model with id " << sourceId
Chris@585 1315 << " for derivation source, falling back to main model" << endl;
Chris@72 1316 m_currentTransformSource = m_document->getMainModel();
Chris@45 1317 }
Chris@45 1318
Chris@72 1319 m_currentTransform = Transform();
Chris@45 1320
Chris@45 1321 bool ok = false;
Chris@45 1322 int channel = attributes.value("channel").trimmed().toInt(&ok);
Chris@72 1323 if (ok) m_currentTransformChannel = channel;
Chris@72 1324 else m_currentTransformChannel = -1;
Chris@45 1325
Chris@72 1326 QString type = attributes.value("type");
Chris@72 1327
Chris@72 1328 if (type == "transform") {
Chris@72 1329 m_currentTransformIsNewStyle = true;
Chris@72 1330 return true;
Chris@72 1331 } else {
Chris@72 1332 m_currentTransformIsNewStyle = false;
Chris@233 1333 SVDEBUG << "NOTE: SV-XML: Reading old-style derivation element"
Chris@229 1334 << endl;
Chris@72 1335 }
Chris@72 1336
Chris@72 1337 QString transformId = attributes.value("transform");
Chris@72 1338
Chris@72 1339 m_currentTransform.setIdentifier(transformId);
Chris@45 1340
Chris@45 1341 int stepSize = attributes.value("stepSize").trimmed().toInt(&ok);
Chris@72 1342 if (ok) m_currentTransform.setStepSize(stepSize);
Chris@45 1343
Chris@45 1344 int blockSize = attributes.value("blockSize").trimmed().toInt(&ok);
Chris@72 1345 if (ok) m_currentTransform.setBlockSize(blockSize);
Chris@45 1346
Chris@45 1347 int windowType = attributes.value("windowType").trimmed().toInt(&ok);
Chris@72 1348 if (ok) m_currentTransform.setWindowType(WindowType(windowType));
Chris@72 1349
Chris@685 1350 auto currentTransformSourceModel = ModelById::get(m_currentTransformSource);
Chris@685 1351 if (!currentTransformSourceModel) return true;
Chris@685 1352
Chris@685 1353 sv_samplerate_t sampleRate = currentTransformSourceModel->getSampleRate();
Chris@45 1354
Chris@45 1355 QString startFrameStr = attributes.value("startFrame");
Chris@45 1356 QString durationStr = attributes.value("duration");
Chris@45 1357
Chris@366 1358 int startFrame = 0;
Chris@366 1359 int duration = 0;
Chris@45 1360
Chris@45 1361 if (startFrameStr != "") {
Chris@45 1362 startFrame = startFrameStr.trimmed().toInt(&ok);
Chris@45 1363 if (!ok) startFrame = 0;
Chris@45 1364 }
Chris@45 1365 if (durationStr != "") {
Chris@45 1366 duration = durationStr.trimmed().toInt(&ok);
Chris@45 1367 if (!ok) duration = 0;
Chris@45 1368 }
Chris@45 1369
Chris@72 1370 m_currentTransform.setStartTime
Chris@685 1371 (RealTime::frame2RealTime(startFrame, sampleRate));
Chris@72 1372
Chris@72 1373 m_currentTransform.setDuration
Chris@685 1374 (RealTime::frame2RealTime(duration, sampleRate));
Chris@45 1375
Chris@45 1376 return true;
Chris@45 1377 }
Chris@45 1378
Chris@45 1379 bool
Chris@45 1380 SVFileReader::readPlayParameters(const QXmlAttributes &attributes)
Chris@45 1381 {
Chris@686 1382 m_currentPlayParameters = {};
Chris@45 1383
Chris@685 1384 int modelExportId = 0;
Chris@45 1385 bool modelOk = false;
Chris@685 1386 modelExportId = attributes.value("model").trimmed().toInt(&modelOk);
Chris@45 1387
Chris@45 1388 if (!modelOk) {
Chris@601 1389 SVCERR << "WARNING: SV-XML: No model id specified for play parameters" << endl;
Chris@595 1390 return false;
Chris@45 1391 }
Chris@45 1392
Chris@685 1393 if (haveModel(modelExportId)) {
Chris@45 1394
Chris@45 1395 bool ok = false;
Chris@45 1396
Chris@686 1397 auto parameters = PlayParameterRepository::getInstance()->
Chris@685 1398 getPlayParameters(m_models[modelExportId].untyped);
Chris@45 1399
Chris@45 1400 if (!parameters) {
Chris@601 1401 SVCERR << "WARNING: SV-XML: Play parameters for model "
Chris@685 1402 << modelExportId
Chris@45 1403 << " not found - has model been added to document?"
Chris@293 1404 << endl;
Chris@45 1405 return false;
Chris@45 1406 }
Chris@45 1407
Chris@45 1408 bool muted = (attributes.value("mute").trimmed() == "true");
Chris@45 1409 parameters->setPlayMuted(muted);
Chris@45 1410
Chris@45 1411 float pan = attributes.value("pan").toFloat(&ok);
Chris@45 1412 if (ok) parameters->setPlayPan(pan);
Chris@45 1413
Chris@45 1414 float gain = attributes.value("gain").toFloat(&ok);
Chris@45 1415 if (ok) parameters->setPlayGain(gain);
Chris@45 1416
Chris@309 1417 QString clipId = attributes.value("clipId");
Chris@309 1418 if (clipId != "") parameters->setPlayClipId(clipId);
Chris@45 1419
Chris@45 1420 m_currentPlayParameters = parameters;
Chris@45 1421
Chris@685 1422 // SVCERR << "Current play parameters for model: " << m_models[modelExportId] << ": " << m_currentPlayParameters << endl;
Chris@45 1423
Chris@45 1424 } else {
Chris@45 1425
Chris@685 1426 SVCERR << "WARNING: SV-XML: Unknown model " << modelExportId
Chris@685 1427 << " for play parameters" << endl;
Chris@45 1428 return false;
Chris@45 1429 }
Chris@45 1430
Chris@45 1431 return true;
Chris@45 1432 }
Chris@45 1433
Chris@45 1434 bool
Chris@45 1435 SVFileReader::readPlugin(const QXmlAttributes &attributes)
Chris@45 1436 {
Chris@685 1437 if (m_pendingDerivedModel != XmlExportable::NO_ID) {
Chris@308 1438 return readPluginForTransform(attributes);
Chris@308 1439 } else if (m_currentPlayParameters) {
Chris@308 1440 return readPluginForPlayback(attributes);
Chris@308 1441 } else {
Chris@601 1442 SVCERR << "WARNING: SV-XML: Plugin found outside derivation or play parameters" << endl;
Chris@45 1443 return false;
Chris@45 1444 }
Chris@308 1445 }
Chris@45 1446
Chris@308 1447 bool
Chris@308 1448 SVFileReader::readPluginForTransform(const QXmlAttributes &attributes)
Chris@308 1449 {
Chris@308 1450 if (m_currentTransformIsNewStyle) {
Chris@308 1451 // Not needed, we have the transform element instead
Chris@72 1452 return true;
Chris@72 1453 }
Chris@72 1454
Chris@45 1455 QString configurationXml = "<plugin";
Chris@308 1456
Chris@45 1457 for (int i = 0; i < attributes.length(); ++i) {
Chris@45 1458 configurationXml += QString(" %1=\"%2\"")
Chris@45 1459 .arg(attributes.qName(i))
Chris@45 1460 .arg(XmlExportable::encodeEntities(attributes.value(i)));
Chris@45 1461 }
Chris@45 1462
Chris@45 1463 configurationXml += "/>";
Chris@45 1464
Chris@308 1465 TransformFactory::getInstance()->
Chris@308 1466 setParametersFromPluginConfigurationXml(m_currentTransform,
Chris@308 1467 configurationXml);
Chris@308 1468 return true;
Chris@308 1469 }
Chris@308 1470
Chris@308 1471 bool
Chris@308 1472 SVFileReader::readPluginForPlayback(const QXmlAttributes &attributes)
Chris@308 1473 {
Chris@308 1474 // Obsolete but supported for compatibility
Chris@308 1475
Chris@308 1476 QString ident = attributes.value("identifier");
Chris@308 1477 if (ident == "sample_player") {
Chris@309 1478 QString clipId = attributes.value("program");
Chris@309 1479 if (clipId != "") m_currentPlayParameters->setPlayClipId(clipId);
Chris@45 1480 }
Chris@45 1481
Chris@45 1482 return true;
Chris@45 1483 }
Chris@45 1484
Chris@45 1485 bool
Chris@72 1486 SVFileReader::readTransform(const QXmlAttributes &attributes)
Chris@72 1487 {
Chris@685 1488 if (m_pendingDerivedModel == XmlExportable::NO_ID) {
Chris@601 1489 SVCERR << "WARNING: SV-XML: Transform found outside derivation" << endl;
Chris@72 1490 return false;
Chris@72 1491 }
Chris@72 1492
Chris@82 1493 m_currentTransform = Transform();
Chris@72 1494 m_currentTransform.setFromXmlAttributes(attributes);
Chris@72 1495 return true;
Chris@72 1496 }
Chris@72 1497
Chris@72 1498 bool
Chris@72 1499 SVFileReader::readParameter(const QXmlAttributes &attributes)
Chris@72 1500 {
Chris@685 1501 if (m_pendingDerivedModel == XmlExportable::NO_ID) {
Chris@601 1502 SVCERR << "WARNING: SV-XML: Parameter found outside derivation" << endl;
Chris@72 1503 return false;
Chris@72 1504 }
Chris@72 1505
Chris@72 1506 QString name = attributes.value("name");
Chris@72 1507 if (name == "") {
Chris@601 1508 SVCERR << "WARNING: SV-XML: Ignoring nameless transform parameter"
Chris@293 1509 << endl;
Chris@72 1510 return false;
Chris@72 1511 }
Chris@72 1512
Chris@72 1513 float value = attributes.value("value").trimmed().toFloat();
Chris@72 1514
Chris@72 1515 m_currentTransform.setParameter(name, value);
Chris@72 1516 return true;
Chris@72 1517 }
Chris@72 1518
Chris@72 1519 bool
Chris@45 1520 SVFileReader::readSelection(const QXmlAttributes &attributes)
Chris@45 1521 {
Chris@45 1522 bool ok;
Chris@45 1523
Chris@45 1524 READ_MANDATORY(int, start, toInt);
Chris@45 1525 READ_MANDATORY(int, end, toInt);
Chris@45 1526
Chris@45 1527 m_paneCallback.addSelection(start, end);
Chris@45 1528
Chris@45 1529 return true;
Chris@45 1530 }
Chris@45 1531
Chris@45 1532 bool
Chris@45 1533 SVFileReader::readMeasurement(const QXmlAttributes &attributes)
Chris@45 1534 {
Chris@233 1535 SVDEBUG << "SVFileReader::readMeasurement: inLayer "
Chris@229 1536 << m_inLayer << ", layer " << m_currentLayer << endl;
Chris@45 1537
Chris@45 1538 if (!m_inLayer) {
Chris@601 1539 SVCERR << "WARNING: SV-XML: Measurement found outside layer" << endl;
Chris@45 1540 return false;
Chris@45 1541 }
Chris@45 1542
Chris@45 1543 m_currentLayer->addMeasurementRect(attributes);
Chris@45 1544 return true;
Chris@45 1545 }
Chris@45 1546
Chris@45 1547 SVFileReaderPaneCallback::~SVFileReaderPaneCallback()
Chris@45 1548 {
Chris@45 1549 }
Chris@45 1550
Chris@140 1551
Chris@140 1552 class SVFileIdentifier : public QXmlDefaultHandler
Chris@140 1553 {
Chris@140 1554 public:
Chris@140 1555 SVFileIdentifier() :
Chris@140 1556 m_inSv(false),
Chris@140 1557 m_inData(false),
Chris@140 1558 m_type(SVFileReader::UnknownFileType)
Chris@140 1559 { }
Chris@633 1560 ~SVFileIdentifier() override { }
Chris@140 1561
Chris@140 1562 void parse(QXmlInputSource &source) {
Chris@140 1563 QXmlSimpleReader reader;
Chris@140 1564 reader.setContentHandler(this);
Chris@140 1565 reader.setErrorHandler(this);
Chris@140 1566 reader.parse(source);
Chris@140 1567 }
Chris@140 1568
Chris@140 1569 SVFileReader::FileType getType() const { return m_type; }
Chris@140 1570
Chris@633 1571 bool startElement(const QString &,
Chris@595 1572 const QString &,
Chris@595 1573 const QString &qName,
Chris@633 1574 const QXmlAttributes& atts) override
Chris@140 1575 {
Chris@140 1576 QString name = qName.toLower();
Chris@140 1577
Chris@140 1578 // SV session files have an sv element containing a data
Chris@140 1579 // element containing a model element with mainModel="true".
Chris@140 1580
Chris@140 1581 // If the sv element is present but the rest does not satisfy,
Chris@140 1582 // then it's (probably) an SV layer file.
Chris@140 1583
Chris@140 1584 // Otherwise, it's of unknown type.
Chris@140 1585
Chris@140 1586 if (name == "sv") {
Chris@140 1587 m_inSv = true;
Chris@140 1588 if (m_type == SVFileReader::UnknownFileType) {
Chris@140 1589 m_type = SVFileReader::SVLayerFile;
Chris@140 1590 }
Chris@140 1591 return true;
Chris@140 1592 } else if (name == "data") {
Chris@140 1593 if (!m_inSv) return true;
Chris@140 1594 m_inData = true;
Chris@140 1595 } else if (name == "model") {
Chris@140 1596 if (!m_inData) return true;
Chris@140 1597 if (atts.value("mainModel").trimmed() == "true") {
Chris@140 1598 if (m_type == SVFileReader::SVLayerFile) {
Chris@140 1599 m_type = SVFileReader::SVSessionFile;
Chris@140 1600 return false; // done
Chris@140 1601 }
Chris@140 1602 }
Chris@140 1603 }
Chris@140 1604 return true;
Chris@140 1605 }
Chris@140 1606
Chris@633 1607 bool endElement(const QString &,
Chris@595 1608 const QString &,
Chris@633 1609 const QString &qName) override
Chris@140 1610 {
Chris@140 1611 QString name = qName.toLower();
Chris@140 1612
Chris@140 1613 if (name == "sv") {
Chris@140 1614 if (m_inSv) {
Chris@140 1615 m_inSv = false;
Chris@140 1616 return false; // done
Chris@140 1617 }
Chris@140 1618 } else if (name == "data") {
Chris@140 1619 if (m_inData) {
Chris@140 1620 m_inData = false;
Chris@140 1621 return false; // also done, nothing after the first
Chris@140 1622 // data element is of use here
Chris@140 1623 }
Chris@140 1624 }
Chris@140 1625 return true;
Chris@140 1626 }
Chris@140 1627
Chris@140 1628 private:
Chris@140 1629 bool m_inSv;
Chris@140 1630 bool m_inData;
Chris@140 1631 SVFileReader::FileType m_type;
Chris@140 1632 };
Chris@140 1633
Chris@140 1634
Chris@140 1635 SVFileReader::FileType
Chris@140 1636 SVFileReader::identifyXmlFile(QString path)
Chris@140 1637 {
Chris@140 1638 QFile file(path);
Chris@140 1639 SVFileIdentifier identifier;
Chris@140 1640 QXmlInputSource source(&file);
Chris@140 1641 identifier.parse(source);
Chris@140 1642 return identifier.getType();
Chris@140 1643 }
Chris@140 1644
Chris@140 1645
Chris@140 1646