annotate runner/AudioDBFeatureWriter.cpp @ 12:d20c728c9840

* Try to avoid leaving output files open after completion
author Chris Cannam
date Tue, 20 Jan 2009 18:10:13 +0000
parents 92911f967a16
children ee56e3e9eeb5
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@0 57 AudioDBFeatureWriter::ParameterList
Chris@0 58 AudioDBFeatureWriter::getSupportedParameters() const
Chris@0 59 {
Chris@0 60 ParameterList pl;
Chris@0 61 Parameter p;
Chris@0 62
Chris@0 63 p.name = catalogueIdParam;
Chris@0 64 p.description = "Catalogue ID";
Chris@0 65 p.hasArg = true;
Chris@0 66 pl.push_back(p);
Chris@0 67
Chris@0 68 p.name = baseDirParam;
Chris@0 69 p.description = "Base output directory path";
Chris@0 70 p.hasArg = true;
Chris@0 71 pl.push_back(p);
Chris@0 72
Chris@0 73 return pl;
Chris@0 74 }
Chris@0 75
Chris@0 76 void
Chris@0 77 AudioDBFeatureWriter::setParameters(map<string, string> &params)
Chris@0 78 {
Chris@0 79 if (params.find(catalogueIdParam) != params.end()) {
Chris@0 80 setCatalogueId(params[catalogueIdParam]);
Chris@0 81 params.erase(catalogueIdParam);
Chris@0 82 }
Chris@0 83 if (params.find(baseDirParam) != params.end()) {
Chris@0 84 setBaseDirectory(params[baseDirParam]);
Chris@0 85 params.erase(baseDirParam);
Chris@0 86 }
Chris@0 87 }
Chris@0 88
Chris@0 89 void
Chris@0 90 AudioDBFeatureWriter::setCatalogueId(const string &catid)
Chris@0 91 {
Chris@0 92 catalogueId = catid;
Chris@0 93 }
Chris@0 94
Chris@0 95 void
Chris@0 96 AudioDBFeatureWriter::setBaseDirectory(const string &base)
Chris@0 97 {
Chris@0 98 baseDir = base;
Chris@0 99 }
Chris@0 100
Chris@0 101 void AudioDBFeatureWriter::write(QString trackid,
Chris@0 102 const Transform &transform,
Chris@0 103 const Vamp::Plugin::OutputDescriptor& output,
Chris@0 104 const Vamp::Plugin::FeatureList& featureList,
Chris@0 105 std::string summaryType)
Chris@0 106 {
Chris@0 107 //!!! use summaryType
Chris@0 108 if (summaryType != "") {
Chris@0 109 //!!! IMPLEMENT
Chris@0 110 cerr << "ERROR: AudioDBFeatureWriter::write: Writing summaries is not yet implemented!" << endl;
Chris@0 111 exit(1);
Chris@0 112 }
Chris@0 113
Chris@0 114
Chris@0 115 // binary output for FeatureSet
Chris@0 116
Chris@0 117 // feature-dimension feature-1 feature-2 ...
Chris@0 118 // timestamp-1 timestamp-2 ...
Chris@0 119
Chris@0 120 // audioDB has to write each feature to a different file
Chris@0 121 // assume a simple naming convention of
Chris@0 122 // <catalog-id>/<track-id>.<feature-id>
Chris@0 123 // with timestamps in a corresponding <catalog-id>/<track-id>.<feature-id>.timestamp file
Chris@0 124 // (start and end times in seconds for each frame -- somewhat optional)
Chris@0 125
Chris@0 126 // the feature writer holds a map of open file descriptors
Chris@0 127 // the catalog-id is passed in to the feature writer's constructor
Chris@0 128
Chris@0 129 // NB -- all "floats" in the file should in fact be doubles
Chris@0 130
Chris@0 131 // TODO:
Chris@0 132 // - write feature end rather than start times, once end time is available in vamp
Chris@0 133 // - write a power file, probably by wrapping plugin in a PluginPowerAdapter :)
Chris@0 134
Chris@0 135 if (output.binCount == 0) // this kind of feature just outputs timestamps and labels, assume of no interest to audioDB
Chris@0 136 return;
Chris@0 137
Chris@0 138 for (int i = 0; i < featureList.size(); ++i)
Chris@0 139 {
Chris@0 140 // replace output files if necessary
Chris@0 141 if (replaceDBFile(trackid, output.identifier))
Chris@0 142 {
Chris@0 143 // write the feature length for the next track feature record
Chris@0 144 // binCount has to be set
Chris@0 145 // - it can be zero, i.e. if the output is really a set of labels + timestamps
Chris@0 146 *dbfiles[output.identifier].ofs /*<< ios::binary*/ << output.binCount;
Chris@0 147
Chris@0 148 cerr << "writing bin count " << output.binCount << " for " << output.identifier << endl;
Chris@0 149 }
Chris@0 150
Chris@0 151 if (replaceDBFile(trackid, output.identifier + ".timestamp"))
Chris@0 152 {
Chris@0 153 // write the start time to the timestamp file
Chris@0 154 // as we want it for the first feature in the file
Chris@0 155 *dbfiles[output.identifier + ".timestamp"].ofs << featureList[i].timestamp.toString() << endl;
Chris@0 156 }
Chris@0 157
Chris@0 158 if (dbfiles[output.identifier].ofs) {
Chris@0 159 for (int j = 0; j < featureList[i].values.size(); ++j)
Chris@0 160 *dbfiles[output.identifier].ofs /*<< ios::binary*/ << featureList[i].values[j];
Chris@0 161
Chris@0 162 // write the *end* time of each feature to the timestamp file
Chris@0 163 // NOT IMPLEMENTED YET
Chris@0 164 // *dbfiles[output.identifier + ".timestamp"].ofs << featureList[i].timestamp.toString() << endl;
Chris@0 165 }
Chris@0 166 }
Chris@0 167 }
Chris@0 168
Chris@0 169 bool AudioDBFeatureWriter::openDBFile(QString trackid, const string& identifier)
Chris@0 170 {
Chris@0 171 QString trackBase = QFileInfo(trackid).fileName();
Chris@0 172 string filepath = baseDir + "/" + catalogueId + "/"
Chris@0 173 + trackBase.toStdString() + "." + identifier;
Chris@0 174 cerr << "AudioDBFeatureWriter::openDBFile: filepath is \"" << filepath << "\"" << endl;
Chris@0 175 ofstream* ofs = new ofstream(filepath.c_str());
Chris@0 176 if (!*ofs)
Chris@0 177 {
Chris@0 178 cerr << "ERROR AudioDBFeatureWriter::openDBFile(): can't open file " << filepath << endl;
Chris@0 179 return false;
Chris@0 180 }
Chris@0 181 TrackStream ts;
Chris@0 182 ts.trackid = trackid;
Chris@0 183 ts.ofs = ofs;
Chris@0 184 dbfiles[identifier] = ts;
Chris@0 185 return true;
Chris@0 186 }
Chris@0 187
Chris@0 188 // replace file if no file open for this track, else return false
Chris@0 189 bool AudioDBFeatureWriter::replaceDBFile(QString trackid,
Chris@0 190 const string& identifier)
Chris@0 191 {
Chris@0 192 if (dbfiles.find(identifier) != dbfiles.end() && dbfiles[identifier].trackid == trackid)
Chris@0 193 return false; // have an open file for this track
Chris@0 194
Chris@0 195 if (dbfiles.find(identifier) != dbfiles.end() && dbfiles[identifier].trackid != trackid)
Chris@0 196 {
Chris@0 197 // close the current file
Chris@0 198 if (dbfiles[identifier].ofs) {
Chris@0 199 dbfiles[identifier].ofs->close();
Chris@0 200 delete dbfiles[identifier].ofs;
Chris@0 201 dbfiles[identifier].ofs = 0;
Chris@0 202 }
Chris@0 203 }
Chris@0 204
Chris@0 205 // open a new file
Chris@0 206 if (!openDBFile(trackid, identifier)) {
Chris@0 207 dbfiles[identifier].ofs = 0;
Chris@0 208 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 209 }
Chris@0 210
Chris@0 211 return true;
Chris@0 212 }
Chris@0 213
Chris@0 214