annotate runner/main.cpp @ 208:c17b184c16db

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