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