annotate runner/main.cpp @ 71:9600f5b8076f tip

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