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

Generated on Wed Jun 18 18:38:30 2008 for VampPluginSDK by  doxygen 1.5.6