annotate runner/AudioDBFeatureWriter.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 0e866ef12d87
children
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@324 116 SVCERR << "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@324 154 SVCERR << "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@327 161 *dbfiles[output.identifier + ".timestamp"].ofs << featureList[i].timestamp.toString() << "\n";
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@324 180 SVCERR << "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@324 184 SVCERR << "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