annotate host/vamp-simple-host.cpp @ 525:8c18bdaad04f c++11-mutex

Avoid simple static allocation of mutex, as it could lead to mutex being destroyed before last adapter that needs to use it (since adapters are usually also static)
author Chris Cannam
date Mon, 09 Sep 2019 10:24:13 +0100
parents 22b29720da41
children
rev   line source
cannam@1 1 /* -*- c-basic-offset: 4 indent-tabs-mode: nil -*- vi:set ts=8 sts=4 sw=4: */
cannam@1 2
cannam@1 3 /*
cannam@1 4 Vamp
cannam@1 5
cannam@1 6 An API for audio analysis and feature extraction plugins.
cannam@1 7
cannam@1 8 Centre for Digital Music, Queen Mary, University of London.
cannam@259 9 Copyright 2006 Chris Cannam, copyright 2007-2008 QMUL.
cannam@1 10
cannam@1 11 Permission is hereby granted, free of charge, to any person
cannam@1 12 obtaining a copy of this software and associated documentation
cannam@1 13 files (the "Software"), to deal in the Software without
cannam@1 14 restriction, including without limitation the rights to use, copy,
cannam@1 15 modify, merge, publish, distribute, sublicense, and/or sell copies
cannam@1 16 of the Software, and to permit persons to whom the Software is
cannam@1 17 furnished to do so, subject to the following conditions:
cannam@1 18
cannam@1 19 The above copyright notice and this permission notice shall be
cannam@1 20 included in all copies or substantial portions of the Software.
cannam@1 21
cannam@1 22 THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
cannam@1 23 EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
cannam@1 24 MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
cannam@6 25 NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR
cannam@1 26 ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF
cannam@1 27 CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
cannam@1 28 WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
cannam@1 29
cannam@1 30 Except as contained in this notice, the names of the Centre for
cannam@1 31 Digital Music; Queen Mary, University of London; and Chris Cannam
cannam@1 32 shall not be used in advertising or otherwise to promote the sale,
cannam@1 33 use or other dealings in this Software without prior written
cannam@1 34 authorization.
cannam@1 35 */
cannam@1 36
cannam@240 37
cannam@240 38 /*
cannam@240 39 * This "simple" Vamp plugin host is no longer as simple as it was; it
cannam@240 40 * now has a lot of options and includes a lot of code to handle the
cannam@241 41 * various useful listing modes it supports.
cannam@240 42 *
cannam@240 43 * However, the runPlugin function still contains a reasonable
cannam@240 44 * implementation of a fairly generic Vamp plugin host capable of
cannam@240 45 * evaluating a given output on a given plugin for a sound file read
cannam@240 46 * via libsndfile.
cannam@240 47 */
cannam@240 48
cannam@230 49 #include <vamp-hostsdk/PluginHostAdapter.h>
cannam@233 50 #include <vamp-hostsdk/PluginInputDomainAdapter.h>
cannam@233 51 #include <vamp-hostsdk/PluginLoader.h>
cannam@1 52
cannam@16 53 #include <iostream>
cannam@88 54 #include <fstream>
cannam@99 55 #include <set>
cannam@16 56 #include <sndfile.h>
cannam@1 57
cannam@130 58 #include <cstring>
cannam@130 59 #include <cstdlib>
cannam@130 60
cannam@1 61 #include "system.h"
cannam@1 62
cannam@19 63 #include <cmath>
cannam@19 64
cannam@99 65 using namespace std;
cannam@16 66
cannam@99 67 using Vamp::Plugin;
cannam@99 68 using Vamp::PluginHostAdapter;
cannam@99 69 using Vamp::RealTime;
cannam@64 70 using Vamp::HostExt::PluginLoader;
cannam@190 71 using Vamp::HostExt::PluginWrapper;
cannam@190 72 using Vamp::HostExt::PluginInputDomainAdapter;
cannam@64 73
Chris@328 74 #define HOST_VERSION "1.5"
cannam@40 75
cannam@95 76 enum Verbosity {
cannam@95 77 PluginIds,
cannam@95 78 PluginOutputIds,
cannam@240 79 PluginInformation,
cannam@240 80 PluginInformationDetailed
cannam@95 81 };
cannam@95 82
Chris@442 83 void printFeatures(int, int,
Chris@442 84 const Plugin::OutputDescriptor &, int,
Chris@442 85 const Plugin::FeatureSet &, ofstream *, bool frames);
cannam@16 86 void transformInput(float *, size_t);
cannam@16 87 void fft(unsigned int, bool, double *, double *, double *, double *);
cannam@64 88 void printPluginPath(bool verbose);
cannam@99 89 void printPluginCategoryList();
cannam@95 90 void enumeratePlugins(Verbosity);
cannam@64 91 void listPluginsInLibrary(string soname);
cannam@64 92 int runPlugin(string myname, string soname, string id, string output,
cannam@109 93 int outputNo, string inputFile, string outfilename, bool frames);
cannam@40 94
cannam@64 95 void usage(const char *name)
cannam@64 96 {
cannam@64 97 cerr << "\n"
cannam@240 98 << name << ": A command-line host for Vamp audio analysis plugins.\n\n"
cannam@64 99 "Centre for Digital Music, Queen Mary, University of London.\n"
cannam@290 100 "Copyright 2006-2009 Chris Cannam and QMUL.\n"
cannam@64 101 "Freely redistributable; published under a BSD-style license.\n\n"
cannam@64 102 "Usage:\n\n"
cannam@109 103 " " << name << " [-s] pluginlibrary[." << PLUGIN_SUFFIX << "]:plugin[:output] file.wav [-o out.txt]\n"
cannam@109 104 " " << name << " [-s] pluginlibrary[." << PLUGIN_SUFFIX << "]:plugin file.wav [outputno] [-o out.txt]\n\n"
cannam@64 105 " -- Load plugin id \"plugin\" from \"pluginlibrary\" and run it on the\n"
cannam@73 106 " audio data in \"file.wav\", retrieving the named \"output\", or output\n"
cannam@73 107 " number \"outputno\" (the first output by default) and dumping it to\n"
cannam@95 108 " standard output, or to \"out.txt\" if the -o option is given.\n\n"
cannam@73 109 " \"pluginlibrary\" should be a library name, not a file path; the\n"
cannam@73 110 " standard Vamp library search path will be used to locate it. If\n"
cannam@73 111 " a file path is supplied, the directory part(s) will be ignored.\n\n"
cannam@109 112 " If the -s option is given, results will be labelled with the audio\n"
cannam@109 113 " sample frame at which they occur. Otherwise, they will be labelled\n"
cannam@109 114 " with time in seconds.\n\n"
cannam@240 115 " " << name << " -l\n"
cannam@240 116 " " << name << " --list\n\n"
cannam@95 117 " -- List the plugin libraries and Vamp plugins in the library search path\n"
cannam@95 118 " in a verbose human-readable format.\n\n"
Chris@361 119 " " << name << " -L\n"
cannam@240 120 " " << name << " --list-full\n\n"
cannam@240 121 " -- List all data reported by all the Vamp plugins in the library search\n"
cannam@240 122 " path in a very verbose human-readable format.\n\n"
cannam@95 123 " " << name << " --list-ids\n\n"
cannam@95 124 " -- List the plugins in the search path in a terse machine-readable format,\n"
cannam@95 125 " in the form vamp:soname:identifier.\n\n"
cannam@95 126 " " << name << " --list-outputs\n\n"
cannam@95 127 " -- List the outputs for plugins in the search path in a machine-readable\n"
cannam@95 128 " format, in the form vamp:soname:identifier:output.\n\n"
cannam@99 129 " " << name << " --list-by-category\n\n"
cannam@99 130 " -- List the plugins as a plugin index by category, in a machine-readable\n"
cannam@99 131 " format. The format may change in future releases.\n\n"
cannam@64 132 " " << name << " -p\n\n"
cannam@73 133 " -- Print out the Vamp library search path.\n\n"
cannam@64 134 " " << name << " -v\n\n"
cannam@95 135 " -- Display version information only.\n"
cannam@64 136 << endl;
cannam@64 137 exit(2);
cannam@64 138 }
cannam@1 139
cannam@1 140 int main(int argc, char **argv)
cannam@1 141 {
cannam@64 142 char *scooter = argv[0];
cannam@64 143 char *name = 0;
cannam@64 144 while (scooter && *scooter) {
cannam@64 145 if (*scooter == '/' || *scooter == '\\') name = ++scooter;
cannam@64 146 else ++scooter;
cannam@1 147 }
cannam@64 148 if (!name || !*name) name = argv[0];
cannam@43 149
cannam@88 150 if (argc < 2) usage(name);
cannam@64 151
cannam@88 152 if (argc == 2) {
cannam@88 153
cannam@88 154 if (!strcmp(argv[1], "-v")) {
cannam@88 155
cannam@88 156 cout << "Simple Vamp plugin host version: " << HOST_VERSION << endl
cannam@88 157 << "Vamp API version: " << VAMP_API_VERSION << endl
cannam@88 158 << "Vamp SDK version: " << VAMP_SDK_VERSION << endl;
cannam@88 159 return 0;
cannam@88 160
cannam@240 161 } else if (!strcmp(argv[1], "-l") || !strcmp(argv[1], "--list")) {
cannam@88 162
cannam@88 163 printPluginPath(true);
cannam@95 164 enumeratePlugins(PluginInformation);
cannam@88 165 return 0;
cannam@88 166
Chris@361 167 } else if (!strcmp(argv[1], "-L") || !strcmp(argv[1], "--list-full")) {
cannam@240 168
cannam@240 169 enumeratePlugins(PluginInformationDetailed);
cannam@240 170 return 0;
cannam@240 171
cannam@88 172 } else if (!strcmp(argv[1], "-p")) {
cannam@88 173
cannam@88 174 printPluginPath(false);
cannam@88 175 return 0;
cannam@88 176
cannam@95 177 } else if (!strcmp(argv[1], "--list-ids")) {
cannam@95 178
cannam@95 179 enumeratePlugins(PluginIds);
cannam@95 180 return 0;
cannam@95 181
cannam@95 182 } else if (!strcmp(argv[1], "--list-outputs")) {
cannam@95 183
cannam@95 184 enumeratePlugins(PluginOutputIds);
cannam@95 185 return 0;
cannam@95 186
cannam@99 187 } else if (!strcmp(argv[1], "--list-by-category")) {
cannam@99 188
cannam@99 189 printPluginCategoryList();
cannam@99 190 return 0;
cannam@99 191
cannam@88 192 } else usage(name);
cannam@64 193 }
cannam@64 194
cannam@88 195 if (argc < 3) usage(name);
cannam@88 196
cannam@109 197 bool useFrames = false;
cannam@109 198
cannam@109 199 int base = 1;
cannam@109 200 if (!strcmp(argv[1], "-s")) {
cannam@109 201 useFrames = true;
cannam@109 202 base = 2;
cannam@109 203 }
cannam@109 204
cannam@109 205 string soname = argv[base];
cannam@109 206 string wavname = argv[base+1];
cannam@88 207 string plugid = "";
cannam@88 208 string output = "";
cannam@88 209 int outputNo = -1;
cannam@88 210 string outfilename;
cannam@88 211
cannam@109 212 if (argc >= base+3) {
cannam@88 213
cannam@109 214 int idx = base+2;
cannam@88 215
cannam@88 216 if (isdigit(*argv[idx])) {
cannam@88 217 outputNo = atoi(argv[idx++]);
cannam@88 218 }
cannam@88 219
cannam@88 220 if (argc == idx + 2) {
cannam@88 221 if (!strcmp(argv[idx], "-o")) {
cannam@88 222 outfilename = argv[idx+1];
cannam@88 223 } else usage(name);
cannam@88 224 } else if (argc != idx) {
cannam@88 225 (usage(name));
cannam@88 226 }
cannam@40 227 }
cannam@40 228
cannam@64 229 cerr << endl << name << ": Running..." << endl;
cannam@1 230
cannam@88 231 cerr << "Reading file: \"" << wavname << "\", writing to ";
cannam@88 232 if (outfilename == "") {
cannam@88 233 cerr << "standard output" << endl;
cannam@88 234 } else {
cannam@88 235 cerr << "\"" << outfilename << "\"" << endl;
cannam@88 236 }
cannam@16 237
cannam@64 238 string::size_type sep = soname.find(':');
cannam@64 239
cannam@64 240 if (sep != string::npos) {
cannam@49 241 plugid = soname.substr(sep + 1);
cannam@20 242 soname = soname.substr(0, sep);
cannam@1 243
cannam@64 244 sep = plugid.find(':');
cannam@64 245 if (sep != string::npos) {
cannam@64 246 output = plugid.substr(sep + 1);
cannam@64 247 plugid = plugid.substr(0, sep);
cannam@16 248 }
cannam@16 249 }
cannam@16 250
cannam@64 251 if (plugid == "") {
cannam@64 252 usage(name);
cannam@16 253 }
cannam@64 254
cannam@64 255 if (output != "" && outputNo != -1) {
cannam@64 256 usage(name);
cannam@64 257 }
cannam@64 258
cannam@84 259 if (output == "" && outputNo == -1) {
cannam@84 260 outputNo = 0;
cannam@84 261 }
cannam@84 262
cannam@88 263 return runPlugin(name, soname, plugid, output, outputNo,
cannam@109 264 wavname, outfilename, useFrames);
cannam@64 265 }
cannam@64 266
cannam@64 267
cannam@64 268 int runPlugin(string myname, string soname, string id,
cannam@88 269 string output, int outputNo, string wavname,
cannam@109 270 string outfilename, bool useFrames)
cannam@64 271 {
cannam@64 272 PluginLoader *loader = PluginLoader::getInstance();
cannam@64 273
cannam@64 274 PluginLoader::PluginKey key = loader->composePluginKey(soname, id);
cannam@16 275
cannam@16 276 SNDFILE *sndfile;
cannam@16 277 SF_INFO sfinfo;
cannam@16 278 memset(&sfinfo, 0, sizeof(SF_INFO));
cannam@16 279
cannam@16 280 sndfile = sf_open(wavname.c_str(), SFM_READ, &sfinfo);
cannam@16 281 if (!sndfile) {
Chris@318 282 cerr << myname << ": ERROR: Failed to open input file \""
cannam@64 283 << wavname << "\": " << sf_strerror(sndfile) << endl;
Chris@318 284 return 1;
cannam@16 285 }
cannam@16 286
cannam@88 287 ofstream *out = 0;
cannam@88 288 if (outfilename != "") {
cannam@88 289 out = new ofstream(outfilename.c_str(), ios::out);
cannam@88 290 if (!*out) {
cannam@88 291 cerr << myname << ": ERROR: Failed to open output file \""
cannam@88 292 << outfilename << "\" for writing" << endl;
cannam@88 293 delete out;
cannam@88 294 return 1;
cannam@88 295 }
cannam@88 296 }
cannam@88 297
cannam@99 298 Plugin *plugin = loader->loadPlugin
cannam@92 299 (key, sfinfo.samplerate, PluginLoader::ADAPT_ALL_SAFE);
cannam@64 300 if (!plugin) {
cannam@64 301 cerr << myname << ": ERROR: Failed to load plugin \"" << id
cannam@64 302 << "\" from library \"" << soname << "\"" << endl;
cannam@64 303 sf_close(sndfile);
cannam@88 304 if (out) {
cannam@88 305 out->close();
cannam@88 306 delete out;
cannam@88 307 }
cannam@64 308 return 1;
cannam@64 309 }
cannam@16 310
cannam@64 311 cerr << "Running plugin: \"" << plugin->getIdentifier() << "\"..." << endl;
cannam@16 312
cannam@240 313 // Note that the following would be much simpler if we used a
cannam@240 314 // PluginBufferingAdapter as well -- i.e. if we had passed
cannam@240 315 // PluginLoader::ADAPT_ALL to loader->loadPlugin() above, instead
cannam@240 316 // of ADAPT_ALL_SAFE. Then we could simply specify our own block
cannam@240 317 // size, keep the step size equal to the block size, and ignore
cannam@240 318 // the plugin's bleatings. However, there are some issues with
cannam@240 319 // using a PluginBufferingAdapter that make the results sometimes
cannam@240 320 // technically different from (if effectively the same as) the
cannam@240 321 // un-adapted plugin, so we aren't doing that here. See the
cannam@240 322 // PluginBufferingAdapter documentation for details.
cannam@240 323
cannam@16 324 int blockSize = plugin->getPreferredBlockSize();
cannam@16 325 int stepSize = plugin->getPreferredStepSize();
cannam@16 326
cannam@91 327 if (blockSize == 0) {
cannam@91 328 blockSize = 1024;
cannam@91 329 }
cannam@83 330 if (stepSize == 0) {
cannam@99 331 if (plugin->getInputDomain() == Plugin::FrequencyDomain) {
cannam@83 332 stepSize = blockSize/2;
cannam@83 333 } else {
cannam@83 334 stepSize = blockSize;
cannam@83 335 }
cannam@91 336 } else if (stepSize > blockSize) {
cannam@91 337 cerr << "WARNING: stepSize " << stepSize << " > blockSize " << blockSize << ", resetting blockSize to ";
cannam@99 338 if (plugin->getInputDomain() == Plugin::FrequencyDomain) {
cannam@91 339 blockSize = stepSize * 2;
cannam@91 340 } else {
cannam@91 341 blockSize = stepSize;
cannam@91 342 }
cannam@91 343 cerr << blockSize << endl;
cannam@83 344 }
Chris@318 345 int overlapSize = blockSize - stepSize;
Chris@318 346 sf_count_t currentStep = 0;
Chris@318 347 int finalStepsRemaining = max(1, (blockSize / stepSize) - 1); // at end of file, this many part-silent frames needed after we hit EOF
cannam@83 348
cannam@16 349 int channels = sfinfo.channels;
cannam@16 350
cannam@16 351 float *filebuf = new float[blockSize * channels];
cannam@16 352 float **plugbuf = new float*[channels];
cannam@47 353 for (int c = 0; c < channels; ++c) plugbuf[c] = new float[blockSize + 2];
cannam@16 354
cannam@16 355 cerr << "Using block size = " << blockSize << ", step size = "
cannam@16 356 << stepSize << endl;
cannam@16 357
cannam@240 358 // The channel queries here are for informational purposes only --
cannam@240 359 // a PluginChannelAdapter is being used automatically behind the
cannam@240 360 // scenes, and it will take case of any channel mismatch
cannam@240 361
cannam@16 362 int minch = plugin->getMinChannelCount();
cannam@16 363 int maxch = plugin->getMaxChannelCount();
cannam@16 364 cerr << "Plugin accepts " << minch << " -> " << maxch << " channel(s)" << endl;
cannam@64 365 cerr << "Sound file has " << channels << " (will mix/augment if necessary)" << endl;
cannam@16 366
cannam@99 367 Plugin::OutputList outputs = plugin->getOutputDescriptors();
cannam@99 368 Plugin::OutputDescriptor od;
Chris@442 369 Plugin::FeatureSet features;
cannam@16 370
cannam@29 371 int returnValue = 1;
cannam@88 372 int progress = 0;
cannam@29 373
cannam@190 374 RealTime rt;
cannam@190 375 PluginWrapper *wrapper = 0;
cannam@190 376 RealTime adjustment = RealTime::zeroTime;
cannam@190 377
cannam@16 378 if (outputs.empty()) {
Chris@318 379 cerr << "ERROR: Plugin has no outputs!" << endl;
cannam@16 380 goto done;
cannam@16 381 }
cannam@16 382
cannam@64 383 if (outputNo < 0) {
cannam@16 384
cannam@64 385 for (size_t oi = 0; oi < outputs.size(); ++oi) {
cannam@64 386 if (outputs[oi].identifier == output) {
cannam@64 387 outputNo = oi;
cannam@64 388 break;
cannam@64 389 }
cannam@64 390 }
cannam@64 391
cannam@64 392 if (outputNo < 0) {
cannam@64 393 cerr << "ERROR: Non-existent output \"" << output << "\" requested" << endl;
cannam@64 394 goto done;
cannam@64 395 }
cannam@64 396
cannam@64 397 } else {
cannam@64 398
cannam@64 399 if (int(outputs.size()) <= outputNo) {
cannam@64 400 cerr << "ERROR: Output " << outputNo << " requested, but plugin has only " << outputs.size() << " output(s)" << endl;
cannam@64 401 goto done;
cannam@64 402 }
cannam@64 403 }
cannam@64 404
cannam@64 405 od = outputs[outputNo];
cannam@64 406 cerr << "Output is: \"" << od.identifier << "\"" << endl;
cannam@16 407
cannam@29 408 if (!plugin->initialise(channels, stepSize, blockSize)) {
cannam@29 409 cerr << "ERROR: Plugin initialise (channels = " << channels
cannam@29 410 << ", stepSize = " << stepSize << ", blockSize = "
cannam@29 411 << blockSize << ") failed." << endl;
cannam@29 412 goto done;
cannam@29 413 }
cannam@16 414
cannam@190 415 wrapper = dynamic_cast<PluginWrapper *>(plugin);
cannam@190 416 if (wrapper) {
cannam@240 417 // See documentation for
cannam@240 418 // PluginInputDomainAdapter::getTimestampAdjustment
cannam@190 419 PluginInputDomainAdapter *ida =
cannam@190 420 wrapper->getWrapper<PluginInputDomainAdapter>();
cannam@190 421 if (ida) adjustment = ida->getTimestampAdjustment();
cannam@190 422 }
Chris@318 423
Chris@318 424 // Here we iterate over the frames, avoiding asking the numframes in case it's streaming input.
Chris@318 425 do {
cannam@16 426
cannam@16 427 int count;
cannam@16 428
Chris@318 429 if ((blockSize==stepSize) || (currentStep==0)) {
Chris@318 430 // read a full fresh block
Chris@318 431 if ((count = sf_readf_float(sndfile, filebuf, blockSize)) < 0) {
Chris@318 432 cerr << "ERROR: sf_readf_float failed: " << sf_strerror(sndfile) << endl;
Chris@318 433 break;
Chris@318 434 }
Chris@318 435 if (count != blockSize) --finalStepsRemaining;
Chris@318 436 } else {
Chris@318 437 // otherwise shunt the existing data down and read the remainder.
Chris@318 438 memmove(filebuf, filebuf + (stepSize * channels), overlapSize * channels * sizeof(float));
Chris@318 439 if ((count = sf_readf_float(sndfile, filebuf + (overlapSize * channels), stepSize)) < 0) {
Chris@318 440 cerr << "ERROR: sf_readf_float failed: " << sf_strerror(sndfile) << endl;
Chris@318 441 break;
Chris@318 442 }
Chris@318 443 if (count != stepSize) --finalStepsRemaining;
Chris@318 444 count += overlapSize;
cannam@16 445 }
cannam@16 446
cannam@16 447 for (int c = 0; c < channels; ++c) {
cannam@64 448 int j = 0;
cannam@64 449 while (j < count) {
cannam@64 450 plugbuf[c][j] = filebuf[j * sfinfo.channels + c];
cannam@64 451 ++j;
cannam@64 452 }
cannam@64 453 while (j < blockSize) {
cannam@16 454 plugbuf[c][j] = 0.0f;
cannam@64 455 ++j;
cannam@16 456 }
cannam@16 457 }
cannam@16 458
Chris@318 459 rt = RealTime::frame2RealTime(currentStep * stepSize, sfinfo.samplerate);
cannam@190 460
Chris@442 461 features = plugin->process(plugbuf, rt);
Chris@442 462
cannam@16 463 printFeatures
cannam@190 464 (RealTime::realTime2Frame(rt + adjustment, sfinfo.samplerate),
Chris@442 465 sfinfo.samplerate, od, outputNo, features, out, useFrames);
cannam@88 466
Chris@318 467 if (sfinfo.frames > 0){
Chris@318 468 int pp = progress;
Chris@358 469 progress = (int)((float(currentStep * stepSize) / sfinfo.frames) * 100.f + 0.5f);
Chris@318 470 if (progress != pp && out) {
Chris@318 471 cerr << "\r" << progress << "%";
Chris@318 472 }
cannam@88 473 }
Chris@318 474
Chris@318 475 ++currentStep;
Chris@318 476
Chris@318 477 } while (finalStepsRemaining > 0);
Chris@318 478
cannam@88 479 if (out) cerr << "\rDone" << endl;
cannam@16 480
Chris@318 481 rt = RealTime::frame2RealTime(currentStep * stepSize, sfinfo.samplerate);
cannam@190 482
Chris@442 483 features = plugin->getRemainingFeatures();
Chris@442 484
cannam@190 485 printFeatures(RealTime::realTime2Frame(rt + adjustment, sfinfo.samplerate),
Chris@442 486 sfinfo.samplerate, od, outputNo, features, out, useFrames);
cannam@16 487
cannam@29 488 returnValue = 0;
cannam@29 489
cannam@16 490 done:
cannam@16 491 delete plugin;
cannam@88 492 if (out) {
cannam@88 493 out->close();
cannam@88 494 delete out;
cannam@88 495 }
cannam@16 496 sf_close(sndfile);
cannam@29 497 return returnValue;
cannam@1 498 }
cannam@1 499
Chris@442 500 static double
Chris@442 501 toSeconds(const RealTime &time)
Chris@442 502 {
Chris@442 503 return time.sec + double(time.nsec + 1) / 1000000000.0;
Chris@442 504 }
Chris@442 505
cannam@16 506 void
Chris@442 507 printFeatures(int frame, int sr,
Chris@442 508 const Plugin::OutputDescriptor &output, int outputNo,
Chris@442 509 const Plugin::FeatureSet &features, ofstream *out, bool useFrames)
cannam@99 510 {
Chris@453 511 static int featureCount = -1;
Chris@442 512
Chris@442 513 if (features.find(outputNo) == features.end()) return;
Chris@442 514
Chris@442 515 for (size_t i = 0; i < features.at(outputNo).size(); ++i) {
cannam@99 516
Chris@442 517 const Plugin::Feature &f = features.at(outputNo).at(i);
Chris@442 518
Chris@442 519 bool haveRt = false;
Chris@442 520 RealTime rt;
Chris@442 521
Chris@442 522 if (output.sampleType == Plugin::OutputDescriptor::VariableSampleRate) {
Chris@442 523 rt = f.timestamp;
Chris@442 524 haveRt = true;
Chris@442 525 } else if (output.sampleType == Plugin::OutputDescriptor::FixedSampleRate) {
Chris@453 526 int n = featureCount + 1;
Chris@442 527 if (f.hasTimestamp) {
Chris@442 528 n = int(round(toSeconds(f.timestamp) * output.sampleRate));
Chris@442 529 }
Chris@442 530 rt = RealTime::fromSeconds(double(n) / output.sampleRate);
Chris@442 531 haveRt = true;
Chris@453 532 featureCount = n;
Chris@442 533 }
Chris@442 534
cannam@109 535 if (useFrames) {
cannam@99 536
cannam@109 537 int displayFrame = frame;
cannam@109 538
Chris@442 539 if (haveRt) {
Chris@442 540 displayFrame = RealTime::realTime2Frame(rt, sr);
cannam@109 541 }
cannam@109 542
cannam@167 543 (out ? *out : cout) << displayFrame;
cannam@167 544
Chris@442 545 if (f.hasDuration) {
Chris@442 546 displayFrame = RealTime::realTime2Frame(f.duration, sr);
cannam@167 547 (out ? *out : cout) << "," << displayFrame;
cannam@167 548 }
cannam@167 549
cannam@167 550 (out ? *out : cout) << ":";
cannam@109 551
cannam@109 552 } else {
cannam@109 553
Chris@442 554 if (!haveRt) {
Chris@442 555 rt = RealTime::frame2RealTime(frame, sr);
cannam@109 556 }
cannam@109 557
cannam@167 558 (out ? *out : cout) << rt.toString();
cannam@167 559
Chris@442 560 if (f.hasDuration) {
Chris@442 561 rt = f.duration;
cannam@167 562 (out ? *out : cout) << "," << rt.toString();
cannam@167 563 }
cannam@167 564
cannam@167 565 (out ? *out : cout) << ":";
cannam@99 566 }
cannam@99 567
Chris@442 568 for (unsigned int j = 0; j < f.values.size(); ++j) {
Chris@442 569 (out ? *out : cout) << " " << f.values[j];
cannam@99 570 }
Chris@442 571 (out ? *out : cout) << " " << f.label;
cannam@99 572
cannam@99 573 (out ? *out : cout) << endl;
cannam@99 574 }
cannam@99 575 }
cannam@99 576
cannam@99 577 void
cannam@64 578 printPluginPath(bool verbose)
cannam@40 579 {
cannam@64 580 if (verbose) {
cannam@64 581 cout << "\nVamp plugin search path: ";
cannam@64 582 }
cannam@64 583
cannam@99 584 vector<string> path = PluginHostAdapter::getPluginPath();
cannam@40 585 for (size_t i = 0; i < path.size(); ++i) {
cannam@64 586 if (verbose) {
cannam@64 587 cout << "[" << path[i] << "]";
cannam@64 588 } else {
cannam@64 589 cout << path[i] << endl;
cannam@64 590 }
cannam@40 591 }
cannam@64 592
cannam@64 593 if (verbose) cout << endl;
cannam@40 594 }
cannam@40 595
cannam@240 596 static
cannam@240 597 string
cannam@240 598 header(string text, int level)
cannam@240 599 {
cannam@240 600 string out = '\n' + text + '\n';
cannam@240 601 for (size_t i = 0; i < text.length(); ++i) {
cannam@240 602 out += (level == 1 ? '=' : level == 2 ? '-' : '~');
cannam@240 603 }
cannam@240 604 out += '\n';
cannam@240 605 return out;
cannam@240 606 }
cannam@240 607
cannam@40 608 void
cannam@95 609 enumeratePlugins(Verbosity verbosity)
cannam@40 610 {
cannam@64 611 PluginLoader *loader = PluginLoader::getInstance();
cannam@64 612
cannam@95 613 if (verbosity == PluginInformation) {
cannam@95 614 cout << "\nVamp plugin libraries found in search path:" << endl;
cannam@95 615 }
cannam@64 616
cannam@99 617 vector<PluginLoader::PluginKey> plugins = loader->listPlugins();
cannam@99 618 typedef multimap<string, PluginLoader::PluginKey>
cannam@64 619 LibraryMap;
cannam@64 620 LibraryMap libraryMap;
cannam@64 621
cannam@64 622 for (size_t i = 0; i < plugins.size(); ++i) {
cannam@99 623 string path = loader->getLibraryPathForPlugin(plugins[i]);
cannam@64 624 libraryMap.insert(LibraryMap::value_type(path, plugins[i]));
cannam@64 625 }
cannam@64 626
cannam@99 627 string prevPath = "";
cannam@64 628 int index = 0;
cannam@64 629
cannam@64 630 for (LibraryMap::iterator i = libraryMap.begin();
cannam@64 631 i != libraryMap.end(); ++i) {
cannam@64 632
cannam@99 633 string path = i->first;
cannam@64 634 PluginLoader::PluginKey key = i->second;
cannam@64 635
cannam@64 636 if (path != prevPath) {
cannam@64 637 prevPath = path;
cannam@64 638 index = 0;
cannam@95 639 if (verbosity == PluginInformation) {
cannam@95 640 cout << "\n " << path << ":" << endl;
cannam@240 641 } else if (verbosity == PluginInformationDetailed) {
cannam@240 642 string::size_type ki = i->second.find(':');
cannam@240 643 string text = "Library \"" + i->second.substr(0, ki) + "\"";
cannam@240 644 cout << "\n" << header(text, 1);
cannam@95 645 }
cannam@40 646 }
cannam@64 647
cannam@99 648 Plugin *plugin = loader->loadPlugin(key, 48000);
cannam@64 649 if (plugin) {
cannam@64 650
cannam@64 651 char c = char('A' + index);
cannam@64 652 if (c > 'Z') c = char('a' + (index - 26));
cannam@64 653
cannam@240 654 PluginLoader::PluginCategoryHierarchy category =
cannam@240 655 loader->getPluginCategory(key);
cannam@240 656 string catstr;
cannam@240 657 if (!category.empty()) {
cannam@240 658 for (size_t ci = 0; ci < category.size(); ++ci) {
cannam@240 659 if (ci > 0) catstr += " > ";
cannam@240 660 catstr += category[ci];
cannam@240 661 }
cannam@240 662 }
cannam@240 663
cannam@95 664 if (verbosity == PluginInformation) {
cannam@64 665
cannam@95 666 cout << " [" << c << "] [v"
cannam@95 667 << plugin->getVampApiVersion() << "] "
cannam@95 668 << plugin->getName() << ", \""
cannam@95 669 << plugin->getIdentifier() << "\"" << " ["
cannam@95 670 << plugin->getMaker() << "]" << endl;
cannam@240 671
cannam@240 672 if (catstr != "") {
cannam@240 673 cout << " > " << catstr << endl;
cannam@64 674 }
cannam@95 675
cannam@95 676 if (plugin->getDescription() != "") {
cannam@95 677 cout << " - " << plugin->getDescription() << endl;
cannam@95 678 }
cannam@95 679
cannam@240 680 } else if (verbosity == PluginInformationDetailed) {
cannam@240 681
cannam@240 682 cout << header(plugin->getName(), 2);
cannam@248 683 cout << " - Identifier: "
cannam@240 684 << key << endl;
cannam@248 685 cout << " - Plugin Version: "
cannam@240 686 << plugin->getPluginVersion() << endl;
cannam@248 687 cout << " - Vamp API Version: "
cannam@240 688 << plugin->getVampApiVersion() << endl;
cannam@248 689 cout << " - Maker: \""
cannam@240 690 << plugin->getMaker() << "\"" << endl;
cannam@248 691 cout << " - Copyright: \""
cannam@240 692 << plugin->getCopyright() << "\"" << endl;
cannam@248 693 cout << " - Description: \""
cannam@240 694 << plugin->getDescription() << "\"" << endl;
cannam@248 695 cout << " - Input Domain: "
cannam@240 696 << (plugin->getInputDomain() == Vamp::Plugin::TimeDomain ?
cannam@240 697 "Time Domain" : "Frequency Domain") << endl;
cannam@248 698 cout << " - Default Step Size: "
cannam@240 699 << plugin->getPreferredStepSize() << endl;
cannam@248 700 cout << " - Default Block Size: "
cannam@240 701 << plugin->getPreferredBlockSize() << endl;
cannam@248 702 cout << " - Minimum Channels: "
cannam@240 703 << plugin->getMinChannelCount() << endl;
cannam@248 704 cout << " - Maximum Channels: "
cannam@240 705 << plugin->getMaxChannelCount() << endl;
cannam@240 706
cannam@95 707 } else if (verbosity == PluginIds) {
cannam@95 708 cout << "vamp:" << key << endl;
cannam@47 709 }
cannam@95 710
cannam@99 711 Plugin::OutputList outputs =
cannam@64 712 plugin->getOutputDescriptors();
cannam@64 713
cannam@240 714 if (verbosity == PluginInformationDetailed) {
cannam@240 715
cannam@240 716 Plugin::ParameterList params = plugin->getParameterDescriptors();
cannam@240 717 for (size_t j = 0; j < params.size(); ++j) {
cannam@240 718 Plugin::ParameterDescriptor &pd(params[j]);
cannam@240 719 cout << "\nParameter " << j+1 << ": \"" << pd.name << "\"" << endl;
cannam@248 720 cout << " - Identifier: " << pd.identifier << endl;
cannam@248 721 cout << " - Description: \"" << pd.description << "\"" << endl;
cannam@240 722 if (pd.unit != "") {
cannam@248 723 cout << " - Unit: " << pd.unit << endl;
cannam@240 724 }
cannam@248 725 cout << " - Range: ";
cannam@240 726 cout << pd.minValue << " -> " << pd.maxValue << endl;
cannam@248 727 cout << " - Default: ";
cannam@240 728 cout << pd.defaultValue << endl;
cannam@240 729 if (pd.isQuantized) {
cannam@248 730 cout << " - Quantize Step: "
cannam@240 731 << pd.quantizeStep << endl;
cannam@240 732 }
cannam@240 733 if (!pd.valueNames.empty()) {
cannam@248 734 cout << " - Value Names: ";
cannam@240 735 for (size_t k = 0; k < pd.valueNames.size(); ++k) {
cannam@240 736 if (k > 0) cout << ", ";
cannam@240 737 cout << "\"" << pd.valueNames[k] << "\"";
cannam@240 738 }
cannam@240 739 cout << endl;
cannam@240 740 }
cannam@240 741 }
cannam@240 742
cannam@240 743 if (outputs.empty()) {
cannam@240 744 cout << "\n** Note: This plugin reports no outputs!" << endl;
cannam@240 745 }
cannam@240 746 for (size_t j = 0; j < outputs.size(); ++j) {
cannam@240 747 Plugin::OutputDescriptor &od(outputs[j]);
cannam@240 748 cout << "\nOutput " << j+1 << ": \"" << od.name << "\"" << endl;
cannam@248 749 cout << " - Identifier: " << od.identifier << endl;
cannam@248 750 cout << " - Description: \"" << od.description << "\"" << endl;
cannam@240 751 if (od.unit != "") {
cannam@248 752 cout << " - Unit: " << od.unit << endl;
cannam@240 753 }
cannam@240 754 if (od.hasFixedBinCount) {
cannam@248 755 cout << " - Default Bin Count: " << od.binCount << endl;
cannam@240 756 }
cannam@240 757 if (!od.binNames.empty()) {
cannam@248 758 bool have = false;
cannam@240 759 for (size_t k = 0; k < od.binNames.size(); ++k) {
cannam@248 760 if (od.binNames[k] != "") {
cannam@248 761 have = true; break;
cannam@248 762 }
cannam@240 763 }
cannam@248 764 if (have) {
cannam@248 765 cout << " - Bin Names: ";
cannam@248 766 for (size_t k = 0; k < od.binNames.size(); ++k) {
cannam@248 767 if (k > 0) cout << ", ";
cannam@248 768 cout << "\"" << od.binNames[k] << "\"";
cannam@248 769 }
cannam@248 770 cout << endl;
cannam@248 771 }
cannam@240 772 }
cannam@240 773 if (od.hasKnownExtents) {
cannam@248 774 cout << " - Default Extents: ";
cannam@240 775 cout << od.minValue << " -> " << od.maxValue << endl;
cannam@240 776 }
cannam@240 777 if (od.isQuantized) {
cannam@248 778 cout << " - Quantize Step: "
cannam@240 779 << od.quantizeStep << endl;
cannam@240 780 }
cannam@248 781 cout << " - Sample Type: "
cannam@240 782 << (od.sampleType ==
cannam@240 783 Plugin::OutputDescriptor::OneSamplePerStep ?
cannam@240 784 "One Sample Per Step" :
cannam@240 785 od.sampleType ==
cannam@240 786 Plugin::OutputDescriptor::FixedSampleRate ?
cannam@240 787 "Fixed Sample Rate" :
cannam@240 788 "Variable Sample Rate") << endl;
cannam@240 789 if (od.sampleType !=
cannam@240 790 Plugin::OutputDescriptor::OneSamplePerStep) {
cannam@248 791 cout << " - Default Rate: "
cannam@240 792 << od.sampleRate << endl;
cannam@240 793 }
cannam@248 794 cout << " - Has Duration: "
cannam@240 795 << (od.hasDuration ? "Yes" : "No") << endl;
cannam@240 796 }
cannam@240 797 }
cannam@240 798
cannam@95 799 if (outputs.size() > 1 || verbosity == PluginOutputIds) {
cannam@64 800 for (size_t j = 0; j < outputs.size(); ++j) {
cannam@95 801 if (verbosity == PluginInformation) {
cannam@95 802 cout << " (" << j << ") "
cannam@95 803 << outputs[j].name << ", \""
cannam@95 804 << outputs[j].identifier << "\"" << endl;
cannam@95 805 if (outputs[j].description != "") {
cannam@95 806 cout << " - "
cannam@95 807 << outputs[j].description << endl;
cannam@95 808 }
cannam@95 809 } else if (verbosity == PluginOutputIds) {
cannam@95 810 cout << "vamp:" << key << ":" << outputs[j].identifier << endl;
cannam@40 811 }
cannam@40 812 }
cannam@64 813 }
cannam@64 814
cannam@64 815 ++index;
cannam@64 816
cannam@64 817 delete plugin;
cannam@40 818 }
cannam@40 819 }
cannam@64 820
cannam@240 821 if (verbosity == PluginInformation ||
cannam@240 822 verbosity == PluginInformationDetailed) {
cannam@95 823 cout << endl;
cannam@95 824 }
cannam@40 825 }
cannam@40 826
cannam@40 827 void
cannam@99 828 printPluginCategoryList()
cannam@16 829 {
cannam@99 830 PluginLoader *loader = PluginLoader::getInstance();
cannam@88 831
cannam@99 832 vector<PluginLoader::PluginKey> plugins = loader->listPlugins();
cannam@88 833
cannam@99 834 set<string> printedcats;
cannam@99 835
cannam@99 836 for (size_t i = 0; i < plugins.size(); ++i) {
cannam@99 837
cannam@99 838 PluginLoader::PluginKey key = plugins[i];
cannam@99 839
cannam@99 840 PluginLoader::PluginCategoryHierarchy category =
cannam@99 841 loader->getPluginCategory(key);
cannam@99 842
cannam@99 843 Plugin *plugin = loader->loadPlugin(key, 48000);
cannam@99 844 if (!plugin) continue;
cannam@99 845
cannam@99 846 string catstr = "";
cannam@99 847
cannam@99 848 if (category.empty()) catstr = '|';
cannam@99 849 else {
cannam@99 850 for (size_t j = 0; j < category.size(); ++j) {
cannam@99 851 catstr += category[j];
cannam@99 852 catstr += '|';
cannam@99 853 if (printedcats.find(catstr) == printedcats.end()) {
cannam@99 854 std::cout << catstr << std::endl;
cannam@99 855 printedcats.insert(catstr);
cannam@99 856 }
cannam@99 857 }
cannam@16 858 }
cannam@88 859
cannam@99 860 std::cout << catstr << key << ":::" << plugin->getName() << ":::" << plugin->getMaker() << ":::" << plugin->getDescription() << std::endl;
cannam@16 861 }
cannam@16 862 }
cannam@16 863