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