Mercurial > hg > sv-dependency-builds
diff src/vamp-plugin-sdk-2.5/host/vamp-simple-host.cpp @ 23:619f715526df sv_v2.1
Update Vamp plugin SDK to 2.5
author | Chris Cannam |
---|---|
date | Thu, 09 May 2013 10:52:46 +0100 |
parents | |
children |
line wrap: on
line diff
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/vamp-plugin-sdk-2.5/host/vamp-simple-host.cpp Thu May 09 10:52:46 2013 +0100 @@ -0,0 +1,832 @@ +/* -*- c-basic-offset: 4 indent-tabs-mode: nil -*- vi:set ts=8 sts=4 sw=4: */ + +/* + Vamp + + An API for audio analysis and feature extraction plugins. + + Centre for Digital Music, Queen Mary, University of London. + Copyright 2006 Chris Cannam, copyright 2007-2008 QMUL. + + Permission is hereby granted, free of charge, to any person + obtaining a copy of this software and associated documentation + files (the "Software"), to deal in the Software without + restriction, including without limitation the rights to use, copy, + modify, merge, publish, distribute, sublicense, and/or sell copies + of the Software, and to permit persons to whom the Software is + furnished to do so, subject to the following conditions: + + The above copyright notice and this permission notice shall be + included in all copies or substantial portions of the Software. + + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR + ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF + CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION + WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + + Except as contained in this notice, the names of the Centre for + Digital Music; Queen Mary, University of London; and Chris Cannam + shall not be used in advertising or otherwise to promote the sale, + use or other dealings in this Software without prior written + authorization. +*/ + + +/* + * This "simple" Vamp plugin host is no longer as simple as it was; it + * now has a lot of options and includes a lot of code to handle the + * various useful listing modes it supports. + * + * However, the runPlugin function still contains a reasonable + * implementation of a fairly generic Vamp plugin host capable of + * evaluating a given output on a given plugin for a sound file read + * via libsndfile. + */ + +#include <vamp-hostsdk/PluginHostAdapter.h> +#include <vamp-hostsdk/PluginInputDomainAdapter.h> +#include <vamp-hostsdk/PluginLoader.h> + +#include <iostream> +#include <fstream> +#include <set> +#include <sndfile.h> + +#include <cstring> +#include <cstdlib> + +#include "system.h" + +#include <cmath> + +using namespace std; + +using Vamp::Plugin; +using Vamp::PluginHostAdapter; +using Vamp::RealTime; +using Vamp::HostExt::PluginLoader; +using Vamp::HostExt::PluginWrapper; +using Vamp::HostExt::PluginInputDomainAdapter; + +#define HOST_VERSION "1.5" + +enum Verbosity { + PluginIds, + PluginOutputIds, + PluginInformation, + PluginInformationDetailed +}; + +void printFeatures(int, int, int, Plugin::FeatureSet, ofstream *, bool frames); +void transformInput(float *, size_t); +void fft(unsigned int, bool, double *, double *, double *, double *); +void printPluginPath(bool verbose); +void printPluginCategoryList(); +void enumeratePlugins(Verbosity); +void listPluginsInLibrary(string soname); +int runPlugin(string myname, string soname, string id, string output, + int outputNo, string inputFile, string outfilename, bool frames); + +void usage(const char *name) +{ + cerr << "\n" + << name << ": A command-line host for Vamp audio analysis plugins.\n\n" + "Centre for Digital Music, Queen Mary, University of London.\n" + "Copyright 2006-2009 Chris Cannam and QMUL.\n" + "Freely redistributable; published under a BSD-style license.\n\n" + "Usage:\n\n" + " " << name << " [-s] pluginlibrary[." << PLUGIN_SUFFIX << "]:plugin[:output] file.wav [-o out.txt]\n" + " " << name << " [-s] pluginlibrary[." << PLUGIN_SUFFIX << "]:plugin file.wav [outputno] [-o out.txt]\n\n" + " -- Load plugin id \"plugin\" from \"pluginlibrary\" and run it on the\n" + " audio data in \"file.wav\", retrieving the named \"output\", or output\n" + " number \"outputno\" (the first output by default) and dumping it to\n" + " standard output, or to \"out.txt\" if the -o option is given.\n\n" + " \"pluginlibrary\" should be a library name, not a file path; the\n" + " standard Vamp library search path will be used to locate it. If\n" + " a file path is supplied, the directory part(s) will be ignored.\n\n" + " If the -s option is given, results will be labelled with the audio\n" + " sample frame at which they occur. Otherwise, they will be labelled\n" + " with time in seconds.\n\n" + " " << name << " -l\n" + " " << name << " --list\n\n" + " -- List the plugin libraries and Vamp plugins in the library search path\n" + " in a verbose human-readable format.\n\n" + " " << name << " --list-full\n\n" + " -- List all data reported by all the Vamp plugins in the library search\n" + " path in a very verbose human-readable format.\n\n" + " " << name << " --list-ids\n\n" + " -- List the plugins in the search path in a terse machine-readable format,\n" + " in the form vamp:soname:identifier.\n\n" + " " << name << " --list-outputs\n\n" + " -- List the outputs for plugins in the search path in a machine-readable\n" + " format, in the form vamp:soname:identifier:output.\n\n" + " " << name << " --list-by-category\n\n" + " -- List the plugins as a plugin index by category, in a machine-readable\n" + " format. The format may change in future releases.\n\n" + " " << name << " -p\n\n" + " -- Print out the Vamp library search path.\n\n" + " " << name << " -v\n\n" + " -- Display version information only.\n" + << endl; + exit(2); +} + +int main(int argc, char **argv) +{ + char *scooter = argv[0]; + char *name = 0; + while (scooter && *scooter) { + if (*scooter == '/' || *scooter == '\\') name = ++scooter; + else ++scooter; + } + if (!name || !*name) name = argv[0]; + + if (argc < 2) usage(name); + + if (argc == 2) { + + if (!strcmp(argv[1], "-v")) { + + cout << "Simple Vamp plugin host version: " << HOST_VERSION << endl + << "Vamp API version: " << VAMP_API_VERSION << endl + << "Vamp SDK version: " << VAMP_SDK_VERSION << endl; + return 0; + + } else if (!strcmp(argv[1], "-l") || !strcmp(argv[1], "--list")) { + + printPluginPath(true); + enumeratePlugins(PluginInformation); + return 0; + + } else if (!strcmp(argv[1], "--list-full")) { + + enumeratePlugins(PluginInformationDetailed); + return 0; + + } else if (!strcmp(argv[1], "-p")) { + + printPluginPath(false); + return 0; + + } else if (!strcmp(argv[1], "--list-ids")) { + + enumeratePlugins(PluginIds); + return 0; + + } else if (!strcmp(argv[1], "--list-outputs")) { + + enumeratePlugins(PluginOutputIds); + return 0; + + } else if (!strcmp(argv[1], "--list-by-category")) { + + printPluginCategoryList(); + return 0; + + } else usage(name); + } + + if (argc < 3) usage(name); + + bool useFrames = false; + + int base = 1; + if (!strcmp(argv[1], "-s")) { + useFrames = true; + base = 2; + } + + string soname = argv[base]; + string wavname = argv[base+1]; + string plugid = ""; + string output = ""; + int outputNo = -1; + string outfilename; + + if (argc >= base+3) { + + int idx = base+2; + + if (isdigit(*argv[idx])) { + outputNo = atoi(argv[idx++]); + } + + if (argc == idx + 2) { + if (!strcmp(argv[idx], "-o")) { + outfilename = argv[idx+1]; + } else usage(name); + } else if (argc != idx) { + (usage(name)); + } + } + + cerr << endl << name << ": Running..." << endl; + + cerr << "Reading file: \"" << wavname << "\", writing to "; + if (outfilename == "") { + cerr << "standard output" << endl; + } else { + cerr << "\"" << outfilename << "\"" << endl; + } + + string::size_type sep = soname.find(':'); + + if (sep != string::npos) { + plugid = soname.substr(sep + 1); + soname = soname.substr(0, sep); + + sep = plugid.find(':'); + if (sep != string::npos) { + output = plugid.substr(sep + 1); + plugid = plugid.substr(0, sep); + } + } + + if (plugid == "") { + usage(name); + } + + if (output != "" && outputNo != -1) { + usage(name); + } + + if (output == "" && outputNo == -1) { + outputNo = 0; + } + + return runPlugin(name, soname, plugid, output, outputNo, + wavname, outfilename, useFrames); +} + + +int runPlugin(string myname, string soname, string id, + string output, int outputNo, string wavname, + string outfilename, bool useFrames) +{ + PluginLoader *loader = PluginLoader::getInstance(); + + PluginLoader::PluginKey key = loader->composePluginKey(soname, id); + + SNDFILE *sndfile; + SF_INFO sfinfo; + memset(&sfinfo, 0, sizeof(SF_INFO)); + + sndfile = sf_open(wavname.c_str(), SFM_READ, &sfinfo); + if (!sndfile) { + cerr << myname << ": ERROR: Failed to open input file \"" + << wavname << "\": " << sf_strerror(sndfile) << endl; + return 1; + } + + ofstream *out = 0; + if (outfilename != "") { + out = new ofstream(outfilename.c_str(), ios::out); + if (!*out) { + cerr << myname << ": ERROR: Failed to open output file \"" + << outfilename << "\" for writing" << endl; + delete out; + return 1; + } + } + + Plugin *plugin = loader->loadPlugin + (key, sfinfo.samplerate, PluginLoader::ADAPT_ALL_SAFE); + if (!plugin) { + cerr << myname << ": ERROR: Failed to load plugin \"" << id + << "\" from library \"" << soname << "\"" << endl; + sf_close(sndfile); + if (out) { + out->close(); + delete out; + } + return 1; + } + + cerr << "Running plugin: \"" << plugin->getIdentifier() << "\"..." << endl; + + // Note that the following would be much simpler if we used a + // PluginBufferingAdapter as well -- i.e. if we had passed + // PluginLoader::ADAPT_ALL to loader->loadPlugin() above, instead + // of ADAPT_ALL_SAFE. Then we could simply specify our own block + // size, keep the step size equal to the block size, and ignore + // the plugin's bleatings. However, there are some issues with + // using a PluginBufferingAdapter that make the results sometimes + // technically different from (if effectively the same as) the + // un-adapted plugin, so we aren't doing that here. See the + // PluginBufferingAdapter documentation for details. + + int blockSize = plugin->getPreferredBlockSize(); + int stepSize = plugin->getPreferredStepSize(); + + if (blockSize == 0) { + blockSize = 1024; + } + if (stepSize == 0) { + if (plugin->getInputDomain() == Plugin::FrequencyDomain) { + stepSize = blockSize/2; + } else { + stepSize = blockSize; + } + } else if (stepSize > blockSize) { + cerr << "WARNING: stepSize " << stepSize << " > blockSize " << blockSize << ", resetting blockSize to "; + if (plugin->getInputDomain() == Plugin::FrequencyDomain) { + blockSize = stepSize * 2; + } else { + blockSize = stepSize; + } + cerr << blockSize << endl; + } + int overlapSize = blockSize - stepSize; + sf_count_t currentStep = 0; + int finalStepsRemaining = max(1, (blockSize / stepSize) - 1); // at end of file, this many part-silent frames needed after we hit EOF + + int channels = sfinfo.channels; + + float *filebuf = new float[blockSize * channels]; + float **plugbuf = new float*[channels]; + for (int c = 0; c < channels; ++c) plugbuf[c] = new float[blockSize + 2]; + + cerr << "Using block size = " << blockSize << ", step size = " + << stepSize << endl; + + // The channel queries here are for informational purposes only -- + // a PluginChannelAdapter is being used automatically behind the + // scenes, and it will take case of any channel mismatch + + int minch = plugin->getMinChannelCount(); + int maxch = plugin->getMaxChannelCount(); + cerr << "Plugin accepts " << minch << " -> " << maxch << " channel(s)" << endl; + cerr << "Sound file has " << channels << " (will mix/augment if necessary)" << endl; + + Plugin::OutputList outputs = plugin->getOutputDescriptors(); + Plugin::OutputDescriptor od; + + int returnValue = 1; + int progress = 0; + + RealTime rt; + PluginWrapper *wrapper = 0; + RealTime adjustment = RealTime::zeroTime; + + if (outputs.empty()) { + cerr << "ERROR: Plugin has no outputs!" << endl; + goto done; + } + + if (outputNo < 0) { + + for (size_t oi = 0; oi < outputs.size(); ++oi) { + if (outputs[oi].identifier == output) { + outputNo = oi; + break; + } + } + + if (outputNo < 0) { + cerr << "ERROR: Non-existent output \"" << output << "\" requested" << endl; + goto done; + } + + } else { + + if (int(outputs.size()) <= outputNo) { + cerr << "ERROR: Output " << outputNo << " requested, but plugin has only " << outputs.size() << " output(s)" << endl; + goto done; + } + } + + od = outputs[outputNo]; + cerr << "Output is: \"" << od.identifier << "\"" << endl; + + if (!plugin->initialise(channels, stepSize, blockSize)) { + cerr << "ERROR: Plugin initialise (channels = " << channels + << ", stepSize = " << stepSize << ", blockSize = " + << blockSize << ") failed." << endl; + goto done; + } + + wrapper = dynamic_cast<PluginWrapper *>(plugin); + if (wrapper) { + // See documentation for + // PluginInputDomainAdapter::getTimestampAdjustment + PluginInputDomainAdapter *ida = + wrapper->getWrapper<PluginInputDomainAdapter>(); + if (ida) adjustment = ida->getTimestampAdjustment(); + } + + // Here we iterate over the frames, avoiding asking the numframes in case it's streaming input. + do { + + int count; + + if ((blockSize==stepSize) || (currentStep==0)) { + // read a full fresh block + if ((count = sf_readf_float(sndfile, filebuf, blockSize)) < 0) { + cerr << "ERROR: sf_readf_float failed: " << sf_strerror(sndfile) << endl; + break; + } + if (count != blockSize) --finalStepsRemaining; + } else { + // otherwise shunt the existing data down and read the remainder. + memmove(filebuf, filebuf + (stepSize * channels), overlapSize * channels * sizeof(float)); + if ((count = sf_readf_float(sndfile, filebuf + (overlapSize * channels), stepSize)) < 0) { + cerr << "ERROR: sf_readf_float failed: " << sf_strerror(sndfile) << endl; + break; + } + if (count != stepSize) --finalStepsRemaining; + count += overlapSize; + } + + for (int c = 0; c < channels; ++c) { + int j = 0; + while (j < count) { + plugbuf[c][j] = filebuf[j * sfinfo.channels + c]; + ++j; + } + while (j < blockSize) { + plugbuf[c][j] = 0.0f; + ++j; + } + } + + rt = RealTime::frame2RealTime(currentStep * stepSize, sfinfo.samplerate); + + printFeatures + (RealTime::realTime2Frame(rt + adjustment, sfinfo.samplerate), + sfinfo.samplerate, outputNo, plugin->process(plugbuf, rt), + out, useFrames); + + if (sfinfo.frames > 0){ + int pp = progress; + progress = lrintf((float(currentStep * stepSize) / sfinfo.frames) * 100.f); + if (progress != pp && out) { + cerr << "\r" << progress << "%"; + } + } + + ++currentStep; + + } while (finalStepsRemaining > 0); + + if (out) cerr << "\rDone" << endl; + + rt = RealTime::frame2RealTime(currentStep * stepSize, sfinfo.samplerate); + + printFeatures(RealTime::realTime2Frame(rt + adjustment, sfinfo.samplerate), + sfinfo.samplerate, outputNo, + plugin->getRemainingFeatures(), out, useFrames); + + returnValue = 0; + +done: + delete plugin; + if (out) { + out->close(); + delete out; + } + sf_close(sndfile); + return returnValue; +} + +void +printFeatures(int frame, int sr, int output, + Plugin::FeatureSet features, ofstream *out, bool useFrames) +{ + for (unsigned int i = 0; i < features[output].size(); ++i) { + + if (useFrames) { + + int displayFrame = frame; + + if (features[output][i].hasTimestamp) { + displayFrame = RealTime::realTime2Frame + (features[output][i].timestamp, sr); + } + + (out ? *out : cout) << displayFrame; + + if (features[output][i].hasDuration) { + displayFrame = RealTime::realTime2Frame + (features[output][i].duration, sr); + (out ? *out : cout) << "," << displayFrame; + } + + (out ? *out : cout) << ":"; + + } else { + + RealTime rt = RealTime::frame2RealTime(frame, sr); + + if (features[output][i].hasTimestamp) { + rt = features[output][i].timestamp; + } + + (out ? *out : cout) << rt.toString(); + + if (features[output][i].hasDuration) { + rt = features[output][i].duration; + (out ? *out : cout) << "," << rt.toString(); + } + + (out ? *out : cout) << ":"; + } + + for (unsigned int j = 0; j < features[output][i].values.size(); ++j) { + (out ? *out : cout) << " " << features[output][i].values[j]; + } + (out ? *out : cout) << " " << features[output][i].label; + + (out ? *out : cout) << endl; + } +} + +void +printPluginPath(bool verbose) +{ + if (verbose) { + cout << "\nVamp plugin search path: "; + } + + vector<string> path = PluginHostAdapter::getPluginPath(); + for (size_t i = 0; i < path.size(); ++i) { + if (verbose) { + cout << "[" << path[i] << "]"; + } else { + cout << path[i] << endl; + } + } + + if (verbose) cout << endl; +} + +static +string +header(string text, int level) +{ + string out = '\n' + text + '\n'; + for (size_t i = 0; i < text.length(); ++i) { + out += (level == 1 ? '=' : level == 2 ? '-' : '~'); + } + out += '\n'; + return out; +} + +void +enumeratePlugins(Verbosity verbosity) +{ + PluginLoader *loader = PluginLoader::getInstance(); + + if (verbosity == PluginInformation) { + cout << "\nVamp plugin libraries found in search path:" << endl; + } + + vector<PluginLoader::PluginKey> plugins = loader->listPlugins(); + typedef multimap<string, PluginLoader::PluginKey> + LibraryMap; + LibraryMap libraryMap; + + for (size_t i = 0; i < plugins.size(); ++i) { + string path = loader->getLibraryPathForPlugin(plugins[i]); + libraryMap.insert(LibraryMap::value_type(path, plugins[i])); + } + + string prevPath = ""; + int index = 0; + + for (LibraryMap::iterator i = libraryMap.begin(); + i != libraryMap.end(); ++i) { + + string path = i->first; + PluginLoader::PluginKey key = i->second; + + if (path != prevPath) { + prevPath = path; + index = 0; + if (verbosity == PluginInformation) { + cout << "\n " << path << ":" << endl; + } else if (verbosity == PluginInformationDetailed) { + string::size_type ki = i->second.find(':'); + string text = "Library \"" + i->second.substr(0, ki) + "\""; + cout << "\n" << header(text, 1); + } + } + + Plugin *plugin = loader->loadPlugin(key, 48000); + if (plugin) { + + char c = char('A' + index); + if (c > 'Z') c = char('a' + (index - 26)); + + PluginLoader::PluginCategoryHierarchy category = + loader->getPluginCategory(key); + string catstr; + if (!category.empty()) { + for (size_t ci = 0; ci < category.size(); ++ci) { + if (ci > 0) catstr += " > "; + catstr += category[ci]; + } + } + + if (verbosity == PluginInformation) { + + cout << " [" << c << "] [v" + << plugin->getVampApiVersion() << "] " + << plugin->getName() << ", \"" + << plugin->getIdentifier() << "\"" << " [" + << plugin->getMaker() << "]" << endl; + + if (catstr != "") { + cout << " > " << catstr << endl; + } + + if (plugin->getDescription() != "") { + cout << " - " << plugin->getDescription() << endl; + } + + } else if (verbosity == PluginInformationDetailed) { + + cout << header(plugin->getName(), 2); + cout << " - Identifier: " + << key << endl; + cout << " - Plugin Version: " + << plugin->getPluginVersion() << endl; + cout << " - Vamp API Version: " + << plugin->getVampApiVersion() << endl; + cout << " - Maker: \"" + << plugin->getMaker() << "\"" << endl; + cout << " - Copyright: \"" + << plugin->getCopyright() << "\"" << endl; + cout << " - Description: \"" + << plugin->getDescription() << "\"" << endl; + cout << " - Input Domain: " + << (plugin->getInputDomain() == Vamp::Plugin::TimeDomain ? + "Time Domain" : "Frequency Domain") << endl; + cout << " - Default Step Size: " + << plugin->getPreferredStepSize() << endl; + cout << " - Default Block Size: " + << plugin->getPreferredBlockSize() << endl; + cout << " - Minimum Channels: " + << plugin->getMinChannelCount() << endl; + cout << " - Maximum Channels: " + << plugin->getMaxChannelCount() << endl; + + } else if (verbosity == PluginIds) { + cout << "vamp:" << key << endl; + } + + Plugin::OutputList outputs = + plugin->getOutputDescriptors(); + + if (verbosity == PluginInformationDetailed) { + + Plugin::ParameterList params = plugin->getParameterDescriptors(); + for (size_t j = 0; j < params.size(); ++j) { + Plugin::ParameterDescriptor &pd(params[j]); + cout << "\nParameter " << j+1 << ": \"" << pd.name << "\"" << endl; + cout << " - Identifier: " << pd.identifier << endl; + cout << " - Description: \"" << pd.description << "\"" << endl; + if (pd.unit != "") { + cout << " - Unit: " << pd.unit << endl; + } + cout << " - Range: "; + cout << pd.minValue << " -> " << pd.maxValue << endl; + cout << " - Default: "; + cout << pd.defaultValue << endl; + if (pd.isQuantized) { + cout << " - Quantize Step: " + << pd.quantizeStep << endl; + } + if (!pd.valueNames.empty()) { + cout << " - Value Names: "; + for (size_t k = 0; k < pd.valueNames.size(); ++k) { + if (k > 0) cout << ", "; + cout << "\"" << pd.valueNames[k] << "\""; + } + cout << endl; + } + } + + if (outputs.empty()) { + cout << "\n** Note: This plugin reports no outputs!" << endl; + } + for (size_t j = 0; j < outputs.size(); ++j) { + Plugin::OutputDescriptor &od(outputs[j]); + cout << "\nOutput " << j+1 << ": \"" << od.name << "\"" << endl; + cout << " - Identifier: " << od.identifier << endl; + cout << " - Description: \"" << od.description << "\"" << endl; + if (od.unit != "") { + cout << " - Unit: " << od.unit << endl; + } + if (od.hasFixedBinCount) { + cout << " - Default Bin Count: " << od.binCount << endl; + } + if (!od.binNames.empty()) { + bool have = false; + for (size_t k = 0; k < od.binNames.size(); ++k) { + if (od.binNames[k] != "") { + have = true; break; + } + } + if (have) { + cout << " - Bin Names: "; + for (size_t k = 0; k < od.binNames.size(); ++k) { + if (k > 0) cout << ", "; + cout << "\"" << od.binNames[k] << "\""; + } + cout << endl; + } + } + if (od.hasKnownExtents) { + cout << " - Default Extents: "; + cout << od.minValue << " -> " << od.maxValue << endl; + } + if (od.isQuantized) { + cout << " - Quantize Step: " + << od.quantizeStep << endl; + } + cout << " - Sample Type: " + << (od.sampleType == + Plugin::OutputDescriptor::OneSamplePerStep ? + "One Sample Per Step" : + od.sampleType == + Plugin::OutputDescriptor::FixedSampleRate ? + "Fixed Sample Rate" : + "Variable Sample Rate") << endl; + if (od.sampleType != + Plugin::OutputDescriptor::OneSamplePerStep) { + cout << " - Default Rate: " + << od.sampleRate << endl; + } + cout << " - Has Duration: " + << (od.hasDuration ? "Yes" : "No") << endl; + } + } + + if (outputs.size() > 1 || verbosity == PluginOutputIds) { + for (size_t j = 0; j < outputs.size(); ++j) { + if (verbosity == PluginInformation) { + cout << " (" << j << ") " + << outputs[j].name << ", \"" + << outputs[j].identifier << "\"" << endl; + if (outputs[j].description != "") { + cout << " - " + << outputs[j].description << endl; + } + } else if (verbosity == PluginOutputIds) { + cout << "vamp:" << key << ":" << outputs[j].identifier << endl; + } + } + } + + ++index; + + delete plugin; + } + } + + if (verbosity == PluginInformation || + verbosity == PluginInformationDetailed) { + cout << endl; + } +} + +void +printPluginCategoryList() +{ + PluginLoader *loader = PluginLoader::getInstance(); + + vector<PluginLoader::PluginKey> plugins = loader->listPlugins(); + + set<string> printedcats; + + for (size_t i = 0; i < plugins.size(); ++i) { + + PluginLoader::PluginKey key = plugins[i]; + + PluginLoader::PluginCategoryHierarchy category = + loader->getPluginCategory(key); + + Plugin *plugin = loader->loadPlugin(key, 48000); + if (!plugin) continue; + + string catstr = ""; + + if (category.empty()) catstr = '|'; + else { + for (size_t j = 0; j < category.size(); ++j) { + catstr += category[j]; + catstr += '|'; + if (printedcats.find(catstr) == printedcats.end()) { + std::cout << catstr << std::endl; + printedcats.insert(catstr); + } + } + } + + std::cout << catstr << key << ":::" << plugin->getName() << ":::" << plugin->getMaker() << ":::" << plugin->getDescription() << std::endl; + } +} +