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.
00010     FFT code from Don Cross's public domain FFT implementation.
00011   
00012     Permission is hereby granted, free of charge, to any person
00013     obtaining a copy of this software and associated documentation
00014     files (the "Software"), to deal in the Software without
00015     restriction, including without limitation the rights to use, copy,
00016     modify, merge, publish, distribute, sublicense, and/or sell copies
00017     of the Software, and to permit persons to whom the Software is
00018     furnished to do so, subject to the following conditions:
00019 
00020     The above copyright notice and this permission notice shall be
00021     included in all copies or substantial portions of the Software.
00022 
00023     THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
00024     EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
00025     MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
00026     NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR
00027     ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF
00028     CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
00029     WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
00030 
00031     Except as contained in this notice, the names of the Centre for
00032     Digital Music; Queen Mary, University of London; and Chris Cannam
00033     shall not be used in advertising or otherwise to promote the sale,
00034     use or other dealings in this Software without prior written
00035     authorization.
00036 */
00037 
00038 #include "vamp-sdk/PluginHostAdapter.h"
00039 #include "vamp-sdk/hostext/PluginChannelAdapter.h"
00040 #include "vamp-sdk/hostext/PluginInputDomainAdapter.h"
00041 #include "vamp-sdk/hostext/PluginLoader.h"
00042 #include "vamp/vamp.h"
00043 
00044 #include <iostream>
00045 #include <fstream>
00046 #include <set>
00047 #include <sndfile.h>
00048 
00049 #include <cstring>
00050 #include <cstdlib>
00051 
00052 #include "system.h"
00053 
00054 #include <cmath>
00055 
00056 using namespace std;
00057 
00058 using Vamp::Plugin;
00059 using Vamp::PluginHostAdapter;
00060 using Vamp::RealTime;
00061 using Vamp::HostExt::PluginLoader;
00062 
00063 #define HOST_VERSION "1.1"
00064 
00065 enum Verbosity {
00066     PluginIds,
00067     PluginOutputIds,
00068     PluginInformation
00069 };
00070 
00071 void printFeatures(int, int, int, Plugin::FeatureSet, ofstream *, bool frames);
00072 void transformInput(float *, size_t);
00073 void fft(unsigned int, bool, double *, double *, double *, double *);
00074 void printPluginPath(bool verbose);
00075 void printPluginCategoryList();
00076 void enumeratePlugins(Verbosity);
00077 void listPluginsInLibrary(string soname);
00078 int runPlugin(string myname, string soname, string id, string output,
00079               int outputNo, string inputFile, string outfilename, bool frames);
00080 
00081 void usage(const char *name)
00082 {
00083     cerr << "\n"
00084          << name << ": A simple Vamp plugin host.\n\n"
00085         "Centre for Digital Music, Queen Mary, University of London.\n"
00086         "Copyright 2006-2007 Chris Cannam and QMUL.\n"
00087         "Freely redistributable; published under a BSD-style license.\n\n"
00088         "Usage:\n\n"
00089         "  " << name << " [-s] pluginlibrary[." << PLUGIN_SUFFIX << "]:plugin[:output] file.wav [-o out.txt]\n"
00090         "  " << name << " [-s] pluginlibrary[." << PLUGIN_SUFFIX << "]:plugin file.wav [outputno] [-o out.txt]\n\n"
00091         "    -- Load plugin id \"plugin\" from \"pluginlibrary\" and run it on the\n"
00092         "       audio data in \"file.wav\", retrieving the named \"output\", or output\n"
00093         "       number \"outputno\" (the first output by default) and dumping it to\n"
00094         "       standard output, or to \"out.txt\" if the -o option is given.\n\n"
00095         "       \"pluginlibrary\" should be a library name, not a file path; the\n"
00096         "       standard Vamp library search path will be used to locate it.  If\n"
00097         "       a file path is supplied, the directory part(s) will be ignored.\n\n"
00098         "       If the -s option is given, results will be labelled with the audio\n"
00099         "       sample frame at which they occur. Otherwise, they will be labelled\n"
00100         "       with time in seconds.\n\n"
00101         "  " << name << " -l\n\n"
00102         "    -- List the plugin libraries and Vamp plugins in the library search path\n"
00103         "       in a verbose human-readable format.\n\n"
00104         "  " << name << " --list-ids\n\n"
00105         "    -- List the plugins in the search path in a terse machine-readable format,\n"
00106         "       in the form vamp:soname:identifier.\n\n"
00107         "  " << name << " --list-outputs\n\n"
00108         "    -- List the outputs for plugins in the search path in a machine-readable\n"
00109         "       format, in the form vamp:soname:identifier:output.\n\n"
00110         "  " << name << " --list-by-category\n\n"
00111         "    -- List the plugins as a plugin index by category, in a machine-readable\n"
00112         "       format.  The format may change in future releases.\n\n"
00113         "  " << name << " -p\n\n"
00114         "    -- Print out the Vamp library search path.\n\n"
00115         "  " << name << " -v\n\n"
00116         "    -- Display version information only.\n"
00117          << endl;
00118     exit(2);
00119 }
00120 
00121 int main(int argc, char **argv)
00122 {
00123     char *scooter = argv[0];
00124     char *name = 0;
00125     while (scooter && *scooter) {
00126         if (*scooter == '/' || *scooter == '\\') name = ++scooter;
00127         else ++scooter;
00128     }
00129     if (!name || !*name) name = argv[0];
00130     
00131     if (argc < 2) usage(name);
00132 
00133     if (argc == 2) {
00134 
00135         if (!strcmp(argv[1], "-v")) {
00136 
00137             cout << "Simple Vamp plugin host version: " << HOST_VERSION << endl
00138                  << "Vamp API version: " << VAMP_API_VERSION << endl
00139                  << "Vamp SDK version: " << VAMP_SDK_VERSION << endl;
00140             return 0;
00141 
00142         } else if (!strcmp(argv[1], "-l")) {
00143 
00144             printPluginPath(true);
00145             enumeratePlugins(PluginInformation);
00146             return 0;
00147 
00148         } else if (!strcmp(argv[1], "-p")) {
00149 
00150             printPluginPath(false);
00151             return 0;
00152 
00153         } else if (!strcmp(argv[1], "--list-ids")) {
00154 
00155             enumeratePlugins(PluginIds);
00156             return 0;
00157 
00158         } else if (!strcmp(argv[1], "--list-outputs")) {
00159 
00160             enumeratePlugins(PluginOutputIds);
00161             return 0;
00162 
00163         } else if (!strcmp(argv[1], "--list-by-category")) {
00164 
00165             printPluginCategoryList();
00166             return 0;
00167 
00168         } else usage(name);
00169     }
00170 
00171     if (argc < 3) usage(name);
00172 
00173     bool useFrames = false;
00174     
00175     int base = 1;
00176     if (!strcmp(argv[1], "-s")) {
00177         useFrames = true;
00178         base = 2;
00179     }
00180 
00181     string soname = argv[base];
00182     string wavname = argv[base+1];
00183     string plugid = "";
00184     string output = "";
00185     int outputNo = -1;
00186     string outfilename;
00187 
00188     if (argc >= base+3) {
00189 
00190         int idx = base+2;
00191 
00192         if (isdigit(*argv[idx])) {
00193             outputNo = atoi(argv[idx++]);
00194         }
00195 
00196         if (argc == idx + 2) {
00197             if (!strcmp(argv[idx], "-o")) {
00198                 outfilename = argv[idx+1];
00199             } else usage(name);
00200         } else if (argc != idx) {
00201             (usage(name));
00202         }
00203     }
00204 
00205     cerr << endl << name << ": Running..." << endl;
00206 
00207     cerr << "Reading file: \"" << wavname << "\", writing to ";
00208     if (outfilename == "") {
00209         cerr << "standard output" << endl;
00210     } else {
00211         cerr << "\"" << outfilename << "\"" << endl;
00212     }
00213 
00214     string::size_type sep = soname.find(':');
00215 
00216     if (sep != string::npos) {
00217         plugid = soname.substr(sep + 1);
00218         soname = soname.substr(0, sep);
00219 
00220         sep = plugid.find(':');
00221         if (sep != string::npos) {
00222             output = plugid.substr(sep + 1);
00223             plugid = plugid.substr(0, sep);
00224         }
00225     }
00226 
00227     if (plugid == "") {
00228         usage(name);
00229     }
00230 
00231     if (output != "" && outputNo != -1) {
00232         usage(name);
00233     }
00234 
00235     if (output == "" && outputNo == -1) {
00236         outputNo = 0;
00237     }
00238 
00239     return runPlugin(name, soname, plugid, output, outputNo,
00240                      wavname, outfilename, useFrames);
00241 }
00242 
00243 
00244 int runPlugin(string myname, string soname, string id,
00245               string output, int outputNo, string wavname,
00246               string outfilename, bool useFrames)
00247 {
00248     PluginLoader *loader = PluginLoader::getInstance();
00249 
00250     PluginLoader::PluginKey key = loader->composePluginKey(soname, id);
00251     
00252     SNDFILE *sndfile;
00253     SF_INFO sfinfo;
00254     memset(&sfinfo, 0, sizeof(SF_INFO));
00255 
00256     sndfile = sf_open(wavname.c_str(), SFM_READ, &sfinfo);
00257     if (!sndfile) {
00258         cerr << myname << ": ERROR: Failed to open input file \""
00259              << wavname << "\": " << sf_strerror(sndfile) << endl;
00260         return 1;
00261     }
00262 
00263     ofstream *out = 0;
00264     if (outfilename != "") {
00265         out = new ofstream(outfilename.c_str(), ios::out);
00266         if (!*out) {
00267             cerr << myname << ": ERROR: Failed to open output file \""
00268                  << outfilename << "\" for writing" << endl;
00269             delete out;
00270             return 1;
00271         }
00272     }
00273 
00274     Plugin *plugin = loader->loadPlugin
00275         (key, sfinfo.samplerate, PluginLoader::ADAPT_ALL_SAFE);
00276     if (!plugin) {
00277         cerr << myname << ": ERROR: Failed to load plugin \"" << id
00278              << "\" from library \"" << soname << "\"" << endl;
00279         sf_close(sndfile);
00280         if (out) {
00281             out->close();
00282             delete out;
00283         }
00284         return 1;
00285     }
00286 
00287     cerr << "Running plugin: \"" << plugin->getIdentifier() << "\"..." << endl;
00288 
00289     int blockSize = plugin->getPreferredBlockSize();
00290     int stepSize = plugin->getPreferredStepSize();
00291 
00292     if (blockSize == 0) {
00293         blockSize = 1024;
00294     }
00295     if (stepSize == 0) {
00296         if (plugin->getInputDomain() == Plugin::FrequencyDomain) {
00297             stepSize = blockSize/2;
00298         } else {
00299             stepSize = blockSize;
00300         }
00301     } else if (stepSize > blockSize) {
00302         cerr << "WARNING: stepSize " << stepSize << " > blockSize " << blockSize << ", resetting blockSize to ";
00303         if (plugin->getInputDomain() == Plugin::FrequencyDomain) {
00304             blockSize = stepSize * 2;
00305         } else {
00306             blockSize = stepSize;
00307         }
00308         cerr << blockSize << endl;
00309     }
00310 
00311     int channels = sfinfo.channels;
00312 
00313     float *filebuf = new float[blockSize * channels];
00314     float **plugbuf = new float*[channels];
00315     for (int c = 0; c < channels; ++c) plugbuf[c] = new float[blockSize + 2];
00316 
00317     cerr << "Using block size = " << blockSize << ", step size = "
00318               << stepSize << endl;
00319 
00320     int minch = plugin->getMinChannelCount();
00321     int maxch = plugin->getMaxChannelCount();
00322     cerr << "Plugin accepts " << minch << " -> " << maxch << " channel(s)" << endl;
00323     cerr << "Sound file has " << channels << " (will mix/augment if necessary)" << endl;
00324 
00325     Plugin::OutputList outputs = plugin->getOutputDescriptors();
00326     Plugin::OutputDescriptor od;
00327 
00328     int returnValue = 1;
00329     int progress = 0;
00330 
00331     if (outputs.empty()) {
00332         cerr << "ERROR: Plugin has no outputs!" << endl;
00333         goto done;
00334     }
00335 
00336     if (outputNo < 0) {
00337 
00338         for (size_t oi = 0; oi < outputs.size(); ++oi) {
00339             if (outputs[oi].identifier == output) {
00340                 outputNo = oi;
00341                 break;
00342             }
00343         }
00344 
00345         if (outputNo < 0) {
00346             cerr << "ERROR: Non-existent output \"" << output << "\" requested" << endl;
00347             goto done;
00348         }
00349 
00350     } else {
00351 
00352         if (int(outputs.size()) <= outputNo) {
00353             cerr << "ERROR: Output " << outputNo << " requested, but plugin has only " << outputs.size() << " output(s)" << endl;
00354             goto done;
00355         }        
00356     }
00357 
00358     od = outputs[outputNo];
00359     cerr << "Output is: \"" << od.identifier << "\"" << endl;
00360 
00361     if (!plugin->initialise(channels, stepSize, blockSize)) {
00362         cerr << "ERROR: Plugin initialise (channels = " << channels
00363              << ", stepSize = " << stepSize << ", blockSize = "
00364              << blockSize << ") failed." << endl;
00365         goto done;
00366     }
00367 
00368     for (size_t i = 0; i < sfinfo.frames; i += stepSize) {
00369 
00370         int count;
00371 
00372         if (sf_seek(sndfile, i, SEEK_SET) < 0) {
00373             cerr << "ERROR: sf_seek failed: " << sf_strerror(sndfile) << endl;
00374             break;
00375         }
00376         
00377         if ((count = sf_readf_float(sndfile, filebuf, blockSize)) < 0) {
00378             cerr << "ERROR: sf_readf_float failed: " << sf_strerror(sndfile) << endl;
00379             break;
00380         }
00381 
00382         for (int c = 0; c < channels; ++c) {
00383             int j = 0;
00384             while (j < count) {
00385                 plugbuf[c][j] = filebuf[j * sfinfo.channels + c];
00386                 ++j;
00387             }
00388             while (j < blockSize) {
00389                 plugbuf[c][j] = 0.0f;
00390                 ++j;
00391             }
00392         }
00393 
00394         printFeatures
00395             (i, sfinfo.samplerate, outputNo, plugin->process
00396              (plugbuf, RealTime::frame2RealTime(i, sfinfo.samplerate)),
00397              out, useFrames);
00398 
00399         int pp = progress;
00400         progress = lrintf((float(i) / sfinfo.frames) * 100.f);
00401         if (progress != pp && out) {
00402             cerr << "\r" << progress << "%";
00403         }
00404     }
00405     if (out) cerr << "\rDone" << endl;
00406 
00407     printFeatures(sfinfo.frames, sfinfo.samplerate, outputNo,
00408                   plugin->getRemainingFeatures(), out, useFrames);
00409 
00410     returnValue = 0;
00411 
00412 done:
00413     delete plugin;
00414     if (out) {
00415         out->close();
00416         delete out;
00417     }
00418     sf_close(sndfile);
00419     return returnValue;
00420 }
00421 
00422 void
00423 printFeatures(int frame, int sr, int output,
00424               Plugin::FeatureSet features, ofstream *out, bool useFrames)
00425 {
00426     for (unsigned int i = 0; i < features[output].size(); ++i) {
00427 
00428         if (useFrames) {
00429 
00430             int displayFrame = frame;
00431 
00432             if (features[output][i].hasTimestamp) {
00433                 displayFrame = RealTime::realTime2Frame
00434                     (features[output][i].timestamp, sr);
00435             }
00436 
00437             (out ? *out : cout) << displayFrame << ":";
00438 
00439         } else {
00440 
00441             RealTime rt = RealTime::frame2RealTime(frame, sr);
00442 
00443             if (features[output][i].hasTimestamp) {
00444                 rt = features[output][i].timestamp;
00445             }
00446 
00447             (out ? *out : cout) << rt.toString() << ":";
00448         }
00449 
00450         for (unsigned int j = 0; j < features[output][i].values.size(); ++j) {
00451             (out ? *out : cout) << " " << features[output][i].values[j];
00452         }
00453 
00454         (out ? *out : cout) << endl;
00455     }
00456 }
00457 
00458 void
00459 printPluginPath(bool verbose)
00460 {
00461     if (verbose) {
00462         cout << "\nVamp plugin search path: ";
00463     }
00464 
00465     vector<string> path = PluginHostAdapter::getPluginPath();
00466     for (size_t i = 0; i < path.size(); ++i) {
00467         if (verbose) {
00468             cout << "[" << path[i] << "]";
00469         } else {
00470             cout << path[i] << endl;
00471         }
00472     }
00473 
00474     if (verbose) cout << endl;
00475 }
00476 
00477 void
00478 enumeratePlugins(Verbosity verbosity)
00479 {
00480     PluginLoader *loader = PluginLoader::getInstance();
00481 
00482     if (verbosity == PluginInformation) {
00483         cout << "\nVamp plugin libraries found in search path:" << endl;
00484     }
00485 
00486     vector<PluginLoader::PluginKey> plugins = loader->listPlugins();
00487     typedef multimap<string, PluginLoader::PluginKey>
00488         LibraryMap;
00489     LibraryMap libraryMap;
00490 
00491     for (size_t i = 0; i < plugins.size(); ++i) {
00492         string path = loader->getLibraryPathForPlugin(plugins[i]);
00493         libraryMap.insert(LibraryMap::value_type(path, plugins[i]));
00494     }
00495 
00496     string prevPath = "";
00497     int index = 0;
00498 
00499     for (LibraryMap::iterator i = libraryMap.begin();
00500          i != libraryMap.end(); ++i) {
00501         
00502         string path = i->first;
00503         PluginLoader::PluginKey key = i->second;
00504 
00505         if (path != prevPath) {
00506             prevPath = path;
00507             index = 0;
00508             if (verbosity == PluginInformation) {
00509                 cout << "\n  " << path << ":" << endl;
00510             }
00511         }
00512 
00513         Plugin *plugin = loader->loadPlugin(key, 48000);
00514         if (plugin) {
00515 
00516             char c = char('A' + index);
00517             if (c > 'Z') c = char('a' + (index - 26));
00518 
00519             if (verbosity == PluginInformation) {
00520 
00521                 cout << "    [" << c << "] [v"
00522                      << plugin->getVampApiVersion() << "] "
00523                      << plugin->getName() << ", \""
00524                      << plugin->getIdentifier() << "\"" << " ["
00525                      << plugin->getMaker() << "]" << endl;
00526 
00527                 PluginLoader::PluginCategoryHierarchy category =
00528                     loader->getPluginCategory(key);
00529 
00530                 if (!category.empty()) {
00531                     cout << "       ";
00532                     for (size_t ci = 0; ci < category.size(); ++ci) {
00533                         cout << " > " << category[ci];
00534                     }
00535                     cout << endl;
00536                 }
00537 
00538                 if (plugin->getDescription() != "") {
00539                     cout << "        - " << plugin->getDescription() << endl;
00540                 }
00541 
00542             } else if (verbosity == PluginIds) {
00543                 cout << "vamp:" << key << endl;
00544             }
00545             
00546             Plugin::OutputList outputs =
00547                 plugin->getOutputDescriptors();
00548 
00549             if (outputs.size() > 1 || verbosity == PluginOutputIds) {
00550                 for (size_t j = 0; j < outputs.size(); ++j) {
00551                     if (verbosity == PluginInformation) {
00552                         cout << "         (" << j << ") "
00553                              << outputs[j].name << ", \""
00554                              << outputs[j].identifier << "\"" << endl;
00555                         if (outputs[j].description != "") {
00556                             cout << "             - " 
00557                                  << outputs[j].description << endl;
00558                         }
00559                     } else if (verbosity == PluginOutputIds) {
00560                         cout << "vamp:" << key << ":" << outputs[j].identifier << endl;
00561                     }
00562                 }
00563             }
00564 
00565             ++index;
00566 
00567             delete plugin;
00568         }
00569     }
00570 
00571     if (verbosity == PluginInformation) {
00572         cout << endl;
00573     }
00574 }
00575 
00576 void
00577 printPluginCategoryList()
00578 {
00579     PluginLoader *loader = PluginLoader::getInstance();
00580 
00581     vector<PluginLoader::PluginKey> plugins = loader->listPlugins();
00582 
00583     set<string> printedcats;
00584 
00585     for (size_t i = 0; i < plugins.size(); ++i) {
00586 
00587         PluginLoader::PluginKey key = plugins[i];
00588         
00589         PluginLoader::PluginCategoryHierarchy category =
00590             loader->getPluginCategory(key);
00591 
00592         Plugin *plugin = loader->loadPlugin(key, 48000);
00593         if (!plugin) continue;
00594 
00595         string catstr = "";
00596 
00597         if (category.empty()) catstr = '|';
00598         else {
00599             for (size_t j = 0; j < category.size(); ++j) {
00600                 catstr += category[j];
00601                 catstr += '|';
00602                 if (printedcats.find(catstr) == printedcats.end()) {
00603                     std::cout << catstr << std::endl;
00604                     printedcats.insert(catstr);
00605                 }
00606             }
00607         }
00608 
00609         std::cout << catstr << key << ":::" << plugin->getName() << ":::" << plugin->getMaker() << ":::" << plugin->getDescription() << std::endl;
00610     }
00611 }
00612 

Generated on Fri Nov 7 13:10:34 2008 for VampPluginSDK by  doxygen 1.5.6