annotate runner/main.cpp @ 271:7b3a80021b7c piper-nopiper

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