annotate transform/FileFeatureWriter.cpp @ 600:3f6d9930bc28

* Fix crash on exit on win32. This was caused by incorrectly using fftw_free() to free the string returned from fftw_export_wisdom_to_string(), instead of plain free(). * Various debug artifacts related to tracking down the above
author Chris Cannam
date Fri, 14 Aug 2009 14:01:09 +0000
parents 7065e921f122
children 4541581067f3
rev   line source
Chris@498 1 /* -*- c-basic-offset: 4 indent-tabs-mode: nil -*- vi:set ts=8 sts=4 sw=4: */
Chris@498 2
Chris@498 3 /*
Chris@498 4 Sonic Visualiser
Chris@498 5 An audio file viewer and annotation editor.
Chris@498 6
Chris@498 7 Sonic Annotator
Chris@498 8 A utility for batch feature extraction from audio files.
Chris@498 9
Chris@498 10 Mark Levy, Chris Sutton and Chris Cannam, Queen Mary, University of London.
Chris@498 11 Copyright 2007-2008 QMUL.
Chris@498 12
Chris@498 13 This program is free software; you can redistribute it and/or
Chris@498 14 modify it under the terms of the GNU General Public License as
Chris@498 15 published by the Free Software Foundation; either version 2 of the
Chris@498 16 License, or (at your option) any later version. See the file
Chris@498 17 COPYING included with this distribution for more information.
Chris@498 18 */
Chris@498 19
Chris@498 20 #include "FileFeatureWriter.h"
Chris@498 21
Chris@498 22 #include "base/Exceptions.h"
Chris@498 23
Chris@498 24 #include <QTextStream>
Chris@498 25 #include <QFile>
Chris@498 26 #include <QFileInfo>
Chris@498 27 #include <QUrl>
Chris@498 28 #include <QDir>
Chris@498 29
Chris@498 30 using namespace std;
Chris@498 31 using namespace Vamp;
Chris@498 32
Chris@498 33 FileFeatureWriter::FileFeatureWriter(int support,
Chris@498 34 QString extension) :
Chris@512 35 m_prevstream(0),
Chris@498 36 m_support(support),
Chris@498 37 m_extension(extension),
Chris@498 38 m_manyFiles(false),
Chris@498 39 m_stdout(false),
Chris@498 40 m_append(false),
Chris@498 41 m_force(false)
Chris@498 42 {
Chris@498 43 if (!(m_support & SupportOneFilePerTrack)) {
Chris@498 44 if (m_support & SupportOneFilePerTrackTransform) {
Chris@498 45 m_manyFiles = true;
Chris@498 46 } else if (m_support & SupportOneFileTotal) {
Chris@498 47 m_singleFileName = QString("output.%1").arg(m_extension);
Chris@498 48 } else {
Chris@498 49 cerr << "FileFeatureWriter::FileFeatureWriter: ERROR: Invalid support specification " << support << endl;
Chris@498 50 }
Chris@498 51 }
Chris@498 52 }
Chris@498 53
Chris@498 54 FileFeatureWriter::~FileFeatureWriter()
Chris@498 55 {
Chris@498 56 while (!m_streams.empty()) {
Chris@498 57 m_streams.begin()->second->flush();
Chris@498 58 delete m_streams.begin()->second;
Chris@498 59 m_streams.erase(m_streams.begin());
Chris@498 60 }
Chris@498 61 while (!m_files.empty()) {
Chris@498 62 delete m_files.begin()->second;
Chris@498 63 m_files.erase(m_files.begin());
Chris@498 64 }
Chris@498 65 }
Chris@498 66
Chris@498 67 FileFeatureWriter::ParameterList
Chris@498 68 FileFeatureWriter::getSupportedParameters() const
Chris@498 69 {
Chris@498 70 ParameterList pl;
Chris@498 71 Parameter p;
Chris@498 72
Chris@498 73 p.name = "basedir";
Chris@498 74 p.description = "Base output directory path. (The default is the same directory as the input file.)";
Chris@498 75 p.hasArg = true;
Chris@498 76 pl.push_back(p);
Chris@498 77
Chris@498 78 if (m_support & SupportOneFilePerTrackTransform &&
Chris@498 79 m_support & SupportOneFilePerTrack) {
Chris@498 80 p.name = "many-files";
Chris@498 81 p.description = "Create a separate output file for every combination of input file and transform. The output file names will be based on the input file names. (The default is to create one output file per input audio file, and write all transform results for that input into it.)";
Chris@498 82 p.hasArg = false;
Chris@498 83 pl.push_back(p);
Chris@498 84 }
Chris@498 85
Chris@498 86 if (m_support & SupportOneFileTotal) {
Chris@498 87 if (m_support & ~SupportOneFileTotal) { // not only option
Chris@498 88 p.name = "one-file";
Chris@498 89 p.description = "Write all transform results for all input files into the single named output file.";
Chris@498 90 p.hasArg = true;
Chris@498 91 pl.push_back(p);
Chris@498 92 }
Chris@498 93 p.name = "stdout";
Chris@498 94 p.description = "Write all transform results directly to standard output.";
Chris@498 95 p.hasArg = false;
Chris@498 96 pl.push_back(p);
Chris@498 97 }
Chris@498 98
Chris@498 99 p.name = "force";
Chris@498 100 p.description = "If an output file already exists, overwrite it.";
Chris@498 101 p.hasArg = false;
Chris@498 102 pl.push_back(p);
Chris@498 103
Chris@498 104 p.name = "append";
Chris@498 105 p.description = "If an output file already exists, append data to it.";
Chris@498 106 p.hasArg = false;
Chris@498 107 pl.push_back(p);
Chris@498 108
Chris@498 109 return pl;
Chris@498 110 }
Chris@498 111
Chris@498 112 void
Chris@498 113 FileFeatureWriter::setParameters(map<string, string> &params)
Chris@498 114 {
Chris@498 115 for (map<string, string>::iterator i = params.begin();
Chris@498 116 i != params.end(); ++i) {
Chris@498 117 if (i->first == "basedir") {
Chris@498 118 m_baseDir = i->second.c_str();
Chris@498 119 } else if (i->first == "many-files") {
Chris@498 120 if (m_support & SupportOneFilePerTrackTransform &&
Chris@498 121 m_support & SupportOneFilePerTrack) {
Chris@498 122 if (m_singleFileName != "") {
Chris@498 123 cerr << "FileFeatureWriter::setParameters: WARNING: Both one-file and many-files parameters provided, ignoring many-files" << endl;
Chris@498 124 } else {
Chris@498 125 m_manyFiles = true;
Chris@498 126 }
Chris@498 127 }
Chris@498 128 } else if (i->first == "one-file") {
Chris@498 129 if (m_support & SupportOneFileTotal) {
Chris@498 130 if (m_support & ~SupportOneFileTotal) { // not only option
Chris@498 131 if (m_manyFiles) {
Chris@498 132 cerr << "FileFeatureWriter::setParameters: WARNING: Both many-files and one-file parameters provided, ignoring one-file" << endl;
Chris@498 133 } else {
Chris@498 134 m_singleFileName = i->second.c_str();
Chris@498 135 }
Chris@498 136 }
Chris@498 137 }
Chris@498 138 } else if (i->first == "stdout") {
Chris@498 139 if (m_support & SupportOneFileTotal) {
Chris@498 140 if (m_singleFileName != "") {
Chris@498 141 cerr << "FileFeatureWriter::setParameters: WARNING: Both stdout and one-file provided, ignoring stdout" << endl;
Chris@498 142 } else {
Chris@498 143 m_stdout = true;
Chris@498 144 }
Chris@498 145 }
Chris@498 146 } else if (i->first == "append") {
Chris@498 147 m_append = true;
Chris@498 148 } else if (i->first == "force") {
Chris@498 149 m_force = true;
Chris@498 150 }
Chris@498 151 }
Chris@498 152 }
Chris@498 153
Chris@498 154 QString FileFeatureWriter::getOutputFilename(QString trackId,
Chris@498 155 TransformId transformId)
Chris@498 156 {
Chris@498 157 if (m_singleFileName != "") {
Chris@498 158 if (QFileInfo(m_singleFileName).exists() && !(m_force || m_append)) {
Chris@498 159 cerr << "FileFeatureWriter: ERROR: Specified output file \"" << m_singleFileName.toStdString() << "\" exists and neither force nor append flag is specified -- not overwriting" << endl;
Chris@498 160 return "";
Chris@498 161 }
Chris@498 162 return m_singleFileName;
Chris@498 163 }
Chris@498 164
Chris@498 165 if (m_stdout) return "";
Chris@498 166
Chris@498 167 QUrl url(trackId);
Chris@498 168 QString scheme = url.scheme().toLower();
Chris@498 169 bool local = (scheme == "" || scheme == "file" || scheme.length() == 1);
Chris@498 170
Chris@498 171 QString dirname, basename;
Chris@498 172 QString infilename = url.toLocalFile();
Chris@519 173 if (infilename == "") {
Chris@519 174 infilename = url.path();
Chris@519 175 }
Chris@498 176 basename = QFileInfo(infilename).baseName();
Chris@519 177 if (scheme.length() == 1) {
Chris@519 178 infilename = scheme + ":" + infilename; // DOS drive!
Chris@519 179 }
Chris@507 180
Chris@524 181 // cerr << "trackId = " << trackId.toStdString() << ", url = " << url.toString().toStdString() << ", infilename = "
Chris@524 182 // << infilename.toStdString() << ", basename = " << basename.toStdString() << endl;
Chris@498 183
Chris@498 184
Chris@498 185 if (m_baseDir != "") dirname = QFileInfo(m_baseDir).absoluteFilePath();
Chris@498 186 else if (local) dirname = QFileInfo(infilename).absolutePath();
Chris@498 187 else dirname = QDir::currentPath();
Chris@498 188
Chris@498 189 QString filename;
Chris@498 190
Chris@498 191 if (m_manyFiles && transformId != "") {
Chris@514 192 filename = QString("%1_%2.%3").arg(basename).arg(transformId).arg(m_extension);
Chris@498 193 } else {
Chris@498 194 filename = QString("%1.%2").arg(basename).arg(m_extension);
Chris@498 195 }
Chris@498 196
Chris@514 197 filename.replace(':', '_'); // ':' not permitted in Windows
Chris@514 198
Chris@498 199 filename = QDir(dirname).filePath(filename);
Chris@498 200
Chris@498 201 if (QFileInfo(filename).exists() && !(m_force || m_append)) {
Chris@498 202 cerr << "FileFeatureWriter: ERROR: Output file \"" << filename.toStdString() << "\" exists (for input file or URL \"" << trackId.toStdString() << "\" and transform \"" << transformId.toStdString() << "\") and neither force nor append is specified -- not overwriting" << endl;
Chris@498 203 return "";
Chris@498 204 }
Chris@498 205
Chris@498 206 return filename;
Chris@498 207 }
Chris@498 208
Chris@498 209
Chris@498 210 QFile *FileFeatureWriter::getOutputFile(QString trackId,
Chris@498 211 TransformId transformId)
Chris@498 212 {
Chris@498 213 pair<QString, TransformId> key;
Chris@498 214
Chris@498 215 if (m_singleFileName != "") {
Chris@498 216 key = pair<QString, TransformId>("", "");
Chris@498 217 } else if (m_manyFiles) {
Chris@498 218 key = pair<QString, TransformId>(trackId, transformId);
Chris@498 219 } else {
Chris@498 220 key = pair<QString, TransformId>(trackId, "");
Chris@498 221 }
Chris@498 222
Chris@498 223 if (m_files.find(key) == m_files.end()) {
Chris@498 224
Chris@498 225 QString filename = getOutputFilename(trackId, transformId);
Chris@498 226
Chris@498 227 if (filename == "") { // stdout
Chris@498 228 return 0;
Chris@498 229 }
Chris@498 230
Chris@498 231 cerr << "FileFeatureWriter: NOTE: Using output filename \""
Chris@498 232 << filename.toStdString() << "\"" << endl;
Chris@498 233
Chris@591 234 if (m_append) {
Chris@591 235 cerr << "FileFeatureWriter: NOTE: Calling reviewFileForAppending" << endl;
Chris@591 236 reviewFileForAppending(filename);
Chris@591 237 }
Chris@591 238
Chris@498 239 QFile *file = new QFile(filename);
Chris@498 240 QIODevice::OpenMode mode = (QIODevice::WriteOnly);
Chris@498 241 if (m_append) mode |= QIODevice::Append;
Chris@498 242
Chris@498 243 if (!file->open(mode)) {
Chris@498 244 cerr << "FileFeatureWriter: ERROR: Failed to open output file \"" << filename.toStdString()
Chris@498 245 << "\" for writing" << endl;
Chris@498 246 delete file;
Chris@498 247 m_files[key] = 0;
Chris@498 248 throw FailedToOpenFile(filename);
Chris@498 249 }
Chris@498 250
Chris@498 251 m_files[key] = file;
Chris@498 252 }
Chris@498 253
Chris@498 254 return m_files[key];
Chris@498 255 }
Chris@498 256
Chris@498 257
Chris@498 258 QTextStream *FileFeatureWriter::getOutputStream(QString trackId,
Chris@498 259 TransformId transformId)
Chris@498 260 {
Chris@498 261 QFile *file = getOutputFile(trackId, transformId);
Chris@498 262 if (!file && !m_stdout) {
Chris@498 263 return 0;
Chris@498 264 }
Chris@498 265
Chris@498 266 if (m_streams.find(file) == m_streams.end()) {
Chris@498 267 if (m_stdout) {
Chris@498 268 m_streams[file] = new QTextStream(stdout);
Chris@498 269 } else {
Chris@498 270 m_streams[file] = new QTextStream(file);
Chris@498 271 }
Chris@498 272 }
Chris@498 273
Chris@512 274 QTextStream *stream = m_streams[file];
Chris@512 275
Chris@512 276 if (m_prevstream && stream != m_prevstream) {
Chris@512 277 m_prevstream->flush();
Chris@512 278 }
Chris@512 279 m_prevstream = stream;
Chris@512 280
Chris@512 281 return stream;
Chris@498 282 }
Chris@498 283
Chris@515 284
Chris@515 285 void
Chris@515 286 FileFeatureWriter::flush()
Chris@515 287 {
Chris@515 288 if (m_prevstream) {
Chris@515 289 m_prevstream->flush();
Chris@515 290 }
Chris@515 291 }
Chris@515 292
Chris@531 293
Chris@531 294 void
Chris@531 295 FileFeatureWriter::finish()
Chris@531 296 {
Chris@579 297 // cerr << "FileFeatureWriter::finish()" << endl;
Chris@531 298
Chris@531 299 if (m_singleFileName != "" || m_stdout) return;
Chris@531 300
Chris@531 301 while (!m_streams.empty()) {
Chris@531 302 m_streams.begin()->second->flush();
Chris@531 303 delete m_streams.begin()->second;
Chris@531 304 m_streams.erase(m_streams.begin());
Chris@531 305 }
Chris@531 306 while (!m_files.empty()) {
Chris@531 307 delete m_files.begin()->second;
Chris@531 308 m_files.erase(m_files.begin());
Chris@531 309 }
Chris@531 310 m_prevstream = 0;
Chris@531 311 }
Chris@531 312