74 #define HOST_VERSION "1.5"
84 const Plugin::OutputDescriptor &,
int,
85 const Plugin::FeatureSet &, ofstream *,
bool frames);
87 void fft(
unsigned int,
bool,
double *,
double *,
double *,
double *);
92 int runPlugin(
string myname,
string soname,
string id,
string output,
93 int outputNo,
string inputFile,
string outfilename,
bool frames);
98 << name <<
": A command-line host for Vamp audio analysis plugins.\n\n"
99 "Centre for Digital Music, Queen Mary, University of London.\n"
100 "Copyright 2006-2009 Chris Cannam and QMUL.\n"
101 "Freely redistributable; published under a BSD-style license.\n\n"
103 " " << name <<
" [-s] pluginlibrary[." <<
PLUGIN_SUFFIX <<
"]:plugin[:output] file.wav [-o out.txt]\n"
104 " " << name <<
" [-s] pluginlibrary[." <<
PLUGIN_SUFFIX <<
"]:plugin file.wav [outputno] [-o out.txt]\n\n"
105 " -- Load plugin id \"plugin\" from \"pluginlibrary\" and run it on the\n"
106 " audio data in \"file.wav\", retrieving the named \"output\", or output\n"
107 " number \"outputno\" (the first output by default) and dumping it to\n"
108 " standard output, or to \"out.txt\" if the -o option is given.\n\n"
109 " \"pluginlibrary\" should be a library name, not a file path; the\n"
110 " standard Vamp library search path will be used to locate it. If\n"
111 " a file path is supplied, the directory part(s) will be ignored.\n\n"
112 " If the -s option is given, results will be labelled with the audio\n"
113 " sample frame at which they occur. Otherwise, they will be labelled\n"
114 " with time in seconds.\n\n"
115 " " << name <<
" -l\n"
116 " " << name <<
" --list\n\n"
117 " -- List the plugin libraries and Vamp plugins in the library search path\n"
118 " in a verbose human-readable format.\n\n"
119 " " << name <<
" -L\n"
120 " " << name <<
" --list-full\n\n"
121 " -- List all data reported by all the Vamp plugins in the library search\n"
122 " path in a very verbose human-readable format.\n\n"
123 " " << name <<
" --list-ids\n\n"
124 " -- List the plugins in the search path in a terse machine-readable format,\n"
125 " in the form vamp:soname:identifier.\n\n"
126 " " << name <<
" --list-outputs\n\n"
127 " -- List the outputs for plugins in the search path in a machine-readable\n"
128 " format, in the form vamp:soname:identifier:output.\n\n"
129 " " << name <<
" --list-by-category\n\n"
130 " -- List the plugins as a plugin index by category, in a machine-readable\n"
131 " format. The format may change in future releases.\n\n"
132 " " << name <<
" -p\n\n"
133 " -- Print out the Vamp library search path.\n\n"
134 " " << name <<
" -v\n\n"
135 " -- Display version information only.\n"
140 int main(
int argc,
char **argv)
142 char *scooter = argv[0];
144 while (scooter && *scooter) {
145 if (*scooter ==
'/' || *scooter ==
'\\') name = ++scooter;
148 if (!name || !*name) name = argv[0];
150 if (argc < 2)
usage(name);
154 if (!strcmp(argv[1],
"-v")) {
156 cout <<
"Simple Vamp plugin host version: " <<
HOST_VERSION << endl
161 }
else if (!strcmp(argv[1],
"-l") || !strcmp(argv[1],
"--list")) {
167 }
else if (!strcmp(argv[1],
"-L") || !strcmp(argv[1],
"--list-full")) {
172 }
else if (!strcmp(argv[1],
"-p")) {
177 }
else if (!strcmp(argv[1],
"--list-ids")) {
182 }
else if (!strcmp(argv[1],
"--list-outputs")) {
187 }
else if (!strcmp(argv[1],
"--list-by-category")) {
195 if (argc < 3)
usage(name);
197 bool useFrames =
false;
200 if (!strcmp(argv[1],
"-s")) {
205 string soname = argv[base];
206 string wavname = argv[base+1];
212 if (argc >= base+3) {
216 if (isdigit(*argv[idx])) {
217 outputNo = atoi(argv[idx++]);
220 if (argc == idx + 2) {
221 if (!strcmp(argv[idx],
"-o")) {
222 outfilename = argv[idx+1];
224 }
else if (argc != idx) {
229 cerr << endl << name <<
": Running..." << endl;
231 cerr <<
"Reading file: \"" << wavname <<
"\", writing to ";
232 if (outfilename ==
"") {
233 cerr <<
"standard output" << endl;
235 cerr <<
"\"" << outfilename <<
"\"" << endl;
238 string::size_type sep = soname.find(
':');
240 if (sep != string::npos) {
241 plugid = soname.substr(sep + 1);
242 soname = soname.substr(0, sep);
244 sep = plugid.find(
':');
245 if (sep != string::npos) {
246 output = plugid.substr(sep + 1);
247 plugid = plugid.substr(0, sep);
255 if (output !=
"" && outputNo != -1) {
259 if (output ==
"" && outputNo == -1) {
263 return runPlugin(name, soname, plugid, output, outputNo,
264 wavname, outfilename, useFrames);
269 string output,
int outputNo,
string wavname,
270 string outfilename,
bool useFrames)
278 memset(&sfinfo, 0,
sizeof(SF_INFO));
280 sndfile = sf_open(wavname.c_str(), SFM_READ, &sfinfo);
282 cerr << myname <<
": ERROR: Failed to open input file \""
283 << wavname <<
"\": " << sf_strerror(sndfile) << endl;
288 if (outfilename !=
"") {
289 out =
new ofstream(outfilename.c_str(), ios::out);
291 cerr << myname <<
": ERROR: Failed to open output file \""
292 << outfilename <<
"\" for writing" << endl;
299 (key, sfinfo.samplerate, PluginLoader::ADAPT_ALL_SAFE);
301 cerr << myname <<
": ERROR: Failed to load plugin \"" <<
id
302 <<
"\" from library \"" << soname <<
"\"" << endl;
311 cerr <<
"Running plugin: \"" << plugin->
getIdentifier() <<
"\"..." << endl;
327 if (blockSize == 0) {
332 stepSize = blockSize/2;
334 stepSize = blockSize;
336 }
else if (stepSize > blockSize) {
337 cerr <<
"WARNING: stepSize " << stepSize <<
" > blockSize " << blockSize <<
", resetting blockSize to ";
339 blockSize = stepSize * 2;
341 blockSize = stepSize;
343 cerr << blockSize << endl;
345 int overlapSize = blockSize - stepSize;
346 sf_count_t currentStep = 0;
347 int finalStepsRemaining = max(1, (blockSize / stepSize) - 1);
349 int channels = sfinfo.channels;
351 float *filebuf =
new float[blockSize * channels];
352 float **plugbuf =
new float*[channels];
353 for (
int c = 0; c < channels; ++c) plugbuf[c] =
new float[blockSize + 2];
355 cerr <<
"Using block size = " << blockSize <<
", step size = "
364 cerr <<
"Plugin accepts " << minch <<
" -> " << maxch <<
" channel(s)" << endl;
365 cerr <<
"Sound file has " << channels <<
" (will mix/augment if necessary)" << endl;
368 Plugin::OutputDescriptor od;
369 Plugin::FeatureSet features;
376 RealTime adjustment = RealTime::zeroTime;
378 if (outputs.empty()) {
379 cerr <<
"ERROR: Plugin has no outputs!" << endl;
385 for (
size_t oi = 0; oi < outputs.size(); ++oi) {
386 if (outputs[oi].identifier == output) {
393 cerr <<
"ERROR: Non-existent output \"" << output <<
"\" requested" << endl;
399 if (
int(outputs.size()) <= outputNo) {
400 cerr <<
"ERROR: Output " << outputNo <<
" requested, but plugin has only " << outputs.size() <<
" output(s)" << endl;
405 od = outputs[outputNo];
406 cerr <<
"Output is: \"" << od.identifier <<
"\"" << endl;
408 if (!plugin->
initialise(channels, stepSize, blockSize)) {
409 cerr <<
"ERROR: Plugin initialise (channels = " << channels
410 <<
", stepSize = " << stepSize <<
", blockSize = "
411 << blockSize <<
") failed." << endl;
429 if ((blockSize==stepSize) || (currentStep==0)) {
431 if ((count = sf_readf_float(sndfile, filebuf, blockSize)) < 0) {
432 cerr <<
"ERROR: sf_readf_float failed: " << sf_strerror(sndfile) << endl;
435 if (count != blockSize) --finalStepsRemaining;
438 memmove(filebuf, filebuf + (stepSize * channels), overlapSize * channels *
sizeof(
float));
439 if ((count = sf_readf_float(sndfile, filebuf + (overlapSize * channels), stepSize)) < 0) {
440 cerr <<
"ERROR: sf_readf_float failed: " << sf_strerror(sndfile) << endl;
443 if (count != stepSize) --finalStepsRemaining;
444 count += overlapSize;
447 for (
int c = 0; c < channels; ++c) {
450 plugbuf[c][j] = filebuf[j * sfinfo.channels + c];
453 while (j < blockSize) {
454 plugbuf[c][j] = 0.0f;
459 rt = RealTime::frame2RealTime(currentStep * stepSize, sfinfo.samplerate);
461 features = plugin->
process(plugbuf, rt);
464 (RealTime::realTime2Frame(rt + adjustment, sfinfo.samplerate),
465 sfinfo.samplerate, od, outputNo, features, out, useFrames);
467 if (sfinfo.frames > 0){
469 progress = (int)((
float(currentStep * stepSize) / sfinfo.frames) * 100.f + 0.5f);
470 if (progress != pp && out) {
471 cerr <<
"\r" << progress <<
"%";
477 }
while (finalStepsRemaining > 0);
479 if (out) cerr <<
"\rDone" << endl;
481 rt = RealTime::frame2RealTime(currentStep * stepSize, sfinfo.samplerate);
485 printFeatures(RealTime::realTime2Frame(rt + adjustment, sfinfo.samplerate),
486 sfinfo.samplerate, od, outputNo, features, out, useFrames);
503 return time.
sec + double(time.
nsec + 1) / 1000000000.0;
508 const Plugin::OutputDescriptor &output,
int outputNo,
509 const Plugin::FeatureSet &features, ofstream *out,
bool useFrames)
511 static int featureCount = -1;
513 if (features.find(outputNo) == features.end())
return;
515 for (
size_t i = 0; i < features.at(outputNo).size(); ++i) {
517 const Plugin::Feature &f = features.at(outputNo).at(i);
522 if (output.sampleType == Plugin::OutputDescriptor::VariableSampleRate) {
525 }
else if (output.sampleType == Plugin::OutputDescriptor::FixedSampleRate) {
526 int n = featureCount + 1;
527 if (f.hasTimestamp) {
528 n = int(round(
toSeconds(f.timestamp) * output.sampleRate));
530 rt = RealTime::fromSeconds(
double(n) / output.sampleRate);
537 int displayFrame = frame;
540 displayFrame = RealTime::realTime2Frame(rt, sr);
543 (out ? *out : cout) << displayFrame;
546 displayFrame = RealTime::realTime2Frame(f.duration, sr);
547 (out ? *out : cout) <<
"," << displayFrame;
550 (out ? *out : cout) <<
":";
555 rt = RealTime::frame2RealTime(frame, sr);
558 (out ? *out : cout) << rt.
toString();
562 (out ? *out : cout) <<
"," << rt.
toString();
565 (out ? *out : cout) <<
":";
568 for (
unsigned int j = 0; j < f.values.size(); ++j) {
569 (out ? *out : cout) <<
" " << f.values[j];
571 (out ? *out : cout) <<
" " << f.label;
573 (out ? *out : cout) << endl;
581 cout <<
"\nVamp plugin search path: ";
584 vector<string> path = PluginHostAdapter::getPluginPath();
585 for (
size_t i = 0; i < path.size(); ++i) {
587 cout <<
"[" << path[i] <<
"]";
589 cout << path[i] << endl;
593 if (verbose) cout << endl;
600 string out =
'\n' + text +
'\n';
601 for (
size_t i = 0; i < text.length(); ++i) {
602 out += (level == 1 ?
'=' : level == 2 ?
'-' :
'~');
614 cout <<
"\nVamp plugin libraries found in search path:" << endl;
617 vector<PluginLoader::PluginKey> plugins = loader->
listPlugins();
618 typedef multimap<string, PluginLoader::PluginKey>
620 LibraryMap libraryMap;
622 for (
size_t i = 0; i < plugins.size(); ++i) {
624 libraryMap.insert(LibraryMap::value_type(path, plugins[i]));
627 string prevPath =
"";
630 for (LibraryMap::iterator i = libraryMap.begin();
631 i != libraryMap.end(); ++i) {
633 string path = i->first;
634 PluginLoader::PluginKey key = i->second;
636 if (path != prevPath) {
640 cout <<
"\n " << path <<
":" << endl;
642 string::size_type ki = i->second.find(
':');
643 string text =
"Library \"" + i->second.substr(0, ki) +
"\"";
644 cout <<
"\n" <<
header(text, 1);
651 char c = char(
'A' + index);
652 if (c >
'Z') c = char(
'a' + (index - 26));
654 PluginLoader::PluginCategoryHierarchy category =
657 if (!category.empty()) {
658 for (
size_t ci = 0; ci < category.size(); ++ci) {
659 if (ci > 0) catstr +=
" > ";
660 catstr += category[ci];
666 cout <<
" [" << c <<
"] [v"
670 << plugin->
getMaker() <<
"]" << endl;
673 cout <<
" > " << catstr << endl;
683 cout <<
" - Identifier: "
685 cout <<
" - Plugin Version: "
687 cout <<
" - Vamp API Version: "
689 cout <<
" - Maker: \""
690 << plugin->
getMaker() <<
"\"" << endl;
691 cout <<
" - Copyright: \""
693 cout <<
" - Description: \""
695 cout <<
" - Input Domain: "
697 "Time Domain" :
"Frequency Domain") << endl;
698 cout <<
" - Default Step Size: "
700 cout <<
" - Default Block Size: "
702 cout <<
" - Minimum Channels: "
704 cout <<
" - Maximum Channels: "
708 cout <<
"vamp:" << key << endl;
711 Plugin::OutputList outputs =
717 for (
size_t j = 0; j < params.size(); ++j) {
718 Plugin::ParameterDescriptor &pd(params[j]);
719 cout <<
"\nParameter " << j+1 <<
": \"" << pd.name <<
"\"" << endl;
720 cout <<
" - Identifier: " << pd.identifier << endl;
721 cout <<
" - Description: \"" << pd.description <<
"\"" << endl;
723 cout <<
" - Unit: " << pd.unit << endl;
725 cout <<
" - Range: ";
726 cout << pd.minValue <<
" -> " << pd.maxValue << endl;
727 cout <<
" - Default: ";
728 cout << pd.defaultValue << endl;
729 if (pd.isQuantized) {
730 cout <<
" - Quantize Step: "
731 << pd.quantizeStep << endl;
733 if (!pd.valueNames.empty()) {
734 cout <<
" - Value Names: ";
735 for (
size_t k = 0; k < pd.valueNames.size(); ++k) {
736 if (k > 0) cout <<
", ";
737 cout <<
"\"" << pd.valueNames[k] <<
"\"";
743 if (outputs.empty()) {
744 cout <<
"\n** Note: This plugin reports no outputs!" << endl;
746 for (
size_t j = 0; j < outputs.size(); ++j) {
747 Plugin::OutputDescriptor &od(outputs[j]);
748 cout <<
"\nOutput " << j+1 <<
": \"" << od.name <<
"\"" << endl;
749 cout <<
" - Identifier: " << od.identifier << endl;
750 cout <<
" - Description: \"" << od.description <<
"\"" << endl;
752 cout <<
" - Unit: " << od.unit << endl;
754 if (od.hasFixedBinCount) {
755 cout <<
" - Default Bin Count: " << od.binCount << endl;
757 if (!od.binNames.empty()) {
759 for (
size_t k = 0; k < od.binNames.size(); ++k) {
760 if (od.binNames[k] !=
"") {
765 cout <<
" - Bin Names: ";
766 for (
size_t k = 0; k < od.binNames.size(); ++k) {
767 if (k > 0) cout <<
", ";
768 cout <<
"\"" << od.binNames[k] <<
"\"";
773 if (od.hasKnownExtents) {
774 cout <<
" - Default Extents: ";
775 cout << od.minValue <<
" -> " << od.maxValue << endl;
777 if (od.isQuantized) {
778 cout <<
" - Quantize Step: "
779 << od.quantizeStep << endl;
781 cout <<
" - Sample Type: "
783 Plugin::OutputDescriptor::OneSamplePerStep ?
784 "One Sample Per Step" :
786 Plugin::OutputDescriptor::FixedSampleRate ?
787 "Fixed Sample Rate" :
788 "Variable Sample Rate") << endl;
790 Plugin::OutputDescriptor::OneSamplePerStep) {
791 cout <<
" - Default Rate: "
792 << od.sampleRate << endl;
794 cout <<
" - Has Duration: "
795 << (od.hasDuration ?
"Yes" :
"No") << endl;
800 for (
size_t j = 0; j < outputs.size(); ++j) {
802 cout <<
" (" << j <<
") "
803 << outputs[j].name <<
", \""
804 << outputs[j].identifier <<
"\"" << endl;
805 if (outputs[j].description !=
"") {
807 << outputs[j].description << endl;
810 cout <<
"vamp:" << key <<
":" << outputs[j].identifier << endl;
832 vector<PluginLoader::PluginKey> plugins = loader->
listPlugins();
834 set<string> printedcats;
836 for (
size_t i = 0; i < plugins.size(); ++i) {
838 PluginLoader::PluginKey key = plugins[i];
840 PluginLoader::PluginCategoryHierarchy category =
844 if (!plugin)
continue;
848 if (category.empty()) catstr =
'|';
850 for (
size_t j = 0; j < category.size(); ++j) {
851 catstr += category[j];
853 if (printedcats.find(catstr) == printedcats.end()) {
854 std::cout << catstr << std::endl;
855 printedcats.insert(catstr);