annotate runner/LabFeatureWriter.cpp @ 168:3e30dbb68ca2 jams

Write time or start/end based simply on whether the feature has them or not, let's not get clever
author Chris Cannam
date Wed, 15 Oct 2014 15:20:16 +0100
parents 237ccacbb85e
children 59abb58b1855
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@154 26
Chris@154 27 using namespace std;
Chris@154 28 using namespace Vamp;
Chris@154 29
Chris@154 30 LabFeatureWriter::LabFeatureWriter() :
Chris@154 31 FileFeatureWriter(SupportOneFilePerTrackTransform |
Chris@154 32 SupportStdOut,
Chris@154 33 "lab"),
Chris@154 34 m_forceEnd(false)
Chris@154 35 {
Chris@154 36 }
Chris@154 37
Chris@154 38 LabFeatureWriter::~LabFeatureWriter()
Chris@154 39 {
Chris@154 40 }
Chris@154 41
Chris@154 42 string
Chris@154 43 LabFeatureWriter::getDescription() const
Chris@154 44 {
Chris@159 45 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 46 }
Chris@154 47
Chris@154 48 LabFeatureWriter::ParameterList
Chris@154 49 LabFeatureWriter::getSupportedParameters() const
Chris@154 50 {
Chris@154 51 ParameterList pl = FileFeatureWriter::getSupportedParameters();
Chris@154 52
Chris@154 53 Parameter p;
Chris@154 54
Chris@154 55 p.name = "fill-ends";
Chris@154 56 p.description = "Include end times even for features without duration, by using the gap to the next feature instead.";
Chris@154 57 p.hasArg = false;
Chris@154 58 pl.push_back(p);
Chris@154 59
Chris@154 60 return pl;
Chris@154 61 }
Chris@154 62
Chris@154 63 void
Chris@154 64 LabFeatureWriter::setParameters(map<string, string> &params)
Chris@154 65 {
Chris@154 66 FileFeatureWriter::setParameters(params);
Chris@154 67
Chris@154 68 for (map<string, string>::iterator i = params.begin();
Chris@154 69 i != params.end(); ++i) {
Chris@154 70 if (i->first == "fill-ends") {
Chris@154 71 m_forceEnd = true;
Chris@154 72 }
Chris@154 73 }
Chris@154 74 }
Chris@154 75
Chris@154 76 void
Chris@154 77 LabFeatureWriter::write(QString trackId,
Chris@154 78 const Transform &transform,
Chris@154 79 const Plugin::OutputDescriptor& ,
Chris@154 80 const Plugin::FeatureList& features,
Chris@157 81 std::string)
Chris@154 82 {
Chris@154 83 // Select appropriate output file for our track/transform
Chris@154 84 // combination
Chris@154 85
Chris@157 86 TransformId transformId = transform.getIdentifier();
Chris@157 87
Chris@157 88 QTextStream *sptr = getOutputStream(trackId, transformId);
Chris@154 89 if (!sptr) {
Chris@157 90 throw FailedToOpenOutputStream(trackId, transformId);
Chris@154 91 }
Chris@154 92
Chris@154 93 QTextStream &stream = *sptr;
Chris@154 94
Chris@157 95 int n = features.size();
Chris@154 96
Chris@157 97 if (n == 0) return;
Chris@154 98
Chris@157 99 TrackTransformPair tt(trackId, transformId);
Chris@154 100
Chris@157 101 if (m_pending.find(tt) != m_pending.end()) {
Chris@157 102 writeFeature(stream, m_pending[tt], &features[0]);
Chris@157 103 m_pending.erase(tt);
Chris@157 104 }
Chris@154 105
Chris@157 106 if (m_forceEnd) {
Chris@157 107 // can't write final feature until we know its end time
Chris@157 108 --n;
Chris@157 109 m_pending[tt] = features[n];
Chris@157 110 }
Chris@154 111
Chris@157 112 for (int i = 0; i < n; ++i) {
Chris@157 113 writeFeature(stream, features[i], m_forceEnd ? &features[i+1] : 0);
Chris@154 114 }
Chris@154 115 }
Chris@154 116
Chris@157 117 void
Chris@157 118 LabFeatureWriter::finish()
Chris@157 119 {
Chris@157 120 for (PendingFeatures::const_iterator i = m_pending.begin();
Chris@157 121 i != m_pending.end(); ++i) {
Chris@157 122 TrackTransformPair tt = i->first;
Chris@157 123 Plugin::Feature f = i->second;
Chris@157 124 QTextStream *sptr = getOutputStream(tt.first, tt.second);
Chris@157 125 if (!sptr) {
Chris@157 126 throw FailedToOpenOutputStream(tt.first, tt.second);
Chris@157 127 }
Chris@157 128 QTextStream &stream = *sptr;
Chris@157 129 // final feature has its own time as end time (we can't
Chris@157 130 // reliably determine the end of audio file, and because of
Chris@157 131 // the nature of block processing, the feature could even
Chris@157 132 // start beyond that anyway)
Chris@157 133 writeFeature(stream, f, &f);
Chris@157 134 }
Chris@158 135
Chris@158 136 m_pending.clear();
Chris@157 137 }
Chris@154 138
Chris@157 139 void
Chris@157 140 LabFeatureWriter::writeFeature(QTextStream &stream,
Chris@157 141 const Plugin::Feature &f,
Chris@157 142 const Plugin::Feature *optionalNextFeature)
Chris@157 143 {
Chris@157 144 QString sep = "\t";
Chris@157 145
Chris@157 146 QString timestamp = f.timestamp.toString().c_str();
Chris@157 147 timestamp.replace(QRegExp("^ +"), "");
Chris@157 148 stream << timestamp;
Chris@157 149
Chris@157 150 Vamp::RealTime endTime;
Chris@157 151 bool haveEndTime = true;
Chris@157 152
Chris@157 153 if (f.hasDuration) {
Chris@157 154 endTime = f.timestamp + f.duration;
Chris@157 155 } else if (optionalNextFeature) {
Chris@157 156 endTime = optionalNextFeature->timestamp;
Chris@157 157 } else {
Chris@157 158 haveEndTime = false;
Chris@157 159 }
Chris@157 160
Chris@157 161 if (haveEndTime) {
Chris@157 162 QString e = endTime.toString().c_str();
Chris@157 163 e.replace(QRegExp("^ +"), "");
Chris@157 164 stream << sep << e;
Chris@157 165 }
Chris@157 166
Chris@157 167 for (unsigned int j = 0; j < f.values.size(); ++j) {
Chris@157 168 stream << sep << f.values[j];
Chris@157 169 }
Chris@157 170
Chris@157 171 if (f.label != "") {
Chris@157 172 stream << sep << "\"" << f.label.c_str() << "\"";
Chris@157 173 }
Chris@157 174
Chris@157 175 stream << "\n";
Chris@157 176 }
Chris@157 177
Chris@157 178