annotate runner/LabFeatureWriter.cpp @ 197:3b7ec45abd1c

Add mandatory option --json-format to JSON feature writer, in preparation for supporting multiple JSON formats (perhaps) in future
author Chris Cannam
date Tue, 01 Sep 2015 17:05:32 +0100
parents 089f1a13963d
children 704a8b27f3ed
rev   line source
Chris@154 1 /* -*- c-basic-offset: 4 indent-tabs-mode: nil -*- vi:set ts=8 sts=4 sw=4: */
Chris@154 2
Chris@154 3 /*
Chris@154 4 Sonic Visualiser
Chris@154 5 An audio file viewer and annotation editor.
Chris@154 6
Chris@154 7 Sonic Annotator
Chris@154 8 A utility for batch feature extraction from audio files.
Chris@154 9
Chris@154 10 Mark Levy, Chris Sutton and Chris Cannam, Queen Mary, University of London.
Chris@154 11 Copyright 2007-2008 QMUL.
Chris@154 12
Chris@154 13 This program is free software; you can redistribute it and/or
Chris@154 14 modify it under the terms of the GNU General Public License as
Chris@154 15 published by the Free Software Foundation; either version 2 of the
Chris@154 16 License, or (at your option) any later version. See the file
Chris@154 17 COPYING included with this distribution for more information.
Chris@154 18 */
Chris@154 19
Chris@154 20 #include "LabFeatureWriter.h"
Chris@154 21
Chris@154 22 #include <iostream>
Chris@154 23
Chris@154 24 #include <QRegExp>
Chris@154 25 #include <QTextStream>
Chris@189 26 #include <QTextCodec>
Chris@154 27
Chris@154 28 using namespace std;
Chris@154 29 using namespace Vamp;
Chris@154 30
Chris@154 31 LabFeatureWriter::LabFeatureWriter() :
Chris@154 32 FileFeatureWriter(SupportOneFilePerTrackTransform |
Chris@154 33 SupportStdOut,
Chris@154 34 "lab"),
Chris@154 35 m_forceEnd(false)
Chris@154 36 {
Chris@154 37 }
Chris@154 38
Chris@154 39 LabFeatureWriter::~LabFeatureWriter()
Chris@154 40 {
Chris@154 41 }
Chris@154 42
Chris@154 43 string
Chris@154 44 LabFeatureWriter::getDescription() const
Chris@154 45 {
Chris@159 46 return "Write features in .lab, a tab-separated columnar format. The first column is always the feature start time in seconds. If the features have duration, the second column will be the feature end time in seconds. Remaining columns are the feature values (if any) and finally the feature label (if any). There is no identification of the audio file or the transform, so confusion will result if features from different audio or transforms are mixed. (For more control over the output, consider using the more general CSV writer.)";
Chris@154 47 }
Chris@154 48
Chris@154 49 LabFeatureWriter::ParameterList
Chris@154 50 LabFeatureWriter::getSupportedParameters() const
Chris@154 51 {
Chris@154 52 ParameterList pl = FileFeatureWriter::getSupportedParameters();
Chris@154 53
Chris@154 54 Parameter p;
Chris@154 55
Chris@154 56 p.name = "fill-ends";
Chris@154 57 p.description = "Include end times even for features without duration, by using the gap to the next feature instead.";
Chris@154 58 p.hasArg = false;
Chris@154 59 pl.push_back(p);
Chris@154 60
Chris@154 61 return pl;
Chris@154 62 }
Chris@154 63
Chris@154 64 void
Chris@154 65 LabFeatureWriter::setParameters(map<string, string> &params)
Chris@154 66 {
Chris@154 67 FileFeatureWriter::setParameters(params);
Chris@154 68
Chris@154 69 for (map<string, string>::iterator i = params.begin();
Chris@154 70 i != params.end(); ++i) {
Chris@154 71 if (i->first == "fill-ends") {
Chris@154 72 m_forceEnd = true;
Chris@154 73 }
Chris@154 74 }
Chris@154 75 }
Chris@154 76
Chris@154 77 void
Chris@154 78 LabFeatureWriter::write(QString trackId,
Chris@154 79 const Transform &transform,
Chris@154 80 const Plugin::OutputDescriptor& ,
Chris@154 81 const Plugin::FeatureList& features,
Chris@157 82 std::string)
Chris@154 83 {
Chris@154 84 // Select appropriate output file for our track/transform
Chris@154 85 // combination
Chris@154 86
Chris@157 87 TransformId transformId = transform.getIdentifier();
Chris@157 88
Chris@189 89 QTextStream *sptr = getOutputStream
Chris@189 90 (trackId, transformId, QTextCodec::codecForName("UTF-8"));
Chris@154 91 if (!sptr) {
Chris@157 92 throw FailedToOpenOutputStream(trackId, transformId);
Chris@154 93 }
Chris@154 94
Chris@154 95 QTextStream &stream = *sptr;
Chris@154 96
Chris@157 97 int n = features.size();
Chris@154 98
Chris@157 99 if (n == 0) return;
Chris@154 100
Chris@176 101 DataId tt(trackId, transform);
Chris@154 102
Chris@157 103 if (m_pending.find(tt) != m_pending.end()) {
Chris@157 104 writeFeature(stream, m_pending[tt], &features[0]);
Chris@157 105 m_pending.erase(tt);
Chris@157 106 }
Chris@154 107
Chris@157 108 if (m_forceEnd) {
Chris@157 109 // can't write final feature until we know its end time
Chris@157 110 --n;
Chris@157 111 m_pending[tt] = features[n];
Chris@157 112 }
Chris@154 113
Chris@157 114 for (int i = 0; i < n; ++i) {
Chris@157 115 writeFeature(stream, features[i], m_forceEnd ? &features[i+1] : 0);
Chris@154 116 }
Chris@154 117 }
Chris@154 118
Chris@157 119 void
Chris@157 120 LabFeatureWriter::finish()
Chris@157 121 {
Chris@157 122 for (PendingFeatures::const_iterator i = m_pending.begin();
Chris@157 123 i != m_pending.end(); ++i) {
Chris@176 124 DataId tt = i->first;
Chris@157 125 Plugin::Feature f = i->second;
Chris@189 126 QTextStream *sptr = getOutputStream
Chris@189 127 (tt.first, tt.second.getIdentifier(),
Chris@189 128 QTextCodec::codecForName("UTF-8"));
Chris@157 129 if (!sptr) {
Chris@176 130 throw FailedToOpenOutputStream(tt.first, tt.second.getIdentifier());
Chris@157 131 }
Chris@157 132 QTextStream &stream = *sptr;
Chris@157 133 // final feature has its own time as end time (we can't
Chris@157 134 // reliably determine the end of audio file, and because of
Chris@157 135 // the nature of block processing, the feature could even
Chris@157 136 // start beyond that anyway)
Chris@157 137 writeFeature(stream, f, &f);
Chris@157 138 }
Chris@158 139
Chris@158 140 m_pending.clear();
Chris@157 141 }
Chris@154 142
Chris@157 143 void
Chris@157 144 LabFeatureWriter::writeFeature(QTextStream &stream,
Chris@157 145 const Plugin::Feature &f,
Chris@157 146 const Plugin::Feature *optionalNextFeature)
Chris@157 147 {
Chris@157 148 QString sep = "\t";
Chris@157 149
Chris@157 150 QString timestamp = f.timestamp.toString().c_str();
Chris@157 151 timestamp.replace(QRegExp("^ +"), "");
Chris@157 152 stream << timestamp;
Chris@157 153
Chris@157 154 Vamp::RealTime endTime;
Chris@157 155 bool haveEndTime = true;
Chris@157 156
Chris@157 157 if (f.hasDuration) {
Chris@157 158 endTime = f.timestamp + f.duration;
Chris@157 159 } else if (optionalNextFeature) {
Chris@157 160 endTime = optionalNextFeature->timestamp;
Chris@157 161 } else {
Chris@157 162 haveEndTime = false;
Chris@157 163 }
Chris@157 164
Chris@157 165 if (haveEndTime) {
Chris@157 166 QString e = endTime.toString().c_str();
Chris@157 167 e.replace(QRegExp("^ +"), "");
Chris@157 168 stream << sep << e;
Chris@157 169 }
Chris@157 170
Chris@157 171 for (unsigned int j = 0; j < f.values.size(); ++j) {
Chris@157 172 stream << sep << f.values[j];
Chris@157 173 }
Chris@157 174
Chris@157 175 if (f.label != "") {
Chris@157 176 stream << sep << "\"" << f.label.c_str() << "\"";
Chris@157 177 }
Chris@157 178
Chris@157 179 stream << "\n";
Chris@157 180 }
Chris@157 181
Chris@157 182