annotate runner/main.cpp @ 240:7c4f7cd065a0

Various tweaks to help text
author Chris Cannam
date Tue, 01 Mar 2016 16:02:53 +0000
parents 9a10c3ffff47
children 4307b34f86c0
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@111 38 #include "base/ProgressPrinter.h"
Chris@0 39
Chris@0 40 #include "data/fileio/AudioFileReaderFactory.h"
Chris@0 41 #include "data/fileio/PlaylistFileReader.h"
Chris@0 42
Chris@0 43 #include "transform/Transform.h"
Chris@0 44 #include "transform/TransformFactory.h"
Chris@0 45
Chris@0 46 #include "FeatureExtractionManager.h"
Chris@0 47 #include "transform/FeatureWriter.h"
Chris@0 48 #include "FeatureWriterFactory.h"
Chris@0 49
Chris@0 50 #include "rdf/RDFTransformFactory.h"
Chris@0 51
Chris@0 52 #include <vamp-hostsdk/PluginSummarisingAdapter.h>
Chris@0 53
Chris@0 54 #ifdef HAVE_FFTW3
Chris@0 55 #include <fftw3.h>
Chris@0 56 #endif
Chris@0 57
Chris@0 58 // Desired options:
Chris@0 59 //
Chris@0 60 // * output preference:
Chris@0 61 // - all data in one file
Chris@0 62 // - one file per input file
Chris@0 63 // - one file per input file per transform
Chris@0 64 // - (any use for: one file per transform?)
Chris@0 65 //
Chris@0 66 // * output location:
Chris@0 67 // - same directory as input file
Chris@0 68 // - current directory
Chris@0 69 //
Chris@0 70 // * output filename:
Chris@0 71 // - based on input (obvious choice for one file per input file modes)
Chris@0 72 // - specified on command line (obvious choice for all in one file mode)
Chris@0 73 //
Chris@0 74 // * output format: one or more of
Chris@0 75 // - RDF
Chris@0 76 // - AudioDB
Chris@0 77 // - Vamp Simple Host format
Chris@0 78 // - CSV
Chris@0 79 //
Chris@0 80 // * input handling:
Chris@0 81 // - run each transform on each input file separately
Chris@0 82 // - provide all input files to the same transform, one per channel
Chris@0 83 //
Chris@0 84 // * format-specific options:
Chris@0 85 // - RDF format: fancy/plain RDF
Chris@0 86 // - CSV format: separator, timestamp type
Chris@0 87 // note: do the output file/location also count as format-specific options?
Chris@0 88 // an output writer that wrote to a database would have different options...
Chris@0 89 //
Chris@0 90 // * debug level and progress output
Chris@0 91 //
Chris@0 92 // * other potential options:
Chris@0 93 // - ignore version mismatches in Transform specifications
Chris@0 94 // - sample rate: force a given rate; use file rate instead of rate in
Chris@0 95 // Transform spec
Chris@0 96 //
Chris@0 97 // * other potential instructions:
Chris@0 98 // - write out a skeleton Transform file for a specified plugin
Chris@0 99 // - write out skeleton RDF for a plugin library (i.e. do the job of
Chris@0 100 // RDF template_generator)
Chris@0 101 // - verify that RDF for a plugin library matches the plugin
Chris@0 102 //
Chris@0 103 // MAYBE:
Chris@0 104 // * transform(s) to run:
Chris@0 105 // - supply transform file names on command line
Chris@0 106 // - use all transforms found in a given directory?
Chris@0 107 //
Chris@0 108 // MAYBE:
Chris@0 109 // * input files to transform:
Chris@0 110 // - supply file names or URIs on command line
Chris@0 111 // - use all files in a given directory or tree
Chris@0 112
Chris@0 113 static QString
Chris@0 114 wrap(QString s, int len, int pfx = 0)
Chris@0 115 {
Chris@0 116 QString ws;
Chris@0 117 QStringList sl(s.split(' '));
Chris@0 118 int i = 0, c = 0;
Chris@0 119 while (i < sl.size()) {
Chris@0 120 int wl = sl[i].length();
Chris@0 121 if (c + wl < len) {
Chris@0 122 if (c > 0) {
Chris@0 123 ws += ' ';
Chris@0 124 ++c;
Chris@0 125 }
Chris@0 126 } else {
Chris@0 127 if (c > 0) {
Chris@0 128 ws += '\n';
Chris@0 129 for (int j = 0; j < pfx; ++j) ws += ' ';
Chris@0 130 c = 0;
Chris@0 131 }
Chris@0 132 }
Chris@0 133 ws += sl[i];
Chris@0 134 c += wl;
Chris@0 135 ++i;
Chris@0 136 }
Chris@0 137 return ws;
Chris@0 138 }
Chris@0 139
Chris@240 140 static QString wrapCol(QString s) {
Chris@240 141 return wrap(s, 56, 22);
Chris@240 142 }
Chris@240 143
Chris@127 144 static bool
Chris@127 145 isVersionNewerThan(QString a, QString b) // from VersionTester in svapp
Chris@127 146 {
Chris@127 147 QRegExp re("[._-]");
Chris@127 148 QStringList alist = a.split(re, QString::SkipEmptyParts);
Chris@127 149 QStringList blist = b.split(re, QString::SkipEmptyParts);
Chris@127 150 int ae = alist.size();
Chris@127 151 int be = blist.size();
Chris@127 152 int e = std::max(ae, be);
Chris@127 153 for (int i = 0; i < e; ++i) {
Chris@127 154 int an = 0, bn = 0;
Chris@127 155 if (i < ae) {
Chris@127 156 an = alist[i].toInt();
Chris@127 157 if (an == 0 && alist[i] != "0") {
Chris@127 158 an = -1; // non-numeric field -> "-pre1" etc
Chris@127 159 }
Chris@127 160 }
Chris@127 161 if (i < be) {
Chris@127 162 bn = blist[i].toInt();
Chris@127 163 if (bn == 0 && blist[i] != "0") {
Chris@127 164 bn = -1;
Chris@127 165 }
Chris@127 166 }
Chris@127 167 if (an < bn) return false;
Chris@127 168 if (an > bn) return true;
Chris@127 169 }
Chris@127 170 return false;
Chris@127 171 }
Chris@127 172
Chris@127 173 static int
Chris@127 174 checkMinVersion(QString myname, QString v)
Chris@127 175 {
Chris@127 176 if (v == RUNNER_VERSION) {
Chris@127 177 return 0;
Chris@127 178 } else if (isVersionNewerThan(RUNNER_VERSION, v)) {
Chris@127 179 return 0;
Chris@127 180 } else {
Chris@127 181 cerr << myname << ": version "
Chris@127 182 << RUNNER_VERSION << " is less than requested min version "
Chris@127 183 << v << ", failing" << endl;
Chris@127 184 return 1;
Chris@127 185 }
Chris@127 186 }
Chris@127 187
Chris@125 188 void printUsage(QString myname)
Chris@0 189 {
Chris@0 190 cerr << endl;
Chris@2 191 cerr << "Sonic Annotator v" << RUNNER_VERSION << endl;
Chris@0 192 cerr << "A utility for batch feature extraction from audio files." << endl;
Chris@240 193 cerr << "Mark Levy, Chris Sutton, and Chris Cannam, Queen Mary, University of London." << endl;
Chris@197 194 cerr << "Copyright 2007-2015 Queen Mary, University of London." << endl;
Chris@0 195 cerr << endl;
Chris@0 196 cerr << "This program is free software. You may redistribute copies of it under the" << endl;
Chris@0 197 cerr << "terms of the GNU General Public License <http://www.gnu.org/licenses/gpl.html>." << endl;
Chris@0 198 cerr << "This program is supplied with NO WARRANTY, to the extent permitted by law." << endl;
Chris@0 199 cerr << endl;
Chris@125 200 cerr << "Usage: " << endl;
Chris@127 201 cerr << " " << myname
Chris@125 202 << " [-mrnf] -t transform.ttl [..] -w <writer> [..] <audio> [..]" << endl;
Chris@127 203 cerr << " " << myname
Chris@125 204 << " [-mrnf] -T translist.txt [..] -w <writer> [..] <audio> [..]" << endl;
Chris@127 205 cerr << " " << myname
Chris@240 206 << " [-mrnf] -d <id> [..] -w <writer> [..] <audio> [...]" << endl;
Chris@127 207 cerr << " " << myname
Chris@0 208 << " -s <transform>" << endl;
Chris@127 209 cerr << " " << myname
Chris@46 210 << " [-lhv]" << endl;
Chris@0 211 cerr << endl;
Chris@0 212 cerr << "Where <audio> is an audio file or URL to use as input: either a local file" << endl;
Chris@125 213 cerr << "path, local \"file://\" URL, or remote \"http://\" or \"ftp://\" URL;" << endl;
Chris@240 214 cerr << "and <id> is a transform id of the form vamp:libname:plugin:output." << endl;
Chris@0 215 cerr << endl;
Chris@125 216 }
Chris@125 217
Chris@197 218 void printOptionHelp(std::string writer, FeatureWriter::Parameter &p)
Chris@197 219 {
Chris@197 220 cerr << " --" << writer << "-" << p.name << " ";
Chris@197 221 int spaceage = 16 - int(writer.length()) - int(p.name.length());
Chris@197 222 if (p.hasArg) { cerr << "<X> "; spaceage -= 4; }
Chris@197 223 for (int k = 0; k < spaceage; ++k) cerr << " ";
Chris@197 224 QString s(p.description.c_str());
Chris@240 225 s = wrapCol(s);
Chris@197 226 cerr << s << endl;
Chris@197 227 }
Chris@197 228
Chris@142 229 void printHelp(QString myname, QString w)
Chris@125 230 {
Chris@142 231 std::string writer = w.toStdString();
Chris@142 232
Chris@125 233 printUsage(myname);
Chris@0 234
Chris@0 235 QString extensions = AudioFileReaderFactory::getKnownExtensions();
Chris@0 236 QStringList extlist = extensions.split(" ", QString::SkipEmptyParts);
Chris@0 237 if (!extlist.empty()) {
Chris@0 238 cerr << "The following audio file extensions are recognised:" << endl;
Chris@0 239 cerr << " ";
Chris@0 240 int c = 2;
Chris@0 241 for (int i = 0; i < extlist.size(); ++i) {
Chris@0 242 QString ext = extlist[i];
Chris@0 243 if (ext.startsWith("*.")) ext = ext.right(ext.length()-2);
Chris@0 244 c += ext.length() + 2;
Chris@0 245 if (c >= 80) {
Chris@0 246 cerr << "\n ";
Chris@0 247 c -= 78;
Chris@0 248 }
Chris@127 249 cerr << ext;
Chris@0 250 if (i + 1 == extlist.size()) cerr << ".";
Chris@0 251 else cerr << ", ";
Chris@0 252 }
Chris@0 253 cerr << endl;
Chris@0 254 }
Chris@0 255
Chris@0 256 cerr << "Playlist files in M3U format are also supported." << endl;
Chris@0 257 cerr << endl;
Chris@125 258
Chris@125 259 set<string> writers = FeatureWriterFactory::getWriterTags();
Chris@142 260
Chris@142 261 QString writerText = "Supported writer types are: ";
Chris@0 262 for (set<string>::const_iterator i = writers.begin();
Chris@0 263 i != writers.end(); ) {
Chris@142 264 writerText += i->c_str();
Chris@142 265 if (++i != writers.end()) writerText += ", ";
Chris@142 266 else writerText += ".";
Chris@0 267 }
Chris@240 268 writerText = wrapCol(writerText);
Chris@0 269
Chris@142 270 if (writer == "" || writers.find(writer) == writers.end()) {
Chris@0 271
Chris@142 272 cerr << "Transformation options:" << endl;
Chris@142 273 cerr << endl;
Chris@240 274 cerr << " -t, --transform <T> "
Chris@240 275 << wrapCol("Apply transform described in transform file <T> to"
Chris@240 276 " all input audio files. You may supply this option"
Chris@240 277 " multiple times. You must supply this option, -T, or -d"
Chris@240 278 " at least once for any work to be done. Transform format"
Chris@240 279 " may be SV transform XML or Vamp transform RDF/Turtle."
Chris@240 280 " A skeleton transform file for"
Chris@240 281 " a given transform id can be generated using the"
Chris@240 282 " -s option (see below). See accompanying"
Chris@240 283 " documentation for transform examples.")
Chris@240 284 << endl << endl;
Chris@240 285 cerr << " -T, --transforms <T> "
Chris@240 286 << wrapCol("Apply all transforms described in transform files"
Chris@240 287 " whose names are listed in text file <T>. You may supply"
Chris@240 288 " this option multiple times.")
Chris@240 289 << endl << endl;
Chris@240 290 cerr << " -d, --default <I> "
Chris@240 291 << wrapCol("Apply the default transform for transform id <I>. This"
Chris@240 292 " is equivalent to generating a skeleton transform for the"
Chris@240 293 " id (using the -s option, below) and then applying that,"
Chris@240 294 " unmodified, with the -t option in the normal way. Note"
Chris@240 295 " that results may vary, as default"
Chris@240 296 " processing parameters may change between releases of "
Chris@240 297 + myname + " as well as of individual plugins. Do not use"
Chris@240 298 " this in production systems. You may supply this option"
Chris@240 299 " multiple times, and mix it with -t and -T.")
Chris@240 300 << endl << endl;
Chris@240 301 cerr << " -w, --writer <W> Write output using writer type <W>.\n"
Chris@240 302 << " " << writerText << endl
Chris@240 303 << " "
Chris@240 304 << wrapCol("You may supply this option multiple times. You must"
Chris@240 305 " supply this option at least once for any work to be done.")
Chris@240 306 << endl << endl;
Chris@240 307 cerr << " -S, --summary <S> "
Chris@240 308 << wrapCol("In addition to the result features, write summary feature"
Chris@240 309 " of summary type <S>.") << endl
Chris@240 310 << " "
Chris@240 311 << wrapCol("Supported summary types are min, max, mean, median, mode,"
Chris@240 312 " sum, variance, sd, count.") << endl
Chris@240 313 << " You may supply this option multiple times."
Chris@240 314 << endl << endl;
Chris@240 315 cerr << " --summary-only "
Chris@240 316 << wrapCol("Write only summary features; do not write the regular"
Chris@240 317 " result features.")
Chris@240 318 << endl << endl;
Chris@240 319 cerr << " --segments <A>,<B>[,...]\n "
Chris@240 320 << wrapCol("Summarise in segments, with segment boundaries"
Chris@240 321 " at A, B, ... seconds.")
Chris@240 322 << endl << endl;
Chris@240 323 cerr << " --segments-from <F>\n "
Chris@240 324 << wrapCol("Summarise in segments, with segment boundaries"
Chris@240 325 " at times read from the text file <F>. (one time per"
Chris@240 326 " line, in seconds).")
Chris@240 327 << endl << endl;
Chris@240 328 cerr << " -m, --multiplex "
Chris@240 329 << wrapCol("If multiple input audio files are given, use mono"
Chris@240 330 " mixdowns of the files as the input channels for a single"
Chris@240 331 " invocation of each transform, instead of running the"
Chris@240 332 " transform against all files separately. The first file"
Chris@240 333 " will be used for output reference name and sample rate.")
Chris@240 334 << endl << endl;
Chris@240 335 cerr << " -r, --recursive "
Chris@240 336 << wrapCol("If any of the <audio> arguments is found to be a local"
Chris@240 337 " directory, search the tree starting at that directory"
Chris@240 338 " for all supported audio files and take all of those as"
Chris@240 339 " input in place of it.")
Chris@240 340 << endl << endl;
Chris@240 341 cerr << " -n, --normalise "
Chris@240 342 << wrapCol("Normalise each input audio file to signal abs max = 1.f.")
Chris@240 343 << endl << endl;
Chris@240 344 cerr << " -f, --force "
Chris@240 345 << wrapCol("Continue with subsequent files following an error.")
Chris@240 346 << endl << endl;
Chris@240 347 cerr << "Housekeeping options:"
Chris@240 348 << endl << endl;
Chris@144 349 cerr << " -l, --list List available transform ids to standard output." << endl;
Chris@144 350 cerr << " --list-writers List supported writer types to standard output." << endl;
Chris@144 351 cerr << " --list-formats List supported input audio formats to standard output." << endl;
Chris@142 352 cerr << endl;
Chris@240 353 cerr << " -s, --skeleton <I> "
Chris@240 354 << wrapCol("Generate a skeleton RDF transform file for transform id"
Chris@240 355 " <I>, with default parameters for that transform, and write it"
Chris@240 356 " to standard output.")
Chris@240 357 << endl << endl;
Chris@142 358 cerr << " -v, --version Show the version number and exit." << endl;
Chris@142 359 cerr << endl;
Chris@240 360 cerr << " --minversion <V> "
Chris@240 361 << wrapCol("Exit with successful return code if the version of "
Chris@240 362 + myname + " is at least <V>, failure otherwise."
Chris@240 363 " For scripts that depend on certain option support.")
Chris@240 364 << endl << endl;
Chris@142 365 cerr << " -h, --help Show help." << endl;
Chris@142 366 cerr << " -h, --help <W> Show help for writer type W." << endl;
Chris@142 367 cerr << " " << writerText << endl;
Chris@142 368
Chris@240 369 cerr << endl
Chris@240 370 << wrap("If no -w (or --writer) options are supplied, one of the"
Chris@240 371 " housekeeping options (-l -s -v -h or long equivalent) must"
Chris@240 372 " be given instead.", 78, 0)
Chris@240 373 << endl;
Chris@142 374
Chris@142 375 } else {
Chris@142 376
Chris@142 377 FeatureWriter *w = FeatureWriterFactory::createWriter(writer);
Chris@0 378 if (!w) {
Chris@142 379 cerr << " (Internal error: failed to create writer of known type \""
Chris@142 380 << writer << "\")" << endl;
Chris@142 381 return;
Chris@0 382 }
Chris@144 383 cerr << "Feature writer \"" << writer << "\":" << endl << endl;
Chris@144 384 cerr << " " << wrap(w->getDescription().c_str(), 76, 2) << endl << endl;
Chris@0 385 FeatureWriter::ParameterList params = w->getSupportedParameters();
Chris@0 386 delete w;
Chris@0 387 if (params.empty()) {
Chris@144 388 cerr << " No additional options are available for this writer." << endl << endl;
Chris@142 389 return;
Chris@0 390 }
Chris@197 391 FeatureWriter::ParameterList mandatory;
Chris@197 392 bool haveOptional = false;
Chris@197 393 for (auto &p: params) {
Chris@197 394 if (p.mandatory) mandatory.push_back(p);
Chris@197 395 else haveOptional = true;
Chris@197 396 }
Chris@197 397 if (!mandatory.empty()) {
Chris@197 398 cerr << "Mandatory parameters for writer type \"" << writer << "\":" << endl;
Chris@197 399 cerr << endl;
Chris@197 400 for (auto &p: mandatory) {
Chris@197 401 printOptionHelp(writer, p);
Chris@197 402 }
Chris@197 403 cerr << endl;
Chris@197 404 }
Chris@197 405 if (haveOptional) {
Chris@197 406 cerr << "Additional options for writer type \"" << writer << "\":" << endl;
Chris@197 407 cerr << endl;
Chris@197 408 for (auto &p: params) {
Chris@197 409 if (p.mandatory) continue;
Chris@197 410 printOptionHelp(writer, p);
Chris@197 411 }
Chris@0 412 }
Chris@0 413 }
Chris@0 414
Chris@0 415 cerr << endl;
Chris@0 416 }
Chris@0 417
Chris@0 418 void
Chris@0 419 listTransforms()
Chris@0 420 {
Chris@0 421 TransformList transforms =
Chris@0 422 TransformFactory::getInstance()->getAllTransformDescriptions();
Chris@0 423
Chris@0 424 for (TransformList::const_iterator iter = transforms.begin();
Chris@0 425 iter != transforms.end(); ++iter) {
Chris@0 426 const TransformDescription &transform = *iter;
Chris@0 427 if (transform.type == TransformDescription::Analysis) {
Chris@127 428 cout << transform.identifier << endl;
Chris@0 429 }
Chris@0 430 }
Chris@0 431 }
Chris@0 432
Chris@0 433 void
Chris@0 434 printSkeleton(QString id)
Chris@0 435 {
Chris@0 436 Transform transform =
Chris@0 437 TransformFactory::getInstance()->getDefaultTransformFor(id);
Chris@0 438 cout << "@prefix xsd: <http://www.w3.org/2001/XMLSchema#> ." << endl
Chris@0 439 << "@prefix vamp: <http://purl.org/ontology/vamp/> ." << endl
Chris@0 440 << "@prefix : <#> ." << endl << endl;
Chris@0 441 QString rdf = RDFTransformFactory::writeTransformToRDF
Chris@0 442 (transform, ":transform");
Chris@127 443 cout << rdf;
Chris@0 444 }
Chris@0 445
Chris@0 446 void
Chris@0 447 findSourcesRecursive(QString dirname, QStringList &addTo, int &found)
Chris@0 448 {
Chris@0 449 QDir dir(dirname);
Chris@0 450
Chris@0 451 QString printable = dir.dirName().left(20);
Chris@127 452 cerr << "\rScanning \"" << printable << "\"..."
Chris@127 453 << QString(" ").left(20 - printable.length())
Chris@0 454 << " [" << found << " audio file(s)]";
Chris@0 455
Chris@0 456 QString extensions = AudioFileReaderFactory::getKnownExtensions();
Chris@0 457 QStringList extlist = extensions.split(" ", QString::SkipEmptyParts);
Chris@0 458
Chris@0 459 QStringList files = dir.entryList
Chris@0 460 (extlist, QDir::Files | QDir::Readable);
Chris@0 461 for (int i = 0; i < files.size(); ++i) {
Chris@0 462 addTo.push_back(dir.filePath(files[i]));
Chris@0 463 ++found;
Chris@0 464 }
Chris@0 465
Chris@0 466 QStringList subdirs = dir.entryList
Chris@0 467 (QStringList(), QDir::Dirs | QDir::NoSymLinks | QDir::NoDotAndDotDot);
Chris@0 468 for (int i = 0; i < subdirs.size(); ++i) {
Chris@0 469 findSourcesRecursive(dir.filePath(subdirs[i]), addTo, found);
Chris@0 470 }
Chris@0 471 }
Chris@0 472
Chris@111 473 QStringList
Chris@111 474 expandPlaylists(QStringList sources)
Chris@111 475 {
Chris@111 476 QStringList expanded;
Chris@111 477 foreach (QString path, sources) {
Chris@111 478 if (QFileInfo(path).suffix().toLower() == "m3u") {
Chris@111 479 ProgressPrinter retrievalProgress("Opening playlist file...");
Chris@111 480 FileSource source(path, &retrievalProgress);
Chris@111 481 if (!source.isAvailable()) {
Chris@117 482 // Don't fail or throw an exception here, just keep
Chris@117 483 // the file in the list -- it will be tested again
Chris@117 484 // when adding it as a source and that's the proper
Chris@117 485 // time to fail. All we're concluding here is that it
Chris@117 486 // isn't a valid playlist
Chris@117 487 expanded.push_back(path);
Chris@117 488 continue;
Chris@111 489 }
Chris@111 490 source.waitForData();
Chris@111 491 PlaylistFileReader reader(source);
Chris@111 492 if (reader.isOK()) {
Chris@111 493 vector<QString> files = reader.load();
Chris@111 494 for (int i = 0; i < (int)files.size(); ++i) {
Chris@111 495 expanded.push_back(files[i]);
Chris@111 496 }
Chris@111 497 }
Chris@111 498 } else {
Chris@111 499 // not a playlist
Chris@111 500 expanded.push_back(path);
Chris@111 501 }
Chris@111 502 }
Chris@111 503 return expanded;
Chris@111 504 }
Chris@0 505
Chris@182 506 bool
Chris@182 507 readSegmentBoundaries(QString url,
Chris@182 508 Vamp::HostExt::PluginSummarisingAdapter::SegmentBoundaries &boundaries)
Chris@182 509 {
Chris@182 510 FileSource source(url);
Chris@182 511 if (!source.isAvailable()) {
Chris@182 512 cerr << "File or URL \"" << url << "\" could not be retrieved" << endl;
Chris@182 513 return false;
Chris@182 514 }
Chris@182 515 source.waitForData();
Chris@182 516
Chris@182 517 QString filename = source.getLocalFilename();
Chris@182 518 QFile file(filename);
Chris@182 519 if (!file.open(QIODevice::ReadOnly | QIODevice::Text)) {
Chris@182 520 cerr << "File \"" << filename << "\" could not be read" << endl;
Chris@182 521 return false;
Chris@182 522 }
Chris@182 523
Chris@182 524 QTextStream in(&file);
Chris@182 525 int lineNo = 0;
Chris@182 526
Chris@182 527 while (!in.atEnd()) {
Chris@182 528
Chris@182 529 ++lineNo;
Chris@182 530
Chris@182 531 QString line = in.readLine();
Chris@182 532 if (line.startsWith("#")) continue;
Chris@182 533
Chris@182 534 QStringList bits = line.split(",", QString::SkipEmptyParts);
Chris@182 535 QString importantBit;
Chris@182 536 if (!bits.empty()) {
Chris@182 537 bits = bits[0].split(" ", QString::SkipEmptyParts);
Chris@182 538 }
Chris@182 539 if (!bits.empty()) {
Chris@182 540 importantBit = bits[0];
Chris@182 541 }
Chris@182 542 if (importantBit == QString()) {
Chris@182 543 cerr << "WARNING: Skipping line " << lineNo << " (no content found)"
Chris@182 544 << endl;
Chris@182 545 continue;
Chris@182 546 }
Chris@182 547 bool good = false;
Chris@182 548 boundaries.insert(Vamp::RealTime::fromSeconds
Chris@182 549 (importantBit.toDouble(&good)));
Chris@182 550 if (!good) {
Chris@182 551 cerr << "Unparseable or non-numeric segment boundary at line "
Chris@182 552 << lineNo << endl;
Chris@182 553 return false;
Chris@182 554 }
Chris@182 555 }
Chris@182 556
Chris@182 557 return true;
Chris@182 558 }
Chris@182 559
Chris@0 560 int main(int argc, char **argv)
Chris@0 561 {
Chris@0 562 QCoreApplication application(argc, argv);
Chris@0 563
Chris@0 564 QCoreApplication::setOrganizationName("QMUL");
Chris@0 565 QCoreApplication::setOrganizationDomain("qmul.ac.uk");
Chris@0 566 QCoreApplication::setApplicationName("Sonic Annotator");
Chris@0 567
Chris@0 568 QStringList args = application.arguments();
Chris@0 569 set<string> requestedWriterTags;
Chris@0 570 set<string> requestedTransformFiles;
Chris@0 571 set<string> requestedTransformListFiles;
Chris@0 572 set<string> requestedDefaultTransforms;
Chris@0 573 set<string> requestedSummaryTypes;
Chris@21 574 bool force = false;
Chris@106 575 bool multiplex = false;
Chris@0 576 bool recursive = false;
Chris@116 577 bool normalise = false;
Chris@0 578 bool list = false;
Chris@144 579 bool listWriters = false;
Chris@144 580 bool listFormats = false;
Chris@0 581 bool summaryOnly = false;
Chris@0 582 QString skeletonFor = "";
Chris@127 583 QString minVersion = "";
Chris@0 584 QString myname = args[0];
Chris@0 585 myname = QFileInfo(myname).baseName();
Chris@0 586 QStringList otherArgs;
Chris@0 587 Vamp::HostExt::PluginSummarisingAdapter::SegmentBoundaries boundaries;
Chris@0 588
Chris@0 589 QString helpStr = myname + ": use -h or --help option for help";
Chris@0 590
Chris@0 591 for (int i = 1; i < args.size(); ++i) {
Chris@0 592
Chris@0 593 QString arg = args[i];
Chris@0 594 bool last = ((i + 1) == args.size());
Chris@0 595
Chris@0 596 if (arg == "-h" || arg == "--help" || arg == "-?") {
Chris@142 597 QString writer;
Chris@142 598 if (!last) {
Chris@142 599 writer = args[i+1];
Chris@142 600 }
Chris@142 601 printHelp(myname, writer);
Chris@125 602 return 0;
Chris@0 603 }
Chris@0 604
Chris@46 605 if (arg == "-v" || arg == "--version") {
Chris@46 606 std::cout << RUNNER_VERSION << std::endl;
Chris@46 607 return 0;
Chris@46 608 }
Chris@46 609
Chris@0 610 if (arg == "-w" || arg == "--writer") {
Chris@0 611 if (last || args[i+1].startsWith("-")) {
Chris@127 612 cerr << myname << ": argument expected for \""
Chris@127 613 << arg << "\" option" << endl;
Chris@127 614 cerr << helpStr << endl;
Chris@0 615 exit(2);
Chris@0 616 } else {
Chris@0 617 string tag = args[++i].toStdString();
Chris@0 618 if (requestedWriterTags.find(tag) != requestedWriterTags.end()) {
Chris@127 619 cerr << myname << ": NOTE: duplicate specification of writer type \"" << tag << "\" ignored" << endl;
Chris@0 620 } else {
Chris@0 621 requestedWriterTags.insert(tag);
Chris@0 622 }
Chris@0 623 continue;
Chris@0 624 }
Chris@0 625 } else if (arg == "-t" || arg == "--transform") {
Chris@0 626 if (last || args[i+1].startsWith("-")) {
Chris@127 627 cerr << myname << ": argument expected for \""
Chris@127 628 << arg << "\" option" << endl;
Chris@127 629 cerr << helpStr << endl;
Chris@0 630 exit(2);
Chris@0 631 } else {
Chris@0 632 string transform = args[++i].toStdString();
Chris@0 633 if (requestedTransformFiles.find(transform) !=
Chris@0 634 requestedTransformFiles.end()) {
Chris@127 635 cerr << myname << ": NOTE: duplicate specification of transform file \"" << transform << "\" ignored" << endl;
Chris@0 636 } else {
Chris@0 637 requestedTransformFiles.insert(transform);
Chris@0 638 }
Chris@0 639 continue;
Chris@0 640 }
Chris@0 641 } else if (arg == "-T" || arg == "--transforms") {
Chris@0 642 if (last || args[i+1].startsWith("-")) {
Chris@127 643 cerr << myname << ": argument expected for \""
Chris@127 644 << arg << "\" option" << endl;
Chris@127 645 cerr << helpStr << endl;
Chris@0 646 exit(2);
Chris@0 647 } else {
Chris@0 648 string transform = args[++i].toStdString();
Chris@0 649 if (requestedTransformListFiles.find(transform) !=
Chris@0 650 requestedTransformListFiles.end()) {
Chris@127 651 cerr << myname << ": NOTE: duplicate specification of transform list file \"" << transform << "\" ignored" << endl;
Chris@0 652 } else {
Chris@0 653 requestedTransformListFiles.insert(transform);
Chris@0 654 }
Chris@0 655 continue;
Chris@0 656 }
Chris@0 657 } else if (arg == "-d" || arg == "--default") {
Chris@0 658 if (last || args[i+1].startsWith("-")) {
Chris@127 659 cerr << myname << ": argument expected for \""
Chris@127 660 << arg << "\" option" << endl;
Chris@127 661 cerr << helpStr << endl;
Chris@0 662 exit(2);
Chris@0 663 } else {
Chris@0 664 string deft = args[++i].toStdString();
Chris@0 665 if (requestedDefaultTransforms.find(deft) !=
Chris@0 666 requestedDefaultTransforms.end()) {
Chris@127 667 cerr << myname << ": NOTE: duplicate specification of default transform \"" << deft << "\" ignored" << endl;
Chris@0 668 } else {
Chris@0 669 requestedDefaultTransforms.insert(deft);
Chris@0 670 }
Chris@0 671 continue;
Chris@0 672 }
Chris@0 673 } else if (arg == "-S" || arg == "--summary") {
Chris@0 674 if (last || args[i+1].startsWith("-")) {
Chris@127 675 cerr << myname << ": argument expected for \""
Chris@127 676 << arg << "\" option" << endl;
Chris@127 677 cerr << helpStr << endl;
Chris@0 678 exit(2);
Chris@0 679 } else {
Chris@0 680 string summary = args[++i].toStdString();
Chris@0 681 requestedSummaryTypes.insert(summary);
Chris@0 682 continue;
Chris@0 683 }
Chris@0 684 } else if (arg == "--summary-only") {
Chris@0 685 summaryOnly = true;
Chris@0 686 continue;
Chris@0 687 } else if (arg == "--segments") {
Chris@0 688 if (last) {
Chris@127 689 cerr << myname << ": argument expected for \""
Chris@127 690 << arg << "\" option" << endl;
Chris@127 691 cerr << helpStr << endl;
Chris@0 692 exit(2);
Chris@0 693 } else {
Chris@0 694 string segmentSpec = args[++i].toStdString();
Chris@0 695 QStringList segmentStrs = QString(segmentSpec.c_str()).split(',');
Chris@0 696 for (int j = 0; j < segmentStrs.size(); ++j) {
Chris@0 697 bool good = false;
Chris@0 698 boundaries.insert(Vamp::RealTime::fromSeconds
Chris@0 699 (segmentStrs[j].toDouble(&good)));
Chris@0 700 if (!good) {
Chris@127 701 cerr << myname << ": segment boundaries must be numeric" << endl;
Chris@127 702 cerr << helpStr << endl;
Chris@0 703 exit(2);
Chris@0 704 }
Chris@0 705 }
Chris@0 706 }
Chris@182 707 } else if (arg == "--segments-from") {
Chris@182 708 if (last) {
Chris@182 709 cerr << myname << ": argument expected for \""
Chris@182 710 << arg << "\" option" << endl;
Chris@182 711 cerr << helpStr << endl;
Chris@182 712 exit(2);
Chris@182 713 } else {
Chris@182 714 QString segmentFilename = args[++i];
Chris@182 715 if (!readSegmentBoundaries(segmentFilename, boundaries)) {
Chris@182 716 cerr << myname << ": failed to read segment boundaries from file" << endl;
Chris@182 717 cerr << helpStr << endl;
Chris@182 718 exit(2);
Chris@182 719 }
Chris@182 720 }
Chris@0 721 } else if (arg == "-m" || arg == "--multiplex") {
Chris@0 722 multiplex = true;
Chris@0 723 continue;
Chris@0 724 } else if (arg == "-r" || arg == "--recursive") {
Chris@0 725 recursive = true;
Chris@0 726 continue;
Chris@116 727 } else if (arg == "-n" || arg == "--normalise") {
Chris@116 728 normalise = true;
Chris@116 729 continue;
Chris@21 730 } else if (arg == "-f" || arg == "--force") {
Chris@21 731 force = true;
Chris@21 732 continue;
Chris@144 733 } else if (arg == "--list-writers") {
Chris@144 734 listWriters = true;
Chris@144 735 continue;
Chris@144 736 } else if (arg == "--list-formats") {
Chris@144 737 listFormats = true;
Chris@144 738 continue;
Chris@0 739 } else if (arg == "-l" || arg == "--list") {
Chris@0 740 list = true;
Chris@0 741 continue;
Chris@127 742 } else if (arg == "--minversion") {
Chris@127 743 if (last || args[i+1].startsWith("-")) {
Chris@127 744 cerr << myname << ": usage: "
Chris@127 745 << myname << " " << arg << " <version>" << endl;
Chris@127 746 cerr << helpStr << endl;
Chris@127 747 exit(2);
Chris@127 748 }
Chris@127 749 minVersion = args[++i];
Chris@127 750 continue;
Chris@0 751 } else if (arg == "-s" || arg == "--skeleton") {
Chris@0 752 if (last || args[i+1].startsWith("-")) {
Chris@127 753 cerr << myname << ": usage: "
Chris@127 754 << myname << " " << arg
Chris@0 755 << " <transform>" << endl;
Chris@127 756 cerr << helpStr << endl;
Chris@0 757 exit(2);
Chris@0 758 } else {
Chris@0 759 skeletonFor = args[++i];
Chris@0 760 continue;
Chris@0 761 }
Chris@0 762 } else {
Chris@0 763 otherArgs.push_back(args[i]);
Chris@0 764 }
Chris@0 765 }
Chris@0 766
Chris@0 767 if (list) {
Chris@0 768 if (!requestedWriterTags.empty() || skeletonFor != "") {
Chris@127 769 cerr << helpStr << endl;
Chris@0 770 exit(2);
Chris@0 771 }
Chris@0 772 listTransforms();
Chris@0 773 exit(0);
Chris@0 774 }
Chris@144 775 if (listWriters) {
Chris@144 776 if (!requestedWriterTags.empty() || skeletonFor != "") {
Chris@144 777 cerr << helpStr << endl;
Chris@144 778 exit(2);
Chris@144 779 }
Chris@144 780 set<string> writers = FeatureWriterFactory::getWriterTags();
Chris@144 781 bool first = true;
Chris@144 782 for (set<string>::const_iterator i = writers.begin();
Chris@144 783 i != writers.end(); ++i) {
Chris@144 784 if (!first) cout << " ";
Chris@144 785 cout << *i;
Chris@144 786 first = false;
Chris@144 787 }
Chris@144 788 cout << endl;
Chris@144 789 exit(0);
Chris@144 790 }
Chris@144 791 if (listFormats) {
Chris@144 792 if (!requestedWriterTags.empty() || skeletonFor != "") {
Chris@144 793 cerr << helpStr << endl;
Chris@144 794 exit(2);
Chris@144 795 }
Chris@144 796 QString extensions = AudioFileReaderFactory::getKnownExtensions();
Chris@144 797 QStringList extlist = extensions.split(" ", QString::SkipEmptyParts);
Chris@144 798 bool first = true;
Chris@144 799 foreach (QString s, extlist) {
Chris@144 800 if (!first) cout << " ";
Chris@144 801 s.replace("*.", "");
Chris@144 802 cout << s;
Chris@144 803 first = false;
Chris@144 804 }
Chris@144 805 cout << endl;
Chris@144 806 exit(0);
Chris@144 807 }
Chris@144 808 if (list) {
Chris@144 809 if (!requestedWriterTags.empty() || skeletonFor != "") {
Chris@144 810 cerr << helpStr << endl;
Chris@144 811 exit(2);
Chris@144 812 }
Chris@144 813 listTransforms();
Chris@144 814 exit(0);
Chris@144 815 }
Chris@0 816 if (skeletonFor != "") {
Chris@0 817 if (!requestedWriterTags.empty()) {
Chris@127 818 cerr << helpStr << endl;
Chris@0 819 exit(2);
Chris@0 820 }
Chris@0 821 printSkeleton(skeletonFor);
Chris@0 822 exit(0);
Chris@0 823 }
Chris@127 824 if (minVersion != "") {
Chris@127 825 if (!requestedWriterTags.empty()) {
Chris@127 826 cerr << helpStr << endl;
Chris@127 827 exit(2);
Chris@127 828 }
Chris@127 829 exit(checkMinVersion(myname, minVersion));
Chris@127 830 }
Chris@0 831
Chris@0 832 if (requestedTransformFiles.empty() &&
Chris@0 833 requestedTransformListFiles.empty() &&
Chris@0 834 requestedDefaultTransforms.empty()) {
Chris@127 835 cerr << myname
Chris@0 836 << ": no transform(s) specified" << endl;
Chris@127 837 cerr << helpStr << endl;
Chris@0 838 exit(2);
Chris@0 839 }
Chris@0 840
Chris@0 841 if (requestedWriterTags.empty()) {
Chris@127 842 cerr << myname
Chris@0 843 << ": no writer(s) specified" << endl;
Chris@127 844 cerr << helpStr << endl;
Chris@0 845 exit(2);
Chris@0 846 }
Chris@0 847
Chris@0 848 if (!boundaries.empty()) {
Chris@0 849 if (requestedSummaryTypes.empty()) {
Chris@127 850 cerr << myname
Chris@0 851 << ": summary segment boundaries provided, but no summary type specified"
Chris@0 852 << endl;
Chris@127 853 cerr << helpStr << endl;
Chris@0 854 exit(2);
Chris@0 855 }
Chris@0 856 }
Chris@0 857
Chris@20 858 QSettings settings;
Chris@20 859
Chris@0 860 #ifdef HAVE_FFTW3
Chris@0 861 settings.beginGroup("FFTWisdom");
Chris@0 862 QString wisdom = settings.value("wisdom").toString();
Chris@0 863 if (wisdom != "") {
Chris@0 864 fftw_import_wisdom_from_string(wisdom.toLocal8Bit().data());
Chris@0 865 }
Chris@0 866 settings.endGroup();
Chris@0 867 #endif
Chris@0 868
Chris@20 869 settings.beginGroup("RDF");
Chris@20 870 if (!settings.contains("rdf-indices")) {
Chris@20 871 QStringList list;
Chris@20 872 list << "http://www.vamp-plugins.org/rdf/plugins/index.txt";
Chris@20 873 settings.setValue("rdf-indices", list);
Chris@20 874 }
Chris@20 875 settings.endGroup();
Chris@20 876
Chris@0 877 FeatureExtractionManager manager;
Chris@0 878
Chris@116 879 manager.setNormalise(normalise);
Chris@116 880
Chris@0 881 if (!requestedSummaryTypes.empty()) {
Chris@0 882 if (!manager.setSummaryTypes(requestedSummaryTypes,
Chris@0 883 boundaries)) {
Chris@233 884 cerr << myname << ": failed to set requested summary types" << endl;
Chris@0 885 exit(1);
Chris@0 886 }
Chris@0 887 }
Chris@102 888
Chris@102 889 manager.setSummariesOnly(summaryOnly);
Chris@116 890
Chris@0 891 vector<FeatureWriter *> writers;
Chris@0 892
Chris@0 893 for (set<string>::const_iterator i = requestedWriterTags.begin();
Chris@0 894 i != requestedWriterTags.end(); ++i) {
Chris@0 895
Chris@0 896 FeatureWriter *writer = FeatureWriterFactory::createWriter(*i);
Chris@0 897
Chris@0 898 if (!writer) {
Chris@127 899 cerr << myname << ": unknown feature writer \""
Chris@0 900 << *i << "\"" << endl;
Chris@127 901 cerr << helpStr << endl;
Chris@0 902 exit(2);
Chris@0 903 }
Chris@0 904
Chris@0 905 map<string, string> writerArgs;
Chris@0 906 FeatureWriter::ParameterList pl(writer->getSupportedParameters());
Chris@0 907
Chris@95 908 for (int k = 0; k < (int)pl.size(); ++k) {
Chris@0 909
Chris@0 910 string argbase = pl[k].name;
Chris@0 911 QString literal = QString("--%1-%2")
Chris@0 912 .arg(i->c_str()).arg(argbase.c_str());
Chris@0 913
Chris@95 914 for (int j = 0; j < (int)otherArgs.size(); ) {
Chris@0 915
Chris@0 916 if (otherArgs[j] != literal) {
Chris@0 917 ++j;
Chris@0 918 continue;
Chris@0 919 }
Chris@0 920
Chris@0 921 otherArgs.removeAt(j);
Chris@0 922
Chris@0 923 if (pl[k].hasArg) {
Chris@0 924 if (j < otherArgs.size()) {
Chris@0 925 writerArgs[argbase] = otherArgs[j].toStdString();
Chris@0 926 otherArgs.removeAt(j);
Chris@0 927 } else {
Chris@127 928 cerr << myname << ": "
Chris@0 929 << "argument required for \""
Chris@127 930 << literal << "\" option"
Chris@0 931 << endl;
Chris@127 932 cerr << helpStr << endl;
Chris@0 933 exit(2);
Chris@0 934 }
Chris@0 935 } else {
Chris@0 936 writerArgs[argbase] = "";
Chris@0 937 }
Chris@0 938 }
Chris@0 939 }
Chris@197 940
Chris@197 941 for (auto &p: pl) {
Chris@197 942 if (p.mandatory) {
Chris@197 943 bool found = false;
Chris@197 944 for (auto &w: writerArgs) {
Chris@197 945 if (w.first == p.name) {
Chris@197 946 found = true;
Chris@197 947 break;
Chris@197 948 }
Chris@197 949 }
Chris@197 950 if (!found) {
Chris@197 951 QString literal = QString("--%1-%2")
Chris@197 952 .arg(i->c_str()).arg(p.name.c_str());
Chris@197 953 cerr << myname << ": "
Chris@197 954 << "the \"" << literal << "\" parameter is mandatory"
Chris@197 955 << endl;
Chris@197 956 cerr << helpStr << endl;
Chris@197 957 exit(2);
Chris@197 958 }
Chris@197 959 }
Chris@197 960 }
Chris@197 961
Chris@197 962 try {
Chris@197 963 writer->setParameters(writerArgs);
Chris@197 964 } catch (std::exception &ex) {
Chris@197 965 cerr << myname << ": " << ex.what() << endl;
Chris@197 966 cerr << helpStr << endl;
Chris@197 967 exit(2);
Chris@197 968 }
Chris@0 969
Chris@0 970 writers.push_back(writer);
Chris@0 971 }
Chris@0 972
Chris@0 973 for (int i = 0; i < otherArgs.size(); ++i) {
Chris@0 974 if (otherArgs[i].startsWith("-")) {
Chris@127 975 cerr << myname << ": unknown option \""
Chris@127 976 << otherArgs[i] << "\"" << endl;
Chris@127 977 cerr << helpStr << endl;
Chris@0 978 exit(2);
Chris@0 979 }
Chris@0 980 }
Chris@0 981
Chris@0 982 if (otherArgs.empty()) {
Chris@127 983 cerr << myname << ": no input(s) specified" << endl;
Chris@127 984 cerr << helpStr << endl;
Chris@0 985 exit(2);
Chris@0 986 }
Chris@0 987
Chris@0 988 for (set<string>::const_iterator i = requestedTransformListFiles.begin();
Chris@0 989 i != requestedTransformListFiles.end(); ++i) {
Chris@0 990 PlaylistFileReader reader(i->c_str());
Chris@0 991 if (reader.isOK()) {
Chris@0 992 vector<QString> files = reader.load();
Chris@95 993 for (int j = 0; j < (int)files.size(); ++j) {
Chris@0 994 requestedTransformFiles.insert(files[j].toStdString());
Chris@0 995 }
Chris@0 996 } else {
Chris@127 997 cerr << myname << ": failed to read template list file \"" << *i << "\"" << endl;
Chris@0 998 exit(2);
Chris@0 999 }
Chris@0 1000 }
Chris@0 1001
Chris@0 1002 QStringList sources;
Chris@0 1003 if (!recursive) {
Chris@0 1004 sources = otherArgs;
Chris@0 1005 } else {
Chris@0 1006 for (QStringList::const_iterator i = otherArgs.begin();
Chris@0 1007 i != otherArgs.end(); ++i) {
Chris@0 1008 if (QDir(*i).exists()) {
Chris@0 1009 cerr << "Directory found and recursive flag set, scanning for audio files..." << endl;
Chris@0 1010 int found = 0;
Chris@0 1011 findSourcesRecursive(*i, sources, found);
Chris@0 1012 cerr << "\rDone, found " << found << " supported audio file(s) " << endl;
Chris@0 1013 } else {
Chris@0 1014 sources.push_back(*i);
Chris@0 1015 }
Chris@0 1016 }
Chris@0 1017 }
Chris@0 1018
Chris@111 1019 sources = expandPlaylists(sources);
Chris@117 1020
Chris@28 1021 bool good = true;
Chris@45 1022 QSet<QString> badSources;
Chris@28 1023
Chris@0 1024 for (QStringList::const_iterator i = sources.begin();
Chris@0 1025 i != sources.end(); ++i) {
Chris@0 1026 try {
Chris@106 1027 manager.addSource(*i, multiplex);
Chris@22 1028 } catch (const std::exception &e) {
Chris@45 1029 badSources.insert(*i);
Chris@21 1030 cerr << "ERROR: Failed to process file \"" << i->toStdString()
Chris@21 1031 << "\": " << e.what() << endl;
Chris@22 1032 if (force) {
Chris@22 1033 // print a note only if we have more files to process
Chris@22 1034 QStringList::const_iterator j = i;
Chris@22 1035 if (++j != sources.end()) {
Chris@22 1036 cerr << "NOTE: \"--force\" option was provided, continuing (more errors may occur)" << endl;
Chris@22 1037 }
Chris@22 1038 } else {
Chris@45 1039 cerr << "NOTE: If you want to continue with processing any further files after an" << endl
Chris@45 1040 << "error like this, use the --force option" << endl;
Chris@28 1041 good = false;
Chris@22 1042 break;
Chris@22 1043 }
Chris@0 1044 }
Chris@0 1045 }
Chris@45 1046
Chris@45 1047 if (good) {
Chris@45 1048
Chris@45 1049 bool haveFeatureExtractor = false;
Chris@45 1050
Chris@45 1051 for (set<string>::const_iterator i = requestedTransformFiles.begin();
Chris@45 1052 i != requestedTransformFiles.end(); ++i) {
Chris@45 1053 if (manager.addFeatureExtractorFromFile(i->c_str(), writers)) {
Chris@45 1054 haveFeatureExtractor = true;
Chris@120 1055 } else {
Chris@120 1056 cerr << "ERROR: Failed to add feature extractor from transform file \"" << *i << "\"" << endl;
Chris@120 1057 good = false;
Chris@45 1058 }
Chris@45 1059 }
Chris@45 1060
Chris@45 1061 for (set<string>::const_iterator i = requestedDefaultTransforms.begin();
Chris@45 1062 i != requestedDefaultTransforms.end(); ++i) {
Chris@45 1063 if (manager.addDefaultFeatureExtractor(i->c_str(), writers)) {
Chris@45 1064 haveFeatureExtractor = true;
Chris@120 1065 } else {
Chris@120 1066 cerr << "ERROR: Failed to add default feature extractor for transform \"" << *i << "\"" << endl;
Chris@120 1067 good = false;
Chris@45 1068 }
Chris@45 1069 }
Chris@45 1070
Chris@45 1071 if (!haveFeatureExtractor) {
Chris@127 1072 cerr << myname << ": no feature extractors added" << endl;
Chris@45 1073 good = false;
Chris@45 1074 }
Chris@45 1075 }
Chris@45 1076
Chris@45 1077 if (good) {
Chris@106 1078 QStringList goodSources;
Chris@106 1079 foreach (QString source, sources) {
Chris@106 1080 if (!badSources.contains(source)) {
Chris@106 1081 goodSources.push_back(source);
Chris@106 1082 }
Chris@106 1083 }
Chris@106 1084 if (multiplex) {
Chris@45 1085 try {
Chris@169 1086 for (int i = 0; i < (int)writers.size(); ++i) {
Chris@169 1087 writers[i]->setNofM(1, 1);
Chris@169 1088 }
Chris@106 1089 manager.extractFeaturesMultiplexed(goodSources);
Chris@45 1090 } catch (const std::exception &e) {
Chris@106 1091 cerr << "ERROR: Feature extraction failed: "
Chris@106 1092 << e.what() << endl;
Chris@106 1093 }
Chris@106 1094 } else {
Chris@169 1095 int n = 0;
Chris@106 1096 for (QStringList::const_iterator i = goodSources.begin();
Chris@106 1097 i != goodSources.end(); ++i) {
Chris@106 1098 std::cerr << "Extracting features for: \"" << i->toStdString()
Chris@106 1099 << "\"" << std::endl;
Chris@169 1100 ++n;
Chris@106 1101 try {
Chris@169 1102 for (int j = 0; j < (int)writers.size(); ++j) {
Chris@169 1103 writers[j]->setNofM(n, goodSources.size());
Chris@169 1104 }
Chris@112 1105 manager.extractFeatures(*i);
Chris@106 1106 } catch (const std::exception &e) {
Chris@106 1107 cerr << "ERROR: Feature extraction failed for \""
Chris@106 1108 << i->toStdString() << "\": " << e.what() << endl;
Chris@106 1109 if (force) {
Chris@106 1110 // print a note only if we have more files to process
Chris@106 1111 QStringList::const_iterator j = i;
Chris@106 1112 if (++j != sources.end()) {
Chris@106 1113 cerr << "NOTE: \"--force\" option was provided, continuing (more errors may occur)" << endl;
Chris@106 1114 }
Chris@106 1115 } else {
Chris@106 1116 cerr << "NOTE: If you want to continue with processing any further files after an" << endl
Chris@106 1117 << "error like this, use the --force option" << endl;
Chris@106 1118 good = false;
Chris@106 1119 break;
Chris@45 1120 }
Chris@45 1121 }
Chris@45 1122 }
Chris@45 1123 }
Chris@45 1124 }
Chris@0 1125
Chris@95 1126 for (int i = 0; i < (int)writers.size(); ++i) delete writers[i];
Chris@0 1127
Chris@0 1128 #ifdef HAVE_FFTW3
Chris@0 1129 settings.beginGroup("FFTWisdom");
Chris@0 1130 char *cwisdom = fftw_export_wisdom_to_string();
Chris@0 1131 if (cwisdom) {
Chris@0 1132 settings.setValue("wisdom", cwisdom);
Chris@0 1133 fftw_free(cwisdom);
Chris@0 1134 }
Chris@0 1135 settings.endGroup();
Chris@0 1136 #endif
Chris@0 1137
Chris@0 1138 TempDirectory::getInstance()->cleanup();
Chris@0 1139
Chris@28 1140 if (good) return 0;
Chris@28 1141 else return 1;
Chris@0 1142 }
Chris@0 1143
Chris@0 1144