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