Chris@145: /* -*- c-basic-offset: 4 indent-tabs-mode: nil -*- vi:set ts=8 sts=4 sw=4: */ Chris@145: Chris@145: /* Chris@145: Sonic Annotator Chris@145: A utility for batch feature extraction from audio files. Chris@145: Mark Levy, Chris Sutton and Chris Cannam, Queen Mary, University of London. Chris@145: Copyright 2007-2014 QMUL. Chris@145: Chris@145: This program is free software; you can redistribute it and/or Chris@145: modify it under the terms of the GNU General Public License as Chris@145: published by the Free Software Foundation; either version 2 of the Chris@145: License, or (at your option) any later version. See the file Chris@145: COPYING included with this distribution for more information. Chris@145: */ Chris@145: Chris@145: #include "JAMSFeatureWriter.h" Chris@145: Chris@145: using namespace std; Chris@145: using Vamp::Plugin; Chris@145: using Vamp::PluginBase; Chris@145: Chris@145: #include "base/Exceptions.h" Chris@145: #include "rdf/PluginRDFIndexer.h" Chris@145: Chris@145: JAMSFeatureWriter::JAMSFeatureWriter() : Chris@145: FileFeatureWriter(SupportOneFilePerTrackTransform | Chris@145: SupportOneFilePerTrack | Chris@145: SupportStdOut, Chris@145: "json"), Chris@145: m_network(false), Chris@145: m_networkRetrieved(false) Chris@145: { Chris@145: } Chris@145: Chris@145: JAMSFeatureWriter::~JAMSFeatureWriter() Chris@145: { Chris@145: } Chris@145: Chris@145: string Chris@145: JAMSFeatureWriter::getDescription() const Chris@145: { Chris@145: return "Write features to JSON files in JAMS (JSON Annotated Music Specification) format."; Chris@145: } Chris@145: Chris@145: JAMSFeatureWriter::ParameterList Chris@145: JAMSFeatureWriter::getSupportedParameters() const Chris@145: { Chris@145: ParameterList pl = FileFeatureWriter::getSupportedParameters(); Chris@145: Parameter p; Chris@145: Chris@145: p.name = "network"; Chris@145: p.description = "Attempt to retrieve RDF descriptions of plugins from network, if not available locally"; Chris@145: p.hasArg = false; Chris@145: pl.push_back(p); Chris@145: Chris@145: return pl; Chris@145: } Chris@145: Chris@145: void Chris@145: JAMSFeatureWriter::setParameters(map ¶ms) Chris@145: { Chris@145: FileFeatureWriter::setParameters(params); Chris@145: Chris@145: for (map::iterator i = params.begin(); Chris@145: i != params.end(); ++i) { Chris@145: if (i->first == "network") { Chris@145: m_network = true; Chris@145: } Chris@145: } Chris@145: } Chris@145: Chris@145: void Chris@145: JAMSFeatureWriter::setTrackMetadata(QString trackId, TrackMetadata metadata) Chris@145: { Chris@145: QString json Chris@145: ("'file_metadata':" Chris@145: " { 'artist': \"%1\"," Chris@145: " 'title': \"%2\" }"); Chris@145: m_metadata[trackId] = json.arg(metadata.maker).arg(metadata.title); Chris@145: } Chris@145: Chris@145: void Chris@145: JAMSFeatureWriter::write(QString trackId, Chris@145: const Transform &transform, Chris@145: const Plugin::OutputDescriptor& , Chris@145: const Plugin::FeatureList& features, Chris@145: std::string /* summaryType */) Chris@145: { Chris@145: QString transformId = transform.getIdentifier(); Chris@145: Chris@145: QTextStream *sptr = getOutputStream(trackId, transformId); Chris@145: if (!sptr) { Chris@145: throw FailedToOpenOutputStream(trackId, transformId); Chris@145: } Chris@145: Chris@145: QTextStream &stream = *sptr; Chris@145: Chris@145: if (m_startedTransforms.find(transformId) == m_startedTransforms.end()) { Chris@145: Chris@145: identifyTask(transform); Chris@145: Chris@145: if (m_manyFiles || Chris@145: (m_startedTracks.find(trackId) == m_startedTracks.end())) { Chris@145: Chris@145: // track-level preamble Chris@145: stream << "{" << m_metadata[trackId] << endl; Chris@145: } Chris@145: Chris@145: stream << "'" << getTaskKey(m_tasks[transformId]) << "':" << endl; Chris@145: stream << " [ "; Chris@145: } Chris@145: Chris@145: m_startedTracks.insert(trackId); Chris@145: m_startedTransforms.insert(transformId); Chris@145: Chris@145: for (int i = 0; i < int(features.size()); ++i) { Chris@145: Chris@145: } Chris@145: } Chris@145: Chris@145: void Chris@145: JAMSFeatureWriter::loadRDFDescription(const Transform &transform) Chris@145: { Chris@145: QString pluginId = transform.getPluginIdentifier(); Chris@145: if (m_rdfDescriptions.find(pluginId) != m_rdfDescriptions.end()) return; Chris@145: Chris@145: if (m_network && !m_networkRetrieved) { Chris@145: PluginRDFIndexer::getInstance()->indexConfiguredURLs(); Chris@145: m_networkRetrieved = true; Chris@145: } Chris@145: Chris@145: m_rdfDescriptions[pluginId] = PluginRDFDescription(pluginId); Chris@145: Chris@145: if (m_rdfDescriptions[pluginId].haveDescription()) { Chris@145: cerr << "NOTE: Have RDF description for plugin ID \"" Chris@145: << pluginId << "\"" << endl; Chris@145: } else { Chris@145: cerr << "NOTE: No RDF description for plugin ID \"" Chris@145: << pluginId << "\"" << endl; Chris@145: if (!m_network) { Chris@145: cerr << " Consider using the --json-network option to retrieve plugin descriptions" << endl; Chris@145: cerr << " from the network where possible." << endl; Chris@145: } Chris@145: } Chris@145: } Chris@145: Chris@145: void Chris@145: JAMSFeatureWriter::identifyTask(const Transform &transform) Chris@145: { Chris@145: QString transformId = transform.getIdentifier(); Chris@145: if (m_tasks.find(transformId) != m_tasks.end()) return; Chris@145: Chris@145: loadRDFDescription(transform); Chris@145: Chris@145: Task task = UnknownTask; Chris@145: Chris@145: QString pluginId = transform.getPluginIdentifier(); Chris@145: QString outputId = transform.getOutput(); Chris@145: Chris@145: const PluginRDFDescription &desc = m_rdfDescriptions[pluginId]; Chris@145: Chris@145: if (desc.haveDescription()) { Chris@145: Chris@145: PluginRDFDescription::OutputDisposition disp = Chris@145: desc.getOutputDisposition(outputId); Chris@145: Chris@145: QString af = "http://purl.org/ontology/af/"; Chris@145: Chris@145: if (disp == PluginRDFDescription::OutputSparse) { Chris@145: Chris@145: QString eventUri = desc.getOutputEventTypeURI(outputId); Chris@145: Chris@145: //!!! todo: allow user to prod writer for task type Chris@145: Chris@145: if (eventUri == af + "Note") { Chris@145: task = NoteTask; Chris@145: } else if (eventUri == af + "Beat") { Chris@145: task = BeatTask; Chris@145: } else if (eventUri == af + "ChordSegment") { Chris@145: task = ChordTask; Chris@145: } else if (eventUri == af + "KeyChange") { Chris@145: task = KeyTask; Chris@145: } else if (eventUri == af + "KeySegment") { Chris@145: task = KeyTask; Chris@145: } else if (eventUri == af + "Onset") { Chris@145: task = OnsetTask; Chris@145: } else if (eventUri == af + "NonTonalOnset") { Chris@145: task = OnsetTask; Chris@145: } else if (eventUri == af + "Segment") { Chris@145: task = SegmentTask; Chris@145: } else if (eventUri == af + "SpeechSegment") { Chris@145: task = SegmentTask; Chris@145: } else if (eventUri == af + "StructuralSegment") { Chris@145: task = SegmentTask; Chris@145: } else { Chris@145: cerr << "WARNING: Unsupported event type URI <" Chris@145: << eventUri << ">, proceeding with UnknownTask type" Chris@145: << endl; Chris@145: } Chris@145: Chris@145: } else { Chris@145: Chris@145: 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: } Chris@145: } Chris@145: Chris@145: m_tasks[transformId] = task; Chris@145: } Chris@145: Chris@145: QString Chris@145: JAMSFeatureWriter::getTaskKey(Task task) Chris@145: { Chris@145: switch (task) { Chris@145: case UnknownTask: return "unknown"; Chris@145: case BeatTask: return "beat"; Chris@145: case OnsetTask: return "onset"; Chris@145: case ChordTask: return "chord"; Chris@145: case SegmentTask: return "segment"; Chris@145: case KeyTask: return "key"; Chris@145: case NoteTask: return "note"; Chris@145: case MelodyTask: return "melody"; Chris@145: case PitchTask: return "pitch"; Chris@145: } Chris@145: return "unknown"; Chris@145: } Chris@145: Chris@145: void Chris@145: JAMSFeatureWriter::finish() Chris@145: { Chris@145: for (FileStreamMap::const_iterator i = m_streams.begin(); Chris@145: i != m_streams.end(); ++i) { Chris@145: *(i->second) << "}" << endl; Chris@145: } Chris@145: Chris@145: FileFeatureWriter::finish(); Chris@145: } Chris@145: