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(nullptr),
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  SVCERR << "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.) The directory must exist already.";
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";
93  if (m_support & SupportOneFilePerTrack) {
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  }
102  if (m_support & SupportStdOut) {
103  p.name = "stdout";
104  p.description = "Write all transform results directly to standard output.";
105  p.hasArg = false;
106  pl.push_back(p);
107  }
108 
109  p.name = "force";
110  p.description = "If an output file already exists, overwrite it.";
111  p.hasArg = false;
112  pl.push_back(p);
113 
114  p.name = "append";
115  p.description = "If an output file already exists, append data to it.";
116  p.hasArg = false;
117  pl.push_back(p);
118 
119  return pl;
120 }
121 
122 void
123 FileFeatureWriter::setParameters(map<string, string> &params)
124 {
125  for (map<string, string>::iterator i = params.begin();
126  i != params.end(); ++i) {
127  if (i->first == "basedir") {
128  m_baseDir = i->second.c_str();
129  } else if (i->first == "many-files") {
132  if (m_singleFileName != "") {
133  SVCERR << "FileFeatureWriter::setParameters: WARNING: Both one-file and many-files parameters provided, ignoring many-files" << endl;
134  } else {
135  m_manyFiles = true;
136  }
137  }
138  } else if (i->first == "one-file") {
140  if (m_support & ~SupportOneFileTotal) { // not only option
141  // No, we cannot do this test because m_manyFiles
142  // may be on by default (for any FileFeatureWriter
143  // that supports OneFilePerTrackTransform but not
144  // OneFilePerTrack), so we need to be able to
145  // override it
146 // if (m_manyFiles) {
147 // SVCERR << "FileFeatureWriter::setParameters: WARNING: Both many-files and one-file parameters provided, ignoring one-file" << endl;
148 // } else {
149  m_singleFileName = i->second.c_str();
150 // }
151  }
152  }
153  } else if (i->first == "stdout") {
154  if (m_support & SupportStdOut) {
155  if (m_singleFileName != "") {
156  SVCERR << "FileFeatureWriter::setParameters: WARNING: Both stdout and one-file provided, ignoring stdout" << endl;
157  } else {
158  m_stdout = true;
159  }
160  }
161  } else if (i->first == "append") {
162  m_append = true;
163  } else if (i->first == "force") {
164  m_force = true;
165  }
166  }
167 }
168 
169 QString
171  TransformId transformId)
172 {
173  if (m_singleFileName != "") {
174  if (QFileInfo(m_singleFileName).exists() && !(m_force || m_append)) {
175  SVCERR << endl << "FileFeatureWriter: ERROR: Specified output file \"" << m_singleFileName << "\" exists and neither --" << getWriterTag() << "-force nor --" << getWriterTag() << "-append flag is specified -- not overwriting" << endl;
176  SVCERR << "NOTE: To find out how to fix this problem, read the help for the --" << getWriterTag() << "-force" << endl << "and --" << getWriterTag() << "-append options" << endl;
177  return "";
178  }
179  return m_singleFileName;
180  }
181 
182  if (m_stdout) {
183  return "";
184  }
185 
186  QUrl url(trackId, QUrl::StrictMode);
187  QString scheme = url.scheme().toLower();
188  bool local = (scheme == "" || scheme == "file" || scheme.length() == 1);
189 
190  QString dirname, basename;
191  QString infilename = url.toLocalFile();
192  if (infilename == "") {
193  infilename = url.path();
194  }
195  basename = QFileInfo(infilename).completeBaseName();
196  if (scheme.length() == 1) {
197  infilename = scheme + ":" + infilename; // DOS drive!
198  }
199 
200 // cerr << "trackId = " << trackId << ", url = " << url.toString() << ", infilename = "
201 // << infilename << ", basename = " << basename << ", m_baseDir = " << m_baseDir << endl;
202 
203  if (m_baseDir != "") dirname = QFileInfo(m_baseDir).absoluteFilePath();
204  else if (local) dirname = QFileInfo(infilename).absolutePath();
205  else dirname = QDir::currentPath();
206 
207 // cerr << "dirname = " << dirname << endl;
208 
209  QString filename;
210 
211  if (m_manyFiles && transformId != "") {
212  filename = QString("%1_%2.%3").arg(basename).arg(transformId).arg(m_extension);
213  } else {
214  filename = QString("%1.%2").arg(basename).arg(m_extension);
215  }
216 
217  filename.replace(':', '_'); // ':' not permitted in Windows
218 
219  filename = QDir(dirname).filePath(filename);
220 
221  if (QFileInfo(filename).exists() && !(m_force || m_append)) {
222  SVCERR << 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;
223  SVCERR << "NOTE: To find out how to fix this problem, read the help for the --" << getWriterTag() << "-force" << endl << "and --" << getWriterTag() << "-append options" << endl;
224  return "";
225  }
226 
227  return filename;
228 }
229 
230 void
232  TransformId transformId)
233 {
234  // Obviously, if we're writing to stdout we can't test for an
235  // openable output file. But when writing a single file we don't
236  // want to either, because this test would fail on the second and
237  // subsequent input files (because the file would already exist).
238  // getOutputFile does the right thing in this case, so we just
239  // leave it to it
240  if (m_stdout || m_singleFileName != "") return;
241 
242  QString filename = createOutputFilename(trackId, transformId);
243  if (filename == "") {
244  throw FailedToOpenOutputStream(trackId, transformId);
245  }
246 }
247 
250  TransformId transformId)
251 {
252  TrackTransformPair key;
253 
254  if (m_singleFileName != "") {
255  key = TrackTransformPair("", "");
256  } else if (m_manyFiles) {
257  key = TrackTransformPair(trackId, transformId);
258  } else {
259  key = TrackTransformPair(trackId, "");
260  }
261 
262  return key;
263 }
264 
265 QString
267  TransformId transformId)
268 {
269  TrackTransformPair key = getFilenameKey(trackId, transformId);
270  if (m_filenames.find(key) == m_filenames.end()) {
271  m_filenames[key] = createOutputFilename(trackId, transformId);
272  }
273  return m_filenames[key];
274 }
275 
276 QFile *
278  TransformId transformId)
279 {
280  TrackTransformPair key = getFilenameKey(trackId, transformId);
281 
282  if (m_files.find(key) == m_files.end()) {
283 
284  QString filename = createOutputFilename(trackId, transformId);
285 
286  if (filename == "") { // stdout or failure
287  return nullptr;
288  }
289 
290  SVDEBUG << "FileFeatureWriter: NOTE: Using output filename \""
291  << filename << "\"" << endl;
292 
293  if (m_append) {
294  SVDEBUG << "FileFeatureWriter: NOTE: Calling reviewFileForAppending" << endl;
295  reviewFileForAppending(filename);
296  }
297 
298  QFile *file = new QFile(filename);
299  QIODevice::OpenMode mode = (QIODevice::WriteOnly);
300  if (m_append) mode |= QIODevice::Append;
301 
302  if (!file->open(mode)) {
303  SVCERR << "FileFeatureWriter: ERROR: Failed to open output file \"" << filename
304  << "\" for writing" << endl;
305  delete file;
306  m_files[key] = nullptr;
307  throw FailedToOpenFile(filename);
308  }
309 
310  m_files[key] = file;
311  }
312 
313  return m_files[key];
314 }
315 
316 
317 QTextStream *FileFeatureWriter::getOutputStream(QString trackId,
318  TransformId transformId,
319  QTextCodec *codec)
320 {
321  QFile *file = getOutputFile(trackId, transformId);
322  if (!file && !m_stdout) {
323  return nullptr;
324  }
325 
326  if (m_streams.find(file) == m_streams.end()) {
327  if (m_stdout) {
328  m_streams[file] = new QTextStream(stdout);
329  } else {
330  m_streams[file] = new QTextStream(file);
331  }
332  m_streams[file]->setCodec(codec);
333  }
334 
335  QTextStream *stream = m_streams[file];
336 
337  if (m_prevstream && stream != m_prevstream) {
338  m_prevstream->flush();
339  }
340  m_prevstream = stream;
341 
342  return stream;
343 }
344 
345 
346 void
348 {
349  if (m_prevstream) {
350  m_prevstream->flush();
351  }
352 }
353 
354 
355 void
357 {
358 // SVDEBUG << "FileFeatureWriter::finish()" << endl;
359 
360  if (m_singleFileName != "" || m_stdout) return;
361 
362  while (!m_streams.empty()) {
363  m_streams.begin()->second->flush();
364  delete m_streams.begin()->second;
365  m_streams.erase(m_streams.begin());
366  }
367  while (!m_files.empty()) {
368  if (m_files.begin()->second) {
369  SVDEBUG << "FileFeatureWriter::finish: NOTE: Closing feature file \""
370  << m_files.begin()->second->fileName() << "\"" << endl;
371  delete m_files.begin()->second;
372  }
373  m_files.erase(m_files.begin());
374  }
375  m_prevstream = nullptr;
376 }
377 
QString createOutputFilename(QString, TransformId)
FileNameMap m_filenames
QFile * getOutputFile(QString, TransformId)
QTextStream * m_prevstream
FileStreamMap m_streams
QTextStream * getOutputStream(QString, TransformId, QTextCodec *)
vector< Parameter > ParameterList
Definition: FeatureWriter.h:51
void testOutputFile(QString trackId, TransformId transformId) override
Throw FailedToOpenOutputStream if we can already tell that we will be unable to write to the output f...
void setParameters(map< string, string > &params) override
TrackTransformPair getFilenameKey(QString, TransformId)
virtual QString getWriterTag() const =0
void flush() override
#define SVDEBUG
Definition: Debug.h:106
void finish() override
#define SVCERR
Definition: Debug.h:109
pair< QString, TransformId > TrackTransformPair
QString getOutputFilename(QString, TransformId)
FileFeatureWriter(int support, QString extension)
ParameterList getSupportedParameters() const override
QString TransformId
Definition: Transform.h:30
virtual void reviewFileForAppending(QString)