annotate runner/main.cpp @ 97:54565c08c197 start-duration

Add multi-output start-duration test (with different start and duration per output!), and clear out data from output files for tests that are not yet implemented so they don't accidentally succeed
author Chris Cannam
date Wed, 01 Oct 2014 09:27:21 +0100
parents 03b1d83fca29
children fae326c22df5
rev   line source
Chris@0 1 /* -*- c-basic-offset: 4 indent-tabs-mode: nil -*- vi:set ts=8 sts=4 sw=4: */
Chris@0 2
Chris@0 3 /*
Chris@0 4 Sonic Annotator
Chris@0 5 A utility for batch feature extraction from audio files.
Chris@0 6 Mark Levy, Chris Sutton and Chris Cannam, Queen Mary, University of London.
Chris@0 7 Copyright 2007-2008 QMUL.
Chris@0 8
Chris@0 9 This program is free software; you can redistribute it and/or
Chris@0 10 modify it under the terms of the GNU General Public License as
Chris@0 11 published by the Free Software Foundation; either version 2 of the
Chris@0 12 License, or (at your option) any later version. See the file
Chris@0 13 COPYING included with this distribution for more information.
Chris@0 14 */
Chris@0 15
Chris@0 16 #include <vector>
Chris@0 17 #include <string>
Chris@0 18 #include <iostream>
Chris@0 19
Chris@0 20 #include <QCoreApplication>
Chris@0 21 #include <QSettings>
Chris@0 22 #include <QStringList>
Chris@0 23 #include <QString>
Chris@0 24 #include <QFileInfo>
Chris@0 25 #include <QDir>
Chris@45 26 #include <QSet>
Chris@0 27
Chris@0 28 using std::cout;
Chris@0 29 using std::cerr;
Chris@0 30 using std::endl;
Chris@0 31 using std::vector;
Chris@0 32 using std::string;
Chris@0 33
Chris@2 34 #include "../version.h"
Chris@2 35
Chris@0 36 #include "base/Exceptions.h"
Chris@0 37 #include "base/TempDirectory.h"
Chris@0 38
Chris@0 39 #include "data/fileio/AudioFileReaderFactory.h"
Chris@0 40 #include "data/fileio/PlaylistFileReader.h"
Chris@0 41
Chris@0 42 #include "transform/Transform.h"
Chris@0 43 #include "transform/TransformFactory.h"
Chris@0 44
Chris@0 45 #include "FeatureExtractionManager.h"
Chris@0 46 #include "transform/FeatureWriter.h"
Chris@0 47 #include "FeatureWriterFactory.h"
Chris@0 48
Chris@0 49 #include "rdf/RDFTransformFactory.h"
Chris@0 50
Chris@0 51 #include <vamp-hostsdk/PluginSummarisingAdapter.h>
Chris@0 52
Chris@0 53 #ifdef HAVE_FFTW3
Chris@0 54 #include <fftw3.h>
Chris@0 55 #endif
Chris@0 56
Chris@0 57 // Desired options:
Chris@0 58 //
Chris@0 59 // * output preference:
Chris@0 60 // - all data in one file
Chris@0 61 // - one file per input file
Chris@0 62 // - one file per input file per transform
Chris@0 63 // - (any use for: one file per transform?)
Chris@0 64 //
Chris@0 65 // * output location:
Chris@0 66 // - same directory as input file
Chris@0 67 // - current directory
Chris@0 68 //
Chris@0 69 // * output filename:
Chris@0 70 // - based on input (obvious choice for one file per input file modes)
Chris@0 71 // - specified on command line (obvious choice for all in one file mode)
Chris@0 72 //
Chris@0 73 // * output format: one or more of
Chris@0 74 // - RDF
Chris@0 75 // - AudioDB
Chris@0 76 // - Vamp Simple Host format
Chris@0 77 // - CSV
Chris@0 78 //
Chris@0 79 // * input handling:
Chris@0 80 // - run each transform on each input file separately
Chris@0 81 // - provide all input files to the same transform, one per channel
Chris@0 82 //
Chris@0 83 // * format-specific options:
Chris@0 84 // - RDF format: fancy/plain RDF
Chris@0 85 // - CSV format: separator, timestamp type
Chris@0 86 // note: do the output file/location also count as format-specific options?
Chris@0 87 // an output writer that wrote to a database would have different options...
Chris@0 88 //
Chris@0 89 // * debug level and progress output
Chris@0 90 //
Chris@0 91 // * other potential options:
Chris@0 92 // - ignore version mismatches in Transform specifications
Chris@0 93 // - sample rate: force a given rate; use file rate instead of rate in
Chris@0 94 // Transform spec
Chris@0 95 //
Chris@0 96 // * other potential instructions:
Chris@0 97 // - write out a skeleton Transform file for a specified plugin
Chris@0 98 // - write out skeleton RDF for a plugin library (i.e. do the job of
Chris@0 99 // RDF template_generator)
Chris@0 100 // - verify that RDF for a plugin library matches the plugin
Chris@0 101 //
Chris@0 102 // MAYBE:
Chris@0 103 // * transform(s) to run:
Chris@0 104 // - supply transform file names on command line
Chris@0 105 // - use all transforms found in a given directory?
Chris@0 106 //
Chris@0 107 // MAYBE:
Chris@0 108 // * input files to transform:
Chris@0 109 // - supply file names or URIs on command line
Chris@0 110 // - use all files in a given directory or tree
Chris@0 111
Chris@0 112 static QString
Chris@0 113 wrap(QString s, int len, int pfx = 0)
Chris@0 114 {
Chris@0 115 QString ws;
Chris@0 116 QStringList sl(s.split(' '));
Chris@0 117 int i = 0, c = 0;
Chris@0 118 while (i < sl.size()) {
Chris@0 119 int wl = sl[i].length();
Chris@0 120 if (c + wl < len) {
Chris@0 121 if (c > 0) {
Chris@0 122 ws += ' ';
Chris@0 123 ++c;
Chris@0 124 }
Chris@0 125 } else {
Chris@0 126 if (c > 0) {
Chris@0 127 ws += '\n';
Chris@0 128 for (int j = 0; j < pfx; ++j) ws += ' ';
Chris@0 129 c = 0;
Chris@0 130 }
Chris@0 131 }
Chris@0 132 ws += sl[i];
Chris@0 133 c += wl;
Chris@0 134 ++i;
Chris@0 135 }
Chris@0 136 return ws;
Chris@0 137 }
Chris@0 138
Chris@0 139 void usage(QString myname)
Chris@0 140 {
Chris@0 141 set<string> writers = FeatureWriterFactory::getWriterTags();
Chris@0 142
Chris@0 143 cerr << endl;
Chris@2 144 cerr << "Sonic Annotator v" << RUNNER_VERSION << endl;
Chris@0 145 cerr << "A utility for batch feature extraction from audio files." << endl;
Chris@0 146 cerr << "Mark Levy, Chris Sutton and Chris Cannam, Queen Mary, University of London." << endl;
Chris@95 147 cerr << "Copyright 2007-2014 Queen Mary, University of London." << endl;
Chris@0 148 cerr << endl;
Chris@0 149 cerr << "This program is free software. You may redistribute copies of it under the" << endl;
Chris@0 150 cerr << "terms of the GNU General Public License <http://www.gnu.org/licenses/gpl.html>." << endl;
Chris@0 151 cerr << "This program is supplied with NO WARRANTY, to the extent permitted by law." << endl;
Chris@0 152 cerr << endl;
Chris@0 153 cerr << " Usage: " << myname.toStdString()
Chris@0 154 << " [-mr] -t trans.xml [...] -w <writer> [...] <audio> [...]" << endl;
Chris@0 155 cerr << " " << myname.toStdString()
Chris@0 156 << " [-mr] -T trans.txt [...] -w <writer> [...] <audio> [...]" << endl;
Chris@0 157 cerr << " " << myname.toStdString()
Chris@0 158 << " -s <transform>" << endl;
Chris@0 159 cerr << " " << myname.toStdString()
Chris@46 160 << " [-lhv]" << endl;
Chris@0 161 cerr << endl;
Chris@0 162 cerr << "Where <audio> is an audio file or URL to use as input: either a local file" << endl;
Chris@0 163 cerr << "path, local \"file://\" URL, or remote \"http://\" or \"ftp://\" URL." << endl;
Chris@0 164 cerr << endl;
Chris@0 165
Chris@0 166 QString extensions = AudioFileReaderFactory::getKnownExtensions();
Chris@0 167 QStringList extlist = extensions.split(" ", QString::SkipEmptyParts);
Chris@0 168 if (!extlist.empty()) {
Chris@0 169 cerr << "The following audio file extensions are recognised:" << endl;
Chris@0 170 cerr << " ";
Chris@0 171 int c = 2;
Chris@0 172 for (int i = 0; i < extlist.size(); ++i) {
Chris@0 173 QString ext = extlist[i];
Chris@0 174 if (ext.startsWith("*.")) ext = ext.right(ext.length()-2);
Chris@0 175 c += ext.length() + 2;
Chris@0 176 if (c >= 80) {
Chris@0 177 cerr << "\n ";
Chris@0 178 c -= 78;
Chris@0 179 }
Chris@0 180 cerr << ext.toStdString();
Chris@0 181 if (i + 1 == extlist.size()) cerr << ".";
Chris@0 182 else cerr << ", ";
Chris@0 183 }
Chris@0 184 cerr << endl;
Chris@0 185 }
Chris@0 186
Chris@0 187 cerr << "Playlist files in M3U format are also supported." << endl;
Chris@0 188 cerr << endl;
Chris@0 189 cerr << "Transformation options:" << endl;
Chris@0 190 cerr << endl;
Chris@0 191 cerr << " -t, --transform <T> Apply transform described in transform file <T> to" << endl;
Chris@0 192 cerr << " all input audio files. You may supply this option" << endl;
Chris@0 193 cerr << " multiple times. You must supply this option or -T at" << endl;
Chris@0 194 cerr << " least once for any work to be done. Transform format" << endl;
Chris@0 195 cerr << " may be SV transform XML or Vamp transform RDF. See" << endl;
Chris@0 196 cerr << " documentation for examples." << endl;
Chris@0 197 cerr << endl;
Chris@0 198 cerr << " -T, --transforms <T> Apply all transforms described in transform files" << endl;
Chris@0 199 cerr << " whose names are listed in text file <T>. You may supply" << endl;
Chris@0 200 cerr << " this option multiple times." << endl;
Chris@0 201 cerr << endl;
Chris@0 202 cerr << " -d, --default <I> Apply the default transform for transform id <I>. This" << endl;
Chris@0 203 cerr << " is equivalent to generating a skeleton transform for this" << endl;
Chris@0 204 cerr << " id (using the -s option, below) and then applying that," << endl;
Chris@0 205 cerr << " unmodified, with the -t option in the normal way. Note" << endl;
Chris@0 206 cerr << " that the results may vary as the implementation's default" << endl;
Chris@0 207 cerr << " processing parameters are not guaranteed. Do not use" << endl;
Chris@0 208 cerr << " this in production systems. You may supply this option" << endl;
Chris@0 209 cerr << " multiple times, and mix it with -t and -T." << endl;
Chris@0 210 cerr << endl;
Chris@0 211 cerr << " -w, --writer <W> Write output using writer type <W>." << endl;
Chris@0 212 cerr << " Supported writer types are: ";
Chris@0 213 for (set<string>::const_iterator i = writers.begin();
Chris@0 214 i != writers.end(); ) {
Chris@0 215 cerr << *i;
Chris@0 216 if (++i != writers.end()) cerr << ", ";
Chris@0 217 else cerr << ".";
Chris@0 218 }
Chris@0 219 cerr << endl;
Chris@0 220 cerr << " You may supply this option multiple times. You must" << endl;
Chris@0 221 cerr << " supply this option at least once for any work to be done." << endl;
Chris@0 222 cerr << endl;
Chris@0 223 cerr << " -S, --summary <S> In addition to the result features, write summary feature" << endl;
Chris@0 224 cerr << " of summary type <S>." << endl;
Chris@0 225 cerr << " Supported summary types are: min, max, mean, median, mode," << endl;
Chris@0 226 cerr << " sum, variance, sd, count." << endl;
Chris@0 227 cerr << " You may supply this option multiple times." << endl;
Chris@0 228 cerr << endl;
Chris@0 229 cerr << " --summary-only Write only summary features; do not write the regular" << endl;
Chris@0 230 cerr << " result features." << endl;
Chris@0 231 cerr << endl;
Chris@0 232 cerr << " --segments <A>,<B>[,...]" << endl;
Chris@0 233 cerr << " Summarise in segments, with segment boundaries" << endl;
Chris@0 234 cerr << " at A, B, ... seconds." << endl;
Chris@0 235 cerr << endl;
Chris@0 236
Chris@0 237 /*!!! This feature not implemented yet (sniff)
Chris@0 238 cerr << " -m, --multiplex If multiple input audio files are given, use mono" << endl;
Chris@0 239 cerr << " mixdowns of all files as the input channels for a single" << endl;
Chris@0 240 cerr << " invocation of each transform, instead of running the" << endl;
Chris@0 241 cerr << " transform against all files separately." << endl;
Chris@0 242 cerr << endl;
Chris@0 243 */
Chris@0 244
Chris@0 245 cerr << " -r, --recursive If any of the <audio> arguments is found to be a local" << endl;
Chris@0 246 cerr << " directory, search the tree starting at that directory" << endl;
Chris@0 247 cerr << " for all supported audio files and take all of those as" << endl;
Chris@0 248 cerr << " input instead." << endl;
Chris@0 249 cerr << endl;
Chris@21 250 cerr << " -f, --force Continue with subsequent files following an error." << endl;
Chris@21 251 cerr << endl;
Chris@0 252 cerr << "Housekeeping options:" << endl;
Chris@0 253 cerr << endl;
Chris@0 254 cerr << " -l, --list List all known transform ids to standard output." << endl;
Chris@0 255 cerr << endl;
Chris@0 256 cerr << " -s, --skeleton <I> Generate a skeleton transform file for transform id <I>" << endl;
Chris@0 257 cerr << " and write it to standard output." << endl;
Chris@0 258 cerr << endl;
Chris@46 259 cerr << " -v, --version Show the version number and exit." << endl;
Chris@0 260 cerr << " -h, --help Show this help." << endl;
Chris@0 261
Chris@0 262 cerr << endl;
Chris@46 263 cerr << "If no -w (or --writer) options are supplied, either the -l -s -v or -h option" << endl;
Chris@46 264 cerr << "(or long equivalent) must be given instead." << endl;
Chris@0 265
Chris@0 266 for (set<string>::const_iterator i = writers.begin();
Chris@0 267 i != writers.end(); ++i) {
Chris@0 268 FeatureWriter *w = FeatureWriterFactory::createWriter(*i);
Chris@0 269 if (!w) {
Chris@0 270 cerr << " (Internal error: failed to create writer of this type)" << endl;
Chris@0 271 continue;
Chris@0 272 }
Chris@0 273 FeatureWriter::ParameterList params = w->getSupportedParameters();
Chris@0 274 delete w;
Chris@0 275 if (params.empty()) {
Chris@0 276 continue;
Chris@0 277 }
Chris@0 278 cerr << endl;
Chris@0 279 cerr << "Additional options for writer type \"" << *i << "\":" << endl;
Chris@0 280 cerr << endl;
Chris@0 281 for (FeatureWriter::ParameterList::const_iterator j = params.begin();
Chris@0 282 j != params.end(); ++j) {
Chris@0 283 cerr << " --" << *i << "-" << j->name << " ";
Chris@0 284 int spaceage = 16 - int(i->length()) - int(j->name.length());
Chris@0 285 if (j->hasArg) { cerr << "<X> "; spaceage -= 4; }
Chris@0 286 for (int k = 0; k < spaceage; ++k) cerr << " ";
Chris@0 287 QString s(j->description.c_str());
Chris@0 288 s = wrap(s, 56, 22);
Chris@0 289 cerr << s.toStdString() << endl;
Chris@0 290 }
Chris@0 291 }
Chris@0 292
Chris@0 293 cerr << endl;
Chris@0 294 exit(0);
Chris@0 295 }
Chris@0 296
Chris@0 297 void
Chris@0 298 listTransforms()
Chris@0 299 {
Chris@0 300 TransformList transforms =
Chris@0 301 TransformFactory::getInstance()->getAllTransformDescriptions();
Chris@0 302
Chris@0 303 for (TransformList::const_iterator iter = transforms.begin();
Chris@0 304 iter != transforms.end(); ++iter) {
Chris@0 305 const TransformDescription &transform = *iter;
Chris@0 306 if (transform.type == TransformDescription::Analysis) {
Chris@0 307 cout << transform.identifier.toStdString() << endl;
Chris@0 308 }
Chris@0 309 }
Chris@0 310 }
Chris@0 311
Chris@0 312 void
Chris@0 313 printSkeleton(QString id)
Chris@0 314 {
Chris@0 315 Transform transform =
Chris@0 316 TransformFactory::getInstance()->getDefaultTransformFor(id);
Chris@0 317 cout << "@prefix xsd: <http://www.w3.org/2001/XMLSchema#> ." << endl
Chris@0 318 << "@prefix vamp: <http://purl.org/ontology/vamp/> ." << endl
Chris@0 319 << "@prefix : <#> ." << endl << endl;
Chris@0 320 QString rdf = RDFTransformFactory::writeTransformToRDF
Chris@0 321 (transform, ":transform");
Chris@0 322 cout << rdf.toStdString();
Chris@0 323 }
Chris@0 324
Chris@0 325 void
Chris@0 326 findSourcesRecursive(QString dirname, QStringList &addTo, int &found)
Chris@0 327 {
Chris@0 328 QDir dir(dirname);
Chris@0 329
Chris@0 330 QString printable = dir.dirName().left(20);
Chris@0 331 cerr << "\rScanning \"" << printable.toStdString() << "\"..."
Chris@0 332 << QString(" ").left(20 - printable.length()).toStdString()
Chris@0 333 << " [" << found << " audio file(s)]";
Chris@0 334
Chris@0 335 QString extensions = AudioFileReaderFactory::getKnownExtensions();
Chris@0 336 QStringList extlist = extensions.split(" ", QString::SkipEmptyParts);
Chris@0 337
Chris@0 338 QStringList files = dir.entryList
Chris@0 339 (extlist, QDir::Files | QDir::Readable);
Chris@0 340 for (int i = 0; i < files.size(); ++i) {
Chris@0 341 addTo.push_back(dir.filePath(files[i]));
Chris@0 342 ++found;
Chris@0 343 }
Chris@0 344
Chris@0 345 QStringList subdirs = dir.entryList
Chris@0 346 (QStringList(), QDir::Dirs | QDir::NoSymLinks | QDir::NoDotAndDotDot);
Chris@0 347 for (int i = 0; i < subdirs.size(); ++i) {
Chris@0 348 findSourcesRecursive(dir.filePath(subdirs[i]), addTo, found);
Chris@0 349 }
Chris@0 350 }
Chris@0 351
Chris@0 352
Chris@0 353 int main(int argc, char **argv)
Chris@0 354 {
Chris@0 355 QCoreApplication application(argc, argv);
Chris@0 356
Chris@0 357 QCoreApplication::setOrganizationName("QMUL");
Chris@0 358 QCoreApplication::setOrganizationDomain("qmul.ac.uk");
Chris@0 359 QCoreApplication::setApplicationName("Sonic Annotator");
Chris@0 360
Chris@0 361 QStringList args = application.arguments();
Chris@0 362 set<string> requestedWriterTags;
Chris@0 363 set<string> requestedTransformFiles;
Chris@0 364 set<string> requestedTransformListFiles;
Chris@0 365 set<string> requestedDefaultTransforms;
Chris@0 366 set<string> requestedSummaryTypes;
Chris@21 367 bool force = false;
Chris@0 368 //!!! bool multiplex = false;
Chris@0 369 bool recursive = false;
Chris@0 370 bool list = false;
Chris@0 371 bool summaryOnly = false;
Chris@0 372 QString skeletonFor = "";
Chris@0 373 QString myname = args[0];
Chris@0 374 myname = QFileInfo(myname).baseName();
Chris@0 375 QStringList otherArgs;
Chris@0 376 Vamp::HostExt::PluginSummarisingAdapter::SegmentBoundaries boundaries;
Chris@0 377
Chris@0 378 QString helpStr = myname + ": use -h or --help option for help";
Chris@0 379
Chris@0 380 for (int i = 1; i < args.size(); ++i) {
Chris@0 381
Chris@0 382 QString arg = args[i];
Chris@0 383 bool last = ((i + 1) == args.size());
Chris@0 384
Chris@0 385 if (arg == "-h" || arg == "--help" || arg == "-?") {
Chris@0 386 usage(myname);
Chris@0 387 }
Chris@0 388
Chris@46 389 if (arg == "-v" || arg == "--version") {
Chris@46 390 std::cout << RUNNER_VERSION << std::endl;
Chris@46 391 return 0;
Chris@46 392 }
Chris@46 393
Chris@0 394 if (arg == "-w" || arg == "--writer") {
Chris@0 395 if (last || args[i+1].startsWith("-")) {
Chris@0 396 cerr << myname.toStdString() << ": argument expected for \""
Chris@0 397 << arg.toStdString() << "\" option" << endl;
Chris@0 398 cerr << helpStr.toStdString() << endl;
Chris@0 399 exit(2);
Chris@0 400 } else {
Chris@0 401 string tag = args[++i].toStdString();
Chris@0 402 if (requestedWriterTags.find(tag) != requestedWriterTags.end()) {
Chris@0 403 cerr << myname.toStdString() << ": NOTE: duplicate specification of writer type \"" << tag << "\" ignored" << endl;
Chris@0 404 } else {
Chris@0 405 requestedWriterTags.insert(tag);
Chris@0 406 }
Chris@0 407 continue;
Chris@0 408 }
Chris@0 409 } else if (arg == "-t" || arg == "--transform") {
Chris@0 410 if (last || args[i+1].startsWith("-")) {
Chris@0 411 cerr << myname.toStdString() << ": argument expected for \""
Chris@0 412 << arg.toStdString() << "\" option" << endl;
Chris@0 413 cerr << helpStr.toStdString() << endl;
Chris@0 414 exit(2);
Chris@0 415 } else {
Chris@0 416 string transform = args[++i].toStdString();
Chris@0 417 if (requestedTransformFiles.find(transform) !=
Chris@0 418 requestedTransformFiles.end()) {
Chris@0 419 cerr << myname.toStdString() << ": NOTE: duplicate specification of transform file \"" << transform << "\" ignored" << endl;
Chris@0 420 } else {
Chris@0 421 requestedTransformFiles.insert(transform);
Chris@0 422 }
Chris@0 423 continue;
Chris@0 424 }
Chris@0 425 } else if (arg == "-T" || arg == "--transforms") {
Chris@0 426 if (last || args[i+1].startsWith("-")) {
Chris@0 427 cerr << myname.toStdString() << ": argument expected for \""
Chris@0 428 << arg.toStdString() << "\" option" << endl;
Chris@0 429 cerr << helpStr.toStdString() << endl;
Chris@0 430 exit(2);
Chris@0 431 } else {
Chris@0 432 string transform = args[++i].toStdString();
Chris@0 433 if (requestedTransformListFiles.find(transform) !=
Chris@0 434 requestedTransformListFiles.end()) {
Chris@0 435 cerr << myname.toStdString() << ": NOTE: duplicate specification of transform list file \"" << transform << "\" ignored" << endl;
Chris@0 436 } else {
Chris@0 437 requestedTransformListFiles.insert(transform);
Chris@0 438 }
Chris@0 439 continue;
Chris@0 440 }
Chris@0 441 } else if (arg == "-d" || arg == "--default") {
Chris@0 442 if (last || args[i+1].startsWith("-")) {
Chris@0 443 cerr << myname.toStdString() << ": argument expected for \""
Chris@0 444 << arg.toStdString() << "\" option" << endl;
Chris@0 445 cerr << helpStr.toStdString() << endl;
Chris@0 446 exit(2);
Chris@0 447 } else {
Chris@0 448 string deft = args[++i].toStdString();
Chris@0 449 if (requestedDefaultTransforms.find(deft) !=
Chris@0 450 requestedDefaultTransforms.end()) {
Chris@0 451 cerr << myname.toStdString() << ": NOTE: duplicate specification of default transform \"" << deft << "\" ignored" << endl;
Chris@0 452 } else {
Chris@0 453 requestedDefaultTransforms.insert(deft);
Chris@0 454 }
Chris@0 455 continue;
Chris@0 456 }
Chris@0 457 } else if (arg == "-S" || arg == "--summary") {
Chris@0 458 if (last || args[i+1].startsWith("-")) {
Chris@0 459 cerr << myname.toStdString() << ": argument expected for \""
Chris@0 460 << arg.toStdString() << "\" option" << endl;
Chris@0 461 cerr << helpStr.toStdString() << endl;
Chris@0 462 exit(2);
Chris@0 463 } else {
Chris@0 464 string summary = args[++i].toStdString();
Chris@0 465 requestedSummaryTypes.insert(summary);
Chris@0 466 continue;
Chris@0 467 }
Chris@0 468 } else if (arg == "--summary-only") {
Chris@0 469 summaryOnly = true;
Chris@0 470 continue;
Chris@0 471 } else if (arg == "--segments") {
Chris@0 472 if (last) {
Chris@0 473 cerr << myname.toStdString() << ": argument expected for \""
Chris@0 474 << arg.toStdString() << "\" option" << endl;
Chris@0 475 cerr << helpStr.toStdString() << endl;
Chris@0 476 exit(2);
Chris@0 477 } else {
Chris@0 478 string segmentSpec = args[++i].toStdString();
Chris@0 479 QStringList segmentStrs = QString(segmentSpec.c_str()).split(',');
Chris@0 480 for (int j = 0; j < segmentStrs.size(); ++j) {
Chris@0 481 bool good = false;
Chris@0 482 boundaries.insert(Vamp::RealTime::fromSeconds
Chris@0 483 (segmentStrs[j].toDouble(&good)));
Chris@0 484 if (!good) {
Chris@0 485 cerr << myname.toStdString() << ": segment boundaries must be numeric" << endl;
Chris@0 486 cerr << helpStr.toStdString() << endl;
Chris@0 487 exit(2);
Chris@0 488 }
Chris@0 489 }
Chris@0 490 }
Chris@0 491 /*!!!
Chris@0 492 } else if (arg == "-m" || arg == "--multiplex") {
Chris@0 493 multiplex = true;
Chris@0 494 cerr << myname.toStdString()
Chris@0 495 << ": WARNING: Multiplex argument not yet implemented" << endl; //!!!
Chris@0 496 continue;
Chris@0 497 */
Chris@0 498 } else if (arg == "-r" || arg == "--recursive") {
Chris@0 499 recursive = true;
Chris@0 500 continue;
Chris@21 501 } else if (arg == "-f" || arg == "--force") {
Chris@21 502 force = true;
Chris@21 503 continue;
Chris@0 504 } else if (arg == "-l" || arg == "--list") {
Chris@0 505 list = true;
Chris@0 506 continue;
Chris@0 507 } else if (arg == "-s" || arg == "--skeleton") {
Chris@0 508 if (last || args[i+1].startsWith("-")) {
Chris@0 509 cerr << myname.toStdString() << ": usage: "
Chris@0 510 << myname.toStdString() << " " << arg.toStdString()
Chris@0 511 << " <transform>" << endl;
Chris@0 512 cerr << helpStr.toStdString() << endl;
Chris@0 513 exit(2);
Chris@0 514 } else {
Chris@0 515 skeletonFor = args[++i];
Chris@0 516 continue;
Chris@0 517 }
Chris@0 518 } else {
Chris@0 519 otherArgs.push_back(args[i]);
Chris@0 520 }
Chris@0 521 }
Chris@0 522
Chris@0 523 if (list) {
Chris@0 524 if (!requestedWriterTags.empty() || skeletonFor != "") {
Chris@0 525 cerr << helpStr.toStdString() << endl;
Chris@0 526 exit(2);
Chris@0 527 }
Chris@0 528 listTransforms();
Chris@0 529 exit(0);
Chris@0 530 }
Chris@0 531 if (skeletonFor != "") {
Chris@0 532 if (!requestedWriterTags.empty()) {
Chris@0 533 cerr << helpStr.toStdString() << endl;
Chris@0 534 exit(2);
Chris@0 535 }
Chris@0 536 printSkeleton(skeletonFor);
Chris@0 537 exit(0);
Chris@0 538 }
Chris@0 539
Chris@0 540 if (requestedTransformFiles.empty() &&
Chris@0 541 requestedTransformListFiles.empty() &&
Chris@0 542 requestedDefaultTransforms.empty()) {
Chris@0 543 cerr << myname.toStdString()
Chris@0 544 << ": no transform(s) specified" << endl;
Chris@0 545 cerr << helpStr.toStdString() << endl;
Chris@0 546 exit(2);
Chris@0 547 }
Chris@0 548
Chris@0 549 if (requestedWriterTags.empty()) {
Chris@0 550 cerr << myname.toStdString()
Chris@0 551 << ": no writer(s) specified" << endl;
Chris@0 552 cerr << helpStr.toStdString() << endl;
Chris@0 553 exit(2);
Chris@0 554 }
Chris@0 555
Chris@0 556 if (!boundaries.empty()) {
Chris@0 557 if (requestedSummaryTypes.empty()) {
Chris@0 558 cerr << myname.toStdString()
Chris@0 559 << ": summary segment boundaries provided, but no summary type specified"
Chris@0 560 << endl;
Chris@0 561 cerr << helpStr.toStdString() << endl;
Chris@0 562 exit(2);
Chris@0 563 }
Chris@0 564 }
Chris@0 565
Chris@20 566 QSettings settings;
Chris@20 567
Chris@0 568 #ifdef HAVE_FFTW3
Chris@0 569 settings.beginGroup("FFTWisdom");
Chris@0 570 QString wisdom = settings.value("wisdom").toString();
Chris@0 571 if (wisdom != "") {
Chris@0 572 fftw_import_wisdom_from_string(wisdom.toLocal8Bit().data());
Chris@0 573 }
Chris@0 574 settings.endGroup();
Chris@0 575 #endif
Chris@0 576
Chris@20 577 settings.beginGroup("RDF");
Chris@20 578 if (!settings.contains("rdf-indices")) {
Chris@20 579 QStringList list;
Chris@20 580 list << "http://www.vamp-plugins.org/rdf/plugins/index.txt";
Chris@20 581 settings.setValue("rdf-indices", list);
Chris@20 582 }
Chris@20 583 settings.endGroup();
Chris@20 584
Chris@0 585 FeatureExtractionManager manager;
Chris@0 586
Chris@0 587 if (!requestedSummaryTypes.empty()) {
Chris@0 588 if (!manager.setSummaryTypes(requestedSummaryTypes,
Chris@0 589 summaryOnly,
Chris@0 590 boundaries)) {
Chris@0 591 cerr << myname.toStdString()
Chris@0 592 << ": failed to set requested summary types" << endl;
Chris@0 593 exit(1);
Chris@0 594 }
Chris@0 595 }
Chris@0 596
Chris@0 597 vector<FeatureWriter *> writers;
Chris@0 598
Chris@0 599 for (set<string>::const_iterator i = requestedWriterTags.begin();
Chris@0 600 i != requestedWriterTags.end(); ++i) {
Chris@0 601
Chris@0 602 FeatureWriter *writer = FeatureWriterFactory::createWriter(*i);
Chris@0 603
Chris@0 604 if (!writer) {
Chris@0 605 cerr << myname.toStdString() << ": unknown feature writer \""
Chris@0 606 << *i << "\"" << endl;
Chris@0 607 cerr << helpStr.toStdString() << endl;
Chris@0 608 exit(2);
Chris@0 609 }
Chris@0 610
Chris@0 611 map<string, string> writerArgs;
Chris@0 612 FeatureWriter::ParameterList pl(writer->getSupportedParameters());
Chris@0 613
Chris@95 614 for (int k = 0; k < (int)pl.size(); ++k) {
Chris@0 615
Chris@0 616 string argbase = pl[k].name;
Chris@0 617 QString literal = QString("--%1-%2")
Chris@0 618 .arg(i->c_str()).arg(argbase.c_str());
Chris@0 619
Chris@95 620 for (int j = 0; j < (int)otherArgs.size(); ) {
Chris@0 621
Chris@0 622 if (otherArgs[j] != literal) {
Chris@0 623 ++j;
Chris@0 624 continue;
Chris@0 625 }
Chris@0 626
Chris@0 627 otherArgs.removeAt(j);
Chris@0 628
Chris@0 629 if (pl[k].hasArg) {
Chris@0 630 if (j < otherArgs.size()) {
Chris@0 631 writerArgs[argbase] = otherArgs[j].toStdString();
Chris@0 632 otherArgs.removeAt(j);
Chris@0 633 } else {
Chris@0 634 cerr << myname.toStdString() << ": "
Chris@0 635 << "argument required for \""
Chris@0 636 << literal.toStdString() << "\" option"
Chris@0 637 << endl;
Chris@0 638 cerr << helpStr.toStdString() << endl;
Chris@0 639 exit(2);
Chris@0 640 }
Chris@0 641 } else {
Chris@0 642 writerArgs[argbase] = "";
Chris@0 643 }
Chris@0 644 }
Chris@0 645 }
Chris@0 646
Chris@0 647 writer->setParameters(writerArgs);
Chris@0 648
Chris@0 649 writers.push_back(writer);
Chris@0 650 }
Chris@0 651
Chris@0 652 for (int i = 0; i < otherArgs.size(); ++i) {
Chris@0 653 if (otherArgs[i].startsWith("-")) {
Chris@0 654 cerr << myname.toStdString() << ": unknown option \""
Chris@0 655 << otherArgs[i].toStdString() << "\"" << endl;
Chris@0 656 cerr << helpStr.toStdString() << endl;
Chris@0 657 exit(2);
Chris@0 658 }
Chris@0 659 }
Chris@0 660
Chris@0 661 if (otherArgs.empty()) {
Chris@0 662 cerr << myname.toStdString() << ": no input(s) specified" << endl;
Chris@0 663 cerr << helpStr.toStdString() << endl;
Chris@0 664 exit(2);
Chris@0 665 }
Chris@0 666
Chris@0 667 for (set<string>::const_iterator i = requestedTransformListFiles.begin();
Chris@0 668 i != requestedTransformListFiles.end(); ++i) {
Chris@0 669 PlaylistFileReader reader(i->c_str());
Chris@0 670 if (reader.isOK()) {
Chris@0 671 vector<QString> files = reader.load();
Chris@95 672 for (int j = 0; j < (int)files.size(); ++j) {
Chris@0 673 requestedTransformFiles.insert(files[j].toStdString());
Chris@0 674 }
Chris@0 675 } else {
Chris@0 676 cerr << myname.toStdString() << ": failed to read template list file \"" << *i << "\"" << endl;
Chris@0 677 exit(2);
Chris@0 678 }
Chris@0 679 }
Chris@0 680
Chris@0 681 QStringList sources;
Chris@0 682 if (!recursive) {
Chris@0 683 sources = otherArgs;
Chris@0 684 } else {
Chris@0 685 for (QStringList::const_iterator i = otherArgs.begin();
Chris@0 686 i != otherArgs.end(); ++i) {
Chris@0 687 if (QDir(*i).exists()) {
Chris@0 688 cerr << "Directory found and recursive flag set, scanning for audio files..." << endl;
Chris@0 689 int found = 0;
Chris@0 690 findSourcesRecursive(*i, sources, found);
Chris@0 691 cerr << "\rDone, found " << found << " supported audio file(s) " << endl;
Chris@0 692 } else {
Chris@0 693 sources.push_back(*i);
Chris@0 694 }
Chris@0 695 }
Chris@0 696 }
Chris@0 697
Chris@28 698 bool good = true;
Chris@45 699 QSet<QString> badSources;
Chris@28 700
Chris@0 701 for (QStringList::const_iterator i = sources.begin();
Chris@0 702 i != sources.end(); ++i) {
Chris@0 703 try {
Chris@45 704 manager.addSource(*i);
Chris@22 705 } catch (const std::exception &e) {
Chris@45 706 badSources.insert(*i);
Chris@21 707 cerr << "ERROR: Failed to process file \"" << i->toStdString()
Chris@21 708 << "\": " << e.what() << endl;
Chris@22 709 if (force) {
Chris@22 710 // print a note only if we have more files to process
Chris@22 711 QStringList::const_iterator j = i;
Chris@22 712 if (++j != sources.end()) {
Chris@22 713 cerr << "NOTE: \"--force\" option was provided, continuing (more errors may occur)" << endl;
Chris@22 714 }
Chris@22 715 } else {
Chris@45 716 cerr << "NOTE: If you want to continue with processing any further files after an" << endl
Chris@45 717 << "error like this, use the --force option" << endl;
Chris@28 718 good = false;
Chris@22 719 break;
Chris@22 720 }
Chris@0 721 }
Chris@0 722 }
Chris@45 723
Chris@45 724 if (good) {
Chris@45 725
Chris@45 726 bool haveFeatureExtractor = false;
Chris@45 727
Chris@45 728 for (set<string>::const_iterator i = requestedTransformFiles.begin();
Chris@45 729 i != requestedTransformFiles.end(); ++i) {
Chris@45 730 if (manager.addFeatureExtractorFromFile(i->c_str(), writers)) {
Chris@45 731 haveFeatureExtractor = true;
Chris@45 732 }
Chris@45 733 }
Chris@45 734
Chris@45 735 for (set<string>::const_iterator i = requestedDefaultTransforms.begin();
Chris@45 736 i != requestedDefaultTransforms.end(); ++i) {
Chris@45 737 if (manager.addDefaultFeatureExtractor(i->c_str(), writers)) {
Chris@45 738 haveFeatureExtractor = true;
Chris@45 739 }
Chris@45 740 }
Chris@45 741
Chris@45 742 if (!haveFeatureExtractor) {
Chris@45 743 cerr << myname.toStdString() << ": no feature extractors added" << endl;
Chris@45 744 good = false;
Chris@45 745 }
Chris@45 746 }
Chris@45 747
Chris@45 748 if (good) {
Chris@45 749 for (QStringList::const_iterator i = sources.begin();
Chris@45 750 i != sources.end(); ++i) {
Chris@45 751 if (badSources.contains(*i)) continue;
Chris@45 752 std::cerr << "Extracting features for: \"" << i->toStdString() << "\"" << std::endl;
Chris@45 753 try {
Chris@47 754 manager.extractFeatures(*i, force);
Chris@45 755 } catch (const std::exception &e) {
Chris@45 756 cerr << "ERROR: Feature extraction failed for \"" << i->toStdString()
Chris@45 757 << "\": " << e.what() << endl;
Chris@45 758 if (force) {
Chris@45 759 // print a note only if we have more files to process
Chris@45 760 QStringList::const_iterator j = i;
Chris@45 761 if (++j != sources.end()) {
Chris@45 762 cerr << "NOTE: \"--force\" option was provided, continuing (more errors may occur)" << endl;
Chris@45 763 }
Chris@45 764 } else {
Chris@45 765 cerr << "NOTE: If you want to continue with processing any further files after an" << endl
Chris@45 766 << "error like this, use the --force option" << endl;
Chris@45 767 good = false;
Chris@45 768 break;
Chris@45 769 }
Chris@45 770 }
Chris@45 771 }
Chris@45 772 }
Chris@0 773
Chris@95 774 for (int i = 0; i < (int)writers.size(); ++i) delete writers[i];
Chris@0 775
Chris@0 776 #ifdef HAVE_FFTW3
Chris@0 777 settings.beginGroup("FFTWisdom");
Chris@0 778 char *cwisdom = fftw_export_wisdom_to_string();
Chris@0 779 if (cwisdom) {
Chris@0 780 settings.setValue("wisdom", cwisdom);
Chris@0 781 fftw_free(cwisdom);
Chris@0 782 }
Chris@0 783 settings.endGroup();
Chris@0 784 #endif
Chris@0 785
Chris@0 786 TempDirectory::getInstance()->cleanup();
Chris@0 787
Chris@28 788 if (good) return 0;
Chris@28 789 else return 1;
Chris@0 790 }
Chris@0 791
Chris@0 792