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