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