svcore
1.9
|
00001 /* -*- c-basic-offset: 4 indent-tabs-mode: nil -*- vi:set ts=8 sts=4 sw=4: */ 00002 00003 /* 00004 Sonic Visualiser 00005 An audio file viewer and annotation editor. 00006 00007 Sonic Annotator 00008 A utility for batch feature extraction from audio files. 00009 00010 Mark Levy, Chris Sutton and Chris Cannam, Queen Mary, University of London. 00011 Copyright 2007-2008 QMUL. 00012 00013 This program is free software; you can redistribute it and/or 00014 modify it under the terms of the GNU General Public License as 00015 published by the Free Software Foundation; either version 2 of the 00016 License, or (at your option) any later version. See the file 00017 COPYING included with this distribution for more information. 00018 */ 00019 00020 #include "FileFeatureWriter.h" 00021 00022 #include "base/Exceptions.h" 00023 00024 #include <QTextStream> 00025 #include <QFile> 00026 #include <QFileInfo> 00027 #include <QUrl> 00028 #include <QDir> 00029 00030 using namespace std; 00031 using namespace Vamp; 00032 00033 FileFeatureWriter::FileFeatureWriter(int support, 00034 QString extension) : 00035 m_prevstream(0), 00036 m_support(support), 00037 m_extension(extension), 00038 m_manyFiles(false), 00039 m_stdout(false), 00040 m_append(false), 00041 m_force(false) 00042 { 00043 if (!(m_support & SupportOneFilePerTrack)) { 00044 if (m_support & SupportOneFilePerTrackTransform) { 00045 m_manyFiles = true; 00046 } else if (m_support & SupportOneFileTotal) { 00047 m_singleFileName = QString("output.%1").arg(m_extension); 00048 } else { 00049 SVDEBUG << "FileFeatureWriter::FileFeatureWriter: ERROR: Invalid support specification " << support << endl; 00050 } 00051 } 00052 } 00053 00054 FileFeatureWriter::~FileFeatureWriter() 00055 { 00056 while (!m_streams.empty()) { 00057 m_streams.begin()->second->flush(); 00058 delete m_streams.begin()->second; 00059 m_streams.erase(m_streams.begin()); 00060 } 00061 while (!m_files.empty()) { 00062 if (m_files.begin()->second) { 00063 SVDEBUG << "FileFeatureWriter::~FileFeatureWriter: NOTE: Closing feature file \"" 00064 << m_files.begin()->second->fileName() << "\"" << endl; 00065 delete m_files.begin()->second; 00066 } 00067 m_files.erase(m_files.begin()); 00068 } 00069 } 00070 00071 FileFeatureWriter::ParameterList 00072 FileFeatureWriter::getSupportedParameters() const 00073 { 00074 ParameterList pl; 00075 Parameter p; 00076 00077 p.name = "basedir"; 00078 p.description = "Base output directory path. (The default is the same directory as the input file.)"; 00079 p.hasArg = true; 00080 pl.push_back(p); 00081 00082 if (m_support & SupportOneFilePerTrackTransform && 00083 m_support & SupportOneFilePerTrack) { 00084 p.name = "many-files"; 00085 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.)"; 00086 p.hasArg = false; 00087 pl.push_back(p); 00088 } 00089 00090 if (m_support & SupportOneFileTotal) { 00091 if (m_support & ~SupportOneFileTotal) { // not only option 00092 p.name = "one-file"; 00093 if (m_support & SupportOneFilePerTrack) { 00094 p.description = "Write all transform results for all input files into the single named output file. (The default is to create one output file per input audio file, and write all transform results for that input into it.)"; 00095 } else { 00096 p.description = "Write all transform results for all input files into the single named output file. (The default is to create a separate output file for each combination of input audio file and transform.)"; 00097 } 00098 p.hasArg = true; 00099 pl.push_back(p); 00100 } 00101 p.name = "stdout"; 00102 p.description = "Write all transform results directly to standard output."; 00103 p.hasArg = false; 00104 pl.push_back(p); 00105 } 00106 00107 p.name = "force"; 00108 p.description = "If an output file already exists, overwrite it."; 00109 p.hasArg = false; 00110 pl.push_back(p); 00111 00112 p.name = "append"; 00113 p.description = "If an output file already exists, append data to it."; 00114 p.hasArg = false; 00115 pl.push_back(p); 00116 00117 return pl; 00118 } 00119 00120 void 00121 FileFeatureWriter::setParameters(map<string, string> ¶ms) 00122 { 00123 for (map<string, string>::iterator i = params.begin(); 00124 i != params.end(); ++i) { 00125 if (i->first == "basedir") { 00126 m_baseDir = i->second.c_str(); 00127 } else if (i->first == "many-files") { 00128 if (m_support & SupportOneFilePerTrackTransform && 00129 m_support & SupportOneFilePerTrack) { 00130 if (m_singleFileName != "") { 00131 SVDEBUG << "FileFeatureWriter::setParameters: WARNING: Both one-file and many-files parameters provided, ignoring many-files" << endl; 00132 } else { 00133 m_manyFiles = true; 00134 } 00135 } 00136 } else if (i->first == "one-file") { 00137 if (m_support & SupportOneFileTotal) { 00138 if (m_support & ~SupportOneFileTotal) { // not only option 00139 // No, we cannot do this test because m_manyFiles 00140 // may be on by default (for any FileFeatureWriter 00141 // that supports OneFilePerTrackTransform but not 00142 // OneFilePerTrack), so we need to be able to 00143 // override it 00144 // if (m_manyFiles) { 00145 // SVDEBUG << "FileFeatureWriter::setParameters: WARNING: Both many-files and one-file parameters provided, ignoring one-file" << endl; 00146 // } else { 00147 m_singleFileName = i->second.c_str(); 00148 // } 00149 } 00150 } 00151 } else if (i->first == "stdout") { 00152 if (m_support & SupportOneFileTotal) { 00153 if (m_singleFileName != "") { 00154 SVDEBUG << "FileFeatureWriter::setParameters: WARNING: Both stdout and one-file provided, ignoring stdout" << endl; 00155 } else { 00156 m_stdout = true; 00157 } 00158 } 00159 } else if (i->first == "append") { 00160 m_append = true; 00161 } else if (i->first == "force") { 00162 m_force = true; 00163 } 00164 } 00165 } 00166 00167 QString 00168 FileFeatureWriter::getOutputFilename(QString trackId, 00169 TransformId transformId) 00170 { 00171 if (m_singleFileName != "") { 00172 if (QFileInfo(m_singleFileName).exists() && !(m_force || m_append)) { 00173 cerr << endl << "FileFeatureWriter: ERROR: Specified output file \"" << m_singleFileName << "\" exists and neither --" << getWriterTag() << "-force nor --" << getWriterTag() << "-append flag is specified -- not overwriting" << endl; 00174 SVDEBUG << "NOTE: To find out how to fix this problem, read the help for the --" << getWriterTag() << "-force" << endl << "and --" << getWriterTag() << "-append options" << endl; 00175 return ""; 00176 } 00177 return m_singleFileName; 00178 } 00179 00180 if (m_stdout) return ""; 00181 00182 QUrl url(trackId, QUrl::StrictMode); 00183 QString scheme = url.scheme().toLower(); 00184 bool local = (scheme == "" || scheme == "file" || scheme.length() == 1); 00185 00186 QString dirname, basename; 00187 QString infilename = url.toLocalFile(); 00188 if (infilename == "") { 00189 infilename = url.path(); 00190 } 00191 basename = QFileInfo(infilename).completeBaseName(); 00192 if (scheme.length() == 1) { 00193 infilename = scheme + ":" + infilename; // DOS drive! 00194 } 00195 00196 // cerr << "trackId = " << trackId << ", url = " << url.toString() << ", infilename = " 00197 // << infilename << ", basename = " << basename << ", m_baseDir = " << m_baseDir << endl; 00198 00199 if (m_baseDir != "") dirname = QFileInfo(m_baseDir).absoluteFilePath(); 00200 else if (local) dirname = QFileInfo(infilename).absolutePath(); 00201 else dirname = QDir::currentPath(); 00202 00203 // cerr << "dirname = " << dirname << endl; 00204 00205 QString filename; 00206 00207 if (m_manyFiles && transformId != "") { 00208 filename = QString("%1_%2.%3").arg(basename).arg(transformId).arg(m_extension); 00209 } else { 00210 filename = QString("%1.%2").arg(basename).arg(m_extension); 00211 } 00212 00213 filename.replace(':', '_'); // ':' not permitted in Windows 00214 00215 filename = QDir(dirname).filePath(filename); 00216 00217 if (QFileInfo(filename).exists() && !(m_force || m_append)) { 00218 cerr << endl << "FileFeatureWriter: ERROR: Output file \"" << filename << "\" exists (for input file or URL \"" << trackId << "\" and transform \"" << transformId << "\") and neither --" << getWriterTag() << "-force nor --" << getWriterTag() << "-append is specified -- not overwriting" << endl; 00219 SVDEBUG << "NOTE: To find out how to fix this problem, read the help for the --" << getWriterTag() << "-force" << endl << "and --" << getWriterTag() << "-append options" << endl; 00220 return ""; 00221 } 00222 00223 return filename; 00224 } 00225 00226 void 00227 FileFeatureWriter::testOutputFile(QString trackId, 00228 TransformId transformId) 00229 { 00230 // Obviously, if we're writing to stdout we can't test for an 00231 // openable output file. But when writing a single file we don't 00232 // want to either, because this test would fail on the second and 00233 // subsequent input files (because the file would already exist). 00234 // getOutputFile does the right thing in this case, so we just 00235 // leave it to it 00236 if (m_stdout || m_singleFileName != "") return; 00237 00238 QString filename = getOutputFilename(trackId, transformId); 00239 if (filename == "") { 00240 throw FailedToOpenOutputStream(trackId, transformId); 00241 } 00242 } 00243 00244 QFile * 00245 FileFeatureWriter::getOutputFile(QString trackId, 00246 TransformId transformId) 00247 { 00248 pair<QString, TransformId> key; 00249 00250 if (m_singleFileName != "") { 00251 key = pair<QString, TransformId>("", ""); 00252 } else if (m_manyFiles) { 00253 key = pair<QString, TransformId>(trackId, transformId); 00254 } else { 00255 key = pair<QString, TransformId>(trackId, ""); 00256 } 00257 00258 if (m_files.find(key) == m_files.end()) { 00259 00260 QString filename = getOutputFilename(trackId, transformId); 00261 00262 if (filename == "") { // stdout or failure 00263 return 0; 00264 } 00265 00266 SVDEBUG << "FileFeatureWriter: NOTE: Using output filename \"" 00267 << filename << "\"" << endl; 00268 00269 if (m_append) { 00270 SVDEBUG << "FileFeatureWriter: NOTE: Calling reviewFileForAppending" << endl; 00271 reviewFileForAppending(filename); 00272 } 00273 00274 QFile *file = new QFile(filename); 00275 QIODevice::OpenMode mode = (QIODevice::WriteOnly); 00276 if (m_append) mode |= QIODevice::Append; 00277 00278 if (!file->open(mode)) { 00279 cerr << "FileFeatureWriter: ERROR: Failed to open output file \"" << filename 00280 << "\" for writing" << endl; 00281 delete file; 00282 m_files[key] = 0; 00283 throw FailedToOpenFile(filename); 00284 } 00285 00286 m_files[key] = file; 00287 } 00288 00289 return m_files[key]; 00290 } 00291 00292 00293 QTextStream *FileFeatureWriter::getOutputStream(QString trackId, 00294 TransformId transformId) 00295 { 00296 QFile *file = getOutputFile(trackId, transformId); 00297 if (!file && !m_stdout) { 00298 return 0; 00299 } 00300 00301 if (m_streams.find(file) == m_streams.end()) { 00302 if (m_stdout) { 00303 m_streams[file] = new QTextStream(stdout); 00304 } else { 00305 m_streams[file] = new QTextStream(file); 00306 } 00307 } 00308 00309 QTextStream *stream = m_streams[file]; 00310 00311 if (m_prevstream && stream != m_prevstream) { 00312 m_prevstream->flush(); 00313 } 00314 m_prevstream = stream; 00315 00316 return stream; 00317 } 00318 00319 00320 void 00321 FileFeatureWriter::flush() 00322 { 00323 if (m_prevstream) { 00324 m_prevstream->flush(); 00325 } 00326 } 00327 00328 00329 void 00330 FileFeatureWriter::finish() 00331 { 00332 // SVDEBUG << "FileFeatureWriter::finish()" << endl; 00333 00334 if (m_singleFileName != "" || m_stdout) return; 00335 00336 while (!m_streams.empty()) { 00337 m_streams.begin()->second->flush(); 00338 delete m_streams.begin()->second; 00339 m_streams.erase(m_streams.begin()); 00340 } 00341 while (!m_files.empty()) { 00342 if (m_files.begin()->second) { 00343 SVDEBUG << "FileFeatureWriter::finish: NOTE: Closing feature file \"" 00344 << m_files.begin()->second->fileName() << "\"" << endl; 00345 delete m_files.begin()->second; 00346 } 00347 m_files.erase(m_files.begin()); 00348 } 00349 m_prevstream = 0; 00350 } 00351