comparison host/vamp-simple-host.cpp @ 59:fa79c4ec847d host-factory-stuff

* Put hostext stuff in the HostExt sub-namespace * Tidy up system-specific stuff in PluginLoader * Make PluginLoader return a deletion-notifying wrapper which permits the library to be unloaded when no longer in use * Add PluginChannelAdapter * Make vamp-simple-host use PluginChannelAdapter, and use the PluginLoader for plugin-running task. Also some other enhancements to host
author cannam
date Thu, 24 May 2007 15:17:07 +0000
parents 09a1aac6c362
children 97c5ac99d725
comparison
equal deleted inserted replaced
58:0284955e31e5 59:fa79c4ec847d
33 shall not be used in advertising or otherwise to promote the sale, 33 shall not be used in advertising or otherwise to promote the sale,
34 use or other dealings in this Software without prior written 34 use or other dealings in this Software without prior written
35 authorization. 35 authorization.
36 */ 36 */
37 37
38 #include "PluginHostAdapter.h" 38 #include "vamp-sdk/PluginHostAdapter.h"
39 #include "PluginInputDomainAdapter.h" 39 #include "vamp-sdk/hostext/PluginChannelAdapter.h"
40 #include "PluginLoader.h" 40 #include "vamp-sdk/hostext/PluginInputDomainAdapter.h"
41 #include "vamp.h" 41 #include "vamp-sdk/hostext/PluginLoader.h"
42 #include "vamp/vamp.h"
42 43
43 #include <iostream> 44 #include <iostream>
44 #include <sndfile.h> 45 #include <sndfile.h>
45 46
46 #include "system.h" 47 #include "system.h"
51 using std::cerr; 52 using std::cerr;
52 using std::endl; 53 using std::endl;
53 using std::string; 54 using std::string;
54 using std::vector; 55 using std::vector;
55 56
56 #define HOST_VERSION "1.0" 57 using Vamp::HostExt::PluginLoader;
58
59 #define HOST_VERSION "1.1"
57 60
58 void printFeatures(int, int, int, Vamp::Plugin::FeatureSet); 61 void printFeatures(int, int, int, Vamp::Plugin::FeatureSet);
59 void transformInput(float *, size_t); 62 void transformInput(float *, size_t);
60 void fft(unsigned int, bool, double *, double *, double *, double *); 63 void fft(unsigned int, bool, double *, double *, double *, double *);
61 void printPluginPath(); 64 void printPluginPath(bool verbose);
62 void enumeratePlugins(); 65 void enumeratePlugins();
63 66 void listPluginsInLibrary(string soname);
64 /* 67 int runPlugin(string myname, string soname, string id, string output,
65 A very simple Vamp plugin host. Given the name of a plugin 68 int outputNo, string inputFile);
66 library and the name of a sound file on the command line, it loads 69
67 the first plugin in the library and runs it on the sound file, 70 void usage(const char *name)
68 dumping the plugin's first output to stdout. 71 {
69 */ 72 cerr << "\n"
73 << name << ": A simple Vamp plugin host.\n\n"
74 "Centre for Digital Music, Queen Mary, University of London.\n"
75 "Copyright 2006-2007 Chris Cannam and QMUL.\n"
76 "Freely redistributable; published under a BSD-style license.\n\n"
77 "Usage:\n\n"
78 " " << name << " pluginlibrary[." << PLUGIN_SUFFIX << "]:plugin[:output] file.wav\n"
79 " " << name << " pluginlibrary[." << PLUGIN_SUFFIX << "]:plugin file.wav [outputno]\n\n"
80 " -- Load plugin id \"plugin\" from \"pluginlibrary\" and run it on the\n"
81 " audio data in \"file.wav\"; retrieve the named \"output\", or output\n"
82 " number \"outputno\" (the first output by default) and dump it to\n"
83 " standard output.\n\n"
84 " " << name << " -l\n\n"
85 " -- List the plugin libraries and Vamp plugins in the plugin search path.\n\n"
86 " " << name << " -p\n\n"
87 " -- Print out the Vamp plugin search path.\n\n"
88 " " << name << " -v\n\n"
89 " -- Display version information only.\n\n"
90 << endl;
91 exit(2);
92 }
70 93
71 int main(int argc, char **argv) 94 int main(int argc, char **argv)
72 { 95 {
96 char *scooter = argv[0];
97 char *name = 0;
98 while (scooter && *scooter) {
99 if (*scooter == '/' || *scooter == '\\') name = ++scooter;
100 else ++scooter;
101 }
102 if (!name || !*name) name = argv[0];
103
73 if (argc < 2 || argc > 4 || 104 if (argc < 2 || argc > 4 ||
74 (argc == 2 && 105 (argc == 2 &&
75 (!strcmp(argv[1], "-?") || 106 (!strcmp(argv[1], "-?") ||
76 !strcmp(argv[1], "-h") || 107 !strcmp(argv[1], "-h") ||
77 !strcmp(argv[1], "--help")))) { 108 !strcmp(argv[1], "--help")))) {
78 109
79 char *scooter = argv[0]; 110 usage(name); // does not return
80 char *name = 0; 111 }
81 while (scooter && *scooter) { 112
82 if (*scooter == '/' || *scooter == '\\') name = ++scooter;
83 else ++scooter;
84 }
85 if (!name || !*name) name = argv[0];
86 cerr << "\n"
87 << name << ": A simple Vamp plugin host.\n\n"
88 "Centre for Digital Music, Queen Mary, University of London.\n"
89 "Copyright 2006 Chris Cannam and QMUL.\n"
90 "Freely redistributable; published under a BSD-style license.\n\n"
91 "Usage:\n\n"
92 " " << name << " pluginlibrary." << PLUGIN_SUFFIX << "\n\n"
93 " -- Load \"pluginlibrary\" and list the Vamp plugins it contains.\n\n"
94 " " << name << " pluginlibrary." << PLUGIN_SUFFIX << ":plugin file.wav [outputno]\n\n"
95 " -- Load plugin id \"plugin\" from \"pluginlibrary\" and run it on the\n"
96 " audio data in \"file.wav\", dumping the output from \"outputno\"\n"
97 " (default 0) to standard output.\n\n"
98 #ifdef HAVE_OPENDIR
99 " " << name << " -l\n\n"
100 " -- List the plugin libraries and Vamp plugins in the plugin search path.\n\n"
101 #endif
102 " " << name << " -p\n\n"
103 " -- Print out the Vamp plugin search path.\n\n"
104 " " << name << " -v\n\n"
105 " -- Display version information only.\n\n"
106 "Note that this host does not use the plugin search path when loadinga plugin.\nIf a plugin library is specified, it should be with a full file path.\n"
107 << endl;
108 return 2;
109 }
110
111 if (argc == 2 && !strcmp(argv[1], "-v")) { 113 if (argc == 2 && !strcmp(argv[1], "-v")) {
112 cout << "Simple Vamp plugin host version: " << HOST_VERSION << endl 114 cout << "Simple Vamp plugin host version: " << HOST_VERSION << endl
113 << "Vamp API version: " << VAMP_API_VERSION << endl 115 << "Vamp API version: " << VAMP_API_VERSION << endl
114 << "Vamp SDK version: " << VAMP_SDK_VERSION << endl; 116 << "Vamp SDK version: " << VAMP_SDK_VERSION << endl;
115 return 0; 117 return 0;
116 } 118 }
117 119
118 if (argc == 2 && !strcmp(argv[1], "-l")) { 120 if (argc == 2 && !strcmp(argv[1], "-l")) {
121 printPluginPath(true);
119 enumeratePlugins(); 122 enumeratePlugins();
120 return 0; 123 return 0;
121 } 124 }
122 if (argc == 2 && !strcmp(argv[1], "-p")) { 125 if (argc == 2 && !strcmp(argv[1], "-p")) {
123 printPluginPath(); 126 printPluginPath(false);
124 return 0; 127 return 0;
125 } 128 }
126 129
127 cerr << endl << argv[0] << ": Running..." << endl; 130 cerr << endl << name << ": Running..." << endl;
128 131
129 string soname = argv[1]; 132 string soname = argv[1];
130 string plugid = ""; 133 string plugid = "";
134 string output = "";
135 int outputNo = -1;
131 string wavname; 136 string wavname;
132 if (argc >= 3) wavname = argv[2]; 137 if (argc >= 3) wavname = argv[2];
133 138
134 int sep = soname.find(":"); 139 string::size_type sep = soname.find(':');
135 if (sep >= 0 && sep < int(soname.length())) { 140
141 if (sep != string::npos) {
136 plugid = soname.substr(sep + 1); 142 plugid = soname.substr(sep + 1);
137 soname = soname.substr(0, sep); 143 soname = soname.substr(0, sep);
138 } 144
139 145 sep = plugid.find(':');
140 void *libraryHandle = DLOPEN(soname, RTLD_LAZY); 146 if (sep != string::npos) {
141 147 output = plugid.substr(sep + 1);
142 if (!libraryHandle) { 148 plugid = plugid.substr(0, sep);
143 cerr << argv[0] << ": Failed to open plugin library " 149 }
144 << soname << ": " << DLERROR() << endl; 150 }
145 return 1; 151
146 } 152 if (plugid == "") {
147 153 usage(name);
148 cerr << argv[0] << ": Opened plugin library " << soname << endl; 154 }
149 155
150 VampGetPluginDescriptorFunction fn = (VampGetPluginDescriptorFunction) 156 if (argc == 4) outputNo = atoi(argv[3]);
151 DLSYM(libraryHandle, "vampGetPluginDescriptor"); 157
152 158 if (output != "" && outputNo != -1) {
153 if (!fn) { 159 usage(name);
154 cerr << argv[0] << ": No Vamp descriptor function in library " 160 }
155 << soname << endl; 161
156 DLCLOSE(libraryHandle); 162 return runPlugin(name, soname, plugid, output, outputNo, wavname);
157 return 1; 163 }
158 } 164
159 165
160 cerr << argv[0] << ": Found plugin descriptor function" << endl; 166 int runPlugin(string myname, string soname, string id,
161 167 string output, int outputNo, string wavname)
162 int index = 0; 168 {
163 int plugnumber = -1; 169 PluginLoader *loader = PluginLoader::getInstance();
164 const VampPluginDescriptor *descriptor = 0; 170
165 171 PluginLoader::PluginKey key = loader->composePluginKey(soname, id);
166 while ((descriptor = fn(VAMP_API_VERSION, index))) {
167
168 Vamp::PluginHostAdapter plugin(descriptor, 48000);
169 cerr << argv[0] << ": Plugin " << (index+1)
170 << " is \"" << plugin.getIdentifier() << "\"" << endl;
171
172 if (plugin.getIdentifier() == plugid) plugnumber = index;
173
174 ++index;
175 }
176
177 cerr << argv[0] << ": Done\n" << endl;
178
179 if (wavname == "") {
180 DLCLOSE(libraryHandle);
181 return 0;
182 }
183
184 if (plugnumber < 0) {
185 if (plugid != "") {
186 cerr << "ERROR: No such plugin as " << plugid << " in library"
187 << endl;
188 DLCLOSE(libraryHandle);
189 return 0;
190 } else {
191 plugnumber = 0;
192 }
193 }
194
195 descriptor = fn(VAMP_API_VERSION, plugnumber);
196 if (!descriptor) {
197 DLCLOSE(libraryHandle);
198 return 0;
199 }
200 172
201 SNDFILE *sndfile; 173 SNDFILE *sndfile;
202 SF_INFO sfinfo; 174 SF_INFO sfinfo;
203 memset(&sfinfo, 0, sizeof(SF_INFO)); 175 memset(&sfinfo, 0, sizeof(SF_INFO));
204 176
205 sndfile = sf_open(wavname.c_str(), SFM_READ, &sfinfo); 177 sndfile = sf_open(wavname.c_str(), SFM_READ, &sfinfo);
206 if (!sndfile) { 178 if (!sndfile) {
207 cerr << "ERROR: Failed to open input file \"" << wavname << "\": " 179 cerr << myname << ": ERROR: Failed to open input file \""
208 << sf_strerror(sndfile) << endl; 180 << wavname << "\": " << sf_strerror(sndfile) << endl;
209 DLCLOSE(libraryHandle);
210 return 1; 181 return 1;
211 } 182 }
212 183
184 Vamp::Plugin *basePlugin = loader->loadPlugin(key, sfinfo.samplerate);
185 if (!basePlugin) {
186 cerr << myname << ": ERROR: Failed to load plugin \"" << id
187 << "\" from library \"" << soname << "\"" << endl;
188 sf_close(sndfile);
189 return 1;
190 }
191
213 Vamp::Plugin *plugin = 192 Vamp::Plugin *plugin =
214 new Vamp::PluginInputDomainAdapter 193 new Vamp::HostExt::PluginChannelAdapter
215 (new Vamp::PluginHostAdapter(descriptor, sfinfo.samplerate)); 194 (new Vamp::HostExt::PluginInputDomainAdapter(basePlugin));
216 195
217 cerr << "Running " << plugin->getIdentifier() << "..." << endl; 196 cerr << "Running plugin: \"" << plugin->getIdentifier() << "\"..." << endl;
218 197
219 int blockSize = plugin->getPreferredBlockSize(); 198 int blockSize = plugin->getPreferredBlockSize();
220 int stepSize = plugin->getPreferredStepSize(); 199 int stepSize = plugin->getPreferredStepSize();
221
222 cerr << "Preferred block size = " << blockSize << ", step size = "
223 << stepSize << endl;
224
225 if (blockSize == 0) blockSize = 1024;
226
227 bool rightBlockSize = true;
228
229 if (plugin->getInputDomain() == Vamp::Plugin::FrequencyDomain) {
230
231 int p = 1, b = blockSize;
232 while (b) {
233 p <<= 1;
234 b >>= 1;
235 }
236 if (p != blockSize * 2) {
237 cerr << "WARNING: Plugin requested non-power-of-two block size of "
238 << blockSize << ",\nwhich is not supported by this host. ";
239 blockSize = p;
240 cerr << "Rounding up to " << blockSize << "." << endl;
241 rightBlockSize = false;
242 }
243 if (stepSize == 0) stepSize = blockSize / 2;
244
245 } else {
246
247 if (stepSize == 0) stepSize = blockSize;
248 }
249 200
250 int channels = sfinfo.channels; 201 int channels = sfinfo.channels;
251 202
252 float *filebuf = new float[blockSize * channels]; 203 float *filebuf = new float[blockSize * channels];
253 float **plugbuf = new float*[channels]; 204 float **plugbuf = new float*[channels];
257 << stepSize << endl; 208 << stepSize << endl;
258 209
259 int minch = plugin->getMinChannelCount(); 210 int minch = plugin->getMinChannelCount();
260 int maxch = plugin->getMaxChannelCount(); 211 int maxch = plugin->getMaxChannelCount();
261 cerr << "Plugin accepts " << minch << " -> " << maxch << " channel(s)" << endl; 212 cerr << "Plugin accepts " << minch << " -> " << maxch << " channel(s)" << endl;
213 cerr << "Sound file has " << channels << " (will mix/augment if necessary)" << endl;
262 214
263 Vamp::Plugin::OutputList outputs = plugin->getOutputDescriptors(); 215 Vamp::Plugin::OutputList outputs = plugin->getOutputDescriptors();
264 Vamp::Plugin::OutputDescriptor od; 216 Vamp::Plugin::OutputDescriptor od;
265 217
266 int returnValue = 1; 218 int returnValue = 1;
267 219
268 int output = 0; 220 if (outputs.empty()) {
269 if (argc == 4) output = atoi(argv[3]); 221 cerr << "ERROR: Plugin has no outputs!" << endl;
270 222 goto done;
271 bool mix = false; 223 }
272 224
273 if (minch > channels || maxch < channels) { 225 if (outputNo < 0) {
274 if (minch == 1) { 226
275 cerr << "WARNING: Sound file has " << channels << " channels, mixing down to 1" << endl; 227 for (size_t oi = 0; oi < outputs.size(); ++oi) {
276 mix = true; 228 if (outputs[oi].identifier == output) {
277 channels = 1; 229 outputNo = oi;
278 } else { 230 break;
279 cerr << "ERROR: Sound file has " << channels << " channels, out of range for plugin" << endl; 231 }
232 }
233
234 if (outputNo < 0) {
235 cerr << "ERROR: Non-existent output \"" << output << "\" requested" << endl;
280 goto done; 236 goto done;
281 } 237 }
282 } 238
283 239 } else {
284 if (outputs.empty()) { 240
285 cerr << "Plugin has no outputs!" << endl; 241 if (int(outputs.size()) <= outputNo) {
286 goto done; 242 cerr << "ERROR: Output " << outputNo << " requested, but plugin has only " << outputs.size() << " output(s)" << endl;
287 } 243 goto done;
288 244 }
289 if (int(outputs.size()) <= output) { 245 }
290 cerr << "Output " << output << " requested, but plugin has only " << outputs.size() << " output(s)" << endl; 246
291 goto done; 247 od = outputs[outputNo];
292 } 248 cerr << "Output is: \"" << od.identifier << "\"" << endl;
293
294 od = outputs[output];
295 cerr << "Output is " << od.identifier << endl;
296 249
297 if (!plugin->initialise(channels, stepSize, blockSize)) { 250 if (!plugin->initialise(channels, stepSize, blockSize)) {
298 cerr << "ERROR: Plugin initialise (channels = " << channels 251 cerr << "ERROR: Plugin initialise (channels = " << channels
299 << ", stepSize = " << stepSize << ", blockSize = " 252 << ", stepSize = " << stepSize << ", blockSize = "
300 << blockSize << ") failed." << endl; 253 << blockSize << ") failed." << endl;
301 if (!rightBlockSize) {
302 cerr << "(Probably because I couldn't provide the plugin's preferred block size.)" << endl;
303 }
304 goto done; 254 goto done;
305 } 255 }
306 256
307 for (size_t i = 0; i < sfinfo.frames; i += stepSize) { 257 for (size_t i = 0; i < sfinfo.frames; i += stepSize) {
308 258
317 cerr << "ERROR: sf_readf_float failed: " << sf_strerror(sndfile) << endl; 267 cerr << "ERROR: sf_readf_float failed: " << sf_strerror(sndfile) << endl;
318 break; 268 break;
319 } 269 }
320 270
321 for (int c = 0; c < channels; ++c) { 271 for (int c = 0; c < channels; ++c) {
322 for (int j = 0; j < blockSize; ++j) { 272 int j = 0;
273 while (j < count) {
274 plugbuf[c][j] = filebuf[j * sfinfo.channels + c];
275 ++j;
276 }
277 while (j < blockSize) {
323 plugbuf[c][j] = 0.0f; 278 plugbuf[c][j] = 0.0f;
324 } 279 ++j;
325 }
326
327 for (int j = 0; j < blockSize && j < count; ++j) {
328 int tc = 0;
329 for (int c = 0; c < sfinfo.channels; ++c) {
330 tc = c;
331 if (mix) tc = 0;
332 plugbuf[tc][j] += filebuf[j * sfinfo.channels + c];
333 }
334 if (mix) {
335 plugbuf[0][j] /= sfinfo.channels;
336 } 280 }
337 } 281 }
338 282
339 printFeatures 283 printFeatures
340 (i, sfinfo.samplerate, output, plugin->process 284 (i, sfinfo.samplerate, outputNo, plugin->process
341 (plugbuf, Vamp::RealTime::frame2RealTime(i, sfinfo.samplerate))); 285 (plugbuf, Vamp::RealTime::frame2RealTime(i, sfinfo.samplerate)));
342 } 286 }
343 287
344 printFeatures(sfinfo.frames, sfinfo.samplerate, output, 288 printFeatures(sfinfo.frames, sfinfo.samplerate, outputNo,
345 plugin->getRemainingFeatures()); 289 plugin->getRemainingFeatures());
346 290
347 returnValue = 0; 291 returnValue = 0;
348 292
349 done: 293 done:
350 delete plugin; 294 delete plugin;
351
352 DLCLOSE(libraryHandle);
353 sf_close(sndfile); 295 sf_close(sndfile);
354 return returnValue; 296 return returnValue;
355 } 297 }
356 298
357 void 299 void
358 printPluginPath() 300 printPluginPath(bool verbose)
359 { 301 {
302 if (verbose) {
303 cout << "\nVamp plugin search path: ";
304 }
305
360 vector<string> path = Vamp::PluginHostAdapter::getPluginPath(); 306 vector<string> path = Vamp::PluginHostAdapter::getPluginPath();
361 for (size_t i = 0; i < path.size(); ++i) { 307 for (size_t i = 0; i < path.size(); ++i) {
362 cerr << path[i] << endl; 308 if (verbose) {
363 } 309 cout << "[" << path[i] << "]";
310 } else {
311 cout << path[i] << endl;
312 }
313 }
314
315 if (verbose) cout << endl;
364 } 316 }
365 317
366 void 318 void
367 enumeratePlugins() 319 enumeratePlugins()
368 { 320 {
369 Vamp::PluginLoader loader; 321 PluginLoader *loader = PluginLoader::getInstance();
370 322
371 cerr << endl << "Vamp plugin libraries found in search path:" << endl; 323 cout << "\nVamp plugin libraries found in search path:" << endl;
372 324
373 std::vector<Vamp::PluginLoader::PluginKey> plugins = loader.listPlugins(); 325 std::vector<PluginLoader::PluginKey> plugins = loader->listPlugins();
374 typedef std::multimap<std::string, Vamp::PluginLoader::PluginKey> 326 typedef std::multimap<std::string, PluginLoader::PluginKey>
375 LibraryMap; 327 LibraryMap;
376 LibraryMap libraryMap; 328 LibraryMap libraryMap;
377 329
378 for (size_t i = 0; i < plugins.size(); ++i) { 330 for (size_t i = 0; i < plugins.size(); ++i) {
379 std::string path = loader.getLibraryPathForPlugin(plugins[i]); 331 std::string path = loader->getLibraryPathForPlugin(plugins[i]);
380 libraryMap.insert(LibraryMap::value_type(path, plugins[i])); 332 libraryMap.insert(LibraryMap::value_type(path, plugins[i]));
381 } 333 }
382 334
383 std::string prevPath = ""; 335 std::string prevPath = "";
384 int index = 0; 336 int index = 0;
385 337
386 for (LibraryMap::iterator i = libraryMap.begin(); 338 for (LibraryMap::iterator i = libraryMap.begin();
387 i != libraryMap.end(); ++i) { 339 i != libraryMap.end(); ++i) {
388 340
389 std::string path = i->first; 341 std::string path = i->first;
390 Vamp::PluginLoader::PluginKey key = i->second; 342 PluginLoader::PluginKey key = i->second;
391 343
392 if (path != prevPath) { 344 if (path != prevPath) {
393 prevPath = path; 345 prevPath = path;
394 index = 0; 346 index = 0;
395 cerr << "\n " << path << ":" << endl; 347 cout << "\n " << path << ":" << endl;
396 } 348 }
397 349
398 Vamp::Plugin *plugin = loader.load(key, 48000); 350 Vamp::Plugin *plugin = loader->loadPlugin(key, 48000);
399 if (plugin) { 351 if (plugin) {
400 352
401 char c = char('A' + index); 353 char c = char('A' + index);
402 if (c > 'Z') c = char('a' + (index - 26)); 354 if (c > 'Z') c = char('a' + (index - 26));
403 355
404 cerr << " [" << c << "] [v" 356 cout << " [" << c << "] [v"
405 << plugin->getVampApiVersion() << "] " 357 << plugin->getVampApiVersion() << "] "
406 << plugin->getName() << ", \"" 358 << plugin->getName() << ", \""
407 << plugin->getIdentifier() << "\"" << " [" 359 << plugin->getIdentifier() << "\"" << " ["
408 << plugin->getMaker() << "]" << endl; 360 << plugin->getMaker() << "]" << endl;
409 361
410 // cerr << "looking up category for " << key << " ..." << endl; 362 PluginLoader::PluginCategoryHierarchy category =
411 Vamp::PluginLoader::PluginCategoryHierarchy category = 363 loader->getPluginCategory(key);
412 loader.getPluginCategory(key);
413 if (!category.empty()) { 364 if (!category.empty()) {
414 cerr << " "; 365 cout << " ";
415 for (size_t ci = 0; ci < category.size(); ++ci) { 366 for (size_t ci = 0; ci < category.size(); ++ci) {
416 cerr << " > " << category[ci]; 367 cout << " > " << category[ci];
417 } 368 }
418 cerr << endl; 369 cout << endl;
419 } 370 }
420 371
421 if (plugin->getDescription() != "") { 372 if (plugin->getDescription() != "") {
422 cerr << " - " << plugin->getDescription() << endl; 373 cout << " - " << plugin->getDescription() << endl;
423 } 374 }
424 375
425 Vamp::Plugin::OutputList outputs = 376 Vamp::Plugin::OutputList outputs =
426 plugin->getOutputDescriptors(); 377 plugin->getOutputDescriptors();
427 378
428 if (outputs.size() > 1) { 379 if (outputs.size() > 1) {
429 for (size_t j = 0; j < outputs.size(); ++j) { 380 for (size_t j = 0; j < outputs.size(); ++j) {
430 cerr << " (" << j << ") " 381 cout << " (" << j << ") "
431 << outputs[j].name << ", \"" 382 << outputs[j].name << ", \""
432 << outputs[j].identifier << "\"" << endl; 383 << outputs[j].identifier << "\"" << endl;
433 if (outputs[j].description != "") { 384 if (outputs[j].description != "") {
434 cerr << " - " 385 cout << " - "
435 << outputs[j].description << endl; 386 << outputs[j].description << endl;
436 } 387 }
437 } 388 }
438 } 389 }
439 390
440 ++index; 391 ++index;
441 } 392
442 } 393 delete plugin;
443 394 }
444 cerr << endl; 395 }
396
397 cout << endl;
445 } 398 }
446 399
447 void 400 void
448 printFeatures(int frame, int sr, int output, Vamp::Plugin::FeatureSet features) 401 printFeatures(int frame, int sr, int output, Vamp::Plugin::FeatureSet features)
449 { 402 {