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> ¶ms)
|
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@498
|
234 QFile *file = new QFile(filename);
|
Chris@498
|
235 QIODevice::OpenMode mode = (QIODevice::WriteOnly);
|
Chris@498
|
236 if (m_append) mode |= QIODevice::Append;
|
Chris@498
|
237
|
Chris@498
|
238 if (!file->open(mode)) {
|
Chris@498
|
239 cerr << "FileFeatureWriter: ERROR: Failed to open output file \"" << filename.toStdString()
|
Chris@498
|
240 << "\" for writing" << endl;
|
Chris@498
|
241 delete file;
|
Chris@498
|
242 m_files[key] = 0;
|
Chris@498
|
243 throw FailedToOpenFile(filename);
|
Chris@498
|
244 }
|
Chris@498
|
245
|
Chris@498
|
246 m_files[key] = file;
|
Chris@498
|
247 }
|
Chris@498
|
248
|
Chris@498
|
249 return m_files[key];
|
Chris@498
|
250 }
|
Chris@498
|
251
|
Chris@498
|
252
|
Chris@498
|
253 QTextStream *FileFeatureWriter::getOutputStream(QString trackId,
|
Chris@498
|
254 TransformId transformId)
|
Chris@498
|
255 {
|
Chris@498
|
256 QFile *file = getOutputFile(trackId, transformId);
|
Chris@498
|
257 if (!file && !m_stdout) {
|
Chris@498
|
258 return 0;
|
Chris@498
|
259 }
|
Chris@498
|
260
|
Chris@498
|
261 if (m_streams.find(file) == m_streams.end()) {
|
Chris@498
|
262 if (m_stdout) {
|
Chris@498
|
263 m_streams[file] = new QTextStream(stdout);
|
Chris@498
|
264 } else {
|
Chris@498
|
265 m_streams[file] = new QTextStream(file);
|
Chris@498
|
266 }
|
Chris@498
|
267 }
|
Chris@498
|
268
|
Chris@512
|
269 QTextStream *stream = m_streams[file];
|
Chris@512
|
270
|
Chris@512
|
271 if (m_prevstream && stream != m_prevstream) {
|
Chris@512
|
272 m_prevstream->flush();
|
Chris@512
|
273 }
|
Chris@512
|
274 m_prevstream = stream;
|
Chris@512
|
275
|
Chris@512
|
276 return stream;
|
Chris@498
|
277 }
|
Chris@498
|
278
|
Chris@515
|
279
|
Chris@515
|
280 void
|
Chris@515
|
281 FileFeatureWriter::flush()
|
Chris@515
|
282 {
|
Chris@515
|
283 if (m_prevstream) {
|
Chris@515
|
284 m_prevstream->flush();
|
Chris@515
|
285 }
|
Chris@515
|
286 }
|
Chris@515
|
287
|
Chris@531
|
288
|
Chris@531
|
289 void
|
Chris@531
|
290 FileFeatureWriter::finish()
|
Chris@531
|
291 {
|
Chris@579
|
292 // cerr << "FileFeatureWriter::finish()" << endl;
|
Chris@531
|
293
|
Chris@531
|
294 if (m_singleFileName != "" || m_stdout) return;
|
Chris@531
|
295
|
Chris@531
|
296 while (!m_streams.empty()) {
|
Chris@531
|
297 m_streams.begin()->second->flush();
|
Chris@531
|
298 delete m_streams.begin()->second;
|
Chris@531
|
299 m_streams.erase(m_streams.begin());
|
Chris@531
|
300 }
|
Chris@531
|
301 while (!m_files.empty()) {
|
Chris@531
|
302 delete m_files.begin()->second;
|
Chris@531
|
303 m_files.erase(m_files.begin());
|
Chris@531
|
304 }
|
Chris@531
|
305 m_prevstream = 0;
|
Chris@531
|
306 }
|
Chris@531
|
307
|