annotate host/vamp-simple-host.cpp @ 415:1522e2f6d700

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