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