vamp-simple-host.cpp
Go to the documentation of this file.
00001 /* -*- c-basic-offset: 4 indent-tabs-mode: nil -*-  vi:set ts=8 sts=4 sw=4: */
00002 
00003 /*
00004     Vamp
00005 
00006     An API for audio analysis and feature extraction plugins.
00007 
00008     Centre for Digital Music, Queen Mary, University of London.
00009     Copyright 2006 Chris Cannam, copyright 2007-2008 QMUL.
00010   
00011     Permission is hereby granted, free of charge, to any person
00012     obtaining a copy of this software and associated documentation
00013     files (the "Software"), to deal in the Software without
00014     restriction, including without limitation the rights to use, copy,
00015     modify, merge, publish, distribute, sublicense, and/or sell copies
00016     of the Software, and to permit persons to whom the Software is
00017     furnished to do so, subject to the following conditions:
00018 
00019     The above copyright notice and this permission notice shall be
00020     included in all copies or substantial portions of the Software.
00021 
00022     THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
00023     EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
00024     MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
00025     NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR
00026     ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF
00027     CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
00028     WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
00029 
00030     Except as contained in this notice, the names of the Centre for
00031     Digital Music; Queen Mary, University of London; and Chris Cannam
00032     shall not be used in advertising or otherwise to promote the sale,
00033     use or other dealings in this Software without prior written
00034     authorization.
00035 */
00036 
00037 
00038 /*
00039  * This "simple" Vamp plugin host is no longer as simple as it was; it
00040  * now has a lot of options and includes a lot of code to handle the
00041  * various useful listing modes it supports.
00042  *
00043  * However, the runPlugin function still contains a reasonable
00044  * implementation of a fairly generic Vamp plugin host capable of
00045  * evaluating a given output on a given plugin for a sound file read
00046  * via libsndfile.
00047  */
00048 
00049 #include <vamp-hostsdk/PluginHostAdapter.h>
00050 #include <vamp-hostsdk/PluginInputDomainAdapter.h>
00051 #include <vamp-hostsdk/PluginLoader.h>
00052 
00053 #include <iostream>
00054 #include <fstream>
00055 #include <set>
00056 #include <sndfile.h>
00057 
00058 #include <cstring>
00059 #include <cstdlib>
00060 
00061 #include "system.h"
00062 
00063 #include <cmath>
00064 
00065 using namespace std;
00066 
00067 using Vamp::Plugin;
00068 using Vamp::PluginHostAdapter;
00069 using Vamp::RealTime;
00070 using Vamp::HostExt::PluginLoader;
00071 using Vamp::HostExt::PluginWrapper;
00072 using Vamp::HostExt::PluginInputDomainAdapter;
00073 
00074 #define HOST_VERSION "1.5"
00075 
00076 enum Verbosity {
00077     PluginIds,
00078     PluginOutputIds,
00079     PluginInformation,
00080     PluginInformationDetailed
00081 };
00082 
00083 void printFeatures(int, int,
00084                    const Plugin::OutputDescriptor &, int,
00085                    const Plugin::FeatureSet &, ofstream *, bool frames);
00086 void transformInput(float *, size_t);
00087 void fft(unsigned int, bool, double *, double *, double *, double *);
00088 void printPluginPath(bool verbose);
00089 void printPluginCategoryList();
00090 void enumeratePlugins(Verbosity);
00091 void listPluginsInLibrary(string soname);
00092 int runPlugin(string myname, string soname, string id, string output,
00093               int outputNo, string inputFile, string outfilename, bool frames);
00094 
00095 void usage(const char *name)
00096 {
00097     cerr << "\n"
00098          << name << ": A command-line host for Vamp audio analysis plugins.\n\n"
00099         "Centre for Digital Music, Queen Mary, University of London.\n"
00100         "Copyright 2006-2009 Chris Cannam and QMUL.\n"
00101         "Freely redistributable; published under a BSD-style license.\n\n"
00102         "Usage:\n\n"
00103         "  " << name << " [-s] pluginlibrary[." << PLUGIN_SUFFIX << "]:plugin[:output] file.wav [-o out.txt]\n"
00104         "  " << name << " [-s] pluginlibrary[." << PLUGIN_SUFFIX << "]:plugin file.wav [outputno] [-o out.txt]\n\n"
00105         "    -- Load plugin id \"plugin\" from \"pluginlibrary\" and run it on the\n"
00106         "       audio data in \"file.wav\", retrieving the named \"output\", or output\n"
00107         "       number \"outputno\" (the first output by default) and dumping it to\n"
00108         "       standard output, or to \"out.txt\" if the -o option is given.\n\n"
00109         "       \"pluginlibrary\" should be a library name, not a file path; the\n"
00110         "       standard Vamp library search path will be used to locate it.  If\n"
00111         "       a file path is supplied, the directory part(s) will be ignored.\n\n"
00112         "       If the -s option is given, results will be labelled with the audio\n"
00113         "       sample frame at which they occur. Otherwise, they will be labelled\n"
00114         "       with time in seconds.\n\n"
00115         "  " << name << " -l\n"
00116         "  " << name << " --list\n\n"
00117         "    -- List the plugin libraries and Vamp plugins in the library search path\n"
00118         "       in a verbose human-readable format.\n\n"
00119         "  " << name << " -L\n"
00120         "  " << name << " --list-full\n\n"
00121         "    -- List all data reported by all the Vamp plugins in the library search\n"
00122         "       path in a very verbose human-readable format.\n\n"
00123         "  " << name << " --list-ids\n\n"
00124         "    -- List the plugins in the search path in a terse machine-readable format,\n"
00125         "       in the form vamp:soname:identifier.\n\n"
00126         "  " << name << " --list-outputs\n\n"
00127         "    -- List the outputs for plugins in the search path in a machine-readable\n"
00128         "       format, in the form vamp:soname:identifier:output.\n\n"
00129         "  " << name << " --list-by-category\n\n"
00130         "    -- List the plugins as a plugin index by category, in a machine-readable\n"
00131         "       format.  The format may change in future releases.\n\n"
00132         "  " << name << " -p\n\n"
00133         "    -- Print out the Vamp library search path.\n\n"
00134         "  " << name << " -v\n\n"
00135         "    -- Display version information only.\n"
00136          << endl;
00137     exit(2);
00138 }
00139 
00140 int main(int argc, char **argv)
00141 {
00142     char *scooter = argv[0];
00143     char *name = 0;
00144     while (scooter && *scooter) {
00145         if (*scooter == '/' || *scooter == '\\') name = ++scooter;
00146         else ++scooter;
00147     }
00148     if (!name || !*name) name = argv[0];
00149     
00150     if (argc < 2) usage(name);
00151 
00152     if (argc == 2) {
00153 
00154         if (!strcmp(argv[1], "-v")) {
00155 
00156             cout << "Simple Vamp plugin host version: " << HOST_VERSION << endl
00157                  << "Vamp API version: " << VAMP_API_VERSION << endl
00158                  << "Vamp SDK version: " << VAMP_SDK_VERSION << endl;
00159             return 0;
00160 
00161         } else if (!strcmp(argv[1], "-l") || !strcmp(argv[1], "--list")) {
00162 
00163             printPluginPath(true);
00164             enumeratePlugins(PluginInformation);
00165             return 0;
00166 
00167         } else if (!strcmp(argv[1], "-L") || !strcmp(argv[1], "--list-full")) {
00168 
00169             enumeratePlugins(PluginInformationDetailed);
00170             return 0;
00171 
00172         } else if (!strcmp(argv[1], "-p")) {
00173 
00174             printPluginPath(false);
00175             return 0;
00176 
00177         } else if (!strcmp(argv[1], "--list-ids")) {
00178 
00179             enumeratePlugins(PluginIds);
00180             return 0;
00181 
00182         } else if (!strcmp(argv[1], "--list-outputs")) {
00183 
00184             enumeratePlugins(PluginOutputIds);
00185             return 0;
00186 
00187         } else if (!strcmp(argv[1], "--list-by-category")) {
00188 
00189             printPluginCategoryList();
00190             return 0;
00191 
00192         } else usage(name);
00193     }
00194 
00195     if (argc < 3) usage(name);
00196 
00197     bool useFrames = false;
00198     
00199     int base = 1;
00200     if (!strcmp(argv[1], "-s")) {
00201         useFrames = true;
00202         base = 2;
00203     }
00204 
00205     string soname = argv[base];
00206     string wavname = argv[base+1];
00207     string plugid = "";
00208     string output = "";
00209     int outputNo = -1;
00210     string outfilename;
00211 
00212     if (argc >= base+3) {
00213 
00214         int idx = base+2;
00215 
00216         if (isdigit(*argv[idx])) {
00217             outputNo = atoi(argv[idx++]);
00218         }
00219 
00220         if (argc == idx + 2) {
00221             if (!strcmp(argv[idx], "-o")) {
00222                 outfilename = argv[idx+1];
00223             } else usage(name);
00224         } else if (argc != idx) {
00225             (usage(name));
00226         }
00227     }
00228 
00229     cerr << endl << name << ": Running..." << endl;
00230 
00231     cerr << "Reading file: \"" << wavname << "\", writing to ";
00232     if (outfilename == "") {
00233         cerr << "standard output" << endl;
00234     } else {
00235         cerr << "\"" << outfilename << "\"" << endl;
00236     }
00237 
00238     string::size_type sep = soname.find(':');
00239 
00240     if (sep != string::npos) {
00241         plugid = soname.substr(sep + 1);
00242         soname = soname.substr(0, sep);
00243 
00244         sep = plugid.find(':');
00245         if (sep != string::npos) {
00246             output = plugid.substr(sep + 1);
00247             plugid = plugid.substr(0, sep);
00248         }
00249     }
00250 
00251     if (plugid == "") {
00252         usage(name);
00253     }
00254 
00255     if (output != "" && outputNo != -1) {
00256         usage(name);
00257     }
00258 
00259     if (output == "" && outputNo == -1) {
00260         outputNo = 0;
00261     }
00262 
00263     return runPlugin(name, soname, plugid, output, outputNo,
00264                      wavname, outfilename, useFrames);
00265 }
00266 
00267 
00268 int runPlugin(string myname, string soname, string id,
00269               string output, int outputNo, string wavname,
00270               string outfilename, bool useFrames)
00271 {
00272     PluginLoader *loader = PluginLoader::getInstance();
00273 
00274     PluginLoader::PluginKey key = loader->composePluginKey(soname, id);
00275     
00276     SNDFILE *sndfile;
00277     SF_INFO sfinfo;
00278     memset(&sfinfo, 0, sizeof(SF_INFO));
00279 
00280     sndfile = sf_open(wavname.c_str(), SFM_READ, &sfinfo);
00281     if (!sndfile) {
00282         cerr << myname << ": ERROR: Failed to open input file \""
00283              << wavname << "\": " << sf_strerror(sndfile) << endl;
00284         return 1;
00285     }
00286 
00287     ofstream *out = 0;
00288     if (outfilename != "") {
00289         out = new ofstream(outfilename.c_str(), ios::out);
00290         if (!*out) {
00291             cerr << myname << ": ERROR: Failed to open output file \""
00292                  << outfilename << "\" for writing" << endl;
00293             delete out;
00294             return 1;
00295         }
00296     }
00297 
00298     Plugin *plugin = loader->loadPlugin
00299         (key, sfinfo.samplerate, PluginLoader::ADAPT_ALL_SAFE);
00300     if (!plugin) {
00301         cerr << myname << ": ERROR: Failed to load plugin \"" << id
00302              << "\" from library \"" << soname << "\"" << endl;
00303         sf_close(sndfile);
00304         if (out) {
00305             out->close();
00306             delete out;
00307         }
00308         return 1;
00309     }
00310 
00311     cerr << "Running plugin: \"" << plugin->getIdentifier() << "\"..." << endl;
00312 
00313     // Note that the following would be much simpler if we used a
00314     // PluginBufferingAdapter as well -- i.e. if we had passed
00315     // PluginLoader::ADAPT_ALL to loader->loadPlugin() above, instead
00316     // of ADAPT_ALL_SAFE.  Then we could simply specify our own block
00317     // size, keep the step size equal to the block size, and ignore
00318     // the plugin's bleatings.  However, there are some issues with
00319     // using a PluginBufferingAdapter that make the results sometimes
00320     // technically different from (if effectively the same as) the
00321     // un-adapted plugin, so we aren't doing that here.  See the
00322     // PluginBufferingAdapter documentation for details.
00323 
00324     int blockSize = plugin->getPreferredBlockSize();
00325     int stepSize = plugin->getPreferredStepSize();
00326 
00327     if (blockSize == 0) {
00328         blockSize = 1024;
00329     }
00330     if (stepSize == 0) {
00331         if (plugin->getInputDomain() == Plugin::FrequencyDomain) {
00332             stepSize = blockSize/2;
00333         } else {
00334             stepSize = blockSize;
00335         }
00336     } else if (stepSize > blockSize) {
00337         cerr << "WARNING: stepSize " << stepSize << " > blockSize " << blockSize << ", resetting blockSize to ";
00338         if (plugin->getInputDomain() == Plugin::FrequencyDomain) {
00339             blockSize = stepSize * 2;
00340         } else {
00341             blockSize = stepSize;
00342         }
00343         cerr << blockSize << endl;
00344     }
00345     int overlapSize = blockSize - stepSize;
00346     sf_count_t currentStep = 0;
00347     int finalStepsRemaining = max(1, (blockSize / stepSize) - 1); // at end of file, this many part-silent frames needed after we hit EOF
00348 
00349     int channels = sfinfo.channels;
00350 
00351     float *filebuf = new float[blockSize * channels];
00352     float **plugbuf = new float*[channels];
00353     for (int c = 0; c < channels; ++c) plugbuf[c] = new float[blockSize + 2];
00354 
00355     cerr << "Using block size = " << blockSize << ", step size = "
00356               << stepSize << endl;
00357 
00358     // The channel queries here are for informational purposes only --
00359     // a PluginChannelAdapter is being used automatically behind the
00360     // scenes, and it will take case of any channel mismatch
00361 
00362     int minch = plugin->getMinChannelCount();
00363     int maxch = plugin->getMaxChannelCount();
00364     cerr << "Plugin accepts " << minch << " -> " << maxch << " channel(s)" << endl;
00365     cerr << "Sound file has " << channels << " (will mix/augment if necessary)" << endl;
00366 
00367     Plugin::OutputList outputs = plugin->getOutputDescriptors();
00368     Plugin::OutputDescriptor od;
00369     Plugin::FeatureSet features;
00370 
00371     int returnValue = 1;
00372     int progress = 0;
00373 
00374     RealTime rt;
00375     PluginWrapper *wrapper = 0;
00376     RealTime adjustment = RealTime::zeroTime;
00377 
00378     if (outputs.empty()) {
00379         cerr << "ERROR: Plugin has no outputs!" << endl;
00380         goto done;
00381     }
00382 
00383     if (outputNo < 0) {
00384 
00385         for (size_t oi = 0; oi < outputs.size(); ++oi) {
00386             if (outputs[oi].identifier == output) {
00387                 outputNo = oi;
00388                 break;
00389             }
00390         }
00391 
00392         if (outputNo < 0) {
00393             cerr << "ERROR: Non-existent output \"" << output << "\" requested" << endl;
00394             goto done;
00395         }
00396 
00397     } else {
00398 
00399         if (int(outputs.size()) <= outputNo) {
00400             cerr << "ERROR: Output " << outputNo << " requested, but plugin has only " << outputs.size() << " output(s)" << endl;
00401             goto done;
00402         }        
00403     }
00404 
00405     od = outputs[outputNo];
00406     cerr << "Output is: \"" << od.identifier << "\"" << endl;
00407 
00408     if (!plugin->initialise(channels, stepSize, blockSize)) {
00409         cerr << "ERROR: Plugin initialise (channels = " << channels
00410              << ", stepSize = " << stepSize << ", blockSize = "
00411              << blockSize << ") failed." << endl;
00412         goto done;
00413     }
00414 
00415     wrapper = dynamic_cast<PluginWrapper *>(plugin);
00416     if (wrapper) {
00417         // See documentation for
00418         // PluginInputDomainAdapter::getTimestampAdjustment
00419         PluginInputDomainAdapter *ida =
00420             wrapper->getWrapper<PluginInputDomainAdapter>();
00421         if (ida) adjustment = ida->getTimestampAdjustment();
00422     }
00423     
00424     // Here we iterate over the frames, avoiding asking the numframes in case it's streaming input.
00425     do {
00426 
00427         int count;
00428 
00429         if ((blockSize==stepSize) || (currentStep==0)) {
00430             // read a full fresh block
00431             if ((count = sf_readf_float(sndfile, filebuf, blockSize)) < 0) {
00432                 cerr << "ERROR: sf_readf_float failed: " << sf_strerror(sndfile) << endl;
00433                 break;
00434             }
00435             if (count != blockSize) --finalStepsRemaining;
00436         } else {
00437             //  otherwise shunt the existing data down and read the remainder.
00438             memmove(filebuf, filebuf + (stepSize * channels), overlapSize * channels * sizeof(float));
00439             if ((count = sf_readf_float(sndfile, filebuf + (overlapSize * channels), stepSize)) < 0) {
00440                 cerr << "ERROR: sf_readf_float failed: " << sf_strerror(sndfile) << endl;
00441                 break;
00442             }
00443             if (count != stepSize) --finalStepsRemaining;
00444             count += overlapSize;
00445         }
00446 
00447         for (int c = 0; c < channels; ++c) {
00448             int j = 0;
00449             while (j < count) {
00450                 plugbuf[c][j] = filebuf[j * sfinfo.channels + c];
00451                 ++j;
00452             }
00453             while (j < blockSize) {
00454                 plugbuf[c][j] = 0.0f;
00455                 ++j;
00456             }
00457         }
00458 
00459         rt = RealTime::frame2RealTime(currentStep * stepSize, sfinfo.samplerate);
00460 
00461         features = plugin->process(plugbuf, rt);
00462         
00463         printFeatures
00464             (RealTime::realTime2Frame(rt + adjustment, sfinfo.samplerate),
00465              sfinfo.samplerate, od, outputNo, features, out, useFrames);
00466 
00467         if (sfinfo.frames > 0){
00468             int pp = progress;
00469             progress = (int)((float(currentStep * stepSize) / sfinfo.frames) * 100.f + 0.5f);
00470             if (progress != pp && out) {
00471                 cerr << "\r" << progress << "%";
00472             }
00473         }
00474 
00475         ++currentStep;
00476 
00477     } while (finalStepsRemaining > 0);
00478 
00479     if (out) cerr << "\rDone" << endl;
00480 
00481     rt = RealTime::frame2RealTime(currentStep * stepSize, sfinfo.samplerate);
00482 
00483     features = plugin->getRemainingFeatures();
00484     
00485     printFeatures(RealTime::realTime2Frame(rt + adjustment, sfinfo.samplerate),
00486                   sfinfo.samplerate, od, outputNo, features, out, useFrames);
00487 
00488     returnValue = 0;
00489 
00490 done:
00491     delete plugin;
00492     if (out) {
00493         out->close();
00494         delete out;
00495     }
00496     sf_close(sndfile);
00497     return returnValue;
00498 }
00499 
00500 static double
00501 toSeconds(const RealTime &time)
00502 {
00503     return time.sec + double(time.nsec + 1) / 1000000000.0;
00504 }
00505 
00506 void
00507 printFeatures(int frame, int sr,
00508               const Plugin::OutputDescriptor &output, int outputNo,
00509               const Plugin::FeatureSet &features, ofstream *out, bool useFrames)
00510 {
00511     static int featureCount = -1;
00512     
00513     if (features.find(outputNo) == features.end()) return;
00514     
00515     for (size_t i = 0; i < features.at(outputNo).size(); ++i) {
00516 
00517         const Plugin::Feature &f = features.at(outputNo).at(i);
00518 
00519         bool haveRt = false;
00520         RealTime rt;
00521 
00522         if (output.sampleType == Plugin::OutputDescriptor::VariableSampleRate) {
00523             rt = f.timestamp;
00524             haveRt = true;
00525         } else if (output.sampleType == Plugin::OutputDescriptor::FixedSampleRate) {
00526             int n = featureCount + 1;
00527             if (f.hasTimestamp) {
00528                 n = int(round(toSeconds(f.timestamp) * output.sampleRate));
00529             }
00530             rt = RealTime::fromSeconds(double(n) / output.sampleRate);
00531             haveRt = true;
00532             featureCount = n;
00533         }
00534         
00535         if (useFrames) {
00536 
00537             int displayFrame = frame;
00538 
00539             if (haveRt) {
00540                 displayFrame = RealTime::realTime2Frame(rt, sr);
00541             }
00542 
00543             (out ? *out : cout) << displayFrame;
00544 
00545             if (f.hasDuration) {
00546                 displayFrame = RealTime::realTime2Frame(f.duration, sr);
00547                 (out ? *out : cout) << "," << displayFrame;
00548             }
00549 
00550             (out ? *out : cout)  << ":";
00551 
00552         } else {
00553 
00554             if (!haveRt) {
00555                 rt = RealTime::frame2RealTime(frame, sr);
00556             }
00557 
00558             (out ? *out : cout) << rt.toString();
00559 
00560             if (f.hasDuration) {
00561                 rt = f.duration;
00562                 (out ? *out : cout) << "," << rt.toString();
00563             }
00564 
00565             (out ? *out : cout) << ":";
00566         }
00567 
00568         for (unsigned int j = 0; j < f.values.size(); ++j) {
00569             (out ? *out : cout) << " " << f.values[j];
00570         }
00571         (out ? *out : cout) << " " << f.label;
00572 
00573         (out ? *out : cout) << endl;
00574     }
00575 }
00576 
00577 void
00578 printPluginPath(bool verbose)
00579 {
00580     if (verbose) {
00581         cout << "\nVamp plugin search path: ";
00582     }
00583 
00584     vector<string> path = PluginHostAdapter::getPluginPath();
00585     for (size_t i = 0; i < path.size(); ++i) {
00586         if (verbose) {
00587             cout << "[" << path[i] << "]";
00588         } else {
00589             cout << path[i] << endl;
00590         }
00591     }
00592 
00593     if (verbose) cout << endl;
00594 }
00595 
00596 static
00597 string
00598 header(string text, int level)
00599 {
00600     string out = '\n' + text + '\n';
00601     for (size_t i = 0; i < text.length(); ++i) {
00602         out += (level == 1 ? '=' : level == 2 ? '-' : '~');
00603     }
00604     out += '\n';
00605     return out;
00606 }
00607 
00608 void
00609 enumeratePlugins(Verbosity verbosity)
00610 {
00611     PluginLoader *loader = PluginLoader::getInstance();
00612 
00613     if (verbosity == PluginInformation) {
00614         cout << "\nVamp plugin libraries found in search path:" << endl;
00615     }
00616 
00617     vector<PluginLoader::PluginKey> plugins = loader->listPlugins();
00618     typedef multimap<string, PluginLoader::PluginKey>
00619         LibraryMap;
00620     LibraryMap libraryMap;
00621 
00622     for (size_t i = 0; i < plugins.size(); ++i) {
00623         string path = loader->getLibraryPathForPlugin(plugins[i]);
00624         libraryMap.insert(LibraryMap::value_type(path, plugins[i]));
00625     }
00626 
00627     string prevPath = "";
00628     int index = 0;
00629 
00630     for (LibraryMap::iterator i = libraryMap.begin();
00631          i != libraryMap.end(); ++i) {
00632         
00633         string path = i->first;
00634         PluginLoader::PluginKey key = i->second;
00635 
00636         if (path != prevPath) {
00637             prevPath = path;
00638             index = 0;
00639             if (verbosity == PluginInformation) {
00640                 cout << "\n  " << path << ":" << endl;
00641             } else if (verbosity == PluginInformationDetailed) {
00642                 string::size_type ki = i->second.find(':');
00643                 string text = "Library \"" + i->second.substr(0, ki) + "\"";
00644                 cout << "\n" << header(text, 1);
00645             }
00646         }
00647 
00648         Plugin *plugin = loader->loadPlugin(key, 48000);
00649         if (plugin) {
00650 
00651             char c = char('A' + index);
00652             if (c > 'Z') c = char('a' + (index - 26));
00653 
00654             PluginLoader::PluginCategoryHierarchy category =
00655                 loader->getPluginCategory(key);
00656             string catstr;
00657             if (!category.empty()) {
00658                 for (size_t ci = 0; ci < category.size(); ++ci) {
00659                     if (ci > 0) catstr += " > ";
00660                         catstr += category[ci];
00661                 }
00662             }
00663 
00664             if (verbosity == PluginInformation) {
00665 
00666                 cout << "    [" << c << "] [v"
00667                      << plugin->getVampApiVersion() << "] "
00668                      << plugin->getName() << ", \""
00669                      << plugin->getIdentifier() << "\"" << " ["
00670                      << plugin->getMaker() << "]" << endl;
00671                 
00672                 if (catstr != "") {
00673                     cout << "       > " << catstr << endl;
00674                 }
00675 
00676                 if (plugin->getDescription() != "") {
00677                     cout << "        - " << plugin->getDescription() << endl;
00678                 }
00679 
00680             } else if (verbosity == PluginInformationDetailed) {
00681 
00682                 cout << header(plugin->getName(), 2);
00683                 cout << " - Identifier:         "
00684                      << key << endl;
00685                 cout << " - Plugin Version:     " 
00686                      << plugin->getPluginVersion() << endl;
00687                 cout << " - Vamp API Version:   "
00688                      << plugin->getVampApiVersion() << endl;
00689                 cout << " - Maker:              \""
00690                      << plugin->getMaker() << "\"" << endl;
00691                 cout << " - Copyright:          \""
00692                      << plugin->getCopyright() << "\"" << endl;
00693                 cout << " - Description:        \""
00694                      << plugin->getDescription() << "\"" << endl;
00695                 cout << " - Input Domain:       "
00696                      << (plugin->getInputDomain() == Vamp::Plugin::TimeDomain ?
00697                          "Time Domain" : "Frequency Domain") << endl;
00698                 cout << " - Default Step Size:  " 
00699                      << plugin->getPreferredStepSize() << endl;
00700                 cout << " - Default Block Size: " 
00701                      << plugin->getPreferredBlockSize() << endl;
00702                 cout << " - Minimum Channels:   " 
00703                      << plugin->getMinChannelCount() << endl;
00704                 cout << " - Maximum Channels:   " 
00705                      << plugin->getMaxChannelCount() << endl;
00706 
00707             } else if (verbosity == PluginIds) {
00708                 cout << "vamp:" << key << endl;
00709             }
00710             
00711             Plugin::OutputList outputs =
00712                 plugin->getOutputDescriptors();
00713 
00714             if (verbosity == PluginInformationDetailed) {
00715 
00716                 Plugin::ParameterList params = plugin->getParameterDescriptors();
00717                 for (size_t j = 0; j < params.size(); ++j) {
00718                     Plugin::ParameterDescriptor &pd(params[j]);
00719                     cout << "\nParameter " << j+1 << ": \"" << pd.name << "\"" << endl;
00720                     cout << " - Identifier:         " << pd.identifier << endl;
00721                     cout << " - Description:        \"" << pd.description << "\"" << endl;
00722                     if (pd.unit != "") {
00723                         cout << " - Unit:               " << pd.unit << endl;
00724                     }
00725                     cout << " - Range:              ";
00726                     cout << pd.minValue << " -> " << pd.maxValue << endl;
00727                     cout << " - Default:            ";
00728                     cout << pd.defaultValue << endl;
00729                     if (pd.isQuantized) {
00730                         cout << " - Quantize Step:      "
00731                              << pd.quantizeStep << endl;
00732                     }
00733                     if (!pd.valueNames.empty()) {
00734                         cout << " - Value Names:        ";
00735                         for (size_t k = 0; k < pd.valueNames.size(); ++k) {
00736                             if (k > 0) cout << ", ";
00737                             cout << "\"" << pd.valueNames[k] << "\"";
00738                         }
00739                         cout << endl;
00740                     }
00741                 }
00742 
00743                 if (outputs.empty()) {
00744                     cout << "\n** Note: This plugin reports no outputs!" << endl;
00745                 }
00746                 for (size_t j = 0; j < outputs.size(); ++j) {
00747                     Plugin::OutputDescriptor &od(outputs[j]);
00748                     cout << "\nOutput " << j+1 << ": \"" << od.name << "\"" << endl;
00749                     cout << " - Identifier:         " << od.identifier << endl;
00750                     cout << " - Description:        \"" << od.description << "\"" << endl;
00751                     if (od.unit != "") {
00752                         cout << " - Unit:               " << od.unit << endl;
00753                     }
00754                     if (od.hasFixedBinCount) {
00755                         cout << " - Default Bin Count:  " << od.binCount << endl;
00756                     }
00757                     if (!od.binNames.empty()) {
00758                         bool have = false;
00759                         for (size_t k = 0; k < od.binNames.size(); ++k) {
00760                             if (od.binNames[k] != "") {
00761                                 have = true; break;
00762                             }
00763                         }
00764                         if (have) {
00765                             cout << " - Bin Names:          ";
00766                             for (size_t k = 0; k < od.binNames.size(); ++k) {
00767                                 if (k > 0) cout << ", ";
00768                                 cout << "\"" << od.binNames[k] << "\"";
00769                             }
00770                             cout << endl;
00771                         }
00772                     }
00773                     if (od.hasKnownExtents) {
00774                         cout << " - Default Extents:    ";
00775                         cout << od.minValue << " -> " << od.maxValue << endl;
00776                     }
00777                     if (od.isQuantized) {
00778                         cout << " - Quantize Step:      "
00779                              << od.quantizeStep << endl;
00780                     }
00781                     cout << " - Sample Type:        "
00782                          << (od.sampleType ==
00783                              Plugin::OutputDescriptor::OneSamplePerStep ?
00784                              "One Sample Per Step" :
00785                              od.sampleType ==
00786                              Plugin::OutputDescriptor::FixedSampleRate ?
00787                              "Fixed Sample Rate" :
00788                              "Variable Sample Rate") << endl;
00789                     if (od.sampleType !=
00790                         Plugin::OutputDescriptor::OneSamplePerStep) {
00791                         cout << " - Default Rate:       "
00792                              << od.sampleRate << endl;
00793                     }
00794                     cout << " - Has Duration:       "
00795                          << (od.hasDuration ? "Yes" : "No") << endl;
00796                 }
00797             }
00798 
00799             if (outputs.size() > 1 || verbosity == PluginOutputIds) {
00800                 for (size_t j = 0; j < outputs.size(); ++j) {
00801                     if (verbosity == PluginInformation) {
00802                         cout << "         (" << j << ") "
00803                              << outputs[j].name << ", \""
00804                              << outputs[j].identifier << "\"" << endl;
00805                         if (outputs[j].description != "") {
00806                             cout << "             - " 
00807                                  << outputs[j].description << endl;
00808                         }
00809                     } else if (verbosity == PluginOutputIds) {
00810                         cout << "vamp:" << key << ":" << outputs[j].identifier << endl;
00811                     }
00812                 }
00813             }
00814 
00815             ++index;
00816 
00817             delete plugin;
00818         }
00819     }
00820 
00821     if (verbosity == PluginInformation ||
00822         verbosity == PluginInformationDetailed) {
00823         cout << endl;
00824     }
00825 }
00826 
00827 void
00828 printPluginCategoryList()
00829 {
00830     PluginLoader *loader = PluginLoader::getInstance();
00831 
00832     vector<PluginLoader::PluginKey> plugins = loader->listPlugins();
00833 
00834     set<string> printedcats;
00835 
00836     for (size_t i = 0; i < plugins.size(); ++i) {
00837 
00838         PluginLoader::PluginKey key = plugins[i];
00839         
00840         PluginLoader::PluginCategoryHierarchy category =
00841             loader->getPluginCategory(key);
00842 
00843         Plugin *plugin = loader->loadPlugin(key, 48000);
00844         if (!plugin) continue;
00845 
00846         string catstr = "";
00847 
00848         if (category.empty()) catstr = '|';
00849         else {
00850             for (size_t j = 0; j < category.size(); ++j) {
00851                 catstr += category[j];
00852                 catstr += '|';
00853                 if (printedcats.find(catstr) == printedcats.end()) {
00854                     std::cout << catstr << std::endl;
00855                     printedcats.insert(catstr);
00856                 }
00857             }
00858         }
00859 
00860         std::cout << catstr << key << ":::" << plugin->getName() << ":::" << plugin->getMaker() << ":::" << plugin->getDescription() << std::endl;
00861     }
00862 }
00863