Chris@498: /* -*- c-basic-offset: 4 indent-tabs-mode: nil -*- vi:set ts=8 sts=4 sw=4: */ Chris@498: Chris@498: /* Chris@498: Sonic Annotator Chris@498: A utility for batch feature extraction from audio files. Chris@498: Mark Levy, Chris Sutton and Chris Cannam, Queen Mary, University of London. Chris@498: Copyright 2007-2008 QMUL. Chris@498: Chris@498: This program is free software; you can redistribute it and/or Chris@498: modify it under the terms of the GNU General Public License as Chris@498: published by the Free Software Foundation; either version 2 of the Chris@498: License, or (at your option) any later version. See the file Chris@498: COPYING included with this distribution for more information. Chris@498: */ Chris@498: Chris@498: #include Chris@498: Chris@498: #include "vamp-hostsdk/PluginHostAdapter.h" Chris@498: #include "vamp-hostsdk/PluginLoader.h" Chris@498: Chris@498: #include "RDFFeatureWriter.h" Chris@498: #include "RDFTransformFactory.h" Chris@498: Chris@498: #include Chris@498: #include Chris@508: #include Chris@498: #include Chris@498: Chris@498: using namespace std; Chris@498: using Vamp::Plugin; Chris@498: using Vamp::PluginBase; Chris@498: Chris@498: RDFFeatureWriter::RDFFeatureWriter() : Chris@498: FileFeatureWriter(SupportOneFilePerTrackTransform | Chris@498: SupportOneFilePerTrack | Chris@498: SupportOneFileTotal, Chris@498: "n3"), Chris@498: m_plain(false), Chris@498: m_count(0) Chris@498: { Chris@498: } Chris@498: Chris@498: RDFFeatureWriter::~RDFFeatureWriter() Chris@498: { Chris@498: } Chris@498: Chris@498: RDFFeatureWriter::ParameterList Chris@498: RDFFeatureWriter::getSupportedParameters() const Chris@498: { Chris@498: ParameterList pl = FileFeatureWriter::getSupportedParameters(); Chris@498: Parameter p; Chris@498: Chris@498: p.name = "plain"; Chris@498: p.description = "Use \"plain\" RDF even if transform metadata is available."; Chris@498: p.hasArg = false; Chris@498: pl.push_back(p); Chris@498: Chris@498: p.name = "signal-uri"; Chris@498: p.description = "Link the output RDF to the given signal URI."; Chris@498: p.hasArg = true; Chris@498: pl.push_back(p); Chris@498: Chris@498: return pl; Chris@498: } Chris@498: Chris@498: void Chris@498: RDFFeatureWriter::setParameters(map ¶ms) Chris@498: { Chris@498: FileFeatureWriter::setParameters(params); Chris@498: Chris@498: for (map::iterator i = params.begin(); Chris@498: i != params.end(); ++i) { Chris@498: if (i->first == "plain") { Chris@498: m_plain = true; Chris@498: } Chris@498: if (i->first == "signal-uri") { Chris@498: m_suri = i->second.c_str(); Chris@498: } Chris@498: } Chris@498: } Chris@498: Chris@504: void Chris@504: RDFFeatureWriter::setTrackMetadata(QString trackId, Chris@504: TrackMetadata metadata) Chris@504: { Chris@504: m_metadata[trackId] = metadata; Chris@504: } Chris@504: Chris@504: void Chris@504: RDFFeatureWriter::write(QString trackId, Chris@504: const Transform &transform, Chris@504: const Plugin::OutputDescriptor& output, Chris@504: const Plugin::FeatureList& features, Chris@504: std::string summaryType) Chris@498: { Chris@498: QString pluginId = transform.getPluginIdentifier(); Chris@498: Chris@498: if (m_rdfDescriptions.find(pluginId) == m_rdfDescriptions.end()) { Chris@498: Chris@498: m_rdfDescriptions[pluginId] = PluginRDFDescription(pluginId); Chris@498: Chris@498: if (m_rdfDescriptions[pluginId].haveDescription()) { Chris@498: cerr << "NOTE: Have RDF description for plugin ID \"" Chris@498: << pluginId.toStdString() << "\"" << endl; Chris@498: } else { Chris@498: cerr << "NOTE: Do not have RDF description for plugin ID \"" Chris@498: << pluginId.toStdString() << "\"" << endl; Chris@498: } Chris@498: } Chris@498: Chris@498: // Need to select appropriate output file for our track/transform Chris@498: // combination Chris@498: Chris@498: QTextStream *stream = getOutputStream(trackId, transform.getIdentifier()); Chris@498: if (!stream) return; //!!! this is probably better handled with an exception Chris@498: Chris@498: if (m_startedStreamTransforms.find(stream) == Chris@498: m_startedStreamTransforms.end()) { Chris@498: cerr << "This stream is new, writing prefixes" << endl; Chris@498: writePrefixes(stream); Chris@498: if (m_singleFileName == "" && !m_stdout) { Chris@498: writeSignalDescription(stream, trackId); Chris@498: } Chris@498: } Chris@498: Chris@498: if (m_startedStreamTransforms[stream].find(transform) == Chris@498: m_startedStreamTransforms[stream].end()) { Chris@498: m_startedStreamTransforms[stream].insert(transform); Chris@498: writeLocalFeatureTypes Chris@498: (stream, transform, output, m_rdfDescriptions[pluginId]); Chris@498: } Chris@498: Chris@498: if (m_singleFileName != "" || m_stdout) { Chris@498: if (m_startedTrackIds.find(trackId) == m_startedTrackIds.end()) { Chris@498: writeSignalDescription(stream, trackId); Chris@498: m_startedTrackIds.insert(trackId); Chris@498: } Chris@498: } Chris@498: Chris@498: QString timelineURI = m_trackTimelineURIs[trackId]; Chris@498: Chris@498: if (timelineURI == "") { Chris@498: cerr << "RDFFeatureWriter: INTERNAL ERROR: writing features without having established a timeline URI!" << endl; Chris@498: exit(1); Chris@498: } Chris@498: Chris@498: if (summaryType != "") { Chris@498: Chris@498: writeSparseRDF(stream, transform, output, features, Chris@498: m_rdfDescriptions[pluginId], timelineURI); Chris@498: Chris@498: } else if (m_rdfDescriptions[pluginId].haveDescription() && Chris@498: m_rdfDescriptions[pluginId].getOutputDisposition Chris@498: (output.identifier.c_str()) == Chris@498: PluginRDFDescription::OutputDense) { Chris@498: Chris@498: QString signalURI = m_trackSignalURIs[trackId]; Chris@498: Chris@498: if (signalURI == "") { Chris@498: cerr << "RDFFeatureWriter: INTERNAL ERROR: writing dense features without having established a signal URI!" << endl; Chris@498: exit(1); Chris@498: } Chris@498: Chris@498: writeDenseRDF(stream, transform, output, features, Chris@498: m_rdfDescriptions[pluginId], signalURI, timelineURI); Chris@498: Chris@507: } else if (!m_plain && Chris@507: m_rdfDescriptions[pluginId].haveDescription() && Chris@507: m_rdfDescriptions[pluginId].getOutputDisposition Chris@507: (output.identifier.c_str()) == Chris@507: PluginRDFDescription::OutputTrackLevel && Chris@507: m_rdfDescriptions[pluginId].getOutputFeatureAttributeURI Chris@507: (output.identifier.c_str()) != "") { Chris@507: Chris@507: QString signalURI = m_trackSignalURIs[trackId]; Chris@507: Chris@507: if (signalURI == "") { Chris@507: cerr << "RDFFeatureWriter: INTERNAL ERROR: writing track-level features without having established a signal URI!" << endl; Chris@507: exit(1); Chris@507: } Chris@507: Chris@507: writeTrackLevelRDF(stream, transform, output, features, Chris@507: m_rdfDescriptions[pluginId], signalURI); Chris@507: Chris@498: } else { Chris@498: Chris@498: writeSparseRDF(stream, transform, output, features, Chris@498: m_rdfDescriptions[pluginId], timelineURI); Chris@498: } Chris@498: } Chris@498: Chris@498: void Chris@498: RDFFeatureWriter::writePrefixes(QTextStream *sptr) Chris@498: { Chris@498: QTextStream &stream = *sptr; Chris@498: Chris@498: stream << "@prefix dc: .\n" Chris@498: << "@prefix mo: .\n" Chris@498: << "@prefix af: .\n" Chris@504: << "@prefix foaf: . \n" Chris@498: << "@prefix event: .\n" Chris@498: << "@prefix rdf: .\n" Chris@498: << "@prefix rdfs: .\n" Chris@498: << "@prefix xsd: .\n" Chris@498: << "@prefix tl: .\n" Chris@498: << "@prefix vamp: .\n" Chris@498: << "@prefix : <#> .\n\n"; Chris@498: } Chris@498: Chris@498: void Chris@498: RDFFeatureWriter::writeSignalDescription(QTextStream *sptr, Chris@498: QString trackId) Chris@498: { Chris@498: QTextStream &stream = *sptr; Chris@498: Chris@498: /* Chris@498: * Describe signal we're analysing (AudioFile, Signal, TimeLine, etc.) Chris@498: */ Chris@498: Chris@498: QUrl url(trackId); Chris@498: QString scheme = url.scheme().toLower(); Chris@498: bool local = (scheme == "" || scheme == "file" || scheme.length() == 1); Chris@498: Chris@498: if (local) { Chris@498: if (scheme == "") { Chris@498: url.setScheme("file"); Chris@508: url.setPath(QFileInfo(url.path()).absoluteFilePath()); Chris@498: } else if (scheme.length() == 1) { // DOS drive letter! Chris@498: url.setScheme("file"); Chris@498: url.setPath(scheme + ":" + url.path()); Chris@498: } Chris@498: } Chris@498: Chris@498: //!!! FIX: If we are appending, we need to start counting after Chris@498: //all of the existing counts that are already in the file! Chris@498: Chris@498: uint64_t signalCount = m_count++; Chris@498: Chris@498: if (m_trackSignalURIs.find(trackId) == m_trackSignalURIs.end()) { Chris@498: m_trackSignalURIs[trackId] = QString(":signal_%1").arg(signalCount); Chris@498: } Chris@498: Chris@498: if (m_suri != NULL) { Chris@498: m_trackSignalURIs[trackId] = "<" + m_suri + ">"; Chris@498: } Chris@498: QString signalURI = m_trackSignalURIs[trackId]; Chris@498: Chris@498: if (m_trackTimelineURIs.find(trackId) == m_trackTimelineURIs.end()) { Chris@498: m_trackTimelineURIs[trackId] = QString(":signal_timeline_%1").arg(signalCount); Chris@498: } Chris@498: QString timelineURI = m_trackTimelineURIs[trackId]; Chris@498: Chris@500: if (trackId != "") { Chris@500: stream << "\n<" << url.toEncoded().data() << "> a mo:AudioFile .\n\n"; Chris@500: } Chris@500: Chris@500: stream << signalURI << " a mo:Signal ;\n"; Chris@500: Chris@500: if (trackId != "") { Chris@500: stream << " mo:available_as <" << url.toEncoded().data() Chris@500: << "> ;\n"; Chris@500: } Chris@500: Chris@504: if (m_metadata.find(trackId) != m_metadata.end()) { Chris@504: TrackMetadata tm = m_metadata[trackId]; Chris@504: if (tm.title != "") { Chris@504: stream << " dc:title \"\"\"" << tm.title << "\"\"\" ;\n"; Chris@504: } Chris@504: if (tm.maker != "") { Chris@507: stream << " foaf:maker [ a mo:MusicArtist; foaf:name \"\"\"" << tm.maker << "\"\"\" ] ;\n"; Chris@504: } Chris@504: } Chris@504: Chris@500: stream << " mo:time [\n" Chris@498: << " a tl:Interval ;\n" Chris@498: << " tl:onTimeLine " Chris@498: << timelineURI << "\n ] .\n\n"; Chris@498: } Chris@498: Chris@498: void Chris@498: RDFFeatureWriter::writeLocalFeatureTypes(QTextStream *sptr, Chris@498: const Transform &transform, Chris@498: const Plugin::OutputDescriptor &od, Chris@498: PluginRDFDescription &desc) Chris@498: { Chris@498: QString outputId = od.identifier.c_str(); Chris@498: QTextStream &stream = *sptr; Chris@498: Chris@507: // There is no "needFeatureType" for track-level outputs, because Chris@507: // we can't meaningfully write a feature at all if we don't know Chris@507: // what property to use for it. If the output is track level but Chris@507: // there is no feature type given, we have to revert to events. Chris@507: Chris@498: bool needEventType = false; Chris@498: bool needSignalType = false; Chris@498: Chris@498: //!!! bin names, extents and so on can be written out using e.g. vamp:bin_names ( "a" "b" "c" ) Chris@498: Chris@498: if (desc.getOutputDisposition(outputId) == Chris@498: PluginRDFDescription::OutputDense) { Chris@498: Chris@498: // no feature events, so may need signal type but won't need Chris@498: // event type Chris@498: Chris@498: if (m_plain) { Chris@498: Chris@498: needSignalType = true; Chris@498: Chris@498: } else if (desc.getOutputSignalTypeURI(outputId) == "") { Chris@498: Chris@498: needSignalType = true; Chris@498: } Chris@498: Chris@507: } else if (desc.getOutputDisposition(outputId) == Chris@507: PluginRDFDescription::OutputTrackLevel) { Chris@507: Chris@507: // see note above -- need to generate an event type if no Chris@507: // feature type given, or if in plain mode Chris@507: Chris@507: if (m_plain) { Chris@507: Chris@507: needEventType = true; Chris@507: Chris@507: } else if (desc.getOutputFeatureAttributeURI(outputId) == "") { Chris@507: Chris@507: if (desc.getOutputEventTypeURI(outputId) == "") { Chris@507: Chris@507: needEventType = true; Chris@507: } Chris@507: } Chris@507: Chris@498: } else { Chris@498: Chris@498: // may need event type but won't need signal type Chris@498: Chris@498: if (m_plain) { Chris@498: Chris@498: needEventType = true; Chris@498: Chris@498: } else if (desc.getOutputEventTypeURI(outputId) == "") { Chris@498: Chris@498: needEventType = true; Chris@498: } Chris@498: } Chris@498: Chris@498: QString transformUri; Chris@498: if (m_transformURIs.find(transform) != m_transformURIs.end()) { Chris@498: transformUri = m_transformURIs[transform]; Chris@498: } else { Chris@498: transformUri = QString(":transform_%1_%2").arg(m_count++).arg(outputId); Chris@498: m_transformURIs[transform] = transformUri; Chris@498: } Chris@498: Chris@500: if (transform.getIdentifier() != "") { Chris@508: stream << endl Chris@508: << RDFTransformFactory::writeTransformToRDF(transform, transformUri) Chris@500: << endl; Chris@500: } Chris@498: Chris@498: if (needEventType) { Chris@498: Chris@498: QString uri; Chris@498: if (m_syntheticEventTypeURIs.find(transform) != Chris@498: m_syntheticEventTypeURIs.end()) { Chris@498: uri = m_syntheticEventTypeURIs[transform]; Chris@498: } else { Chris@498: uri = QString(":event_type_%1").arg(m_count++); Chris@498: m_syntheticEventTypeURIs[transform] = uri; Chris@498: } Chris@498: Chris@498: stream << uri Chris@498: << " rdfs:subClassOf event:Event ;" << endl Chris@498: << " dc:title \"" << od.name.c_str() << "\" ;" << endl Chris@498: << " dc:format \"" << od.unit.c_str() << "\" ;" << endl Chris@498: << " dc:description \"" << od.description.c_str() << "\" ." Chris@498: << endl << endl; Chris@498: } Chris@498: Chris@498: if (needSignalType) { Chris@498: Chris@498: QString uri; Chris@498: if (m_syntheticSignalTypeURIs.find(transform) != Chris@498: m_syntheticSignalTypeURIs.end()) { Chris@498: uri = m_syntheticSignalTypeURIs[transform]; Chris@498: } else { Chris@498: uri = QString(":signal_type_%1").arg(m_count++); Chris@498: m_syntheticSignalTypeURIs[transform] = uri; Chris@498: } Chris@498: Chris@498: stream << uri Chris@498: << " rdfs:subClassOf af:Signal ;" << endl Chris@498: << " dc:title \"" << od.name.c_str() << "\" ;" << endl Chris@498: << " dc:format \"" << od.unit.c_str() << "\" ;" << endl Chris@498: << " dc:description \"" << od.description.c_str() << "\" ." Chris@498: << endl << endl; Chris@498: } Chris@498: } Chris@498: Chris@498: void Chris@498: RDFFeatureWriter::writeSparseRDF(QTextStream *sptr, Chris@498: const Transform &transform, Chris@498: const Plugin::OutputDescriptor& od, Chris@498: const Plugin::FeatureList& featureList, Chris@498: PluginRDFDescription &desc, Chris@498: QString timelineURI) Chris@498: { Chris@498: if (featureList.empty()) return; Chris@498: QTextStream &stream = *sptr; Chris@498: Chris@498: bool plain = (m_plain || !desc.haveDescription()); Chris@498: Chris@498: QString outputId = od.identifier.c_str(); Chris@498: Chris@498: // iterate through FeatureLists Chris@498: Chris@498: for (int i = 0; i < featureList.size(); ++i) { Chris@498: Chris@498: const Plugin::Feature &feature = featureList[i]; Chris@498: uint64_t featureNumber = m_count++; Chris@498: Chris@498: stream << ":event_" << featureNumber << " a "; Chris@498: Chris@498: QString eventTypeURI = desc.getOutputEventTypeURI(outputId); Chris@498: if (plain || eventTypeURI == "") { Chris@498: if (m_syntheticEventTypeURIs.find(transform) != Chris@498: m_syntheticEventTypeURIs.end()) { Chris@498: stream << m_syntheticEventTypeURIs[transform] << " ;\n"; Chris@498: } else { Chris@498: stream << ":event_type_" << outputId << " ;\n"; Chris@498: } Chris@498: } else { Chris@498: stream << "<" << eventTypeURI << "> ;\n"; Chris@498: } Chris@498: Chris@498: QString timestamp = feature.timestamp.toString().c_str(); Chris@498: timestamp.replace(QRegExp("^ +"), ""); Chris@498: Chris@498: if (feature.hasDuration && feature.duration > Vamp::RealTime::zeroTime) { Chris@498: Chris@498: QString duration = feature.duration.toString().c_str(); Chris@498: duration.replace(QRegExp("^ +"), ""); Chris@498: Chris@498: stream << " event:time [ \n" Chris@498: << " a tl:Interval ;\n" Chris@498: << " tl:onTimeLine " << timelineURI << " ;\n" Chris@498: << " tl:beginsAt \"PT" << timestamp Chris@498: << "S\"^^xsd:duration ;\n" Chris@498: << " tl:duration \"PT" << duration Chris@498: << "S\"^^xsd:duration ;\n" Chris@498: << " ] "; Chris@498: Chris@498: } else { Chris@498: Chris@498: stream << " event:time [ \n" Chris@498: << " a tl:Instant ;\n" //location of the event in time Chris@498: << " tl:onTimeLine " << timelineURI << " ;\n" Chris@498: << " tl:at \"PT" << timestamp Chris@498: << "S\"^^xsd:duration ;\n ] "; Chris@498: } Chris@498: Chris@500: if (transform.getIdentifier() != "") { Chris@500: stream << ";\n"; Chris@500: stream << " vamp:computed_by " << m_transformURIs[transform] << " "; Chris@500: } Chris@498: Chris@498: if (feature.label.length() > 0) { Chris@498: stream << ";\n"; Chris@498: stream << " rdfs:label \"" << feature.label.c_str() << "\" "; Chris@498: } Chris@498: Chris@498: if (!feature.values.empty()) { Chris@498: stream << ";\n"; Chris@498: //!!! named bins? Chris@498: stream << " af:feature \"" << feature.values[0]; Chris@498: for (int j = 1; j < feature.values.size(); ++j) { Chris@498: stream << " " << feature.values[j]; Chris@498: } Chris@498: stream << "\" "; Chris@498: } Chris@498: Chris@498: stream << ".\n"; Chris@498: } Chris@498: } Chris@498: Chris@498: void Chris@507: RDFFeatureWriter::writeTrackLevelRDF(QTextStream *sptr, Chris@507: const Transform &transform, Chris@507: const Plugin::OutputDescriptor& od, Chris@507: const Plugin::FeatureList& featureList, Chris@507: PluginRDFDescription &desc, Chris@507: QString signalURI) Chris@507: { Chris@507: if (featureList.empty()) return; Chris@507: QTextStream &stream = *sptr; Chris@507: Chris@507: bool plain = (m_plain || !desc.haveDescription()); Chris@507: Chris@507: QString outputId = od.identifier.c_str(); Chris@507: QString featureUri = desc.getOutputFeatureAttributeURI(outputId); Chris@507: Chris@507: if (featureUri == "") { Chris@507: cerr << "RDFFeatureWriter::writeTrackLevelRDF: ERROR: No feature URI available -- this function should not have been called!" << endl; Chris@507: return; Chris@507: } Chris@507: Chris@507: for (int i = 0; i < featureList.size(); ++i) { Chris@507: Chris@507: const Plugin::Feature &feature = featureList[i]; Chris@507: Chris@507: if (feature.values.empty()) { Chris@507: Chris@507: if (feature.label == "") continue; Chris@507: Chris@508: stream << signalURI << " " << featureUri << " \"\"\"" Chris@508: << feature.label.c_str() << "\"\"\" .\n"; Chris@507: Chris@507: } else { Chris@507: Chris@507: stream << signalURI << " " << featureUri << " \"" Chris@507: << feature.values[0] << "\"^^xsd:float .\n"; Chris@507: } Chris@507: } Chris@507: } Chris@507: Chris@507: void Chris@498: RDFFeatureWriter::writeDenseRDF(QTextStream *sptr, Chris@498: const Transform &transform, Chris@498: const Plugin::OutputDescriptor& od, Chris@498: const Plugin::FeatureList& featureList, Chris@498: PluginRDFDescription &desc, Chris@498: QString signalURI, Chris@498: QString timelineURI) Chris@498: { Chris@498: if (featureList.empty()) return; Chris@498: Chris@498: StringTransformPair sp(signalURI, transform); Chris@498: Chris@498: if (m_openDenseFeatures.find(sp) == m_openDenseFeatures.end()) { Chris@498: Chris@498: StreamBuffer b(sptr, ""); Chris@498: m_openDenseFeatures[sp] = b; Chris@498: Chris@498: QString &str(m_openDenseFeatures[sp].second); Chris@498: QTextStream stream(&str); Chris@498: Chris@498: bool plain = (m_plain || !desc.haveDescription()); Chris@498: QString outputId = od.identifier.c_str(); Chris@498: Chris@498: uint64_t featureNumber = m_count++; Chris@498: Chris@498: // need to write out feature timeline map -- for this we need Chris@498: // the sample rate, window length and hop size from the Chris@498: // transform Chris@498: Chris@498: stream << "\n:feature_timeline_" << featureNumber << " a tl:DiscreteTimeLine .\n\n"; Chris@498: Chris@498: size_t stepSize = transform.getStepSize(); Chris@498: if (stepSize == 0) { Chris@498: cerr << "RDFFeatureWriter: INTERNAL ERROR: writing dense features without having set the step size properly!" << endl; Chris@498: return; Chris@498: } Chris@498: Chris@498: size_t blockSize = transform.getBlockSize(); Chris@498: if (blockSize == 0) { Chris@498: cerr << "RDFFeatureWriter: INTERNAL ERROR: writing dense features without having set the block size properly!" << endl; Chris@498: return; Chris@498: } Chris@498: Chris@498: float sampleRate = transform.getSampleRate(); Chris@498: if (sampleRate == 0.f) { Chris@498: cerr << "RDFFeatureWriter: INTERNAL ERROR: writing dense features without having set the sample rate properly!" << endl; Chris@498: return; Chris@498: } Chris@498: Chris@498: stream << ":feature_timeline_map_" << featureNumber Chris@498: << " a tl:UniformSamplingWindowingMap ;\n" Chris@498: << " tl:rangeTimeLine :feature_timeline_" << featureNumber << " ;\n" Chris@498: << " tl:domainTimeLine " << timelineURI << " ;\n" Chris@498: << " tl:sampleRate \"" << int(sampleRate) << "\"^^xsd:int ;\n" Chris@498: << " tl:windowLength \"" << blockSize << "\"^^xsd:int ;\n" Chris@498: << " tl:hopSize \"" << stepSize << "\"^^xsd:int .\n\n"; Chris@498: Chris@498: stream << signalURI << " af:signal_feature :feature_" Chris@498: << featureNumber << " ." << endl << endl; Chris@498: Chris@498: stream << ":feature_" << featureNumber << " a "; Chris@498: Chris@498: QString signalTypeURI = desc.getOutputSignalTypeURI(outputId); Chris@498: if (plain || signalTypeURI == "") { Chris@498: if (m_syntheticSignalTypeURIs.find(transform) != Chris@498: m_syntheticSignalTypeURIs.end()) { Chris@498: stream << m_syntheticSignalTypeURIs[transform] << " ;\n"; Chris@498: } else { Chris@498: stream << ":signal_type_" << outputId << " ;\n"; Chris@498: } Chris@498: } else { Chris@498: stream << signalTypeURI << " ;\n"; Chris@498: } Chris@498: Chris@498: stream << " mo:time [" Chris@498: << "\n a tl:Interval ;" Chris@498: << "\n tl:onTimeLine :feature_timeline_" << featureNumber << " ;"; Chris@498: Chris@498: RealTime startrt = transform.getStartTime(); Chris@498: RealTime durationrt = transform.getDuration(); Chris@498: Chris@498: int start = RealTime::realTime2Frame(startrt, sampleRate) / stepSize; Chris@498: int duration = RealTime::realTime2Frame(durationrt, sampleRate) / stepSize; Chris@498: Chris@498: if (start != 0) { Chris@498: stream << "\n tl:start \"" << start << "\"^^xsd:int ;"; Chris@498: } Chris@498: if (duration != 0) { Chris@498: stream << "\n tl:duration \"" << duration << "\"^^xsd:int ;"; Chris@498: } Chris@498: Chris@498: stream << "\n ] ;\n"; Chris@498: Chris@498: if (od.hasFixedBinCount) { Chris@498: // We only know the height, so write the width as zero Chris@498: stream << " af:dimensions \"" << od.binCount << " 0\" ;\n"; Chris@498: } Chris@498: Chris@498: stream << " af:value \""; Chris@498: } Chris@498: Chris@498: QString &str = m_openDenseFeatures[sp].second; Chris@498: QTextStream stream(&str); Chris@498: Chris@498: for (int i = 0; i < featureList.size(); ++i) { Chris@498: Chris@498: const Plugin::Feature &feature = featureList[i]; Chris@498: Chris@498: for (int j = 0; j < feature.values.size(); ++j) { Chris@498: stream << feature.values[j] << " "; Chris@498: } Chris@498: } Chris@498: } Chris@498: Chris@498: void RDFFeatureWriter::finish() Chris@498: { Chris@498: // cerr << "RDFFeatureWriter::finish()" << endl; Chris@498: Chris@498: // close any open dense feature literals Chris@498: Chris@498: for (map::iterator i = Chris@498: m_openDenseFeatures.begin(); Chris@498: i != m_openDenseFeatures.end(); ++i) { Chris@498: cerr << "closing a stream" << endl; Chris@498: StreamBuffer &b = i->second; Chris@498: *(b.first) << b.second << "\" ." << endl; Chris@498: } Chris@498: Chris@498: m_openDenseFeatures.clear(); Chris@498: } Chris@498: Chris@498: