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@506
|
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@498
|
192 filename = QDir(dirname).filePath(filename);
|
Chris@498
|
193
|
Chris@498
|
194 if (QFileInfo(filename).exists() && !(m_force || m_append)) {
|
Chris@498
|
195 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
|
196 return "";
|
Chris@498
|
197 }
|
Chris@498
|
198
|
Chris@498
|
199 return filename;
|
Chris@498
|
200 }
|
Chris@498
|
201
|
Chris@498
|
202
|
Chris@498
|
203 QFile *FileFeatureWriter::getOutputFile(QString trackId,
|
Chris@498
|
204 TransformId transformId)
|
Chris@498
|
205 {
|
Chris@498
|
206 pair<QString, TransformId> key;
|
Chris@498
|
207
|
Chris@498
|
208 if (m_singleFileName != "") {
|
Chris@498
|
209 key = pair<QString, TransformId>("", "");
|
Chris@498
|
210 } else if (m_manyFiles) {
|
Chris@498
|
211 key = pair<QString, TransformId>(trackId, transformId);
|
Chris@498
|
212 } else {
|
Chris@498
|
213 key = pair<QString, TransformId>(trackId, "");
|
Chris@498
|
214 }
|
Chris@498
|
215
|
Chris@498
|
216 if (m_files.find(key) == m_files.end()) {
|
Chris@498
|
217
|
Chris@498
|
218 QString filename = getOutputFilename(trackId, transformId);
|
Chris@498
|
219
|
Chris@498
|
220 if (filename == "") { // stdout
|
Chris@498
|
221 return 0;
|
Chris@498
|
222 }
|
Chris@498
|
223
|
Chris@498
|
224 cerr << "FileFeatureWriter: NOTE: Using output filename \""
|
Chris@498
|
225 << filename.toStdString() << "\"" << endl;
|
Chris@498
|
226
|
Chris@498
|
227 QFile *file = new QFile(filename);
|
Chris@498
|
228 QIODevice::OpenMode mode = (QIODevice::WriteOnly);
|
Chris@498
|
229 if (m_append) mode |= QIODevice::Append;
|
Chris@498
|
230
|
Chris@498
|
231 if (!file->open(mode)) {
|
Chris@498
|
232 cerr << "FileFeatureWriter: ERROR: Failed to open output file \"" << filename.toStdString()
|
Chris@498
|
233 << "\" for writing" << endl;
|
Chris@498
|
234 delete file;
|
Chris@498
|
235 m_files[key] = 0;
|
Chris@498
|
236 throw FailedToOpenFile(filename);
|
Chris@498
|
237 }
|
Chris@498
|
238
|
Chris@498
|
239 m_files[key] = file;
|
Chris@498
|
240 }
|
Chris@498
|
241
|
Chris@498
|
242 return m_files[key];
|
Chris@498
|
243 }
|
Chris@498
|
244
|
Chris@498
|
245
|
Chris@498
|
246 QTextStream *FileFeatureWriter::getOutputStream(QString trackId,
|
Chris@498
|
247 TransformId transformId)
|
Chris@498
|
248 {
|
Chris@498
|
249 QFile *file = getOutputFile(trackId, transformId);
|
Chris@498
|
250 if (!file && !m_stdout) {
|
Chris@498
|
251 return 0;
|
Chris@498
|
252 }
|
Chris@498
|
253
|
Chris@498
|
254 if (m_streams.find(file) == m_streams.end()) {
|
Chris@498
|
255 if (m_stdout) {
|
Chris@498
|
256 m_streams[file] = new QTextStream(stdout);
|
Chris@498
|
257 } else {
|
Chris@498
|
258 m_streams[file] = new QTextStream(file);
|
Chris@498
|
259 }
|
Chris@498
|
260 }
|
Chris@498
|
261
|
Chris@512
|
262 QTextStream *stream = m_streams[file];
|
Chris@512
|
263
|
Chris@512
|
264 if (m_prevstream && stream != m_prevstream) {
|
Chris@512
|
265 m_prevstream->flush();
|
Chris@512
|
266 }
|
Chris@512
|
267 m_prevstream = stream;
|
Chris@512
|
268
|
Chris@512
|
269 return stream;
|
Chris@498
|
270 }
|
Chris@498
|
271
|