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