svcore  1.9
RDFFeatureWriter.cpp
Go to the documentation of this file.
00001 /* -*- c-basic-offset: 4 indent-tabs-mode: nil -*-  vi:set ts=8 sts=4 sw=4: */
00002 
00003 /*
00004     Sonic Annotator
00005     A utility for batch feature extraction from audio files.
00006     Mark Levy, Chris Sutton and Chris Cannam, Queen Mary, University of London.
00007     Copyright 2007-2008 QMUL.
00008 
00009     This program is free software; you can redistribute it and/or
00010     modify it under the terms of the GNU General Public License as
00011     published by the Free Software Foundation; either version 2 of the
00012     License, or (at your option) any later version.  See the file
00013     COPYING included with this distribution for more information.
00014 */
00015 
00016 #include <fstream>
00017 
00018 #include "vamp-hostsdk/PluginHostAdapter.h"
00019 #include "vamp-hostsdk/PluginLoader.h"
00020 
00021 #include "base/Exceptions.h"
00022 
00023 #include "RDFFeatureWriter.h"
00024 #include "RDFTransformFactory.h"
00025 #include "PluginRDFIndexer.h"
00026 
00027 #include <QTextStream>
00028 #include <QUrl>
00029 #include <QFileInfo>
00030 #include <QRegExp>
00031 
00032 using namespace std;
00033 using Vamp::Plugin;
00034 using Vamp::PluginBase;
00035 
00036 RDFFeatureWriter::RDFFeatureWriter() :
00037     FileFeatureWriter(SupportOneFilePerTrackTransform |
00038                       SupportOneFilePerTrack |
00039                       SupportOneFileTotal,
00040                       "n3"),
00041     m_plain(false),
00042     m_network(false),
00043     m_networkRetrieved(false),
00044     m_count(0)
00045 {
00046 }
00047 
00048 RDFFeatureWriter::~RDFFeatureWriter()
00049 {
00050 }
00051 
00052 RDFFeatureWriter::ParameterList
00053 RDFFeatureWriter::getSupportedParameters() const
00054 {
00055     ParameterList pl = FileFeatureWriter::getSupportedParameters();
00056     Parameter p;
00057 
00058     p.name = "plain";
00059     p.description = "Use \"plain\" RDF even if transform metadata is available.";
00060     p.hasArg = false;
00061     pl.push_back(p);
00062 
00063     p.name = "audiofile-uri";
00064     p.description = "Link the output RDF to the given audio file URI instead of its actual location.";
00065     p.hasArg = true;
00066     pl.push_back(p);
00067 
00068     p.name = "track-uri";
00069     p.description = "Link the output RDF to the given track URI.";
00070     p.hasArg = true;
00071     pl.push_back(p);
00072 
00073     p.name = "maker-uri";
00074     p.description = "Link the track in the output RDF to the given foaf:maker URI.";
00075     p.hasArg = true;
00076     pl.push_back(p);
00077 
00078     p.name = "network";
00079     p.description = "Attempt to retrieve RDF descriptions of plugins from network, if not available locally";
00080     p.hasArg = false;
00081     pl.push_back(p);
00082     
00083     return pl;
00084 }
00085 
00086 void
00087 RDFFeatureWriter::setParameters(map<string, string> &params)
00088 {
00089     FileFeatureWriter::setParameters(params);
00090 
00091     for (map<string, string>::iterator i = params.begin();
00092          i != params.end(); ++i) {
00093         if (i->first == "plain") {
00094             m_plain = true;
00095         }
00096         if (i->first == "audiofile-uri") {
00097             m_userAudioFileUri = i->second.c_str();
00098         }
00099         if (i->first == "track-uri") {
00100             m_userTrackUri = i->second.c_str();
00101         }
00102         if (i->first == "maker-uri") {
00103             m_userMakerUri = i->second.c_str();
00104         }
00105         if (i->first == "network") {
00106             m_network = true;
00107         }
00108     }
00109 }
00110 
00111 void
00112 RDFFeatureWriter::setTrackMetadata(QString trackId,
00113                                    TrackMetadata metadata)
00114 {
00115 //    cerr << "setTrackMetadata: title = " << metadata.title << ", maker = " << metadata.maker << endl;
00116     m_metadata[trackId] = metadata;
00117 }
00118 
00119 void
00120 RDFFeatureWriter::setFixedEventTypeURI(QString uri)
00121 {
00122     m_fixedEventTypeURI = uri;
00123 }
00124 
00125 void
00126 RDFFeatureWriter::write(QString trackId,
00127                         const Transform &transform,
00128                         const Plugin::OutputDescriptor& output,
00129                         const Plugin::FeatureList& features,
00130                         std::string summaryType)
00131 {
00132     QString pluginId = transform.getPluginIdentifier();
00133 
00134     if (m_rdfDescriptions.find(pluginId) == m_rdfDescriptions.end()) {
00135 
00136         if (m_network && !m_networkRetrieved) {
00137             PluginRDFIndexer::getInstance()->indexConfiguredURLs();
00138             m_networkRetrieved = true;
00139         }
00140 
00141         m_rdfDescriptions[pluginId] = PluginRDFDescription(pluginId);
00142 
00143         if (m_rdfDescriptions[pluginId].haveDescription()) {
00144             cerr << "NOTE: Have RDF description for plugin ID \""
00145                  << pluginId << "\"" << endl;
00146         } else {
00147             cerr << "NOTE: No RDF description for plugin ID \""
00148                  << pluginId << "\"" << endl;
00149             if (!m_network) {
00150                 cerr << "      Consider using the --rdf-network option to retrieve plugin descriptions"  << endl;
00151                 cerr << "      from the network where possible." << endl;
00152             }
00153         }
00154     }
00155 
00156     // Need to select appropriate output file for our track/transform
00157     // combination
00158 
00159     QTextStream *stream = getOutputStream(trackId, transform.getIdentifier());
00160     if (!stream) {
00161         throw FailedToOpenOutputStream(trackId, transform.getIdentifier());
00162     }
00163 
00164     if (m_startedStreamTransforms.find(stream) ==
00165         m_startedStreamTransforms.end()) {
00166 //        cerr << "This stream is new, writing prefixes" << endl;
00167         writePrefixes(stream);
00168         if (m_singleFileName == "" && !m_stdout) {
00169             writeSignalDescription(stream, trackId);
00170         }
00171     }
00172 
00173     if (m_startedStreamTransforms[stream].find(transform) ==
00174         m_startedStreamTransforms[stream].end()) {
00175         m_startedStreamTransforms[stream].insert(transform);
00176         writeLocalFeatureTypes
00177             (stream, transform, output, m_rdfDescriptions[pluginId],
00178              summaryType);
00179     }
00180 
00181     if (m_singleFileName != "" || m_stdout) {
00182         if (m_startedTrackIds.find(trackId) == m_startedTrackIds.end()) {
00183             writeSignalDescription(stream, trackId);
00184             m_startedTrackIds.insert(trackId);
00185         }
00186     }
00187 
00188     QString timelineURI = m_trackTimelineURIs[trackId];
00189     
00190     if (timelineURI == "") {
00191         cerr << "RDFFeatureWriter: INTERNAL ERROR: writing features without having established a timeline URI!" << endl;
00192         exit(1);
00193     }
00194 
00195     if (summaryType != "") {
00196 
00197         writeSparseRDF(stream, transform, output, features,
00198                        m_rdfDescriptions[pluginId], timelineURI);
00199 
00200     } else if (m_rdfDescriptions[pluginId].haveDescription() &&
00201                m_rdfDescriptions[pluginId].getOutputDisposition
00202                (output.identifier.c_str()) == 
00203                PluginRDFDescription::OutputDense) {
00204 
00205         QString signalURI = m_trackSignalURIs[trackId];
00206 
00207         if (signalURI == "") {
00208             cerr << "RDFFeatureWriter: INTERNAL ERROR: writing dense features without having established a signal URI!" << endl;
00209             exit(1);
00210         }
00211 
00212         writeDenseRDF(stream, transform, output, features,
00213                       m_rdfDescriptions[pluginId], signalURI, timelineURI);
00214 
00215     } else if (!m_plain &&
00216                m_rdfDescriptions[pluginId].haveDescription() &&
00217                m_rdfDescriptions[pluginId].getOutputDisposition
00218                (output.identifier.c_str()) ==
00219                PluginRDFDescription::OutputTrackLevel &&
00220                m_rdfDescriptions[pluginId].getOutputFeatureAttributeURI
00221                (output.identifier.c_str()) != "") {
00222 
00223         QString signalURI = m_trackSignalURIs[trackId];
00224 
00225         if (signalURI == "") {
00226             cerr << "RDFFeatureWriter: INTERNAL ERROR: writing track-level features without having established a signal URI!" << endl;
00227             exit(1);
00228         }
00229 
00230         writeTrackLevelRDF(stream, transform, output, features,
00231                            m_rdfDescriptions[pluginId], signalURI);
00232 
00233     } else {
00234 
00235         writeSparseRDF(stream, transform, output, features,
00236                        m_rdfDescriptions[pluginId], timelineURI);
00237     }
00238 }
00239 
00240 void
00241 RDFFeatureWriter::writePrefixes(QTextStream *sptr)
00242 {
00243     QTextStream &stream = *sptr;
00244 
00245     stream << "@prefix dc: <http://purl.org/dc/elements/1.1/> .\n"
00246            << "@prefix mo: <http://purl.org/ontology/mo/> .\n"
00247            << "@prefix af: <http://purl.org/ontology/af/> .\n"
00248            << "@prefix foaf: <http://xmlns.com/foaf/0.1/> . \n"
00249            << "@prefix event: <http://purl.org/NET/c4dm/event.owl#> .\n"
00250            << "@prefix rdf: <http://www.w3.org/1999/02/22-rdf-syntax-ns#> .\n"
00251            << "@prefix rdfs: <http://www.w3.org/2000/01/rdf-schema#> .\n"
00252            << "@prefix xsd: <http://www.w3.org/2001/XMLSchema#> .\n"
00253            << "@prefix tl: <http://purl.org/NET/c4dm/timeline.owl#> .\n"
00254            << "@prefix vamp: <http://purl.org/ontology/vamp/> .\n"
00255            << "@prefix : <#> .\n\n";
00256 }
00257 
00258 void
00259 RDFFeatureWriter::reviewFileForAppending(QString filename)
00260 {
00261     // Appending to an RDF file is tricky, because we need to ensure
00262     // that our URIs differ from any already in the file.  This is a
00263     // dirty grubby low-rent way of doing that.  This function is
00264     // called by FileFeatureWriter::getOutputFile when in append mode.
00265 
00266 //    cerr << "reviewFileForAppending(" << filename << ")" << endl;
00267 
00268     QFile file(filename);
00269 
00270     // just return, don't report failure -- function that called us will do that
00271     if (!file.open(QIODevice::ReadOnly)) return;
00272 
00273     QTextStream in(&file);
00274 
00275     QRegExp localObjectUriWithDigits(":[^ ]+_([0-9]+) a ");
00276 
00277     while (!in.atEnd()) {
00278         QString line = in.readLine();
00279         if (line.length() > 120) { // probably data
00280             continue;
00281         }
00282         if (localObjectUriWithDigits.indexIn(line) > -1) {
00283             QString numeric = localObjectUriWithDigits.cap(1);
00284             int number = numeric.toInt();
00285             if (number >= m_count) m_count = number + 1;
00286         }
00287     }
00288 
00289     file.close();
00290 }
00291 
00292 void
00293 RDFFeatureWriter::writeSignalDescription(QTextStream *sptr,
00294                                          QString trackId)
00295 {
00296 //    SVDEBUG << "RDFFeatureWriter::writeSignalDescription" << endl;
00297 
00298     QTextStream &stream = *sptr;
00299 
00300     /*
00301      * Describe signal we're analysing (AudioFile, Signal, TimeLine, etc.)
00302      */
00303     
00304     QUrl url(trackId, QUrl::StrictMode);
00305     QString scheme = url.scheme().toLower();
00306     bool local = (scheme == "" || scheme == "file" || scheme.length() == 1);
00307 
00308     if (local) {
00309         if (scheme == "") {
00310             url.setScheme("file");
00311             url.setPath(QFileInfo(url.path()).absoluteFilePath());
00312         } else if (scheme.length() == 1) { // DOS drive letter!
00313             url.setScheme("file");
00314             url.setPath(scheme + ":" + url.path());
00315         }
00316     }
00317 
00318     // Note reviewFileForAppending above (when opening in append mode)
00319 
00320     unsigned long signalCount = m_count++;
00321 
00322     if (m_trackSignalURIs.find(trackId) == m_trackSignalURIs.end()) {
00323         m_trackSignalURIs[trackId] = QString(":signal_%1").arg(signalCount);
00324     }
00325     QString signalURI = m_trackSignalURIs[trackId];
00326    
00327     if (m_trackTrackURIs.find(trackId) == m_trackTrackURIs.end()) {
00328         m_trackTrackURIs[trackId] = QString(":track_%1").arg(signalCount);
00329     }
00330     QString trackURI = m_trackTrackURIs[trackId];
00331 
00332     bool userSpecifiedTrack = false;
00333     if (m_userTrackUri != "") {
00334         trackURI = "<" + m_userTrackUri + ">";
00335         m_trackTrackURIs[trackId] = trackURI;
00336         userSpecifiedTrack = true;
00337     }
00338     
00339     if (m_trackTimelineURIs.find(trackId) == m_trackTimelineURIs.end()) {
00340         m_trackTimelineURIs[trackId] = QString(":signal_timeline_%1").arg(signalCount);
00341     }
00342     QString timelineURI = m_trackTimelineURIs[trackId];
00343 
00344     QString afURI = url.toEncoded().data();
00345     if (m_userAudioFileUri != "") afURI = m_userAudioFileUri;
00346 
00347     bool wantTrack = (userSpecifiedTrack ||
00348                       (m_userMakerUri != "") ||
00349                       (m_metadata.find(trackId) != m_metadata.end()));
00350 
00351 //    cerr << "wantTrack = " << wantTrack << " (userSpecifiedTrack = "
00352 //         << userSpecifiedTrack << ", m_userMakerUri = " << m_userMakerUri << ", have metadata = " << (m_metadata.find(trackId) != m_metadata.end()) << ")" << endl;
00353 
00354     if (wantTrack) {
00355         // We only write a Track at all if we have some title/artist
00356         // metadata to put in it, or if the user has requested a
00357         // specific track URI.  Otherwise we can't be sure that what
00358         // we have is a Track, in the publication sense -- it may just
00359         // be a fragment, a test file, whatever.  Since we'd have no
00360         // metadata to associate with our Track, the only effect of
00361         // including a Track would be to assert that this was one,
00362         // which is the one thing we wouldn't know...
00363         TrackMetadata tm;
00364         if (m_metadata.find(trackId) != m_metadata.end()) {
00365             tm = m_metadata[trackId];
00366         }
00367         stream << trackURI << " a mo:Track ";
00368         if (tm.title != "") {
00369             stream << ";\n    dc:title \"\"\"" << tm.title << "\"\"\" ";
00370         }
00371         if (m_userMakerUri != "") {
00372             stream << ";\n    foaf:maker <" << m_userMakerUri << "> ";
00373         } else if (tm.maker != "") {
00374             stream << ";\n    foaf:maker [ a mo:MusicArtist; foaf:name \"\"\"" << tm.maker << "\"\"\" ] ";
00375         }
00376         if (afURI != "") {
00377             stream << ";\n    mo:available_as <" << afURI << "> ";
00378         }
00379         stream << ".\n\n";
00380     }
00381 
00382     if (afURI != "") {
00383         stream << "<" << afURI << "> a mo:AudioFile ;\n";
00384         stream << "    mo:encodes " << signalURI << ".\n\n";
00385     }
00386 
00387     stream << signalURI << " a mo:Signal ;\n";
00388 
00389     stream << "    mo:time [\n"
00390            << "        a tl:Interval ;\n"
00391            << "        tl:onTimeLine "
00392            << timelineURI << "\n    ] .\n\n";
00393 
00394     stream << timelineURI << " a tl:Timeline .\n\n";
00395 } 
00396 
00397 void
00398 RDFFeatureWriter::writeLocalFeatureTypes(QTextStream *sptr,
00399                                          const Transform &transform,
00400                                          const Plugin::OutputDescriptor &od,
00401                                          PluginRDFDescription &desc, 
00402                                          std::string summaryType)
00403 {
00404     QString outputId = od.identifier.c_str();
00405     QTextStream &stream = *sptr;
00406 
00407     // There is no "needFeatureType" for track-level outputs, because
00408     // we can't meaningfully write a feature at all if we don't know
00409     // what property to use for it.  If the output is track level but
00410     // there is no feature type given, we have to revert to events.
00411 
00412     bool needEventType = false;
00413     bool needSignalType = false;
00414 
00416 
00417     if (summaryType == "" &&
00418         desc.getOutputDisposition(outputId) == 
00419         PluginRDFDescription::OutputDense) {
00420 
00421         // no feature events, so may need signal type but won't need
00422         // event type
00423 
00424         if (m_plain) {
00425 
00426             needSignalType = true;
00427 
00428         } else if (desc.getOutputSignalTypeURI(outputId) == "") {
00429             
00430             needSignalType = true;
00431         }
00432 
00433     } else if (desc.getOutputDisposition(outputId) ==
00434                PluginRDFDescription::OutputTrackLevel) {
00435 
00436         // see note above -- need to generate an event type if no
00437         // feature type given, or if in plain mode
00438 
00439         if (m_plain) {
00440         
00441             needEventType = true;
00442 
00443         } else if (desc.getOutputFeatureAttributeURI(outputId) == "") {
00444     
00445             if (desc.getOutputEventTypeURI(outputId) == "") {
00446 
00447                 needEventType = true;
00448             }
00449         }
00450 
00451     } else {
00452 
00453         // may need event type but won't need signal type
00454 
00455         if (m_plain) {
00456         
00457             needEventType = true;
00458     
00459         } else if (desc.getOutputEventTypeURI(outputId) == "") {
00460 
00461             needEventType = true;
00462         }
00463     }
00464 
00465     QString transformUri;
00466     if (m_transformURIs.find(transform) != m_transformURIs.end()) {
00467         transformUri = m_transformURIs[transform];
00468     } else {
00469         transformUri = QString(":transform_%1_%2").arg(m_count++).arg(outputId);
00470         m_transformURIs[transform] = transformUri;
00471     }
00472 
00473     if (transform.getIdentifier() != "") {
00474         stream << endl
00475                << RDFTransformFactory::writeTransformToRDF(transform, transformUri)
00476                << endl;
00477     }
00478 
00479     if (needEventType && m_fixedEventTypeURI == "") {
00480 
00481         QString uri;
00482         if (m_syntheticEventTypeURIs.find(transform) !=
00483             m_syntheticEventTypeURIs.end()) {
00484             uri = m_syntheticEventTypeURIs[transform];
00485         } else {
00486             uri = QString(":event_type_%1").arg(m_count++);
00487             m_syntheticEventTypeURIs[transform] = uri;
00488         }
00489 
00490         stream << uri
00491                << " rdfs:subClassOf event:Event ;" << endl
00492                << "    dc:title \"" << od.name.c_str() << "\" ;" << endl
00493                << "    dc:format \"" << od.unit.c_str() << "\" ;" << endl
00494                << "    dc:description \"" << od.description.c_str() << "\" ."
00495                << endl << endl;
00496     }
00497 
00498     if (needSignalType) {
00499 
00500         QString uri;
00501         if (m_syntheticSignalTypeURIs.find(transform) !=
00502             m_syntheticSignalTypeURIs.end()) {
00503             uri = m_syntheticSignalTypeURIs[transform];
00504         } else {
00505             uri = QString(":signal_type_%1").arg(m_count++);
00506             m_syntheticSignalTypeURIs[transform] = uri;
00507         }
00508 
00509         stream << uri
00510                << " rdfs:subClassOf af:Signal ;" << endl
00511                << "    dc:title \"" << od.name.c_str() << "\" ;" << endl
00512                << "    dc:format \"" << od.unit.c_str() << "\" ;" << endl
00513                << "    dc:description \"" << od.description.c_str() << "\" ."
00514                << endl << endl;
00515     }
00516 }
00517 
00518 void
00519 RDFFeatureWriter::writeSparseRDF(QTextStream *sptr,
00520                                  const Transform &transform,
00521                                  const Plugin::OutputDescriptor& od,
00522                                  const Plugin::FeatureList& featureList,
00523                                  PluginRDFDescription &desc,
00524                                  QString timelineURI)
00525 {
00526 //    SVDEBUG << "RDFFeatureWriter::writeSparseRDF: have " << featureList.size() << " features" << endl;
00527 
00528     if (featureList.empty()) return;
00529     QTextStream &stream = *sptr;
00530         
00531     bool plain = (m_plain || !desc.haveDescription());
00532 
00533     QString outputId = od.identifier.c_str();
00534 
00535     // iterate through FeatureLists
00536         
00537     for (int i = 0; i < (int)featureList.size(); ++i) {
00538 
00539         const Plugin::Feature &feature = featureList[i];
00540         unsigned long featureNumber = m_count++;
00541 
00542         stream << ":event_" << featureNumber << " a ";
00543 
00544         if (m_fixedEventTypeURI != "") {
00545             stream << m_fixedEventTypeURI << " ;\n";
00546         } else {
00547             QString eventTypeURI = desc.getOutputEventTypeURI(outputId);
00548             if (plain || eventTypeURI == "") {
00549                 if (m_syntheticEventTypeURIs.find(transform) != 
00550                     m_syntheticEventTypeURIs.end()) {
00551                     stream << m_syntheticEventTypeURIs[transform] << " ;\n";
00552                 } else {
00553                     stream << ":event_type_" << outputId << " ;\n";
00554                 }
00555             } else {
00556                 stream << "<" << eventTypeURI << "> ;\n";
00557             }
00558         }
00559 
00560         QString timestamp = feature.timestamp.toString().c_str();
00561         timestamp.replace(QRegExp("^ +"), "");
00562 
00563         if (feature.hasDuration && feature.duration > Vamp::RealTime::zeroTime) {
00564 
00565             QString duration = feature.duration.toString().c_str();
00566             duration.replace(QRegExp("^ +"), "");
00567 
00568             stream << "    event:time [ \n"
00569                    << "        a tl:Interval ;\n"
00570                    << "        tl:onTimeLine " << timelineURI << " ;\n"
00571                    << "        tl:beginsAt \"PT" << timestamp
00572                    << "S\"^^xsd:duration ;\n"
00573                    << "        tl:duration \"PT" << duration
00574                    << "S\"^^xsd:duration ;\n"
00575                    << "    ] ";
00576 
00577         } else {
00578 
00579             stream << "    event:time [ \n"
00580                    << "        a tl:Instant ;\n" //location of the event in time
00581                    << "        tl:onTimeLine " << timelineURI << " ;\n"
00582                    << "        tl:at \"PT" << timestamp
00583                    << "S\"^^xsd:duration ;\n    ] ";
00584         }
00585 
00586         if (transform.getIdentifier() != "") {
00587             stream << ";\n";
00588             stream << "    vamp:computed_by " << m_transformURIs[transform] << " ";
00589         }
00590 
00591         if (feature.label.length() > 0) {
00592             stream << ";\n";
00593             stream << "    rdfs:label \"\"\"" << feature.label.c_str() << "\"\"\" ";
00594         }
00595 
00596         if (!feature.values.empty()) {
00597             stream << ";\n";
00599             stream << "    af:feature \"" << feature.values[0];
00600             for (int j = 1; j < (int)feature.values.size(); ++j) {
00601                 stream << " " << feature.values[j];
00602             }
00603             stream << "\" ";
00604         }
00605 
00606         stream << ".\n";
00607     }
00608 }
00609 
00610 void
00611 RDFFeatureWriter::writeTrackLevelRDF(QTextStream *sptr,
00612                                      const Transform &,
00613                                      const Plugin::OutputDescriptor& od,
00614                                      const Plugin::FeatureList& featureList,
00615                                      PluginRDFDescription &desc,
00616                                      QString signalURI)
00617 {
00618     if (featureList.empty()) return;
00619     QTextStream &stream = *sptr;
00620         
00621 //    bool plain = (m_plain || !desc.haveDescription());
00622 
00623     QString outputId = od.identifier.c_str();
00624     QString featureUri = desc.getOutputFeatureAttributeURI(outputId);
00625 
00626     if (featureUri == "") {
00627         SVDEBUG << "RDFFeatureWriter::writeTrackLevelRDF: ERROR: No feature URI available -- this function should not have been called!" << endl;
00628         return;
00629     }
00630 
00631     for (int i = 0; i < (int)featureList.size(); ++i) {
00632 
00633         const Plugin::Feature &feature = featureList[i];
00634 
00635         if (feature.values.empty()) {
00636 
00637             if (feature.label == "") continue;
00638 
00639             stream << signalURI << " " << featureUri << " \"\"\""
00640                    << feature.label.c_str() << "\"\"\" .\n";
00641 
00642         } else {
00643 
00644             stream << signalURI << " " << featureUri << " \""
00645                    << feature.values[0] << "\"^^xsd:float .\n";
00646         }
00647     }
00648 }
00649 
00650 void
00651 RDFFeatureWriter::writeDenseRDF(QTextStream *sptr,
00652                                 const Transform &transform,
00653                                 const Plugin::OutputDescriptor& od,
00654                                 const Plugin::FeatureList& featureList,
00655                                 PluginRDFDescription &desc,
00656                                 QString signalURI, 
00657                                 QString timelineURI)
00658 {
00659     if (featureList.empty()) return;
00660 
00661     StringTransformPair sp(signalURI, transform);
00662 
00663     if (m_openDenseFeatures.find(sp) == m_openDenseFeatures.end()) {
00664 
00665         StreamBuffer b(sptr, "");
00666         m_openDenseFeatures[sp] = b;
00667         
00668         QString &str(m_openDenseFeatures[sp].second);
00669         QTextStream stream(&str);
00670 
00671         bool plain = (m_plain || !desc.haveDescription());
00672         QString outputId = od.identifier.c_str();
00673 
00674         unsigned long featureNumber = m_count++;
00675 
00676         // need to write out feature timeline map -- for this we need
00677         // the sample rate, window length and hop size from the
00678         // transform
00679 
00680         stream << "\n:feature_timeline_" << featureNumber << " a tl:DiscreteTimeLine .\n\n";
00681 
00682         int stepSize = transform.getStepSize();
00683         if (stepSize == 0) {
00684             cerr << "RDFFeatureWriter: INTERNAL ERROR: writing dense features without having set the step size properly!" << endl;
00685             return;
00686         }
00687 
00688         int blockSize = transform.getBlockSize();
00689         if (blockSize == 0) {
00690             cerr << "RDFFeatureWriter: INTERNAL ERROR: writing dense features without having set the block size properly!" << endl;
00691             return;
00692         }
00693 
00694         float sampleRate = transform.getSampleRate();
00695         if (sampleRate == 0.f) {
00696             cerr << "RDFFeatureWriter: INTERNAL ERROR: writing dense features without having set the sample rate properly!" << endl;
00697             return;
00698         }
00699 
00700         stream << ":feature_timeline_map_" << featureNumber
00701                << " a tl:UniformSamplingWindowingMap ;\n"
00702                << "    tl:rangeTimeLine :feature_timeline_" << featureNumber << " ;\n"
00703                << "    tl:domainTimeLine " << timelineURI << " ;\n"
00704                << "    tl:sampleRate \"" << int(sampleRate) << "\"^^xsd:int ;\n"
00705                << "    tl:windowLength \"" << blockSize << "\"^^xsd:int ;\n"
00706                << "    tl:hopSize \"" << stepSize << "\"^^xsd:int .\n\n";
00707 
00708         stream << signalURI << " af:signal_feature :feature_"
00709                << featureNumber << " ." << endl << endl;
00710 
00711         stream << ":feature_" << featureNumber << " a ";
00712 
00713         QString signalTypeURI = desc.getOutputSignalTypeURI(outputId);
00714         if (plain || signalTypeURI == "") {
00715             if (m_syntheticSignalTypeURIs.find(transform) !=
00716                 m_syntheticSignalTypeURIs.end()) {
00717                 stream << m_syntheticSignalTypeURIs[transform] << " ;\n";
00718             } else {
00719                 stream << ":signal_type_" << outputId << " ;\n";
00720             }
00721         } else {
00722             stream << "<" << signalTypeURI << "> ;\n";
00723         }
00724 
00725         stream << "    mo:time ["
00726                << "\n        a tl:Interval ;"
00727                << "\n        tl:onTimeLine :feature_timeline_" << featureNumber << " ;";
00728 
00729         RealTime startrt = transform.getStartTime();
00730         RealTime durationrt = transform.getDuration();
00731 
00732         int start = RealTime::realTime2Frame(startrt, sampleRate) / stepSize;
00733         int duration = RealTime::realTime2Frame(durationrt, sampleRate) / stepSize;
00734 
00735         if (start != 0) {
00736             stream << "\n        tl:start \"" << start << "\"^^xsd:int ;";
00737         }
00738         if (duration != 0) {
00739             stream << "\n        tl:duration \"" << duration << "\"^^xsd:int ;";
00740         }
00741 
00742         stream << "\n    ] ;\n";
00743 
00744         if (transform.getIdentifier() != "") {
00745             stream << "    vamp:computed_by " << m_transformURIs[transform] << " ;\n";
00746         }
00747 
00748         if (od.hasFixedBinCount) {
00749             // We only know the height, so write the width as zero
00750             stream << "    af:dimensions \"" << od.binCount << " 0\" ;\n";
00751         }
00752 
00753         stream << "    af:value \"";
00754     }
00755 
00756     QString &str = m_openDenseFeatures[sp].second;
00757     QTextStream stream(&str);
00758 
00759     for (int i = 0; i < (int)featureList.size(); ++i) {
00760 
00761         const Plugin::Feature &feature = featureList[i];
00762 
00763         for (int j = 0; j < (int)feature.values.size(); ++j) {
00764             stream << feature.values[j] << " ";
00765         }
00766     }
00767 }
00768 
00769 void RDFFeatureWriter::finish()
00770 {
00771 //    SVDEBUG << "RDFFeatureWriter::finish()" << endl;
00772 
00773     // close any open dense feature literals
00774 
00775     for (map<StringTransformPair, StreamBuffer>::iterator i =
00776              m_openDenseFeatures.begin();
00777          i != m_openDenseFeatures.end(); ++i) {
00778 //        SVDEBUG << "closing a stream" << endl;
00779         StreamBuffer &b = i->second;
00780         *(b.first) << b.second << "\" ." << endl;
00781     }
00782 
00783     m_openDenseFeatures.clear();
00784     m_startedStreamTransforms.clear();
00785 
00786     FileFeatureWriter::finish();
00787 }
00788 
00789