svcore  1.9
FileFeatureWriter.cpp
Go to the documentation of this file.
1 /* -*- c-basic-offset: 4 indent-tabs-mode: nil -*- vi:set ts=8 sts=4 sw=4: */
2 
3 /*
4  Sonic Visualiser
5  An audio file viewer and annotation editor.
6 
7  Sonic Annotator
8  A utility for batch feature extraction from audio files.
9 
10  Mark Levy, Chris Sutton and Chris Cannam, Queen Mary, University of London.
11  Copyright 2007-2008 QMUL.
12 
13  This program is free software; you can redistribute it and/or
14  modify it under the terms of the GNU General Public License as
15  published by the Free Software Foundation; either version 2 of the
16  License, or (at your option) any later version. See the file
17  COPYING included with this distribution for more information.
18 */
19 
20 #include "FileFeatureWriter.h"
21 
22 #include "base/Exceptions.h"
23 
24 #include <QTextStream>
25 #include <QFile>
26 #include <QFileInfo>
27 #include <QUrl>
28 #include <QDir>
29 
30 using namespace std;
31 using namespace Vamp;
32 
34  QString extension) :
35  m_prevstream(0),
36  m_support(support),
37  m_extension(extension),
38  m_manyFiles(false),
39  m_stdout(false),
40  m_append(false),
41  m_force(false)
42 {
45  m_manyFiles = true;
46  } else if (m_support & SupportOneFileTotal) {
47  m_singleFileName = QString("output.%1").arg(m_extension);
48  } else {
49  SVDEBUG << "FileFeatureWriter::FileFeatureWriter: ERROR: Invalid support specification " << support << endl;
50  }
51  }
52 }
53 
55 {
56  while (!m_streams.empty()) {
57  m_streams.begin()->second->flush();
58  delete m_streams.begin()->second;
59  m_streams.erase(m_streams.begin());
60  }
61  while (!m_files.empty()) {
62  if (m_files.begin()->second) {
63  SVDEBUG << "FileFeatureWriter::~FileFeatureWriter: NOTE: Closing feature file \""
64  << m_files.begin()->second->fileName() << "\"" << endl;
65  delete m_files.begin()->second;
66  }
67  m_files.erase(m_files.begin());
68  }
69 }
70 
73 {
74  ParameterList pl;
75  Parameter p;
76 
77  p.name = "basedir";
78  p.description = "Base output directory path. (The default is the same directory as the input file.)";
79  p.hasArg = true;
80  pl.push_back(p);
81 
84  p.name = "many-files";
85  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.)";
86  p.hasArg = false;
87  pl.push_back(p);
88  }
89 
91  if (m_support & ~SupportOneFileTotal) { // not only option
92  p.name = "one-file";
94  p.description = "Write all transform results for all input files into the single named output file. (The default is to create one output file per input audio file, and write all transform results for that input into it.)";
95  } else {
96  p.description = "Write all transform results for all input files into the single named output file. (The default is to create a separate output file for each combination of input audio file and transform.)";
97  }
98  p.hasArg = true;
99  pl.push_back(p);
100  }
101  p.name = "stdout";
102  p.description = "Write all transform results directly to standard output.";
103  p.hasArg = false;
104  pl.push_back(p);
105  }
106 
107  p.name = "force";
108  p.description = "If an output file already exists, overwrite it.";
109  p.hasArg = false;
110  pl.push_back(p);
111 
112  p.name = "append";
113  p.description = "If an output file already exists, append data to it.";
114  p.hasArg = false;
115  pl.push_back(p);
116 
117  return pl;
118 }
119 
120 void
121 FileFeatureWriter::setParameters(map<string, string> &params)
122 {
123  for (map<string, string>::iterator i = params.begin();
124  i != params.end(); ++i) {
125  if (i->first == "basedir") {
126  m_baseDir = i->second.c_str();
127  } else if (i->first == "many-files") {
130  if (m_singleFileName != "") {
131  SVDEBUG << "FileFeatureWriter::setParameters: WARNING: Both one-file and many-files parameters provided, ignoring many-files" << endl;
132  } else {
133  m_manyFiles = true;
134  }
135  }
136  } else if (i->first == "one-file") {
138  if (m_support & ~SupportOneFileTotal) { // not only option
139  // No, we cannot do this test because m_manyFiles
140  // may be on by default (for any FileFeatureWriter
141  // that supports OneFilePerTrackTransform but not
142  // OneFilePerTrack), so we need to be able to
143  // override it
144 // if (m_manyFiles) {
145 // SVDEBUG << "FileFeatureWriter::setParameters: WARNING: Both many-files and one-file parameters provided, ignoring one-file" << endl;
146 // } else {
147  m_singleFileName = i->second.c_str();
148 // }
149  }
150  }
151  } else if (i->first == "stdout") {
153  if (m_singleFileName != "") {
154  SVDEBUG << "FileFeatureWriter::setParameters: WARNING: Both stdout and one-file provided, ignoring stdout" << endl;
155  } else {
156  m_stdout = true;
157  }
158  }
159  } else if (i->first == "append") {
160  m_append = true;
161  } else if (i->first == "force") {
162  m_force = true;
163  }
164  }
165 }
166 
167 QString
169  TransformId transformId)
170 {
171  if (m_singleFileName != "") {
172  if (QFileInfo(m_singleFileName).exists() && !(m_force || m_append)) {
173  cerr << endl << "FileFeatureWriter: ERROR: Specified output file \"" << m_singleFileName << "\" exists and neither --" << getWriterTag() << "-force nor --" << getWriterTag() << "-append flag is specified -- not overwriting" << endl;
174  SVDEBUG << "NOTE: To find out how to fix this problem, read the help for the --" << getWriterTag() << "-force" << endl << "and --" << getWriterTag() << "-append options" << endl;
175  return "";
176  }
177  return m_singleFileName;
178  }
179 
180  if (m_stdout) return "";
181 
182  QUrl url(trackId, QUrl::StrictMode);
183  QString scheme = url.scheme().toLower();
184  bool local = (scheme == "" || scheme == "file" || scheme.length() == 1);
185 
186  QString dirname, basename;
187  QString infilename = url.toLocalFile();
188  if (infilename == "") {
189  infilename = url.path();
190  }
191  basename = QFileInfo(infilename).completeBaseName();
192  if (scheme.length() == 1) {
193  infilename = scheme + ":" + infilename; // DOS drive!
194  }
195 
196 // cerr << "trackId = " << trackId << ", url = " << url.toString() << ", infilename = "
197 // << infilename << ", basename = " << basename << ", m_baseDir = " << m_baseDir << endl;
198 
199  if (m_baseDir != "") dirname = QFileInfo(m_baseDir).absoluteFilePath();
200  else if (local) dirname = QFileInfo(infilename).absolutePath();
201  else dirname = QDir::currentPath();
202 
203 // cerr << "dirname = " << dirname << endl;
204 
205  QString filename;
206 
207  if (m_manyFiles && transformId != "") {
208  filename = QString("%1_%2.%3").arg(basename).arg(transformId).arg(m_extension);
209  } else {
210  filename = QString("%1.%2").arg(basename).arg(m_extension);
211  }
212 
213  filename.replace(':', '_'); // ':' not permitted in Windows
214 
215  filename = QDir(dirname).filePath(filename);
216 
217  if (QFileInfo(filename).exists() && !(m_force || m_append)) {
218  cerr << endl << "FileFeatureWriter: ERROR: Output file \"" << filename << "\" exists (for input file or URL \"" << trackId << "\" and transform \"" << transformId << "\") and neither --" << getWriterTag() << "-force nor --" << getWriterTag() << "-append is specified -- not overwriting" << endl;
219  SVDEBUG << "NOTE: To find out how to fix this problem, read the help for the --" << getWriterTag() << "-force" << endl << "and --" << getWriterTag() << "-append options" << endl;
220  return "";
221  }
222 
223  return filename;
224 }
225 
226 void
228  TransformId transformId)
229 {
230  // Obviously, if we're writing to stdout we can't test for an
231  // openable output file. But when writing a single file we don't
232  // want to either, because this test would fail on the second and
233  // subsequent input files (because the file would already exist).
234  // getOutputFile does the right thing in this case, so we just
235  // leave it to it
236  if (m_stdout || m_singleFileName != "") return;
237 
238  QString filename = getOutputFilename(trackId, transformId);
239  if (filename == "") {
240  throw FailedToOpenOutputStream(trackId, transformId);
241  }
242 }
243 
244 QFile *
246  TransformId transformId)
247 {
248  pair<QString, TransformId> key;
249 
250  if (m_singleFileName != "") {
251  key = pair<QString, TransformId>("", "");
252  } else if (m_manyFiles) {
253  key = pair<QString, TransformId>(trackId, transformId);
254  } else {
255  key = pair<QString, TransformId>(trackId, "");
256  }
257 
258  if (m_files.find(key) == m_files.end()) {
259 
260  QString filename = getOutputFilename(trackId, transformId);
261 
262  if (filename == "") { // stdout or failure
263  return 0;
264  }
265 
266  SVDEBUG << "FileFeatureWriter: NOTE: Using output filename \""
267  << filename << "\"" << endl;
268 
269  if (m_append) {
270  SVDEBUG << "FileFeatureWriter: NOTE: Calling reviewFileForAppending" << endl;
271  reviewFileForAppending(filename);
272  }
273 
274  QFile *file = new QFile(filename);
275  QIODevice::OpenMode mode = (QIODevice::WriteOnly);
276  if (m_append) mode |= QIODevice::Append;
277 
278  if (!file->open(mode)) {
279  cerr << "FileFeatureWriter: ERROR: Failed to open output file \"" << filename
280  << "\" for writing" << endl;
281  delete file;
282  m_files[key] = 0;
283  throw FailedToOpenFile(filename);
284  }
285 
286  m_files[key] = file;
287  }
288 
289  return m_files[key];
290 }
291 
292 
293 QTextStream *FileFeatureWriter::getOutputStream(QString trackId,
294  TransformId transformId)
295 {
296  QFile *file = getOutputFile(trackId, transformId);
297  if (!file && !m_stdout) {
298  return 0;
299  }
300 
301  if (m_streams.find(file) == m_streams.end()) {
302  if (m_stdout) {
303  m_streams[file] = new QTextStream(stdout);
304  } else {
305  m_streams[file] = new QTextStream(file);
306  }
307  }
308 
309  QTextStream *stream = m_streams[file];
310 
311  if (m_prevstream && stream != m_prevstream) {
312  m_prevstream->flush();
313  }
314  m_prevstream = stream;
315 
316  return stream;
317 }
318 
319 
320 void
322 {
323  if (m_prevstream) {
324  m_prevstream->flush();
325  }
326 }
327 
328 
329 void
331 {
332 // SVDEBUG << "FileFeatureWriter::finish()" << endl;
333 
334  if (m_singleFileName != "" || m_stdout) return;
335 
336  while (!m_streams.empty()) {
337  m_streams.begin()->second->flush();
338  delete m_streams.begin()->second;
339  m_streams.erase(m_streams.begin());
340  }
341  while (!m_files.empty()) {
342  if (m_files.begin()->second) {
343  SVDEBUG << "FileFeatureWriter::finish: NOTE: Closing feature file \""
344  << m_files.begin()->second->fileName() << "\"" << endl;
345  delete m_files.begin()->second;
346  }
347  m_files.erase(m_files.begin());
348  }
349  m_prevstream = 0;
350 }
351 
virtual ParameterList getSupportedParameters() const
QFile * getOutputFile(QString, TransformId)
QTextStream * m_prevstream
FileStreamMap m_streams
vector< Parameter > ParameterList
Definition: FeatureWriter.h:47
virtual void testOutputFile(QString trackId, TransformId transformId)
Throw FailedToOpenOutputStream if we can already tell that we will be unable to write to the output f...
virtual void setParameters(map< string, string > &params)
virtual QString getWriterTag() const =0
#define SVDEBUG
Definition: Debug.h:42
QString getOutputFilename(QString, TransformId)
FileFeatureWriter(int support, QString extension)
QTextStream * getOutputStream(QString, TransformId)
QString TransformId
Definition: Transform.h:30
virtual void reviewFileForAppending(QString)