Chris@439: /* -*- c-basic-offset: 4 indent-tabs-mode: nil -*- vi:set ts=8 sts=4 sw=4: */ Chris@439: Chris@439: /* Chris@439: Sonic Visualiser Chris@439: An audio file viewer and annotation editor. Chris@439: Centre for Digital Music, Queen Mary, University of London. Chris@727: This file copyright 2008-2012 QMUL. Chris@439: Chris@439: This program is free software; you can redistribute it and/or Chris@439: modify it under the terms of the GNU General Public License as Chris@439: published by the Free Software Foundation; either version 2 of the Chris@439: License, or (at your option) any later version. See the file Chris@439: COPYING included with this distribution for more information. Chris@439: */ Chris@439: Chris@439: #include "RDFImporter.h" Chris@439: Chris@439: #include Chris@439: #include Chris@439: Chris@439: #include Chris@439: #include Chris@439: Chris@439: #include "base/ProgressReporter.h" Chris@439: #include "base/RealTime.h" Chris@439: Chris@439: #include "data/model/SparseOneDimensionalModel.h" Chris@439: #include "data/model/SparseTimeValueModel.h" Chris@439: #include "data/model/EditableDenseThreeDimensionalModel.h" Chris@449: #include "data/model/NoteModel.h" Chris@510: #include "data/model/TextModel.h" Chris@449: #include "data/model/RegionModel.h" Chris@1122: #include "data/model/ReadOnlyWaveFileModel.h" Chris@499: Chris@499: #include "data/fileio/FileSource.h" Chris@520: #include "data/fileio/CachedFile.h" Chris@581: #include "data/fileio/FileFinder.h" Chris@522: Chris@726: #include Chris@726: #include Chris@726: Chris@726: using Dataquay::Uri; Chris@726: using Dataquay::Node; Chris@726: using Dataquay::Nodes; Chris@726: using Dataquay::Triple; Chris@726: using Dataquay::Triples; Chris@726: using Dataquay::BasicStore; Chris@726: using Dataquay::PropertyObject; Chris@726: Chris@439: class RDFImporterImpl Chris@439: { Chris@439: public: Chris@1040: RDFImporterImpl(QString url, sv_samplerate_t sampleRate); Chris@439: virtual ~RDFImporterImpl(); Chris@490: Chris@1040: void setSampleRate(sv_samplerate_t sampleRate) { m_sampleRate = sampleRate; } Chris@439: Chris@439: bool isOK(); Chris@439: QString getErrorString() const; Chris@439: Chris@1752: std::vector getDataModels(ProgressReporter *); Chris@439: Chris@439: protected: Chris@726: BasicStore *m_store; Chris@730: Uri expand(QString s) { return m_store->expand(s); } Chris@726: Chris@439: QString m_uristring; Chris@439: QString m_errorString; Chris@1752: std::map m_audioModelMap; Chris@1040: sv_samplerate_t m_sampleRate; Chris@439: Chris@1752: std::map > m_labelValueMap; Chris@617: Chris@1752: void getDataModelsAudio(std::vector &, ProgressReporter *); Chris@1752: void getDataModelsSparse(std::vector &, ProgressReporter *); Chris@1752: void getDataModelsDense(std::vector &, ProgressReporter *); Chris@440: Chris@1752: QString getDenseModelTitle(QString featureUri, QString featureTypeUri); Chris@493: Chris@440: void getDenseFeatureProperties(QString featureUri, Chris@1040: sv_samplerate_t &sampleRate, int &windowLength, Chris@440: int &hopSize, int &width, int &height); Chris@440: Chris@1752: void fillModel(ModelId, sv_frame_t, sv_frame_t, Chris@1039: bool, std::vector &, QString); Chris@439: }; Chris@439: Chris@439: QString Chris@439: RDFImporter::getKnownExtensions() Chris@439: { Chris@439: return "*.rdf *.n3 *.ttl"; Chris@439: } Chris@439: Chris@1040: RDFImporter::RDFImporter(QString url, sv_samplerate_t sampleRate) : Chris@439: m_d(new RDFImporterImpl(url, sampleRate)) Chris@439: { Chris@439: } Chris@439: Chris@439: RDFImporter::~RDFImporter() Chris@439: { Chris@439: delete m_d; Chris@439: } Chris@439: Chris@490: void Chris@1040: RDFImporter::setSampleRate(sv_samplerate_t sampleRate) Chris@490: { Chris@490: m_d->setSampleRate(sampleRate); Chris@490: } Chris@490: Chris@439: bool Chris@439: RDFImporter::isOK() Chris@439: { Chris@439: return m_d->isOK(); Chris@439: } Chris@439: Chris@439: QString Chris@439: RDFImporter::getErrorString() const Chris@439: { Chris@439: return m_d->getErrorString(); Chris@439: } Chris@439: Chris@1752: std::vector Chris@439: RDFImporter::getDataModels(ProgressReporter *r) Chris@439: { Chris@439: return m_d->getDataModels(r); Chris@439: } Chris@439: Chris@1040: RDFImporterImpl::RDFImporterImpl(QString uri, sv_samplerate_t sampleRate) : Chris@726: m_store(new BasicStore), Chris@439: m_uristring(uri), Chris@439: m_sampleRate(sampleRate) Chris@439: { Chris@726: //!!! retrieve data if remote... then Chris@726: Chris@726: m_store->addPrefix("mo", Uri("http://purl.org/ontology/mo/")); Chris@726: m_store->addPrefix("af", Uri("http://purl.org/ontology/af/")); Chris@726: m_store->addPrefix("dc", Uri("http://purl.org/dc/elements/1.1/")); Chris@726: m_store->addPrefix("tl", Uri("http://purl.org/NET/c4dm/timeline.owl#")); Chris@726: m_store->addPrefix("event", Uri("http://purl.org/NET/c4dm/event.owl#")); Chris@726: m_store->addPrefix("rdfs", Uri("http://www.w3.org/2000/01/rdf-schema#")); Chris@727: Chris@738: try { Chris@738: QUrl url; Chris@738: if (uri.startsWith("file:")) { Chris@738: url = QUrl(uri); Chris@738: } else { Chris@738: url = QUrl::fromLocalFile(uri); Chris@738: } Chris@738: m_store->import(url, BasicStore::ImportIgnoreDuplicates); Chris@738: } catch (std::exception &e) { Chris@738: m_errorString = e.what(); Chris@736: } Chris@439: } Chris@439: Chris@439: RDFImporterImpl::~RDFImporterImpl() Chris@439: { Chris@726: delete m_store; Chris@439: } Chris@439: Chris@439: bool Chris@439: RDFImporterImpl::isOK() Chris@439: { Chris@439: return (m_errorString == ""); Chris@439: } Chris@439: Chris@439: QString Chris@439: RDFImporterImpl::getErrorString() const Chris@439: { Chris@439: return m_errorString; Chris@439: } Chris@439: Chris@1752: std::vector Chris@439: RDFImporterImpl::getDataModels(ProgressReporter *reporter) Chris@439: { Chris@1752: std::vector models; Chris@439: Chris@499: getDataModelsAudio(models, reporter); Chris@499: Chris@490: if (m_sampleRate == 0) { Chris@616: m_errorString = QString("Invalid audio data model (is audio file format supported?)"); Chris@843: cerr << m_errorString << endl; Chris@490: return models; Chris@490: } Chris@490: Chris@508: QString error; Chris@508: Chris@522: if (m_errorString != "") { Chris@522: error = m_errorString; Chris@522: } Chris@508: m_errorString = ""; Chris@508: Chris@440: getDataModelsDense(models, reporter); Chris@440: Chris@522: if (m_errorString != "") { Chris@522: error = m_errorString; Chris@522: } Chris@440: m_errorString = ""; Chris@440: Chris@440: getDataModelsSparse(models, reporter); Chris@440: Chris@522: if (m_errorString == "" && error != "") { Chris@522: m_errorString = error; Chris@522: } Chris@440: Chris@440: return models; Chris@440: } Chris@440: Chris@440: void Chris@1752: RDFImporterImpl::getDataModelsAudio(std::vector &models, Chris@499: ProgressReporter *reporter) Chris@499: { Chris@726: Nodes sigs = m_store->match Chris@730: (Triple(Node(), Uri("a"), expand("mo:Signal"))).subjects(); Chris@499: Chris@726: foreach (Node sig, sigs) { Chris@726: Chris@730: Node file = m_store->complete(Triple(Node(), expand("mo:encodes"), sig)); Chris@726: if (file == Node()) { Chris@730: file = m_store->complete(Triple(sig, expand("mo:available_as"), Node())); Chris@726: } Chris@726: if (file == Node()) { Chris@843: cerr << "RDFImporterImpl::getDataModelsAudio: ERROR: No source for signal " << sig << endl; Chris@726: continue; Chris@726: } Chris@499: Chris@726: QString signal = sig.value; Chris@726: QString source = file.value; Chris@589: Chris@726: SVDEBUG << "NOTE: Seeking signal source \"" << source Chris@726: << "\"..." << endl; Chris@616: Chris@522: FileSource *fs = new FileSource(source, reporter); Chris@616: if (fs->isAvailable()) { Chris@690: SVDEBUG << "NOTE: Source is available: Local filename is \"" Chris@726: << fs->getLocalFilename() Chris@726: << "\"..." << endl; Chris@616: } Chris@616: Chris@522: #ifdef NO_SV_GUI Chris@522: if (!fs->isAvailable()) { Chris@522: m_errorString = QString("Signal source \"%1\" is not available").arg(source); Chris@522: delete fs; Chris@522: continue; Chris@522: } Chris@522: #else Chris@522: if (!fs->isAvailable()) { Chris@726: SVDEBUG << "NOTE: Signal source \"" << source Chris@726: << "\" is not available, using file finder..." << endl; Chris@522: FileFinder *ff = FileFinder::getInstance(); Chris@581: if (ff) { Chris@581: QString path = ff->find(FileFinder::AudioFile, Chris@581: fs->getLocation(), Chris@581: m_uristring); Chris@581: if (path != "") { Chris@844: cerr << "File finder returns: \"" << path Chris@843: << "\"" << endl; Chris@522: delete fs; Chris@581: fs = new FileSource(path, reporter); Chris@581: if (!fs->isAvailable()) { Chris@581: delete fs; Chris@581: m_errorString = QString("Signal source \"%1\" is not available").arg(source); Chris@581: continue; Chris@581: } Chris@522: } Chris@499: } Chris@522: } Chris@522: #endif Chris@522: Chris@522: if (reporter) { Chris@522: reporter->setMessage(RDFImporter::tr("Importing audio referenced in RDF...")); Chris@522: } Chris@522: fs->waitForData(); Chris@1752: auto newModel = std::make_shared Chris@1752: (*fs, m_sampleRate); Chris@522: if (newModel->isOK()) { Chris@843: cerr << "Successfully created wave file model from source at \"" << source << "\"" << endl; Chris@1752: auto modelId = ModelById::add(newModel); Chris@1752: models.push_back(modelId); Chris@1752: m_audioModelMap[signal] = modelId; Chris@522: if (m_sampleRate == 0) { Chris@522: m_sampleRate = newModel->getSampleRate(); Chris@499: } Chris@508: } else { Chris@522: m_errorString = QString("Failed to create wave file model from source at \"%1\"").arg(source); Chris@499: } Chris@522: delete fs; Chris@499: } Chris@499: } Chris@499: Chris@499: void Chris@1752: RDFImporterImpl::getDataModelsDense(std::vector &models, Chris@440: ProgressReporter *reporter) Chris@440: { Chris@499: if (reporter) { Chris@499: reporter->setMessage(RDFImporter::tr("Importing dense signal data from RDF...")); Chris@499: } Chris@499: Chris@726: Nodes sigFeatures = m_store->match Chris@730: (Triple(Node(), expand("af:signal_feature"), Node())).objects(); Chris@440: Chris@726: foreach (Node sf, sigFeatures) { Chris@440: Chris@726: if (sf.type != Node::URI && sf.type != Node::Blank) continue; Chris@726: Chris@730: Node t = m_store->complete(Triple(sf, expand("a"), Node())); Chris@730: Node v = m_store->complete(Triple(sf, expand("af:value"), Node())); Chris@440: Chris@726: QString feature = sf.value; Chris@726: QString type = t.value; Chris@726: QString value = v.value; Chris@726: Chris@726: if (type == "" || value == "") continue; Chris@440: Chris@1040: sv_samplerate_t sampleRate = 0; Chris@440: int windowLength = 0; Chris@440: int hopSize = 0; Chris@440: int width = 0; Chris@440: int height = 0; Chris@440: getDenseFeatureProperties Chris@440: (feature, sampleRate, windowLength, hopSize, width, height); Chris@440: Chris@440: if (sampleRate != 0 && sampleRate != m_sampleRate) { Chris@440: cerr << "WARNING: Sample rate in dense feature description does not match our underlying rate -- using rate from feature description" << endl; Chris@440: } Chris@440: if (sampleRate == 0) sampleRate = m_sampleRate; Chris@440: Chris@440: if (hopSize == 0) { Chris@440: cerr << "WARNING: Dense feature description does not specify a hop size -- assuming 1" << endl; Chris@440: hopSize = 1; Chris@440: } Chris@440: Chris@440: if (height == 0) { Chris@440: cerr << "WARNING: Dense feature description does not specify feature signal dimensions -- assuming one-dimensional (height = 1)" << endl; Chris@440: height = 1; Chris@440: } Chris@440: Chris@440: QStringList values = value.split(' ', QString::SkipEmptyParts); Chris@440: Chris@440: if (values.empty()) { Chris@440: cerr << "WARNING: Dense feature description does not specify any values!" << endl; Chris@440: continue; Chris@440: } Chris@440: Chris@440: if (height == 1) { Chris@440: Chris@1752: auto m = std::make_shared Chris@440: (sampleRate, hopSize, false); Chris@440: Chris@440: for (int j = 0; j < values.size(); ++j) { Chris@440: float f = values[j].toFloat(); Chris@1651: Event e(j * hopSize, f, ""); Chris@1651: m->add(e); Chris@440: } Chris@493: Chris@1752: m->setObjectName(getDenseModelTitle(feature, type)); Chris@558: m->setRDFTypeURI(type); Chris@1752: models.push_back(ModelById::add(m)); Chris@440: Chris@440: } else { Chris@440: Chris@1752: auto m = std::make_shared Chris@1777: (sampleRate, hopSize, height, false); Chris@440: Chris@440: EditableDenseThreeDimensionalModel::Column column; Chris@440: Chris@440: int x = 0; Chris@440: Chris@440: for (int j = 0; j < values.size(); ++j) { Chris@440: if (j % height == 0 && !column.empty()) { Chris@440: m->setColumn(x++, column); Chris@440: column.clear(); Chris@440: } Chris@440: column.push_back(values[j].toFloat()); Chris@440: } Chris@440: Chris@440: if (!column.empty()) { Chris@440: m->setColumn(x++, column); Chris@440: } Chris@440: Chris@1752: m->setObjectName(getDenseModelTitle(feature, type)); Chris@558: m->setRDFTypeURI(type); Chris@1752: models.push_back(ModelById::add(m)); Chris@440: } Chris@440: } Chris@440: } Chris@440: Chris@1752: QString Chris@1752: RDFImporterImpl::getDenseModelTitle(QString featureUri, Chris@493: QString featureTypeUri) Chris@493: { Chris@730: Node n = m_store->complete Chris@730: (Triple(Uri(featureUri), expand("dc:title"), Node())); Chris@493: Chris@726: if (n.type == Node::Literal && n.value != "") { Chris@726: SVDEBUG << "RDFImporterImpl::getDenseModelTitle: Title (from signal) \"" << n.value << "\"" << endl; Chris@1752: return n.value; Chris@493: } Chris@493: Chris@730: n = m_store->complete Chris@730: (Triple(Uri(featureTypeUri), expand("dc:title"), Node())); Chris@726: Chris@726: if (n.type == Node::Literal && n.value != "") { Chris@726: SVDEBUG << "RDFImporterImpl::getDenseModelTitle: Title (from signal type) \"" << n.value << "\"" << endl; Chris@1752: return n.value; Chris@493: } Chris@493: Chris@690: SVDEBUG << "RDFImporterImpl::getDenseModelTitle: No title available for feature <" << featureUri << ">" << endl; Chris@1752: return {}; Chris@493: } Chris@493: Chris@493: void Chris@440: RDFImporterImpl::getDenseFeatureProperties(QString featureUri, Chris@1040: sv_samplerate_t &sampleRate, int &windowLength, Chris@440: int &hopSize, int &width, int &height) Chris@440: { Chris@730: Node dim = m_store->complete Chris@730: (Triple(Uri(featureUri), expand("af:dimensions"), Node())); Chris@489: Chris@726: cerr << "Dimensions = \"" << dim.value << "\"" << endl; Chris@440: Chris@726: if (dim.type == Node::Literal && dim.value != "") { Chris@726: QStringList dl = dim.value.split(" "); Chris@726: if (dl.empty()) dl.push_back(dim.value); Chris@440: if (dl.size() > 0) height = dl[0].toInt(); Chris@440: if (dl.size() > 1) width = dl[1].toInt(); Chris@440: } Chris@726: Chris@726: // Looking for rate, hop, window from: Chris@726: // Chris@726: // ?feature mo:time ?time . Chris@726: // ?time a tl:Interval . Chris@726: // ?time tl:onTimeLine ?timeline . Chris@726: // ?map tl:rangeTimeLine ?timeline . Chris@726: // ?map tl:sampleRate ?rate . Chris@726: // ?map tl:hopSize ?hop . Chris@726: // ?map tl:windowLength ?window . Chris@440: Chris@730: Node interval = m_store->complete(Triple(Uri(featureUri), expand("mo:time"), Node())); Chris@440: Chris@730: if (!m_store->contains(Triple(interval, expand("a"), expand("tl:Interval")))) { Chris@726: cerr << "RDFImporterImpl::getDenseFeatureProperties: Feature time node " Chris@726: << interval << " is not a tl:Interval" << endl; Chris@726: return; Chris@440: } Chris@440: Chris@730: Node tl = m_store->complete(Triple(interval, expand("tl:onTimeLine"), Node())); Chris@726: Chris@726: if (tl == Node()) { Chris@726: cerr << "RDFImporterImpl::getDenseFeatureProperties: Interval node " Chris@726: << interval << " lacks tl:onTimeLine property" << endl; Chris@726: return; Chris@440: } Chris@440: Chris@730: Node map = m_store->complete(Triple(Node(), expand("tl:rangeTimeLine"), tl)); Chris@726: Chris@726: if (map == Node()) { Chris@726: cerr << "RDFImporterImpl::getDenseFeatureProperties: No map for " Chris@726: << "timeline node " << tl << endl; Chris@726: } Chris@726: Chris@726: PropertyObject po(m_store, "tl:", map); Chris@726: Chris@726: if (po.hasProperty("sampleRate")) { Chris@1040: sampleRate = po.getProperty("sampleRate").toDouble(); Chris@726: } Chris@726: if (po.hasProperty("hopSize")) { Chris@726: hopSize = po.getProperty("hopSize").toInt(); Chris@726: } Chris@726: if (po.hasProperty("windowLength")) { Chris@726: windowLength = po.getProperty("windowLength").toInt(); Chris@440: } Chris@440: Chris@440: cerr << "sr = " << sampleRate << ", hop = " << hopSize << ", win = " << windowLength << endl; Chris@440: } Chris@440: Chris@440: void Chris@1752: RDFImporterImpl::getDataModelsSparse(std::vector &models, Chris@440: ProgressReporter *reporter) Chris@440: { Chris@499: if (reporter) { Chris@499: reporter->setMessage(RDFImporter::tr("Importing event data from RDF...")); Chris@499: } Chris@499: Chris@726: /* Chris@726: This function is only used for sparse data (for dense data we Chris@726: would be in getDataModelsDense instead). Chris@489: Chris@726: Our query is intended to retrieve every thing that has a time, Chris@726: and every feature type and value associated with a thing that Chris@726: has a time. Chris@439: Chris@726: We will then need to refine this big bag of results into a set Chris@726: of data models. Chris@439: Chris@726: Results that have different source signals should go into Chris@726: different models. Chris@439: Chris@726: Results that have different feature types should go into Chris@726: different models. Chris@726: */ Chris@439: Chris@726: Nodes sigs = m_store->match Chris@730: (Triple(Node(), expand("a"), expand("mo:Signal"))).subjects(); Chris@449: Chris@616: // Map from timeline uri to event type to dimensionality to Chris@1752: // presence of duration to model id. Whee! Chris@1752: std::map > > > Chris@449: modelMap; Chris@449: Chris@726: foreach (Node sig, sigs) { Chris@726: Chris@730: Node interval = m_store->complete(Triple(sig, expand("mo:time"), Node())); Chris@726: if (interval == Node()) continue; Chris@439: Chris@730: Node tl = m_store->complete(Triple(interval, expand("tl:onTimeLine"), Node())); Chris@726: if (tl == Node()) continue; Chris@499: Chris@730: Nodes times = m_store->match(Triple(Node(), expand("tl:onTimeLine"), tl)).subjects(); Chris@449: Chris@726: foreach (Node tn, times) { Chris@726: Chris@730: Nodes timedThings = m_store->match(Triple(Node(), expand("event:time"), tn)).subjects(); Chris@439: Chris@726: foreach (Node thing, timedThings) { Chris@726: Chris@730: Node typ = m_store->complete(Triple(thing, expand("a"), Node())); Chris@726: if (typ == Node()) continue; Chris@439: Chris@730: Node valu = m_store->complete(Triple(thing, expand("af:feature"), Node())); Chris@510: Chris@726: QString source = sig.value; Chris@726: QString timeline = tl.value; Chris@726: QString type = typ.value; Chris@726: QString thinguri = thing.value; Chris@510: Chris@726: /* Chris@726: For sparse data, the determining factors in deciding Chris@726: what model to use are: Do the features have values? Chris@726: and Do the features have duration? Chris@449: Chris@726: We can run through the results and check off whether Chris@726: we find values and duration for each of the Chris@726: source+type keys, and then run through the Chris@726: source+type keys pushing each of the results into a Chris@726: suitable model. Chris@439: Chris@726: Unfortunately, at this point we do not yet have any Chris@726: actual timing data (time/duration) -- just the time Chris@726: URI. Chris@449: Chris@726: What we _could_ do is to create one of each type of Chris@726: model at the start, for each of the source+type Chris@726: keys, and then push each feature into the relevant Chris@726: model depending on what we find out about it. Then Chris@726: return only non-empty models. Chris@726: */ Chris@439: Chris@726: QString label = ""; Chris@726: bool text = (type.contains("Text") || type.contains("text")); // Ha, ha Chris@726: bool note = (type.contains("Note") || type.contains("note")); // Guffaw Chris@449: Chris@726: if (text) { Chris@730: label = m_store->complete(Triple(thing, expand("af:text"), Node())).value; Chris@726: } Chris@726: Chris@726: if (label == "") { Chris@730: label = m_store->complete(Triple(thing, expand("rdfs:label"), Node())).value; Chris@726: } Chris@449: Chris@726: RealTime time; Chris@726: RealTime duration; Chris@726: Chris@930: // bool haveTime = false; Chris@726: bool haveDuration = false; Chris@726: Chris@730: Node at = m_store->complete(Triple(tn, expand("tl:at"), Node())); Chris@726: Chris@726: if (at != Node()) { Chris@726: time = RealTime::fromXsdDuration(at.value.toStdString()); Chris@930: // haveTime = true; Chris@726: } else { Chris@726: //!!! NB we're using rather old terminology for these things, apparently: Chris@726: // beginsAt -> start Chris@726: // onTimeLine -> timeline Chris@726: Chris@730: Node start = m_store->complete(Triple(tn, expand("tl:beginsAt"), Node())); Chris@730: Node dur = m_store->complete(Triple(tn, expand("tl:duration"), Node())); Chris@726: if (start != Node() && dur != Node()) { Chris@726: time = RealTime::fromXsdDuration Chris@726: (start.value.toStdString()); Chris@726: duration = RealTime::fromXsdDuration Chris@726: (dur.value.toStdString()); Chris@930: // haveTime = haveDuration = true; Chris@726: } Chris@726: } Chris@726: Chris@726: QString valuestring = valu.value; Chris@726: std::vector values; Chris@726: Chris@726: if (valuestring != "") { Chris@726: QStringList vsl = valuestring.split(" ", QString::SkipEmptyParts); Chris@726: for (int j = 0; j < vsl.size(); ++j) { Chris@726: bool success = false; Chris@726: float v = vsl[j].toFloat(&success); Chris@726: if (success) values.push_back(v); Chris@726: } Chris@726: } Chris@726: Chris@726: int dimensions = 1; Chris@726: if (values.size() == 1) dimensions = 2; Chris@726: else if (values.size() > 1) dimensions = 3; Chris@726: Chris@1752: ModelId modelId; Chris@726: Chris@726: if (modelMap[timeline][type][dimensions].find(haveDuration) == Chris@726: modelMap[timeline][type][dimensions].end()) { Chris@449: Chris@449: /* Chris@690: SVDEBUG << "Creating new model: source = " << source << ", type = " << type << ", dimensions = " Chris@449: << dimensions << ", haveDuration = " << haveDuration Chris@449: << ", time = " << time << ", duration = " << duration Chris@687: << endl; Chris@449: */ Chris@1752: Chris@1752: Model *model = nullptr; Chris@1752: Chris@726: if (!haveDuration) { Chris@449: Chris@726: if (dimensions == 1) { Chris@726: if (text) { Chris@726: model = new TextModel(m_sampleRate, 1, false); Chris@726: } else { Chris@726: model = new SparseOneDimensionalModel(m_sampleRate, 1, false); Chris@726: } Chris@726: } else if (dimensions == 2) { Chris@726: if (text) { Chris@726: model = new TextModel(m_sampleRate, 1, false); Chris@726: } else { Chris@726: model = new SparseTimeValueModel(m_sampleRate, 1, false); Chris@726: } Chris@726: } else { Chris@726: // We don't have a three-dimensional sparse model, Chris@726: // so use a note model. We do have some logic (in Chris@726: // extractStructure below) for guessing whether Chris@726: // this should after all have been a dense model, Chris@726: // but it's hard to apply it because we don't have Chris@726: // all the necessary timing data yet... hmm Chris@726: model = new NoteModel(m_sampleRate, 1, false); Chris@726: } Chris@449: Chris@726: } else { // haveDuration Chris@510: Chris@726: if (note || (dimensions > 2)) { Chris@726: model = new NoteModel(m_sampleRate, 1, false); Chris@726: } else { Chris@726: // If our units are frequency or midi pitch, we Chris@726: // should be using a note model... hm Chris@726: model = new RegionModel(m_sampleRate, 1, false); Chris@726: } Chris@510: } Chris@449: Chris@726: model->setRDFTypeURI(type); Chris@449: Chris@726: if (m_audioModelMap.find(source) != m_audioModelMap.end()) { Chris@843: cerr << "source model for " << model << " is " << m_audioModelMap[source] << endl; Chris@1752: model->setSourceModel(m_audioModelMap[source]); Chris@510: } Chris@449: Chris@730: QString title = m_store->complete Chris@730: (Triple(typ, expand("dc:title"), Node())).value; Chris@726: if (title == "") { Chris@726: // take it from the end of the event type Chris@726: title = type; Chris@726: title.replace(QRegExp("^.*[/#]"), ""); Chris@726: } Chris@726: model->setObjectName(title); Chris@449: Chris@1752: modelId = ModelById::add(std::shared_ptr(model)); Chris@1752: modelMap[timeline][type][dimensions][haveDuration] = modelId; Chris@1752: models.push_back(modelId); Chris@449: } Chris@449: Chris@1752: modelId = modelMap[timeline][type][dimensions][haveDuration]; Chris@449: Chris@1752: if (!modelId.isNone()) { Chris@1752: sv_frame_t ftime = Chris@1752: RealTime::realTime2Frame(time, m_sampleRate); Chris@1752: sv_frame_t fduration = Chris@1752: RealTime::realTime2Frame(duration, m_sampleRate); Chris@1752: fillModel(modelId, ftime, fduration, Chris@1752: haveDuration, values, label); Chris@449: } Chris@449: } Chris@439: } Chris@439: } Chris@439: } Chris@439: Chris@439: void Chris@1752: RDFImporterImpl::fillModel(ModelId modelId, Chris@1039: sv_frame_t ftime, Chris@1039: sv_frame_t fduration, Chris@449: bool haveDuration, Chris@449: std::vector &values, Chris@449: QString label) Chris@449: { Chris@690: // SVDEBUG << "RDFImporterImpl::fillModel: adding point at frame " << ftime << endl; Chris@492: Chris@1752: if (auto sodm = ModelById::getAs(modelId)) { Chris@1658: Event point(ftime, label); Chris@1658: sodm->add(point); Chris@449: return; Chris@449: } Chris@449: Chris@1752: if (auto tm = ModelById::getAs(modelId)) { Chris@1661: Event e Chris@510: (ftime, Chris@510: values.empty() ? 0.5f : values[0] < 0.f ? 0.f : values[0] > 1.f ? 1.f : values[0], // I was young and feckless once too Chris@510: label); Chris@1661: tm->add(e); Chris@510: return; Chris@510: } Chris@510: Chris@1752: if (auto stvm = ModelById::getAs(modelId)) { Chris@1651: Event e(ftime, values.empty() ? 0.f : values[0], label); Chris@1651: stvm->add(e); Chris@449: return; Chris@449: } Chris@449: Chris@1752: if (auto nm = ModelById::getAs(modelId)) { Chris@449: if (haveDuration) { Chris@449: float value = 0.f, level = 1.f; Chris@449: if (!values.empty()) { Chris@449: value = values[0]; Chris@449: if (values.size() > 1) { Chris@449: level = values[1]; Chris@449: } Chris@449: } Chris@1644: Event e(ftime, value, fduration, level, label); Chris@1644: nm->add(e); Chris@449: } else { Chris@449: float value = 0.f, duration = 1.f, level = 1.f; Chris@449: if (!values.empty()) { Chris@449: value = values[0]; Chris@449: if (values.size() > 1) { Chris@449: duration = values[1]; Chris@449: if (values.size() > 2) { Chris@449: level = values[2]; Chris@449: } Chris@449: } Chris@449: } Chris@1644: Event e(ftime, value, sv_frame_t(lrintf(duration)), Chris@1643: level, label); Chris@1644: nm->add(e); Chris@449: } Chris@449: return; Chris@449: } Chris@449: Chris@1752: if (auto rm = ModelById::getAs(modelId)) { Chris@617: float value = 0.f; Chris@617: if (values.empty()) { Chris@617: // no values? map each unique label to a distinct value Chris@1752: if (m_labelValueMap[modelId].find(label) == m_labelValueMap[modelId].end()) { Chris@1752: m_labelValueMap[modelId][label] = rm->getValueMaximum() + 1.f; Chris@617: } Chris@1752: value = m_labelValueMap[modelId][label]; Chris@617: } else { Chris@617: value = values[0]; Chris@617: } Chris@449: if (haveDuration) { Chris@1649: Event e(ftime, value, fduration, label); Chris@1649: rm->add(e); Chris@449: } else { Chris@449: // This won't actually happen -- we only create region models Chris@449: // if we do have duration -- but just for completeness Chris@617: float duration = 1.f; Chris@449: if (!values.empty()) { Chris@449: value = values[0]; Chris@449: if (values.size() > 1) { Chris@449: duration = values[1]; Chris@449: } Chris@449: } Chris@1649: Event e(ftime, value, sv_frame_t(lrintf(duration)), label); Chris@1649: rm->add(e); Chris@449: } Chris@449: return; Chris@449: } Chris@449: Chris@843: cerr << "WARNING: RDFImporterImpl::fillModel: Unknown or unexpected model type" << endl; Chris@449: return; Chris@449: } Chris@449: Chris@490: RDFImporter::RDFDocumentType Chris@490: RDFImporter::identifyDocumentType(QString url) Chris@490: { Chris@490: bool haveAudio = false; Chris@490: bool haveAnnotations = false; Chris@726: bool haveRDF = false; Chris@449: Chris@1582: BasicStore *store = nullptr; Chris@726: Chris@726: // This is not expected to return anything useful, but if it does Chris@726: // anything at all then we know we have RDF Chris@726: try { Chris@738: //!!! non-local document? Chris@726: store = BasicStore::load(QUrl(url)); Chris@730: Triple t = store->matchOnce(Triple()); Chris@726: if (t != Triple()) haveRDF = true; Chris@1471: } catch (std::exception &) { Chris@738: // nothing; haveRDF will be false so the next bit catches it Chris@726: } Chris@726: Chris@726: if (!haveRDF) { Chris@726: delete store; Chris@499: return NotRDF; Chris@499: } Chris@499: Chris@726: store->addPrefix("mo", Uri("http://purl.org/ontology/mo/")); Chris@726: store->addPrefix("event", Uri("http://purl.org/NET/c4dm/event.owl#")); Chris@726: store->addPrefix("af", Uri("http://purl.org/ontology/af/")); Chris@726: Chris@588: // "MO-conformant" structure for audio files Chris@588: Chris@730: Node n = store->complete(Triple(Node(), Uri("a"), store->expand("mo:AudioFile"))); Chris@726: if (n != Node() && n.type == Node::URI) { Chris@588: Chris@490: haveAudio = true; Chris@588: Chris@588: } else { Chris@588: Chris@588: // Sonic Annotator v0.2 and below used to write this structure Chris@588: // (which is not properly in conformance with the Music Chris@588: // Ontology) Chris@588: Chris@730: Nodes sigs = store->match(Triple(Node(), Uri("a"), store->expand("mo:Signal"))).subjects(); Chris@726: foreach (Node sig, sigs) { Chris@730: Node aa = store->complete(Triple(sig, store->expand("mo:available_as"), Node())); Chris@726: if (aa != Node()) { Chris@726: haveAudio = true; Chris@726: break; Chris@726: } Chris@588: } Chris@490: } Chris@490: Chris@690: SVDEBUG << "NOTE: RDFImporter::identifyDocumentType: haveAudio = " Chris@687: << haveAudio << endl; Chris@616: Chris@736: // can't call complete() with two Nothing nodes Chris@736: n = store->matchOnce(Triple(Node(), store->expand("event:time"), Node())).c; Chris@726: if (n != Node()) { Chris@490: haveAnnotations = true; Chris@490: } Chris@490: Chris@490: if (!haveAnnotations) { Chris@736: // can't call complete() with two Nothing nodes Chris@736: n = store->matchOnce(Triple(Node(), store->expand("af:signal_feature"), Node())).c; Chris@726: if (n != Node()) { Chris@490: haveAnnotations = true; Chris@490: } Chris@490: } Chris@490: Chris@690: SVDEBUG << "NOTE: RDFImporter::identifyDocumentType: haveAnnotations = " Chris@687: << haveAnnotations << endl; Chris@616: Chris@726: delete store; Chris@542: Chris@490: if (haveAudio) { Chris@490: if (haveAnnotations) { Chris@490: return AudioRefAndAnnotations; Chris@490: } else { Chris@490: return AudioRef; Chris@490: } Chris@490: } else { Chris@490: if (haveAnnotations) { Chris@490: return Annotations; Chris@490: } else { Chris@499: return OtherRDFDocument; Chris@490: } Chris@490: } Chris@492: Chris@542: return OtherRDFDocument; Chris@490: } Chris@490: