annotate runner/JAMSFeatureWriter.cpp @ 145:3921e0c1f4dd jams

Start to sketch out JAMS writer
author Chris Cannam
date Mon, 13 Oct 2014 16:58:30 +0100
parents
children db83ea0e102d
rev   line source
Chris@145 1 /* -*- c-basic-offset: 4 indent-tabs-mode: nil -*- vi:set ts=8 sts=4 sw=4: */
Chris@145 2
Chris@145 3 /*
Chris@145 4 Sonic Annotator
Chris@145 5 A utility for batch feature extraction from audio files.
Chris@145 6 Mark Levy, Chris Sutton and Chris Cannam, Queen Mary, University of London.
Chris@145 7 Copyright 2007-2014 QMUL.
Chris@145 8
Chris@145 9 This program is free software; you can redistribute it and/or
Chris@145 10 modify it under the terms of the GNU General Public License as
Chris@145 11 published by the Free Software Foundation; either version 2 of the
Chris@145 12 License, or (at your option) any later version. See the file
Chris@145 13 COPYING included with this distribution for more information.
Chris@145 14 */
Chris@145 15
Chris@145 16 #include "JAMSFeatureWriter.h"
Chris@145 17
Chris@145 18 using namespace std;
Chris@145 19 using Vamp::Plugin;
Chris@145 20 using Vamp::PluginBase;
Chris@145 21
Chris@145 22 #include "base/Exceptions.h"
Chris@145 23 #include "rdf/PluginRDFIndexer.h"
Chris@145 24
Chris@145 25 JAMSFeatureWriter::JAMSFeatureWriter() :
Chris@145 26 FileFeatureWriter(SupportOneFilePerTrackTransform |
Chris@145 27 SupportOneFilePerTrack |
Chris@145 28 SupportStdOut,
Chris@145 29 "json"),
Chris@145 30 m_network(false),
Chris@145 31 m_networkRetrieved(false)
Chris@145 32 {
Chris@145 33 }
Chris@145 34
Chris@145 35 JAMSFeatureWriter::~JAMSFeatureWriter()
Chris@145 36 {
Chris@145 37 }
Chris@145 38
Chris@145 39 string
Chris@145 40 JAMSFeatureWriter::getDescription() const
Chris@145 41 {
Chris@145 42 return "Write features to JSON files in JAMS (JSON Annotated Music Specification) format.";
Chris@145 43 }
Chris@145 44
Chris@145 45 JAMSFeatureWriter::ParameterList
Chris@145 46 JAMSFeatureWriter::getSupportedParameters() const
Chris@145 47 {
Chris@145 48 ParameterList pl = FileFeatureWriter::getSupportedParameters();
Chris@145 49 Parameter p;
Chris@145 50
Chris@145 51 p.name = "network";
Chris@145 52 p.description = "Attempt to retrieve RDF descriptions of plugins from network, if not available locally";
Chris@145 53 p.hasArg = false;
Chris@145 54 pl.push_back(p);
Chris@145 55
Chris@145 56 return pl;
Chris@145 57 }
Chris@145 58
Chris@145 59 void
Chris@145 60 JAMSFeatureWriter::setParameters(map<string, string> &params)
Chris@145 61 {
Chris@145 62 FileFeatureWriter::setParameters(params);
Chris@145 63
Chris@145 64 for (map<string, string>::iterator i = params.begin();
Chris@145 65 i != params.end(); ++i) {
Chris@145 66 if (i->first == "network") {
Chris@145 67 m_network = true;
Chris@145 68 }
Chris@145 69 }
Chris@145 70 }
Chris@145 71
Chris@145 72 void
Chris@145 73 JAMSFeatureWriter::setTrackMetadata(QString trackId, TrackMetadata metadata)
Chris@145 74 {
Chris@145 75 QString json
Chris@145 76 ("'file_metadata':"
Chris@145 77 " { 'artist': \"%1\","
Chris@145 78 " 'title': \"%2\" }");
Chris@145 79 m_metadata[trackId] = json.arg(metadata.maker).arg(metadata.title);
Chris@145 80 }
Chris@145 81
Chris@145 82 void
Chris@145 83 JAMSFeatureWriter::write(QString trackId,
Chris@145 84 const Transform &transform,
Chris@145 85 const Plugin::OutputDescriptor& ,
Chris@145 86 const Plugin::FeatureList& features,
Chris@145 87 std::string /* summaryType */)
Chris@145 88 {
Chris@145 89 QString transformId = transform.getIdentifier();
Chris@145 90
Chris@145 91 QTextStream *sptr = getOutputStream(trackId, transformId);
Chris@145 92 if (!sptr) {
Chris@145 93 throw FailedToOpenOutputStream(trackId, transformId);
Chris@145 94 }
Chris@145 95
Chris@145 96 QTextStream &stream = *sptr;
Chris@145 97
Chris@145 98 if (m_startedTransforms.find(transformId) == m_startedTransforms.end()) {
Chris@145 99
Chris@145 100 identifyTask(transform);
Chris@145 101
Chris@145 102 if (m_manyFiles ||
Chris@145 103 (m_startedTracks.find(trackId) == m_startedTracks.end())) {
Chris@145 104
Chris@145 105 // track-level preamble
Chris@145 106 stream << "{" << m_metadata[trackId] << endl;
Chris@145 107 }
Chris@145 108
Chris@145 109 stream << "'" << getTaskKey(m_tasks[transformId]) << "':" << endl;
Chris@145 110 stream << " [ ";
Chris@145 111 }
Chris@145 112
Chris@145 113 m_startedTracks.insert(trackId);
Chris@145 114 m_startedTransforms.insert(transformId);
Chris@145 115
Chris@145 116 for (int i = 0; i < int(features.size()); ++i) {
Chris@145 117
Chris@145 118 }
Chris@145 119 }
Chris@145 120
Chris@145 121 void
Chris@145 122 JAMSFeatureWriter::loadRDFDescription(const Transform &transform)
Chris@145 123 {
Chris@145 124 QString pluginId = transform.getPluginIdentifier();
Chris@145 125 if (m_rdfDescriptions.find(pluginId) != m_rdfDescriptions.end()) return;
Chris@145 126
Chris@145 127 if (m_network && !m_networkRetrieved) {
Chris@145 128 PluginRDFIndexer::getInstance()->indexConfiguredURLs();
Chris@145 129 m_networkRetrieved = true;
Chris@145 130 }
Chris@145 131
Chris@145 132 m_rdfDescriptions[pluginId] = PluginRDFDescription(pluginId);
Chris@145 133
Chris@145 134 if (m_rdfDescriptions[pluginId].haveDescription()) {
Chris@145 135 cerr << "NOTE: Have RDF description for plugin ID \""
Chris@145 136 << pluginId << "\"" << endl;
Chris@145 137 } else {
Chris@145 138 cerr << "NOTE: No RDF description for plugin ID \""
Chris@145 139 << pluginId << "\"" << endl;
Chris@145 140 if (!m_network) {
Chris@145 141 cerr << " Consider using the --json-network option to retrieve plugin descriptions" << endl;
Chris@145 142 cerr << " from the network where possible." << endl;
Chris@145 143 }
Chris@145 144 }
Chris@145 145 }
Chris@145 146
Chris@145 147 void
Chris@145 148 JAMSFeatureWriter::identifyTask(const Transform &transform)
Chris@145 149 {
Chris@145 150 QString transformId = transform.getIdentifier();
Chris@145 151 if (m_tasks.find(transformId) != m_tasks.end()) return;
Chris@145 152
Chris@145 153 loadRDFDescription(transform);
Chris@145 154
Chris@145 155 Task task = UnknownTask;
Chris@145 156
Chris@145 157 QString pluginId = transform.getPluginIdentifier();
Chris@145 158 QString outputId = transform.getOutput();
Chris@145 159
Chris@145 160 const PluginRDFDescription &desc = m_rdfDescriptions[pluginId];
Chris@145 161
Chris@145 162 if (desc.haveDescription()) {
Chris@145 163
Chris@145 164 PluginRDFDescription::OutputDisposition disp =
Chris@145 165 desc.getOutputDisposition(outputId);
Chris@145 166
Chris@145 167 QString af = "http://purl.org/ontology/af/";
Chris@145 168
Chris@145 169 if (disp == PluginRDFDescription::OutputSparse) {
Chris@145 170
Chris@145 171 QString eventUri = desc.getOutputEventTypeURI(outputId);
Chris@145 172
Chris@145 173 //!!! todo: allow user to prod writer for task type
Chris@145 174
Chris@145 175 if (eventUri == af + "Note") {
Chris@145 176 task = NoteTask;
Chris@145 177 } else if (eventUri == af + "Beat") {
Chris@145 178 task = BeatTask;
Chris@145 179 } else if (eventUri == af + "ChordSegment") {
Chris@145 180 task = ChordTask;
Chris@145 181 } else if (eventUri == af + "KeyChange") {
Chris@145 182 task = KeyTask;
Chris@145 183 } else if (eventUri == af + "KeySegment") {
Chris@145 184 task = KeyTask;
Chris@145 185 } else if (eventUri == af + "Onset") {
Chris@145 186 task = OnsetTask;
Chris@145 187 } else if (eventUri == af + "NonTonalOnset") {
Chris@145 188 task = OnsetTask;
Chris@145 189 } else if (eventUri == af + "Segment") {
Chris@145 190 task = SegmentTask;
Chris@145 191 } else if (eventUri == af + "SpeechSegment") {
Chris@145 192 task = SegmentTask;
Chris@145 193 } else if (eventUri == af + "StructuralSegment") {
Chris@145 194 task = SegmentTask;
Chris@145 195 } else {
Chris@145 196 cerr << "WARNING: Unsupported event type URI <"
Chris@145 197 << eventUri << ">, proceeding with UnknownTask type"
Chris@145 198 << endl;
Chris@145 199 }
Chris@145 200
Chris@145 201 } else {
Chris@145 202
Chris@145 203 cerr << "WARNING: Cannot currently write dense or track-level outputs to JSON format (only sparse ones). Will proceed using UnknownTask type, but this probably isn't going to work" << endl;
Chris@145 204 }
Chris@145 205 }
Chris@145 206
Chris@145 207 m_tasks[transformId] = task;
Chris@145 208 }
Chris@145 209
Chris@145 210 QString
Chris@145 211 JAMSFeatureWriter::getTaskKey(Task task)
Chris@145 212 {
Chris@145 213 switch (task) {
Chris@145 214 case UnknownTask: return "unknown";
Chris@145 215 case BeatTask: return "beat";
Chris@145 216 case OnsetTask: return "onset";
Chris@145 217 case ChordTask: return "chord";
Chris@145 218 case SegmentTask: return "segment";
Chris@145 219 case KeyTask: return "key";
Chris@145 220 case NoteTask: return "note";
Chris@145 221 case MelodyTask: return "melody";
Chris@145 222 case PitchTask: return "pitch";
Chris@145 223 }
Chris@145 224 return "unknown";
Chris@145 225 }
Chris@145 226
Chris@145 227 void
Chris@145 228 JAMSFeatureWriter::finish()
Chris@145 229 {
Chris@145 230 for (FileStreamMap::const_iterator i = m_streams.begin();
Chris@145 231 i != m_streams.end(); ++i) {
Chris@145 232 *(i->second) << "}" << endl;
Chris@145 233 }
Chris@145 234
Chris@145 235 FileFeatureWriter::finish();
Chris@145 236 }
Chris@145 237