svcore
1.9
|
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> ¶ms) 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