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@439: This file copyright 2008 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 "SimpleSPARQLQuery.h" 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@499: #include "data/model/WaveFileModel.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@439: using std::cerr; Chris@439: using std::endl; Chris@439: Chris@439: class RDFImporterImpl Chris@439: { Chris@439: public: Chris@439: RDFImporterImpl(QString url, int sampleRate); Chris@439: virtual ~RDFImporterImpl(); Chris@490: Chris@490: void setSampleRate(int sampleRate) { m_sampleRate = sampleRate; } Chris@439: Chris@439: bool isOK(); Chris@439: QString getErrorString() const; Chris@439: Chris@439: std::vector getDataModels(ProgressReporter *); Chris@439: Chris@439: protected: Chris@439: QString m_uristring; Chris@439: QString m_errorString; Chris@499: std::map m_audioModelMap; Chris@439: int m_sampleRate; Chris@439: Chris@520: static bool m_prefixesLoaded; Chris@520: static void loadPrefixes(ProgressReporter *reporter); Chris@520: Chris@499: void getDataModelsAudio(std::vector &, ProgressReporter *); Chris@440: void getDataModelsSparse(std::vector &, ProgressReporter *); Chris@440: void getDataModelsDense(std::vector &, ProgressReporter *); Chris@440: Chris@493: void getDenseModelTitle(Model *, QString, QString); Chris@493: Chris@440: void getDenseFeatureProperties(QString featureUri, Chris@440: int &sampleRate, int &windowLength, Chris@440: int &hopSize, int &width, int &height); Chris@440: Chris@449: void fillModel(Model *, long, long, bool, std::vector &, QString); Chris@439: }; Chris@439: Chris@520: bool RDFImporterImpl::m_prefixesLoaded = false; Chris@439: Chris@439: QString Chris@439: RDFImporter::getKnownExtensions() Chris@439: { Chris@439: return "*.rdf *.n3 *.ttl"; Chris@439: } Chris@439: Chris@439: RDFImporter::RDFImporter(QString url, int 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@490: RDFImporter::setSampleRate(int 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@439: std::vector Chris@439: RDFImporter::getDataModels(ProgressReporter *r) Chris@439: { Chris@439: return m_d->getDataModels(r); Chris@439: } Chris@439: Chris@439: RDFImporterImpl::RDFImporterImpl(QString uri, int sampleRate) : Chris@439: m_uristring(uri), Chris@439: m_sampleRate(sampleRate) Chris@439: { Chris@439: } Chris@439: Chris@439: RDFImporterImpl::~RDFImporterImpl() Chris@439: { Chris@492: SimpleSPARQLQuery::closeSingleSource(m_uristring); 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@439: std::vector Chris@439: RDFImporterImpl::getDataModels(ProgressReporter *reporter) Chris@439: { Chris@520: loadPrefixes(reporter); Chris@520: Chris@439: std::vector models; Chris@439: Chris@499: getDataModelsAudio(models, reporter); Chris@499: Chris@490: if (m_sampleRate == 0) { Chris@589: std::cerr << "RDFImporter::getDataModels: invalid sample rate from audio" << std::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@499: RDFImporterImpl::getDataModelsAudio(std::vector &models, Chris@499: ProgressReporter *reporter) Chris@499: { Chris@589: SimpleSPARQLQuery query Chris@499: (SimpleSPARQLQuery::QueryFromSingleSource, Chris@499: QString Chris@499: ( Chris@499: " PREFIX mo: " Chris@499: " SELECT ?signal ?source FROM <%1> " Chris@589: " WHERE { ?source a mo:AudioFile . " Chris@589: " ?signal a mo:Signal . " Chris@589: " ?source mo:encodes ?signal } " Chris@499: ) Chris@499: .arg(m_uristring)); Chris@499: Chris@499: SimpleSPARQLQuery::ResultList results = query.execute(); Chris@499: Chris@589: if (results.empty()) { Chris@589: Chris@589: SimpleSPARQLQuery query2 Chris@589: (SimpleSPARQLQuery::QueryFromSingleSource, Chris@589: QString Chris@589: ( Chris@589: " PREFIX mo: " Chris@589: " SELECT ?signal ?source FROM <%1> " Chris@589: " WHERE { ?signal a mo:Signal ; mo:available_as ?source } " Chris@589: ) Chris@589: .arg(m_uristring)); Chris@589: Chris@589: results = query.execute(); Chris@589: } Chris@589: Chris@499: for (int i = 0; i < results.size(); ++i) { Chris@499: Chris@499: QString signal = results[i]["signal"].value; Chris@499: QString source = results[i]["source"].value; Chris@499: Chris@522: FileSource *fs = new FileSource(source, reporter); 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@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@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@522: WaveFileModel *newModel = new WaveFileModel(*fs, m_sampleRate); Chris@522: if (newModel->isOK()) { Chris@522: std::cerr << "Successfully created wave file model from source at \"" << source.toStdString() << "\"" << std::endl; Chris@522: models.push_back(newModel); Chris@522: m_audioModelMap[signal] = newModel; 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@522: delete newModel; Chris@499: } Chris@522: delete fs; Chris@499: } Chris@499: } Chris@499: Chris@499: void Chris@440: 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@589: SimpleSPARQLQuery query Chris@489: (SimpleSPARQLQuery::QueryFromSingleSource, Chris@480: QString Chris@440: ( Chris@440: " PREFIX mo: " Chris@440: " PREFIX af: " Chris@440: Chris@493: " SELECT ?feature ?feature_signal_type ?value " Chris@440: " FROM <%1> " Chris@440: Chris@440: " WHERE { " Chris@440: Chris@493: " ?signal af:signal_feature ?feature . " Chris@440: Chris@440: " ?feature a ?feature_signal_type ; " Chris@440: " af:value ?value . " Chris@440: Chris@440: " } " Chris@440: ) Chris@440: .arg(m_uristring)); Chris@440: Chris@440: SimpleSPARQLQuery::ResultList results = query.execute(); Chris@440: Chris@440: if (!query.isOK()) { Chris@440: m_errorString = query.getErrorString(); Chris@440: return; Chris@440: } Chris@440: Chris@440: if (query.wasCancelled()) { Chris@440: m_errorString = "Query cancelled"; Chris@440: return; Chris@440: } Chris@440: Chris@440: for (int i = 0; i < results.size(); ++i) { Chris@440: Chris@440: QString feature = results[i]["feature"].value; Chris@440: QString type = results[i]["feature_signal_type"].value; Chris@440: QString value = results[i]["value"].value; Chris@440: Chris@440: int 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@440: SparseTimeValueModel *m = new SparseTimeValueModel 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@440: SparseTimeValueModel::Point point(j * hopSize, f, ""); Chris@440: m->addPoint(point); Chris@440: } Chris@493: Chris@493: getDenseModelTitle(m, feature, type); Chris@440: Chris@558: m->setRDFTypeURI(type); Chris@558: Chris@440: models.push_back(m); Chris@440: Chris@440: } else { Chris@440: Chris@440: EditableDenseThreeDimensionalModel *m = Chris@535: new EditableDenseThreeDimensionalModel Chris@535: (sampleRate, hopSize, height, Chris@535: EditableDenseThreeDimensionalModel::NoCompression, 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@493: getDenseModelTitle(m, feature, type); Chris@493: Chris@558: m->setRDFTypeURI(type); Chris@558: Chris@440: models.push_back(m); Chris@440: } Chris@440: } Chris@440: } Chris@440: Chris@440: void Chris@493: RDFImporterImpl::getDenseModelTitle(Model *m, Chris@493: QString featureUri, Chris@493: QString featureTypeUri) Chris@493: { Chris@493: QString titleQuery = QString Chris@493: ( Chris@493: " PREFIX dc: " Chris@493: " SELECT ?title " Chris@493: " FROM <%1> " Chris@493: " WHERE { " Chris@493: " <%2> dc:title ?title . " Chris@493: " } " Chris@493: ).arg(m_uristring); Chris@493: Chris@493: SimpleSPARQLQuery::Value v; Chris@493: Chris@493: v = SimpleSPARQLQuery::singleResultQuery Chris@493: (SimpleSPARQLQuery::QueryFromSingleSource, Chris@493: titleQuery.arg(featureUri), Chris@493: "title"); Chris@493: Chris@493: if (v.value != "") { Chris@493: std::cerr << "RDFImporterImpl::getDenseModelTitle: Title (from signal) \"" << v.value.toStdString() << "\"" << std::endl; Chris@493: m->setObjectName(v.value); Chris@493: return; Chris@493: } Chris@493: Chris@493: v = SimpleSPARQLQuery::singleResultQuery Chris@493: (SimpleSPARQLQuery::QueryFromSingleSource, Chris@493: titleQuery.arg(featureTypeUri), Chris@493: "title"); Chris@493: Chris@493: if (v.value != "") { Chris@493: std::cerr << "RDFImporterImpl::getDenseModelTitle: Title (from signal type) \"" << v.value.toStdString() << "\"" << std::endl; Chris@493: m->setObjectName(v.value); Chris@493: return; Chris@493: } Chris@493: Chris@493: std::cerr << "RDFImporterImpl::getDenseModelTitle: No title available for feature <" << featureUri.toStdString() << ">" << std::endl; Chris@493: } Chris@493: Chris@493: void Chris@440: RDFImporterImpl::getDenseFeatureProperties(QString featureUri, Chris@440: int &sampleRate, int &windowLength, Chris@440: int &hopSize, int &width, int &height) Chris@440: { Chris@489: SimpleSPARQLQuery::QueryType s = SimpleSPARQLQuery::QueryFromSingleSource; Chris@489: Chris@440: QString dimensionsQuery Chris@440: ( Chris@440: " PREFIX mo: " Chris@440: " PREFIX af: " Chris@440: Chris@440: " SELECT ?dimensions " Chris@440: " FROM <%1> " Chris@440: Chris@440: " WHERE { " Chris@440: Chris@440: " <%2> af:dimensions ?dimensions . " Chris@440: Chris@440: " } " Chris@440: ); Chris@440: Chris@440: SimpleSPARQLQuery::Value dimensionsValue = Chris@489: SimpleSPARQLQuery::singleResultQuery Chris@489: (s, dimensionsQuery.arg(m_uristring).arg(featureUri), "dimensions"); Chris@440: Chris@440: cerr << "Dimensions = \"" << dimensionsValue.value.toStdString() << "\"" Chris@440: << endl; Chris@440: Chris@440: if (dimensionsValue.value != "") { Chris@440: QStringList dl = dimensionsValue.value.split(" "); Chris@440: if (dl.empty()) dl.push_back(dimensionsValue.value); Chris@440: if (dl.size() > 0) height = dl[0].toInt(); Chris@440: if (dl.size() > 1) width = dl[1].toInt(); Chris@440: } Chris@440: Chris@440: QString queryTemplate Chris@440: ( Chris@440: " PREFIX mo: " Chris@440: " PREFIX af: " Chris@440: " PREFIX tl: " Chris@440: Chris@440: " SELECT ?%3 " Chris@440: " FROM <%1> " Chris@440: Chris@440: " WHERE { " Chris@440: Chris@440: " <%2> mo:time ?time . " Chris@440: Chris@440: " ?time a tl:Interval ; " Chris@440: " tl:onTimeLine ?timeline . " Chris@440: Chris@440: " ?map tl:rangeTimeLine ?timeline . " Chris@440: Chris@440: " ?map tl:%3 ?%3 . " Chris@440: Chris@440: " } " Chris@440: ); Chris@440: Chris@440: // Another laborious workaround for rasqal's failure to handle Chris@440: // multiple optionals properly Chris@440: Chris@440: SimpleSPARQLQuery::Value srValue = Chris@489: SimpleSPARQLQuery::singleResultQuery(s, Chris@480: queryTemplate Chris@440: .arg(m_uristring).arg(featureUri) Chris@440: .arg("sampleRate"), Chris@440: "sampleRate"); Chris@440: if (srValue.value != "") { Chris@440: sampleRate = srValue.value.toInt(); Chris@440: } Chris@440: Chris@440: SimpleSPARQLQuery::Value hopValue = Chris@489: SimpleSPARQLQuery::singleResultQuery(s, Chris@480: queryTemplate Chris@440: .arg(m_uristring).arg(featureUri) Chris@440: .arg("hopSize"), Chris@440: "hopSize"); Chris@440: if (srValue.value != "") { Chris@440: hopSize = hopValue.value.toInt(); Chris@440: } Chris@440: Chris@440: SimpleSPARQLQuery::Value winValue = Chris@489: SimpleSPARQLQuery::singleResultQuery(s, Chris@480: queryTemplate Chris@440: .arg(m_uristring).arg(featureUri) Chris@440: .arg("windowLength"), Chris@440: "windowLength"); Chris@440: if (winValue.value != "") { Chris@440: windowLength = winValue.value.toInt(); Chris@440: } Chris@440: Chris@440: cerr << "sr = " << sampleRate << ", hop = " << hopSize << ", win = " << windowLength << endl; Chris@440: } Chris@440: Chris@440: void Chris@440: 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@489: SimpleSPARQLQuery::QueryType s = SimpleSPARQLQuery::QueryFromSingleSource; Chris@489: Chris@439: // Our query is intended to retrieve every thing that has a time, Chris@439: // and every feature type and value associated with a thing that Chris@439: // has a time. Chris@439: Chris@439: // We will then need to refine this big bag of results into a set Chris@439: // of data models. Chris@439: Chris@439: // Results that have different source signals should go into Chris@439: // different models. Chris@439: Chris@439: // Results that have different feature types should go into Chris@439: // different models. Chris@439: Chris@439: // Results that are sparse should go into different models from Chris@439: // those that are dense (we need to examine the timestamps to Chris@439: // establish this -- if the timestamps are regular, the results Chris@439: // are dense -- so we can't do it as we go along, only after Chris@439: // collecting all results). Chris@439: Chris@439: // Timed things that have features associated with them should not Chris@439: // appear directly in any model -- their features should appear Chris@439: // instead -- and these should be different models from those used Chris@439: // for timed things that do not have features. Chris@439: Chris@439: // As we load the results, we'll push them into a partially Chris@439: // structured container that maps from source signal (URI as Chris@439: // string) -> feature type (likewise) -> time -> list of values. Chris@439: // If the source signal or feature type is unavailable, the empty Chris@439: // string will do. Chris@439: Chris@449: QString prefixes = QString( Chris@439: " PREFIX event: " Chris@449: " PREFIX tl: " Chris@439: " PREFIX mo: " Chris@439: " PREFIX af: " Chris@449: " PREFIX rdfs: " Chris@449: ); Chris@439: Chris@449: QString queryString = prefixes + QString( Chris@449: Chris@499: " SELECT ?signal ?timed_thing ?event_type ?value" Chris@439: " FROM <%1>" Chris@439: Chris@439: " WHERE {" Chris@440: Chris@440: " ?signal a mo:Signal ." Chris@440: Chris@439: " ?signal mo:time ?interval ." Chris@449: " ?interval tl:onTimeLine ?tl ." Chris@449: " ?time tl:onTimeLine ?tl ." Chris@449: " ?timed_thing event:time ?time ." Chris@440: " ?timed_thing a ?event_type ." Chris@440: Chris@439: " OPTIONAL {" Chris@440: " ?timed_thing af:feature ?value" Chris@439: " }" Chris@439: " }" Chris@439: Chris@439: ).arg(m_uristring); Chris@439: Chris@449: QString timeQueryString = prefixes + QString( Chris@449: Chris@449: " SELECT ?time FROM <%1> " Chris@449: " WHERE { " Chris@449: " <%2> event:time ?t . " Chris@449: " ?t tl:at ?time . " Chris@449: " } " Chris@449: Chris@449: ).arg(m_uristring); Chris@449: Chris@449: QString rangeQueryString = prefixes + QString( Chris@449: Chris@449: " SELECT ?time ?duration FROM <%1> " Chris@449: " WHERE { " Chris@449: " <%2> event:time ?t . " Chris@449: " ?t tl:beginsAt ?time . " Chris@449: " ?t tl:duration ?duration . " Chris@449: " } " Chris@449: Chris@449: ).arg(m_uristring); Chris@449: Chris@449: QString labelQueryString = prefixes + QString( Chris@449: Chris@449: " SELECT ?label FROM <%1> " Chris@449: " WHERE { " Chris@449: " <%2> rdfs:label ?label . " Chris@449: " } " Chris@449: Chris@449: ).arg(m_uristring); Chris@449: Chris@510: QString textQueryString = prefixes + QString( Chris@510: Chris@510: " SELECT ?label FROM <%1> " Chris@510: " WHERE { " Chris@510: " <%2> af:text ?label . " Chris@510: " } " Chris@510: Chris@510: ).arg(m_uristring); Chris@510: Chris@489: SimpleSPARQLQuery query(s, queryString); Chris@439: query.setProgressReporter(reporter); Chris@439: Chris@500: // cerr << "Query will be: " << queryString.toStdString() << endl; Chris@439: Chris@439: SimpleSPARQLQuery::ResultList results = query.execute(); Chris@439: Chris@439: if (!query.isOK()) { Chris@439: m_errorString = query.getErrorString(); Chris@440: return; Chris@439: } Chris@439: Chris@439: if (query.wasCancelled()) { Chris@439: m_errorString = "Query cancelled"; Chris@440: return; Chris@439: } Chris@439: Chris@449: /* Chris@449: This function is now only used for sparse data (for dense data Chris@449: we would be in getDataModelsDense instead). Chris@449: Chris@449: For sparse data, the determining factors in deciding what model Chris@449: to use are: Do the features have values? and Do the features Chris@449: have duration? Chris@449: Chris@449: We can run through the results and check off whether we find Chris@449: values and duration for each of the source+type keys, and then Chris@449: run through the source+type keys pushing each of the results Chris@449: into a suitable model. Chris@449: Chris@449: Unfortunately, at this point we do not yet have any actual Chris@449: timing data (time/duration) -- just the time URI. Chris@449: Chris@449: What we _could_ do is to create one of each type of model at the Chris@449: start, for each of the source+type keys, and then push each Chris@449: feature into the relevant model depending on what we find out Chris@449: about it. Then return only non-empty models. Chris@449: */ Chris@449: Chris@449: // Map from signal source to event type to dimensionality to Chris@449: // presence of duration to model ptr. Whee! Chris@449: std::map > > > Chris@449: modelMap; Chris@449: Chris@439: for (int i = 0; i < results.size(); ++i) { Chris@439: Chris@499: if (i % 4 == 0) { Chris@499: if (reporter) reporter->setProgress(i/4); Chris@499: } Chris@499: Chris@499: QString source = results[i]["signal"].value; Chris@449: QString type = results[i]["event_type"].value; Chris@449: QString thinguri = results[i]["timed_thing"].value; Chris@449: Chris@449: RealTime time; Chris@449: RealTime duration; Chris@439: Chris@449: bool haveTime = false; Chris@449: bool haveDuration = false; Chris@439: Chris@510: QString label = ""; Chris@510: bool text = (type.contains("Text") || type.contains("text")); // Ha, ha Chris@510: Chris@510: if (text) { Chris@510: label = SimpleSPARQLQuery::singleResultQuery Chris@510: (s, textQueryString.arg(thinguri), "label").value; Chris@510: } Chris@510: Chris@510: if (label == "") { Chris@510: label = SimpleSPARQLQuery::singleResultQuery Chris@510: (s, labelQueryString.arg(thinguri), "label").value; Chris@510: } Chris@449: Chris@489: SimpleSPARQLQuery rangeQuery(s, rangeQueryString.arg(thinguri)); Chris@450: SimpleSPARQLQuery::ResultList rangeResults = rangeQuery.execute(); Chris@450: if (!rangeResults.empty()) { Chris@450: // std::cerr << rangeResults.size() << " range results" << std::endl; Chris@450: time = RealTime::fromXsdDuration Chris@450: (rangeResults[0]["time"].value.toStdString()); Chris@450: duration = RealTime::fromXsdDuration Chris@450: (rangeResults[0]["duration"].value.toStdString()); Chris@450: // std::cerr << "duration string " << rangeResults[0]["duration"].value.toStdString() << std::endl; Chris@449: haveTime = true; Chris@450: haveDuration = true; Chris@449: } else { Chris@450: QString timestring = SimpleSPARQLQuery::singleResultQuery Chris@489: (s, timeQueryString.arg(thinguri), "time").value; Chris@520: // std::cerr << "timestring = " << timestring.toStdString() << std::endl; Chris@450: if (timestring != "") { Chris@450: time = RealTime::fromXsdDuration(timestring.toStdString()); Chris@449: haveTime = true; Chris@449: } Chris@449: } Chris@439: Chris@439: QString valuestring = results[i]["value"].value; Chris@449: std::vector values; Chris@449: Chris@439: if (valuestring != "") { Chris@449: QStringList vsl = valuestring.split(" ", QString::SkipEmptyParts); Chris@449: for (int j = 0; j < vsl.size(); ++j) { Chris@449: bool success = false; Chris@449: float v = vsl[j].toFloat(&success); Chris@449: if (success) values.push_back(v); Chris@449: } Chris@439: } Chris@439: Chris@449: int dimensions = 1; Chris@449: if (values.size() == 1) dimensions = 2; Chris@449: else if (values.size() > 1) dimensions = 3; Chris@449: Chris@449: Model *model = 0; Chris@449: Chris@449: if (modelMap[source][type][dimensions].find(haveDuration) == Chris@449: modelMap[source][type][dimensions].end()) { Chris@449: Chris@449: /* Chris@449: std::cerr << "Creating new model: source = " << source.toStdString() Chris@449: << ", type = " << type.toStdString() << ", dimensions = " Chris@449: << dimensions << ", haveDuration = " << haveDuration Chris@449: << ", time = " << time << ", duration = " << duration Chris@449: << std::endl; Chris@449: */ Chris@449: Chris@449: if (!haveDuration) { Chris@449: Chris@449: if (dimensions == 1) { Chris@449: Chris@510: if (text) { Chris@510: Chris@510: model = new TextModel(m_sampleRate, 1, false); Chris@510: Chris@510: } else { Chris@510: Chris@510: model = new SparseOneDimensionalModel(m_sampleRate, 1, false); Chris@510: } Chris@449: Chris@449: } else if (dimensions == 2) { Chris@449: Chris@510: if (text) { Chris@510: Chris@510: model = new TextModel(m_sampleRate, 1, false); Chris@510: Chris@510: } else { Chris@510: Chris@510: model = new SparseTimeValueModel(m_sampleRate, 1, false); Chris@510: } Chris@449: Chris@449: } else { Chris@449: Chris@449: // We don't have a three-dimensional sparse model, Chris@449: // so use a note model. We do have some logic (in Chris@449: // extractStructure below) for guessing whether Chris@449: // this should after all have been a dense model, Chris@449: // but it's hard to apply it because we don't have Chris@449: // all the necessary timing data yet... hmm Chris@449: Chris@449: model = new NoteModel(m_sampleRate, 1, false); Chris@449: } Chris@449: Chris@449: } else { // haveDuration Chris@449: Chris@449: if (dimensions == 1 || dimensions == 2) { Chris@449: Chris@449: // If our units are frequency or midi pitch, we Chris@449: // should be using a note model... hm Chris@449: Chris@449: model = new RegionModel(m_sampleRate, 1, false); Chris@449: Chris@449: } else { Chris@449: Chris@449: // We don't have a three-dimensional sparse model, Chris@449: // so use a note model. We do have some logic (in Chris@449: // extractStructure below) for guessing whether Chris@449: // this should after all have been a dense model, Chris@449: // but it's hard to apply it because we don't have Chris@449: // all the necessary timing data yet... hmm Chris@449: Chris@449: model = new NoteModel(m_sampleRate, 1, false); Chris@449: } Chris@449: } Chris@449: Chris@558: model->setRDFTypeURI(type); Chris@558: Chris@499: if (m_audioModelMap.find(source) != m_audioModelMap.end()) { Chris@499: std::cerr << "source model for " << model << " is " << m_audioModelMap[source] << std::endl; Chris@499: model->setSourceModel(m_audioModelMap[source]); Chris@499: } Chris@499: Chris@493: QString titleQuery = QString Chris@493: ( Chris@493: " PREFIX dc: " Chris@493: " SELECT ?title " Chris@493: " FROM <%1> " Chris@493: " WHERE { " Chris@493: " <%2> dc:title ?title . " Chris@493: " } " Chris@493: ).arg(m_uristring).arg(type); Chris@493: QString title = SimpleSPARQLQuery::singleResultQuery Chris@493: (s, titleQuery, "title").value; Chris@493: if (title != "") model->setObjectName(title); Chris@493: Chris@449: modelMap[source][type][dimensions][haveDuration] = model; Chris@449: models.push_back(model); Chris@449: } Chris@449: Chris@449: model = modelMap[source][type][dimensions][haveDuration]; Chris@449: Chris@449: if (model) { Chris@449: long ftime = RealTime::realTime2Frame(time, m_sampleRate); Chris@449: long fduration = RealTime::realTime2Frame(duration, m_sampleRate); Chris@449: fillModel(model, ftime, fduration, haveDuration, values, label); Chris@439: } Chris@439: } Chris@439: } Chris@439: Chris@439: void Chris@449: RDFImporterImpl::fillModel(Model *model, Chris@449: long ftime, Chris@449: long fduration, Chris@449: bool haveDuration, Chris@449: std::vector &values, Chris@449: QString label) Chris@449: { Chris@493: // std::cerr << "RDFImporterImpl::fillModel: adding point at frame " << ftime << std::endl; Chris@492: Chris@449: SparseOneDimensionalModel *sodm = Chris@449: dynamic_cast(model); Chris@449: if (sodm) { Chris@449: SparseOneDimensionalModel::Point point(ftime, label); Chris@449: sodm->addPoint(point); Chris@449: return; Chris@449: } Chris@449: Chris@510: TextModel *tm = Chris@510: dynamic_cast(model); Chris@510: if (tm) { Chris@510: TextModel::Point point 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@510: tm->addPoint(point); Chris@510: return; Chris@510: } Chris@510: Chris@449: SparseTimeValueModel *stvm = Chris@449: dynamic_cast(model); Chris@449: if (stvm) { Chris@449: SparseTimeValueModel::Point point Chris@449: (ftime, values.empty() ? 0.f : values[0], label); Chris@449: stvm->addPoint(point); Chris@449: return; Chris@449: } Chris@449: Chris@449: NoteModel *nm = Chris@449: dynamic_cast(model); Chris@449: if (nm) { 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@449: NoteModel::Point point(ftime, value, fduration, level, label); Chris@449: nm->addPoint(point); 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@449: NoteModel::Point point(ftime, value, duration, level, label); Chris@449: nm->addPoint(point); Chris@449: } Chris@449: return; Chris@449: } Chris@449: Chris@449: RegionModel *rm = Chris@449: dynamic_cast(model); Chris@449: if (rm) { Chris@449: if (haveDuration) { Chris@449: RegionModel::Point point Chris@449: (ftime, values.empty() ? 0.f : values[0], fduration, label); Chris@449: rm->addPoint(point); 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@449: float value = 0.f, 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@449: RegionModel::Point point(ftime, value, duration, label); Chris@449: rm->addPoint(point); Chris@449: } Chris@449: return; Chris@449: } Chris@449: Chris@449: std::cerr << "WARNING: RDFImporterImpl::fillModel: Unknown or unexpected model type" << std::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@449: Chris@499: // This query is not expected to return any values, but if it Chris@499: // executes successfully (leaving no error in the error string) Chris@499: // then we know we have RDF Chris@499: SimpleSPARQLQuery q(SimpleSPARQLQuery::QueryFromSingleSource, Chris@499: QString(" SELECT ?x FROM <%1> WHERE { ?x } ") Chris@499: .arg(url)); Chris@499: Chris@499: SimpleSPARQLQuery::ResultList r = q.execute(); Chris@499: if (!q.isOK()) { Chris@542: SimpleSPARQLQuery::closeSingleSource(url); Chris@499: return NotRDF; Chris@499: } Chris@499: Chris@588: // "MO-conformant" structure for audio files Chris@588: Chris@490: SimpleSPARQLQuery::Value value = Chris@490: SimpleSPARQLQuery::singleResultQuery Chris@490: (SimpleSPARQLQuery::QueryFromSingleSource, Chris@490: QString Chris@490: (" PREFIX mo: " Chris@490: " SELECT ?url FROM <%1> " Chris@588: " WHERE { ?url a mo:AudioFile } " Chris@490: ).arg(url), Chris@490: "url"); Chris@490: Chris@490: if (value.type == SimpleSPARQLQuery::URIValue) { 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@588: value = Chris@588: SimpleSPARQLQuery::singleResultQuery Chris@588: (SimpleSPARQLQuery::QueryFromSingleSource, Chris@588: QString Chris@588: (" PREFIX mo: " Chris@588: " SELECT ?url FROM <%1> " Chris@588: " WHERE { ?signal a mo:Signal ; mo:available_as ?url } " Chris@588: ).arg(url), Chris@588: "url"); Chris@588: Chris@588: if (value.type == SimpleSPARQLQuery::URIValue) { Chris@588: haveAudio = true; Chris@588: } Chris@490: } Chris@490: Chris@490: value = Chris@490: SimpleSPARQLQuery::singleResultQuery Chris@490: (SimpleSPARQLQuery::QueryFromSingleSource, Chris@490: QString Chris@490: (" PREFIX event: " Chris@490: " SELECT ?thing FROM <%1> " Chris@490: " WHERE { ?thing event:time ?time } " Chris@490: ).arg(url), Chris@490: "thing"); Chris@490: Chris@490: if (value.type == SimpleSPARQLQuery::URIValue) { Chris@490: haveAnnotations = true; Chris@490: } Chris@490: Chris@490: if (!haveAnnotations) { Chris@490: Chris@490: value = Chris@490: SimpleSPARQLQuery::singleResultQuery Chris@490: (SimpleSPARQLQuery::QueryFromSingleSource, Chris@490: QString Chris@490: (" PREFIX af: " Chris@490: " SELECT ?thing FROM <%1> " Chris@490: " WHERE { ?signal af:signal_feature ?thing } " Chris@490: ).arg(url), Chris@490: "thing"); Chris@490: Chris@490: if (value.type == SimpleSPARQLQuery::URIValue) { Chris@490: haveAnnotations = true; Chris@490: } Chris@490: } Chris@490: Chris@542: SimpleSPARQLQuery::closeSingleSource(url); 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: Chris@520: void Chris@520: RDFImporterImpl::loadPrefixes(ProgressReporter *reporter) Chris@520: { Chris@520: return; Chris@520: //!!! Chris@520: if (m_prefixesLoaded) return; Chris@520: const char *prefixes[] = { Chris@520: "http://purl.org/NET/c4dm/event.owl", Chris@520: "http://purl.org/NET/c4dm/timeline.owl", Chris@520: "http://purl.org/ontology/mo/", Chris@520: "http://purl.org/ontology/af/", Chris@520: "http://www.w3.org/2000/01/rdf-schema", Chris@520: "http://purl.org/dc/elements/1.1/", Chris@520: }; Chris@520: for (size_t i = 0; i < sizeof(prefixes)/sizeof(prefixes[0]); ++i) { Chris@520: CachedFile cf(prefixes[i], reporter, "application/rdf+xml"); Chris@520: if (!cf.isOK()) continue; Chris@520: SimpleSPARQLQuery::addSourceToModel Chris@520: (QUrl::fromLocalFile(cf.getLocalFilename()).toString()); Chris@520: } Chris@520: m_prefixesLoaded = true; Chris@520: }