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@604
|
62 cerr << "FileFeatureWriter::~FileFeatureWriter: NOTE: Closing feature file \""
|
Chris@604
|
63 << m_files.begin()->second->fileName().toStdString() << "\"" << endl;
|
Chris@498
|
64 delete m_files.begin()->second;
|
Chris@498
|
65 m_files.erase(m_files.begin());
|
Chris@498
|
66 }
|
Chris@498
|
67 }
|
Chris@498
|
68
|
Chris@498
|
69 FileFeatureWriter::ParameterList
|
Chris@498
|
70 FileFeatureWriter::getSupportedParameters() const
|
Chris@498
|
71 {
|
Chris@498
|
72 ParameterList pl;
|
Chris@498
|
73 Parameter p;
|
Chris@498
|
74
|
Chris@498
|
75 p.name = "basedir";
|
Chris@498
|
76 p.description = "Base output directory path. (The default is the same directory as the input file.)";
|
Chris@498
|
77 p.hasArg = true;
|
Chris@498
|
78 pl.push_back(p);
|
Chris@498
|
79
|
Chris@498
|
80 if (m_support & SupportOneFilePerTrackTransform &&
|
Chris@498
|
81 m_support & SupportOneFilePerTrack) {
|
Chris@498
|
82 p.name = "many-files";
|
Chris@498
|
83 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
|
84 p.hasArg = false;
|
Chris@498
|
85 pl.push_back(p);
|
Chris@498
|
86 }
|
Chris@498
|
87
|
Chris@498
|
88 if (m_support & SupportOneFileTotal) {
|
Chris@498
|
89 if (m_support & ~SupportOneFileTotal) { // not only option
|
Chris@498
|
90 p.name = "one-file";
|
Chris@498
|
91 p.description = "Write all transform results for all input files into the single named output file.";
|
Chris@498
|
92 p.hasArg = true;
|
Chris@498
|
93 pl.push_back(p);
|
Chris@498
|
94 }
|
Chris@498
|
95 p.name = "stdout";
|
Chris@498
|
96 p.description = "Write all transform results directly to standard output.";
|
Chris@498
|
97 p.hasArg = false;
|
Chris@498
|
98 pl.push_back(p);
|
Chris@498
|
99 }
|
Chris@498
|
100
|
Chris@498
|
101 p.name = "force";
|
Chris@498
|
102 p.description = "If an output file already exists, overwrite it.";
|
Chris@498
|
103 p.hasArg = false;
|
Chris@498
|
104 pl.push_back(p);
|
Chris@498
|
105
|
Chris@498
|
106 p.name = "append";
|
Chris@498
|
107 p.description = "If an output file already exists, append data to it.";
|
Chris@498
|
108 p.hasArg = false;
|
Chris@498
|
109 pl.push_back(p);
|
Chris@498
|
110
|
Chris@498
|
111 return pl;
|
Chris@498
|
112 }
|
Chris@498
|
113
|
Chris@498
|
114 void
|
Chris@498
|
115 FileFeatureWriter::setParameters(map<string, string> ¶ms)
|
Chris@498
|
116 {
|
Chris@498
|
117 for (map<string, string>::iterator i = params.begin();
|
Chris@498
|
118 i != params.end(); ++i) {
|
Chris@498
|
119 if (i->first == "basedir") {
|
Chris@498
|
120 m_baseDir = i->second.c_str();
|
Chris@498
|
121 } else if (i->first == "many-files") {
|
Chris@498
|
122 if (m_support & SupportOneFilePerTrackTransform &&
|
Chris@498
|
123 m_support & SupportOneFilePerTrack) {
|
Chris@498
|
124 if (m_singleFileName != "") {
|
Chris@498
|
125 cerr << "FileFeatureWriter::setParameters: WARNING: Both one-file and many-files parameters provided, ignoring many-files" << endl;
|
Chris@498
|
126 } else {
|
Chris@498
|
127 m_manyFiles = true;
|
Chris@498
|
128 }
|
Chris@498
|
129 }
|
Chris@498
|
130 } else if (i->first == "one-file") {
|
Chris@498
|
131 if (m_support & SupportOneFileTotal) {
|
Chris@498
|
132 if (m_support & ~SupportOneFileTotal) { // not only option
|
Chris@498
|
133 if (m_manyFiles) {
|
Chris@498
|
134 cerr << "FileFeatureWriter::setParameters: WARNING: Both many-files and one-file parameters provided, ignoring one-file" << endl;
|
Chris@498
|
135 } else {
|
Chris@498
|
136 m_singleFileName = i->second.c_str();
|
Chris@498
|
137 }
|
Chris@498
|
138 }
|
Chris@498
|
139 }
|
Chris@498
|
140 } else if (i->first == "stdout") {
|
Chris@498
|
141 if (m_support & SupportOneFileTotal) {
|
Chris@498
|
142 if (m_singleFileName != "") {
|
Chris@498
|
143 cerr << "FileFeatureWriter::setParameters: WARNING: Both stdout and one-file provided, ignoring stdout" << endl;
|
Chris@498
|
144 } else {
|
Chris@498
|
145 m_stdout = true;
|
Chris@498
|
146 }
|
Chris@498
|
147 }
|
Chris@498
|
148 } else if (i->first == "append") {
|
Chris@498
|
149 m_append = true;
|
Chris@498
|
150 } else if (i->first == "force") {
|
Chris@498
|
151 m_force = true;
|
Chris@498
|
152 }
|
Chris@498
|
153 }
|
Chris@498
|
154 }
|
Chris@498
|
155
|
Chris@498
|
156 QString FileFeatureWriter::getOutputFilename(QString trackId,
|
Chris@498
|
157 TransformId transformId)
|
Chris@498
|
158 {
|
Chris@498
|
159 if (m_singleFileName != "") {
|
Chris@498
|
160 if (QFileInfo(m_singleFileName).exists() && !(m_force || m_append)) {
|
Chris@604
|
161 cerr << endl << "FileFeatureWriter: ERROR: Specified output file \"" << m_singleFileName.toStdString() << "\" exists and neither --" << getWriterTag().toStdString() << "-force nor --" << getWriterTag().toStdString() << "-append flag is specified -- not overwriting" << endl;
|
Chris@604
|
162 cerr << "NOTE: To find out how to fix this problem, read the help for the --" << getWriterTag().toStdString() << "-force" << endl << "and --" << getWriterTag().toStdString() << "-append options" << endl;
|
Chris@498
|
163 return "";
|
Chris@498
|
164 }
|
Chris@498
|
165 return m_singleFileName;
|
Chris@498
|
166 }
|
Chris@498
|
167
|
Chris@498
|
168 if (m_stdout) return "";
|
Chris@498
|
169
|
Christophe@615
|
170 QUrl url(trackId, QUrl::StrictMode);
|
Chris@498
|
171 QString scheme = url.scheme().toLower();
|
Chris@498
|
172 bool local = (scheme == "" || scheme == "file" || scheme.length() == 1);
|
Chris@498
|
173
|
Chris@498
|
174 QString dirname, basename;
|
Chris@498
|
175 QString infilename = url.toLocalFile();
|
Chris@519
|
176 if (infilename == "") {
|
Chris@519
|
177 infilename = url.path();
|
Chris@519
|
178 }
|
Christophe@615
|
179 basename = QFileInfo(infilename).completeBaseName();
|
Chris@519
|
180 if (scheme.length() == 1) {
|
Chris@519
|
181 infilename = scheme + ":" + infilename; // DOS drive!
|
Chris@519
|
182 }
|
Chris@507
|
183
|
Chris@604
|
184 cerr << "trackId = " << trackId.toStdString() << ", url = " << url.toString().toStdString() << ", infilename = "
|
Chris@604
|
185 << infilename.toStdString() << ", basename = " << basename.toStdString() << ", m_baseDir = " << m_baseDir.toStdString() << endl;
|
Chris@498
|
186
|
Chris@498
|
187 if (m_baseDir != "") dirname = QFileInfo(m_baseDir).absoluteFilePath();
|
Chris@498
|
188 else if (local) dirname = QFileInfo(infilename).absolutePath();
|
Chris@498
|
189 else dirname = QDir::currentPath();
|
Chris@498
|
190
|
Chris@604
|
191 cerr << "dirname = " << dirname.toStdString() << endl;
|
Chris@604
|
192
|
Chris@498
|
193 QString filename;
|
Chris@498
|
194
|
Chris@498
|
195 if (m_manyFiles && transformId != "") {
|
Chris@514
|
196 filename = QString("%1_%2.%3").arg(basename).arg(transformId).arg(m_extension);
|
Chris@498
|
197 } else {
|
Chris@498
|
198 filename = QString("%1.%2").arg(basename).arg(m_extension);
|
Chris@498
|
199 }
|
Chris@498
|
200
|
Chris@514
|
201 filename.replace(':', '_'); // ':' not permitted in Windows
|
Chris@514
|
202
|
Chris@498
|
203 filename = QDir(dirname).filePath(filename);
|
Chris@498
|
204
|
Chris@498
|
205 if (QFileInfo(filename).exists() && !(m_force || m_append)) {
|
Chris@604
|
206 cerr << endl << "FileFeatureWriter: ERROR: Output file \"" << filename.toStdString() << "\" exists (for input file or URL \"" << trackId.toStdString() << "\" and transform \"" << transformId.toStdString() << "\") and neither --" << getWriterTag().toStdString() << "-force nor --" << getWriterTag().toStdString() << "-append is specified -- not overwriting" << endl;
|
Chris@604
|
207 cerr << "NOTE: To find out how to fix this problem, read the help for the --" << getWriterTag().toStdString() << "-force" << endl << "and --" << getWriterTag().toStdString() << "-append options" << endl;
|
Chris@498
|
208 return "";
|
Chris@498
|
209 }
|
Chris@498
|
210
|
Chris@498
|
211 return filename;
|
Chris@498
|
212 }
|
Chris@498
|
213
|
Chris@498
|
214
|
Chris@498
|
215 QFile *FileFeatureWriter::getOutputFile(QString trackId,
|
Chris@498
|
216 TransformId transformId)
|
Chris@498
|
217 {
|
Chris@498
|
218 pair<QString, TransformId> key;
|
Chris@498
|
219
|
Chris@498
|
220 if (m_singleFileName != "") {
|
Chris@498
|
221 key = pair<QString, TransformId>("", "");
|
Chris@498
|
222 } else if (m_manyFiles) {
|
Chris@498
|
223 key = pair<QString, TransformId>(trackId, transformId);
|
Chris@498
|
224 } else {
|
Chris@498
|
225 key = pair<QString, TransformId>(trackId, "");
|
Chris@498
|
226 }
|
Chris@498
|
227
|
Chris@498
|
228 if (m_files.find(key) == m_files.end()) {
|
Chris@498
|
229
|
Chris@498
|
230 QString filename = getOutputFilename(trackId, transformId);
|
Chris@498
|
231
|
Chris@498
|
232 if (filename == "") { // stdout
|
Chris@498
|
233 return 0;
|
Chris@498
|
234 }
|
Chris@498
|
235
|
Chris@498
|
236 cerr << "FileFeatureWriter: NOTE: Using output filename \""
|
Chris@498
|
237 << filename.toStdString() << "\"" << endl;
|
Chris@498
|
238
|
Chris@591
|
239 if (m_append) {
|
Chris@591
|
240 cerr << "FileFeatureWriter: NOTE: Calling reviewFileForAppending" << endl;
|
Chris@591
|
241 reviewFileForAppending(filename);
|
Chris@591
|
242 }
|
Chris@591
|
243
|
Chris@498
|
244 QFile *file = new QFile(filename);
|
Chris@498
|
245 QIODevice::OpenMode mode = (QIODevice::WriteOnly);
|
Chris@498
|
246 if (m_append) mode |= QIODevice::Append;
|
Chris@498
|
247
|
Chris@498
|
248 if (!file->open(mode)) {
|
Chris@498
|
249 cerr << "FileFeatureWriter: ERROR: Failed to open output file \"" << filename.toStdString()
|
Chris@498
|
250 << "\" for writing" << endl;
|
Chris@498
|
251 delete file;
|
Chris@498
|
252 m_files[key] = 0;
|
Chris@498
|
253 throw FailedToOpenFile(filename);
|
Chris@498
|
254 }
|
Chris@498
|
255
|
Chris@498
|
256 m_files[key] = file;
|
Chris@498
|
257 }
|
Chris@498
|
258
|
Chris@498
|
259 return m_files[key];
|
Chris@498
|
260 }
|
Chris@498
|
261
|
Chris@498
|
262
|
Chris@498
|
263 QTextStream *FileFeatureWriter::getOutputStream(QString trackId,
|
Chris@498
|
264 TransformId transformId)
|
Chris@498
|
265 {
|
Chris@498
|
266 QFile *file = getOutputFile(trackId, transformId);
|
Chris@498
|
267 if (!file && !m_stdout) {
|
Chris@498
|
268 return 0;
|
Chris@498
|
269 }
|
Chris@498
|
270
|
Chris@498
|
271 if (m_streams.find(file) == m_streams.end()) {
|
Chris@498
|
272 if (m_stdout) {
|
Chris@498
|
273 m_streams[file] = new QTextStream(stdout);
|
Chris@498
|
274 } else {
|
Chris@498
|
275 m_streams[file] = new QTextStream(file);
|
Chris@498
|
276 }
|
Chris@498
|
277 }
|
Chris@498
|
278
|
Chris@512
|
279 QTextStream *stream = m_streams[file];
|
Chris@512
|
280
|
Chris@512
|
281 if (m_prevstream && stream != m_prevstream) {
|
Chris@512
|
282 m_prevstream->flush();
|
Chris@512
|
283 }
|
Chris@512
|
284 m_prevstream = stream;
|
Chris@512
|
285
|
Chris@512
|
286 return stream;
|
Chris@498
|
287 }
|
Chris@498
|
288
|
Chris@515
|
289
|
Chris@515
|
290 void
|
Chris@515
|
291 FileFeatureWriter::flush()
|
Chris@515
|
292 {
|
Chris@515
|
293 if (m_prevstream) {
|
Chris@515
|
294 m_prevstream->flush();
|
Chris@515
|
295 }
|
Chris@515
|
296 }
|
Chris@515
|
297
|
Chris@531
|
298
|
Chris@531
|
299 void
|
Chris@531
|
300 FileFeatureWriter::finish()
|
Chris@531
|
301 {
|
Chris@579
|
302 // cerr << "FileFeatureWriter::finish()" << endl;
|
Chris@531
|
303
|
Chris@531
|
304 if (m_singleFileName != "" || m_stdout) return;
|
Chris@531
|
305
|
Chris@531
|
306 while (!m_streams.empty()) {
|
Chris@531
|
307 m_streams.begin()->second->flush();
|
Chris@531
|
308 delete m_streams.begin()->second;
|
Chris@531
|
309 m_streams.erase(m_streams.begin());
|
Chris@531
|
310 }
|
Chris@531
|
311 while (!m_files.empty()) {
|
Chris@604
|
312 cerr << "FileFeatureWriter::finish: NOTE: Closing feature file \""
|
Chris@604
|
313 << m_files.begin()->second->fileName().toStdString() << "\"" << endl;
|
Chris@531
|
314 delete m_files.begin()->second;
|
Chris@531
|
315 m_files.erase(m_files.begin());
|
Chris@531
|
316 }
|
Chris@531
|
317 m_prevstream = 0;
|
Chris@531
|
318 }
|
Chris@531
|
319
|