annotate runner/LabFeatureWriter.cpp @ 399:a3912193ce69 tip

Default branch is now named default on git as well as hg, in case we ever want to switch to mirroring in the other direction
author Chris Cannam
date Thu, 27 Aug 2020 15:57:37 +0100
parents e39307a8d22d
children
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@206 35 m_forceEnd(false),
Chris@206 36 m_digits(6)
Chris@154 37 {
Chris@154 38 }
Chris@154 39
Chris@154 40 LabFeatureWriter::~LabFeatureWriter()
Chris@154 41 {
Chris@154 42 }
Chris@154 43
Chris@154 44 string
Chris@154 45 LabFeatureWriter::getDescription() const
Chris@154 46 {
Chris@159 47 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 48 }
Chris@154 49
Chris@154 50 LabFeatureWriter::ParameterList
Chris@154 51 LabFeatureWriter::getSupportedParameters() const
Chris@154 52 {
Chris@154 53 ParameterList pl = FileFeatureWriter::getSupportedParameters();
Chris@154 54
Chris@154 55 Parameter p;
Chris@154 56
Chris@154 57 p.name = "fill-ends";
Chris@154 58 p.description = "Include end times even for features without duration, by using the gap to the next feature instead.";
Chris@154 59 p.hasArg = false;
Chris@154 60 pl.push_back(p);
Chris@154 61
Chris@206 62 p.name = "digits";
Chris@206 63 p.description = "Specify the number of significant digits to use when printing transform outputs. Outputs are represented internally using single-precision floating-point, so digits beyond the 8th or 9th place are usually meaningless. The default is 6.";
Chris@206 64 p.hasArg = true;
Chris@206 65 pl.push_back(p);
Chris@206 66
Chris@154 67 return pl;
Chris@154 68 }
Chris@154 69
Chris@154 70 void
Chris@154 71 LabFeatureWriter::setParameters(map<string, string> &params)
Chris@154 72 {
Chris@154 73 FileFeatureWriter::setParameters(params);
Chris@154 74
Chris@154 75 for (map<string, string>::iterator i = params.begin();
Chris@154 76 i != params.end(); ++i) {
Chris@154 77 if (i->first == "fill-ends") {
Chris@154 78 m_forceEnd = true;
Chris@206 79 } else if (i->first == "digits") {
Chris@206 80 int digits = atoi(i->second.c_str());
Chris@206 81 if (digits <= 0 || digits > 100) {
Chris@324 82 SVCERR << "LabFeatureWriter: ERROR: Invalid or out-of-range value for number of significant digits: " << i->second << endl;
Chris@324 83 SVCERR << "LabFeatureWriter: NOTE: Continuing with default settings" << endl;
Chris@206 84 } else {
Chris@206 85 m_digits = digits;
Chris@206 86 }
Chris@154 87 }
Chris@154 88 }
Chris@154 89 }
Chris@154 90
Chris@154 91 void
Chris@154 92 LabFeatureWriter::write(QString trackId,
Chris@154 93 const Transform &transform,
Chris@154 94 const Plugin::OutputDescriptor& ,
Chris@154 95 const Plugin::FeatureList& features,
Chris@157 96 std::string)
Chris@154 97 {
Chris@154 98 // Select appropriate output file for our track/transform
Chris@154 99 // combination
Chris@154 100
Chris@157 101 TransformId transformId = transform.getIdentifier();
Chris@157 102
Chris@189 103 QTextStream *sptr = getOutputStream
Chris@189 104 (trackId, transformId, QTextCodec::codecForName("UTF-8"));
Chris@154 105 if (!sptr) {
Chris@157 106 throw FailedToOpenOutputStream(trackId, transformId);
Chris@154 107 }
Chris@154 108
Chris@154 109 QTextStream &stream = *sptr;
Chris@154 110
Chris@331 111 int n = int(features.size());
Chris@154 112
Chris@157 113 if (n == 0) return;
Chris@154 114
Chris@176 115 DataId tt(trackId, transform);
Chris@154 116
Chris@157 117 if (m_pending.find(tt) != m_pending.end()) {
Chris@157 118 writeFeature(stream, m_pending[tt], &features[0]);
Chris@157 119 m_pending.erase(tt);
Chris@157 120 }
Chris@154 121
Chris@157 122 if (m_forceEnd) {
Chris@157 123 // can't write final feature until we know its end time
Chris@157 124 --n;
Chris@157 125 m_pending[tt] = features[n];
Chris@157 126 }
Chris@154 127
Chris@157 128 for (int i = 0; i < n; ++i) {
Chris@157 129 writeFeature(stream, features[i], m_forceEnd ? &features[i+1] : 0);
Chris@154 130 }
Chris@154 131 }
Chris@154 132
Chris@157 133 void
Chris@157 134 LabFeatureWriter::finish()
Chris@157 135 {
Chris@157 136 for (PendingFeatures::const_iterator i = m_pending.begin();
Chris@157 137 i != m_pending.end(); ++i) {
Chris@176 138 DataId tt = i->first;
Chris@157 139 Plugin::Feature f = i->second;
Chris@189 140 QTextStream *sptr = getOutputStream
Chris@189 141 (tt.first, tt.second.getIdentifier(),
Chris@189 142 QTextCodec::codecForName("UTF-8"));
Chris@157 143 if (!sptr) {
Chris@176 144 throw FailedToOpenOutputStream(tt.first, tt.second.getIdentifier());
Chris@157 145 }
Chris@157 146 QTextStream &stream = *sptr;
Chris@157 147 // final feature has its own time as end time (we can't
Chris@157 148 // reliably determine the end of audio file, and because of
Chris@157 149 // the nature of block processing, the feature could even
Chris@157 150 // start beyond that anyway)
Chris@157 151 writeFeature(stream, f, &f);
Chris@157 152 }
Chris@158 153
Chris@158 154 m_pending.clear();
Chris@157 155 }
Chris@154 156
Chris@157 157 void
Chris@157 158 LabFeatureWriter::writeFeature(QTextStream &stream,
Chris@157 159 const Plugin::Feature &f,
Chris@157 160 const Plugin::Feature *optionalNextFeature)
Chris@157 161 {
Chris@157 162 QString sep = "\t";
Chris@157 163
Chris@157 164 QString timestamp = f.timestamp.toString().c_str();
Chris@157 165 timestamp.replace(QRegExp("^ +"), "");
Chris@157 166 stream << timestamp;
Chris@157 167
Chris@157 168 Vamp::RealTime endTime;
Chris@157 169 bool haveEndTime = true;
Chris@157 170
Chris@157 171 if (f.hasDuration) {
Chris@157 172 endTime = f.timestamp + f.duration;
Chris@157 173 } else if (optionalNextFeature) {
Chris@157 174 endTime = optionalNextFeature->timestamp;
Chris@157 175 } else {
Chris@157 176 haveEndTime = false;
Chris@157 177 }
Chris@157 178
Chris@157 179 if (haveEndTime) {
Chris@157 180 QString e = endTime.toString().c_str();
Chris@157 181 e.replace(QRegExp("^ +"), "");
Chris@157 182 stream << sep << e;
Chris@157 183 }
Chris@157 184
Chris@157 185 for (unsigned int j = 0; j < f.values.size(); ++j) {
Chris@206 186 stream << sep << QString("%1").arg(f.values[j], 0, 'g', m_digits);
Chris@157 187 }
Chris@157 188
Chris@157 189 if (f.label != "") {
Chris@157 190 stream << sep << "\"" << f.label.c_str() << "\"";
Chris@157 191 }
Chris@157 192
Chris@157 193 stream << "\n";
Chris@157 194 }
Chris@157 195
Chris@157 196