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@498
|
35 m_support(support),
|
Chris@498
|
36 m_extension(extension),
|
Chris@498
|
37 m_manyFiles(false),
|
Chris@498
|
38 m_stdout(false),
|
Chris@498
|
39 m_append(false),
|
Chris@498
|
40 m_force(false)
|
Chris@498
|
41 {
|
Chris@498
|
42 if (!(m_support & SupportOneFilePerTrack)) {
|
Chris@498
|
43 if (m_support & SupportOneFilePerTrackTransform) {
|
Chris@498
|
44 m_manyFiles = true;
|
Chris@498
|
45 } else if (m_support & SupportOneFileTotal) {
|
Chris@498
|
46 m_singleFileName = QString("output.%1").arg(m_extension);
|
Chris@498
|
47 } else {
|
Chris@498
|
48 cerr << "FileFeatureWriter::FileFeatureWriter: ERROR: Invalid support specification " << support << endl;
|
Chris@498
|
49 }
|
Chris@498
|
50 }
|
Chris@498
|
51 }
|
Chris@498
|
52
|
Chris@498
|
53 FileFeatureWriter::~FileFeatureWriter()
|
Chris@498
|
54 {
|
Chris@498
|
55 while (!m_streams.empty()) {
|
Chris@498
|
56 m_streams.begin()->second->flush();
|
Chris@498
|
57 delete m_streams.begin()->second;
|
Chris@498
|
58 m_streams.erase(m_streams.begin());
|
Chris@498
|
59 }
|
Chris@498
|
60 while (!m_files.empty()) {
|
Chris@498
|
61 delete m_files.begin()->second;
|
Chris@498
|
62 m_files.erase(m_files.begin());
|
Chris@498
|
63 }
|
Chris@498
|
64 }
|
Chris@498
|
65
|
Chris@498
|
66 FileFeatureWriter::ParameterList
|
Chris@498
|
67 FileFeatureWriter::getSupportedParameters() const
|
Chris@498
|
68 {
|
Chris@498
|
69 ParameterList pl;
|
Chris@498
|
70 Parameter p;
|
Chris@498
|
71
|
Chris@498
|
72 p.name = "basedir";
|
Chris@498
|
73 p.description = "Base output directory path. (The default is the same directory as the input file.)";
|
Chris@498
|
74 p.hasArg = true;
|
Chris@498
|
75 pl.push_back(p);
|
Chris@498
|
76
|
Chris@498
|
77 if (m_support & SupportOneFilePerTrackTransform &&
|
Chris@498
|
78 m_support & SupportOneFilePerTrack) {
|
Chris@498
|
79 p.name = "many-files";
|
Chris@498
|
80 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
|
81 p.hasArg = false;
|
Chris@498
|
82 pl.push_back(p);
|
Chris@498
|
83 }
|
Chris@498
|
84
|
Chris@498
|
85 if (m_support & SupportOneFileTotal) {
|
Chris@498
|
86 if (m_support & ~SupportOneFileTotal) { // not only option
|
Chris@498
|
87 p.name = "one-file";
|
Chris@498
|
88 p.description = "Write all transform results for all input files into the single named output file.";
|
Chris@498
|
89 p.hasArg = true;
|
Chris@498
|
90 pl.push_back(p);
|
Chris@498
|
91 }
|
Chris@498
|
92 p.name = "stdout";
|
Chris@498
|
93 p.description = "Write all transform results directly to standard output.";
|
Chris@498
|
94 p.hasArg = false;
|
Chris@498
|
95 pl.push_back(p);
|
Chris@498
|
96 }
|
Chris@498
|
97
|
Chris@498
|
98 p.name = "force";
|
Chris@498
|
99 p.description = "If an output file already exists, overwrite it.";
|
Chris@498
|
100 p.hasArg = false;
|
Chris@498
|
101 pl.push_back(p);
|
Chris@498
|
102
|
Chris@498
|
103 p.name = "append";
|
Chris@498
|
104 p.description = "If an output file already exists, append data to it.";
|
Chris@498
|
105 p.hasArg = false;
|
Chris@498
|
106 pl.push_back(p);
|
Chris@498
|
107
|
Chris@498
|
108 return pl;
|
Chris@498
|
109 }
|
Chris@498
|
110
|
Chris@498
|
111 void
|
Chris@498
|
112 FileFeatureWriter::setParameters(map<string, string> ¶ms)
|
Chris@498
|
113 {
|
Chris@498
|
114 for (map<string, string>::iterator i = params.begin();
|
Chris@498
|
115 i != params.end(); ++i) {
|
Chris@498
|
116 if (i->first == "basedir") {
|
Chris@498
|
117 m_baseDir = i->second.c_str();
|
Chris@498
|
118 } else if (i->first == "many-files") {
|
Chris@498
|
119 if (m_support & SupportOneFilePerTrackTransform &&
|
Chris@498
|
120 m_support & SupportOneFilePerTrack) {
|
Chris@498
|
121 if (m_singleFileName != "") {
|
Chris@498
|
122 cerr << "FileFeatureWriter::setParameters: WARNING: Both one-file and many-files parameters provided, ignoring many-files" << endl;
|
Chris@498
|
123 } else {
|
Chris@498
|
124 m_manyFiles = true;
|
Chris@498
|
125 }
|
Chris@498
|
126 }
|
Chris@498
|
127 } else if (i->first == "one-file") {
|
Chris@498
|
128 if (m_support & SupportOneFileTotal) {
|
Chris@498
|
129 if (m_support & ~SupportOneFileTotal) { // not only option
|
Chris@498
|
130 if (m_manyFiles) {
|
Chris@498
|
131 cerr << "FileFeatureWriter::setParameters: WARNING: Both many-files and one-file parameters provided, ignoring one-file" << endl;
|
Chris@498
|
132 } else {
|
Chris@498
|
133 m_singleFileName = i->second.c_str();
|
Chris@498
|
134 }
|
Chris@498
|
135 }
|
Chris@498
|
136 }
|
Chris@498
|
137 } else if (i->first == "stdout") {
|
Chris@498
|
138 if (m_support & SupportOneFileTotal) {
|
Chris@498
|
139 if (m_singleFileName != "") {
|
Chris@498
|
140 cerr << "FileFeatureWriter::setParameters: WARNING: Both stdout and one-file provided, ignoring stdout" << endl;
|
Chris@498
|
141 } else {
|
Chris@498
|
142 m_stdout = true;
|
Chris@498
|
143 }
|
Chris@498
|
144 }
|
Chris@498
|
145 } else if (i->first == "append") {
|
Chris@498
|
146 m_append = true;
|
Chris@498
|
147 } else if (i->first == "force") {
|
Chris@498
|
148 m_force = true;
|
Chris@498
|
149 }
|
Chris@498
|
150 }
|
Chris@498
|
151 }
|
Chris@498
|
152
|
Chris@498
|
153 QString FileFeatureWriter::getOutputFilename(QString trackId,
|
Chris@498
|
154 TransformId transformId)
|
Chris@498
|
155 {
|
Chris@498
|
156 if (m_singleFileName != "") {
|
Chris@498
|
157 if (QFileInfo(m_singleFileName).exists() && !(m_force || m_append)) {
|
Chris@498
|
158 cerr << "FileFeatureWriter: ERROR: Specified output file \"" << m_singleFileName.toStdString() << "\" exists and neither force nor append flag is specified -- not overwriting" << endl;
|
Chris@498
|
159 return "";
|
Chris@498
|
160 }
|
Chris@498
|
161 return m_singleFileName;
|
Chris@498
|
162 }
|
Chris@498
|
163
|
Chris@498
|
164 if (m_stdout) return "";
|
Chris@498
|
165
|
Chris@498
|
166 QUrl url(trackId);
|
Chris@498
|
167 QString scheme = url.scheme().toLower();
|
Chris@498
|
168 bool local = (scheme == "" || scheme == "file" || scheme.length() == 1);
|
Chris@498
|
169
|
Chris@498
|
170 QString dirname, basename;
|
Chris@498
|
171 QString infilename = url.toLocalFile();
|
Chris@498
|
172 if (infilename == "") infilename = url.path();
|
Chris@498
|
173 basename = QFileInfo(infilename).baseName();
|
Chris@507
|
174
|
Chris@507
|
175 cerr << "trackId = " << trackId.toStdString() << ", url = " << url.toString().toStdString() << ", infilename = "
|
Chris@507
|
176 << infilename.toStdString() << ", basename = " << basename.toStdString() << endl;
|
Chris@498
|
177
|
Chris@498
|
178
|
Chris@498
|
179 if (m_baseDir != "") dirname = QFileInfo(m_baseDir).absoluteFilePath();
|
Chris@498
|
180 else if (local) dirname = QFileInfo(infilename).absolutePath();
|
Chris@498
|
181 else dirname = QDir::currentPath();
|
Chris@498
|
182
|
Chris@498
|
183 QString filename;
|
Chris@498
|
184
|
Chris@498
|
185 if (m_manyFiles && transformId != "") {
|
Chris@506
|
186 filename = QString("%1-%2.%3").arg(basename).arg(transformId).arg(m_extension);
|
Chris@498
|
187 } else {
|
Chris@498
|
188 filename = QString("%1.%2").arg(basename).arg(m_extension);
|
Chris@498
|
189 }
|
Chris@498
|
190
|
Chris@498
|
191 filename = QDir(dirname).filePath(filename);
|
Chris@498
|
192
|
Chris@498
|
193 if (QFileInfo(filename).exists() && !(m_force || m_append)) {
|
Chris@498
|
194 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
|
195 return "";
|
Chris@498
|
196 }
|
Chris@498
|
197
|
Chris@498
|
198 return filename;
|
Chris@498
|
199 }
|
Chris@498
|
200
|
Chris@498
|
201
|
Chris@498
|
202 QFile *FileFeatureWriter::getOutputFile(QString trackId,
|
Chris@498
|
203 TransformId transformId)
|
Chris@498
|
204 {
|
Chris@498
|
205 pair<QString, TransformId> key;
|
Chris@498
|
206
|
Chris@498
|
207 if (m_singleFileName != "") {
|
Chris@498
|
208 key = pair<QString, TransformId>("", "");
|
Chris@498
|
209 } else if (m_manyFiles) {
|
Chris@498
|
210 key = pair<QString, TransformId>(trackId, transformId);
|
Chris@498
|
211 } else {
|
Chris@498
|
212 key = pair<QString, TransformId>(trackId, "");
|
Chris@498
|
213 }
|
Chris@498
|
214
|
Chris@498
|
215 if (m_files.find(key) == m_files.end()) {
|
Chris@498
|
216
|
Chris@498
|
217 QString filename = getOutputFilename(trackId, transformId);
|
Chris@498
|
218
|
Chris@498
|
219 if (filename == "") { // stdout
|
Chris@498
|
220 return 0;
|
Chris@498
|
221 }
|
Chris@498
|
222
|
Chris@498
|
223 cerr << "FileFeatureWriter: NOTE: Using output filename \""
|
Chris@498
|
224 << filename.toStdString() << "\"" << endl;
|
Chris@498
|
225
|
Chris@498
|
226 QFile *file = new QFile(filename);
|
Chris@498
|
227 QIODevice::OpenMode mode = (QIODevice::WriteOnly);
|
Chris@498
|
228 if (m_append) mode |= QIODevice::Append;
|
Chris@498
|
229
|
Chris@498
|
230 if (!file->open(mode)) {
|
Chris@498
|
231 cerr << "FileFeatureWriter: ERROR: Failed to open output file \"" << filename.toStdString()
|
Chris@498
|
232 << "\" for writing" << endl;
|
Chris@498
|
233 delete file;
|
Chris@498
|
234 m_files[key] = 0;
|
Chris@498
|
235 throw FailedToOpenFile(filename);
|
Chris@498
|
236 }
|
Chris@498
|
237
|
Chris@498
|
238 m_files[key] = file;
|
Chris@498
|
239 }
|
Chris@498
|
240
|
Chris@498
|
241 return m_files[key];
|
Chris@498
|
242 }
|
Chris@498
|
243
|
Chris@498
|
244
|
Chris@498
|
245 QTextStream *FileFeatureWriter::getOutputStream(QString trackId,
|
Chris@498
|
246 TransformId transformId)
|
Chris@498
|
247 {
|
Chris@498
|
248 QFile *file = getOutputFile(trackId, transformId);
|
Chris@498
|
249 if (!file && !m_stdout) {
|
Chris@498
|
250 return 0;
|
Chris@498
|
251 }
|
Chris@498
|
252
|
Chris@498
|
253 if (m_streams.find(file) == m_streams.end()) {
|
Chris@498
|
254 if (m_stdout) {
|
Chris@498
|
255 m_streams[file] = new QTextStream(stdout);
|
Chris@498
|
256 } else {
|
Chris@498
|
257 m_streams[file] = new QTextStream(file);
|
Chris@498
|
258 }
|
Chris@498
|
259 }
|
Chris@498
|
260
|
Chris@498
|
261 return m_streams[file];
|
Chris@498
|
262 }
|
Chris@498
|
263
|