annotate runner/AudioDBFeatureWriter.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 b3d73c08b6ce
children ef03350baec7
rev   line source
Chris@0 1 /* -*- c-basic-offset: 4 indent-tabs-mode: nil -*- vi:set ts=8 sts=4 sw=4: */
Chris@0 2
Chris@0 3 /*
Chris@0 4 Sonic Annotator
Chris@0 5 A utility for batch feature extraction from audio files.
Chris@0 6 Mark Levy, Chris Sutton and Chris Cannam, Queen Mary, University of London.
Chris@0 7 Copyright 2007-2008 QMUL.
Chris@0 8
Chris@0 9 This program is free software; you can redistribute it and/or
Chris@0 10 modify it under the terms of the GNU General Public License as
Chris@0 11 published by the Free Software Foundation; either version 2 of the
Chris@0 12 License, or (at your option) any later version. See the file
Chris@0 13 COPYING included with this distribution for more information.
Chris@0 14 */
Chris@0 15
Chris@0 16 #include <fstream>
Chris@0 17
Chris@0 18 #include <QFileInfo>
Chris@0 19
Chris@0 20 #include "AudioDBFeatureWriter.h"
Chris@0 21
Chris@0 22 using namespace std;
Chris@0 23 using namespace Vamp;
Chris@0 24
Chris@0 25 string
Chris@0 26 AudioDBFeatureWriter::catalogueIdParam = "catid";
Chris@0 27
Chris@0 28 string
Chris@0 29 AudioDBFeatureWriter::baseDirParam = "basedir";
Chris@0 30
Chris@0 31 struct AudioDBFeatureWriter::TrackStream
Chris@0 32 {
Chris@0 33 QString trackid;
Chris@0 34 ofstream* ofs;
Chris@0 35 };
Chris@0 36
Chris@0 37 AudioDBFeatureWriter::AudioDBFeatureWriter() :
Chris@0 38 catalogueId("catalog"), baseDir("audiodb")
Chris@0 39 {
Chris@0 40
Chris@0 41 }
Chris@0 42
Chris@0 43 AudioDBFeatureWriter::~AudioDBFeatureWriter()
Chris@0 44 {
Chris@0 45 // close all open files
Chris@0 46 for (map<string, TrackStream>::iterator iter = dbfiles.begin(); iter != dbfiles.end(); ++iter)
Chris@0 47 {
Chris@0 48 if (iter->second.ofs) {
Chris@0 49 iter->second.ofs->close();
Chris@0 50 delete iter->second.ofs;
Chris@0 51 }
Chris@0 52 }
Chris@0 53
Chris@0 54 // TODO: error handling on close
Chris@0 55 }
Chris@0 56
Chris@144 57 string
Chris@144 58 AudioDBFeatureWriter::getDescription() const
Chris@144 59 {
Chris@144 60 return "Write features in a binary format intended for import into AudioDB.";
Chris@144 61 }
Chris@144 62
Chris@0 63 AudioDBFeatureWriter::ParameterList
Chris@0 64 AudioDBFeatureWriter::getSupportedParameters() const
Chris@0 65 {
Chris@0 66 ParameterList pl;
Chris@0 67 Parameter p;
Chris@0 68
Chris@0 69 p.name = catalogueIdParam;
Chris@0 70 p.description = "Catalogue ID";
Chris@0 71 p.hasArg = true;
Chris@0 72 pl.push_back(p);
Chris@0 73
Chris@0 74 p.name = baseDirParam;
Chris@0 75 p.description = "Base output directory path";
Chris@0 76 p.hasArg = true;
Chris@0 77 pl.push_back(p);
Chris@0 78
Chris@0 79 return pl;
Chris@0 80 }
Chris@0 81
Chris@0 82 void
Chris@0 83 AudioDBFeatureWriter::setParameters(map<string, string> &params)
Chris@0 84 {
Chris@0 85 if (params.find(catalogueIdParam) != params.end()) {
Chris@0 86 setCatalogueId(params[catalogueIdParam]);
Chris@0 87 params.erase(catalogueIdParam);
Chris@0 88 }
Chris@0 89 if (params.find(baseDirParam) != params.end()) {
Chris@0 90 setBaseDirectory(params[baseDirParam]);
Chris@0 91 params.erase(baseDirParam);
Chris@0 92 }
Chris@0 93 }
Chris@0 94
Chris@0 95 void
Chris@0 96 AudioDBFeatureWriter::setCatalogueId(const string &catid)
Chris@0 97 {
Chris@0 98 catalogueId = catid;
Chris@0 99 }
Chris@0 100
Chris@0 101 void
Chris@0 102 AudioDBFeatureWriter::setBaseDirectory(const string &base)
Chris@0 103 {
Chris@0 104 baseDir = base;
Chris@0 105 }
Chris@0 106
Chris@0 107 void AudioDBFeatureWriter::write(QString trackid,
Chris@138 108 const Transform &,
Chris@0 109 const Vamp::Plugin::OutputDescriptor& output,
Chris@0 110 const Vamp::Plugin::FeatureList& featureList,
Chris@0 111 std::string summaryType)
Chris@0 112 {
Chris@0 113 //!!! use summaryType
Chris@0 114 if (summaryType != "") {
Chris@0 115 //!!! IMPLEMENT
Chris@0 116 cerr << "ERROR: AudioDBFeatureWriter::write: Writing summaries is not yet implemented!" << endl;
Chris@0 117 exit(1);
Chris@0 118 }
Chris@0 119
Chris@0 120
Chris@0 121 // binary output for FeatureSet
Chris@0 122
Chris@0 123 // feature-dimension feature-1 feature-2 ...
Chris@0 124 // timestamp-1 timestamp-2 ...
Chris@0 125
Chris@0 126 // audioDB has to write each feature to a different file
Chris@0 127 // assume a simple naming convention of
Chris@0 128 // <catalog-id>/<track-id>.<feature-id>
Chris@0 129 // with timestamps in a corresponding <catalog-id>/<track-id>.<feature-id>.timestamp file
Chris@0 130 // (start and end times in seconds for each frame -- somewhat optional)
Chris@0 131
Chris@0 132 // the feature writer holds a map of open file descriptors
Chris@0 133 // the catalog-id is passed in to the feature writer's constructor
Chris@0 134
Chris@0 135 // NB -- all "floats" in the file should in fact be doubles
Chris@0 136
Chris@0 137 // TODO:
Chris@0 138 // - write feature end rather than start times, once end time is available in vamp
Chris@0 139 // - write a power file, probably by wrapping plugin in a PluginPowerAdapter :)
Chris@0 140
Chris@0 141 if (output.binCount == 0) // this kind of feature just outputs timestamps and labels, assume of no interest to audioDB
Chris@0 142 return;
Chris@0 143
Chris@138 144 for (int i = 0; i < (int)featureList.size(); ++i) {
Chris@138 145
Chris@0 146 // replace output files if necessary
Chris@0 147 if (replaceDBFile(trackid, output.identifier))
Chris@0 148 {
Chris@0 149 // write the feature length for the next track feature record
Chris@0 150 // binCount has to be set
Chris@0 151 // - it can be zero, i.e. if the output is really a set of labels + timestamps
Chris@0 152 *dbfiles[output.identifier].ofs /*<< ios::binary*/ << output.binCount;
Chris@0 153
Chris@0 154 cerr << "writing bin count " << output.binCount << " for " << output.identifier << endl;
Chris@0 155 }
Chris@0 156
Chris@0 157 if (replaceDBFile(trackid, output.identifier + ".timestamp"))
Chris@0 158 {
Chris@0 159 // write the start time to the timestamp file
Chris@0 160 // as we want it for the first feature in the file
Chris@0 161 *dbfiles[output.identifier + ".timestamp"].ofs << featureList[i].timestamp.toString() << endl;
Chris@0 162 }
Chris@0 163
Chris@0 164 if (dbfiles[output.identifier].ofs) {
Chris@138 165 for (int j = 0; j < (int) featureList[i].values.size(); ++j)
Chris@0 166 *dbfiles[output.identifier].ofs /*<< ios::binary*/ << featureList[i].values[j];
Chris@0 167
Chris@0 168 // write the *end* time of each feature to the timestamp file
Chris@0 169 // NOT IMPLEMENTED YET
Chris@0 170 // *dbfiles[output.identifier + ".timestamp"].ofs << featureList[i].timestamp.toString() << endl;
Chris@0 171 }
Chris@0 172 }
Chris@0 173 }
Chris@0 174
Chris@0 175 bool AudioDBFeatureWriter::openDBFile(QString trackid, const string& identifier)
Chris@0 176 {
Chris@0 177 QString trackBase = QFileInfo(trackid).fileName();
Chris@0 178 string filepath = baseDir + "/" + catalogueId + "/"
Chris@0 179 + trackBase.toStdString() + "." + identifier;
Chris@0 180 cerr << "AudioDBFeatureWriter::openDBFile: filepath is \"" << filepath << "\"" << endl;
Chris@0 181 ofstream* ofs = new ofstream(filepath.c_str());
Chris@0 182 if (!*ofs)
Chris@0 183 {
Chris@0 184 cerr << "ERROR AudioDBFeatureWriter::openDBFile(): can't open file " << filepath << endl;
Chris@0 185 return false;
Chris@0 186 }
Chris@0 187 TrackStream ts;
Chris@0 188 ts.trackid = trackid;
Chris@0 189 ts.ofs = ofs;
Chris@0 190 dbfiles[identifier] = ts;
Chris@0 191 return true;
Chris@0 192 }
Chris@0 193
Chris@0 194 // replace file if no file open for this track, else return false
Chris@0 195 bool AudioDBFeatureWriter::replaceDBFile(QString trackid,
Chris@0 196 const string& identifier)
Chris@0 197 {
Chris@0 198 if (dbfiles.find(identifier) != dbfiles.end() && dbfiles[identifier].trackid == trackid)
Chris@0 199 return false; // have an open file for this track
Chris@0 200
Chris@0 201 if (dbfiles.find(identifier) != dbfiles.end() && dbfiles[identifier].trackid != trackid)
Chris@0 202 {
Chris@0 203 // close the current file
Chris@0 204 if (dbfiles[identifier].ofs) {
Chris@0 205 dbfiles[identifier].ofs->close();
Chris@0 206 delete dbfiles[identifier].ofs;
Chris@0 207 dbfiles[identifier].ofs = 0;
Chris@0 208 }
Chris@0 209 }
Chris@0 210
Chris@0 211 // open a new file
Chris@0 212 if (!openDBFile(trackid, identifier)) {
Chris@0 213 dbfiles[identifier].ofs = 0;
Chris@0 214 return false; //!!! should throw an exception, otherwise we'll try to open the file again and again every time we want to write to it
Chris@0 215 }
Chris@0 216
Chris@0 217 return true;
Chris@0 218 }
Chris@0 219
Chris@0 220