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@518
|
173 if (scheme.length() == 1) infilename = scheme + ":" + infilename; // DOS drive!
|
Chris@498
|
174 if (infilename == "") infilename = url.path();
|
Chris@498
|
175 basename = QFileInfo(infilename).baseName();
|
Chris@507
|
176
|
Chris@507
|
177 cerr << "trackId = " << trackId.toStdString() << ", url = " << url.toString().toStdString() << ", infilename = "
|
Chris@507
|
178 << infilename.toStdString() << ", basename = " << basename.toStdString() << endl;
|
Chris@498
|
179
|
Chris@498
|
180
|
Chris@498
|
181 if (m_baseDir != "") dirname = QFileInfo(m_baseDir).absoluteFilePath();
|
Chris@498
|
182 else if (local) dirname = QFileInfo(infilename).absolutePath();
|
Chris@498
|
183 else dirname = QDir::currentPath();
|
Chris@498
|
184
|
Chris@498
|
185 QString filename;
|
Chris@498
|
186
|
Chris@498
|
187 if (m_manyFiles && transformId != "") {
|
Chris@514
|
188 filename = QString("%1_%2.%3").arg(basename).arg(transformId).arg(m_extension);
|
Chris@498
|
189 } else {
|
Chris@498
|
190 filename = QString("%1.%2").arg(basename).arg(m_extension);
|
Chris@498
|
191 }
|
Chris@498
|
192
|
Chris@514
|
193 filename.replace(':', '_'); // ':' not permitted in Windows
|
Chris@514
|
194
|
Chris@498
|
195 filename = QDir(dirname).filePath(filename);
|
Chris@498
|
196
|
Chris@498
|
197 if (QFileInfo(filename).exists() && !(m_force || m_append)) {
|
Chris@498
|
198 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
|
199 return "";
|
Chris@498
|
200 }
|
Chris@498
|
201
|
Chris@498
|
202 return filename;
|
Chris@498
|
203 }
|
Chris@498
|
204
|
Chris@498
|
205
|
Chris@498
|
206 QFile *FileFeatureWriter::getOutputFile(QString trackId,
|
Chris@498
|
207 TransformId transformId)
|
Chris@498
|
208 {
|
Chris@498
|
209 pair<QString, TransformId> key;
|
Chris@498
|
210
|
Chris@498
|
211 if (m_singleFileName != "") {
|
Chris@498
|
212 key = pair<QString, TransformId>("", "");
|
Chris@498
|
213 } else if (m_manyFiles) {
|
Chris@498
|
214 key = pair<QString, TransformId>(trackId, transformId);
|
Chris@498
|
215 } else {
|
Chris@498
|
216 key = pair<QString, TransformId>(trackId, "");
|
Chris@498
|
217 }
|
Chris@498
|
218
|
Chris@498
|
219 if (m_files.find(key) == m_files.end()) {
|
Chris@498
|
220
|
Chris@498
|
221 QString filename = getOutputFilename(trackId, transformId);
|
Chris@498
|
222
|
Chris@498
|
223 if (filename == "") { // stdout
|
Chris@498
|
224 return 0;
|
Chris@498
|
225 }
|
Chris@498
|
226
|
Chris@498
|
227 cerr << "FileFeatureWriter: NOTE: Using output filename \""
|
Chris@498
|
228 << filename.toStdString() << "\"" << endl;
|
Chris@498
|
229
|
Chris@498
|
230 QFile *file = new QFile(filename);
|
Chris@498
|
231 QIODevice::OpenMode mode = (QIODevice::WriteOnly);
|
Chris@498
|
232 if (m_append) mode |= QIODevice::Append;
|
Chris@498
|
233
|
Chris@498
|
234 if (!file->open(mode)) {
|
Chris@498
|
235 cerr << "FileFeatureWriter: ERROR: Failed to open output file \"" << filename.toStdString()
|
Chris@498
|
236 << "\" for writing" << endl;
|
Chris@498
|
237 delete file;
|
Chris@498
|
238 m_files[key] = 0;
|
Chris@498
|
239 throw FailedToOpenFile(filename);
|
Chris@498
|
240 }
|
Chris@498
|
241
|
Chris@498
|
242 m_files[key] = file;
|
Chris@498
|
243 }
|
Chris@498
|
244
|
Chris@498
|
245 return m_files[key];
|
Chris@498
|
246 }
|
Chris@498
|
247
|
Chris@498
|
248
|
Chris@498
|
249 QTextStream *FileFeatureWriter::getOutputStream(QString trackId,
|
Chris@498
|
250 TransformId transformId)
|
Chris@498
|
251 {
|
Chris@498
|
252 QFile *file = getOutputFile(trackId, transformId);
|
Chris@498
|
253 if (!file && !m_stdout) {
|
Chris@498
|
254 return 0;
|
Chris@498
|
255 }
|
Chris@498
|
256
|
Chris@498
|
257 if (m_streams.find(file) == m_streams.end()) {
|
Chris@498
|
258 if (m_stdout) {
|
Chris@498
|
259 m_streams[file] = new QTextStream(stdout);
|
Chris@498
|
260 } else {
|
Chris@498
|
261 m_streams[file] = new QTextStream(file);
|
Chris@498
|
262 }
|
Chris@498
|
263 }
|
Chris@498
|
264
|
Chris@512
|
265 QTextStream *stream = m_streams[file];
|
Chris@512
|
266
|
Chris@512
|
267 if (m_prevstream && stream != m_prevstream) {
|
Chris@512
|
268 m_prevstream->flush();
|
Chris@512
|
269 }
|
Chris@512
|
270 m_prevstream = stream;
|
Chris@512
|
271
|
Chris@512
|
272 return stream;
|
Chris@498
|
273 }
|
Chris@498
|
274
|
Chris@515
|
275
|
Chris@515
|
276 void
|
Chris@515
|
277 FileFeatureWriter::flush()
|
Chris@515
|
278 {
|
Chris@515
|
279 if (m_prevstream) {
|
Chris@515
|
280 m_prevstream->flush();
|
Chris@515
|
281 }
|
Chris@515
|
282 }
|
Chris@515
|
283
|