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