annotate host/vamp-simple-host.cpp @ 27:44ec6c633113

* Permit plugins to vary the number of values per output based on the number of channels, step size, and block size passed to initialise().
author cannam
date Wed, 10 May 2006 16:30:39 +0000
parents 1eb44d33a371
children d97aafa99828
rev   line source
cannam@1 1 /* -*- c-basic-offset: 4 indent-tabs-mode: nil -*- vi:set ts=8 sts=4 sw=4: */
cannam@1 2
cannam@1 3 /*
cannam@1 4 Vamp
cannam@1 5
cannam@1 6 An API for audio analysis and feature extraction plugins.
cannam@1 7
cannam@1 8 Centre for Digital Music, Queen Mary, University of London.
cannam@1 9 Copyright 2006 Chris Cannam.
cannam@16 10 FFT code from Don Cross's public domain FFT implementation.
cannam@1 11
cannam@1 12 Permission is hereby granted, free of charge, to any person
cannam@1 13 obtaining a copy of this software and associated documentation
cannam@1 14 files (the "Software"), to deal in the Software without
cannam@1 15 restriction, including without limitation the rights to use, copy,
cannam@1 16 modify, merge, publish, distribute, sublicense, and/or sell copies
cannam@1 17 of the Software, and to permit persons to whom the Software is
cannam@1 18 furnished to do so, subject to the following conditions:
cannam@1 19
cannam@1 20 The above copyright notice and this permission notice shall be
cannam@1 21 included in all copies or substantial portions of the Software.
cannam@1 22
cannam@1 23 THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
cannam@1 24 EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
cannam@1 25 MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
cannam@6 26 NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR
cannam@1 27 ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF
cannam@1 28 CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
cannam@1 29 WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
cannam@1 30
cannam@1 31 Except as contained in this notice, the names of the Centre for
cannam@1 32 Digital Music; Queen Mary, University of London; and Chris Cannam
cannam@1 33 shall not be used in advertising or otherwise to promote the sale,
cannam@1 34 use or other dealings in this Software without prior written
cannam@1 35 authorization.
cannam@1 36 */
cannam@1 37
cannam@16 38 #include "PluginHostAdapter.h"
cannam@1 39 #include "vamp.h"
cannam@1 40
cannam@16 41 #include <iostream>
cannam@16 42 #include <sndfile.h>
cannam@1 43
cannam@1 44 #include "system.h"
cannam@1 45
cannam@19 46 #include <cmath>
cannam@19 47
cannam@16 48 using std::cout;
cannam@16 49 using std::cerr;
cannam@16 50 using std::endl;
cannam@16 51 using std::string;
cannam@16 52
cannam@16 53 void printFeatures(int, int, int, Vamp::Plugin::FeatureSet);
cannam@16 54 void transformInput(float *, size_t);
cannam@16 55 void fft(unsigned int, bool, double *, double *, double *, double *);
cannam@16 56
cannam@1 57 /*
cannam@16 58 A very simple Vamp plugin host. Given the name of a plugin
cannam@16 59 library and the name of a sound file on the command line, it loads
cannam@16 60 the first plugin in the library and runs it on the sound file,
cannam@16 61 dumping the plugin's first output to stdout.
cannam@1 62 */
cannam@1 63
cannam@1 64 int main(int argc, char **argv)
cannam@1 65 {
cannam@16 66 if (argc < 2 || argc > 4) {
cannam@16 67 cerr << "Usage: " << argv[0] << " pluginlibrary.so[:plugin] [file.wav] [outputno]" << endl;
cannam@1 68 return 2;
cannam@1 69 }
cannam@1 70
cannam@16 71 cerr << endl << argv[0] << ": Running..." << endl;
cannam@1 72
cannam@16 73 string soname = argv[1];
cannam@16 74 string plugname = "";
cannam@16 75 string wavname;
cannam@16 76 if (argc >= 3) wavname = argv[2];
cannam@16 77
cannam@20 78 int sep = soname.find(":");
cannam@20 79 if (sep >= 0 && sep < soname.length()) {
cannam@20 80 plugname = soname.substr(sep + 1);
cannam@20 81 soname = soname.substr(0, sep);
cannam@16 82 }
cannam@1 83
cannam@1 84 void *libraryHandle = DLOPEN(soname, RTLD_LAZY);
cannam@1 85
cannam@1 86 if (!libraryHandle) {
cannam@16 87 cerr << argv[0] << ": Failed to open plugin library "
cannam@16 88 << soname << ": " << DLERROR() << endl;
cannam@1 89 return 1;
cannam@1 90 }
cannam@1 91
cannam@16 92 cerr << argv[0] << ": Opened plugin library " << soname << endl;
cannam@1 93
cannam@1 94 VampGetPluginDescriptorFunction fn = (VampGetPluginDescriptorFunction)
cannam@1 95 DLSYM(libraryHandle, "vampGetPluginDescriptor");
cannam@1 96
cannam@1 97 if (!fn) {
cannam@16 98 cerr << argv[0] << ": No Vamp descriptor function in library "
cannam@16 99 << soname << endl;
cannam@1 100 DLCLOSE(libraryHandle);
cannam@1 101 return 1;
cannam@1 102 }
cannam@1 103
cannam@16 104 cerr << argv[0] << ": Found plugin descriptor function" << endl;
cannam@1 105
cannam@1 106 int index = 0;
cannam@16 107 int plugnumber = -1;
cannam@1 108 const VampPluginDescriptor *descriptor = 0;
cannam@1 109
cannam@1 110 while ((descriptor = fn(index))) {
cannam@1 111
cannam@16 112 Vamp::PluginHostAdapter plugin(descriptor, 48000);
cannam@16 113 cerr << argv[0] << ": Plugin " << (index+1)
cannam@16 114 << " is \"" << plugin.getName() << "\"" << endl;
cannam@16 115
cannam@16 116 if (plugin.getName() == plugname) plugnumber = index;
cannam@1 117
cannam@22 118 cerr << "(testing overlap...)" << endl;
cannam@22 119 {
cannam@22 120 Vamp::PluginHostAdapter otherPlugin(descriptor, 48000);
cannam@22 121 cerr << "(other plugin reports min " << otherPlugin.getMinChannelCount() << " channels)" << endl;
cannam@22 122 }
cannam@22 123
cannam@1 124 ++index;
cannam@1 125 }
cannam@1 126
cannam@16 127 cerr << argv[0] << ": Done\n" << endl;
cannam@16 128
cannam@16 129 if (wavname == "") {
cannam@16 130 DLCLOSE(libraryHandle);
cannam@16 131 return 0;
cannam@16 132 }
cannam@16 133
cannam@16 134 if (plugnumber < 0) {
cannam@16 135 if (plugname != "") {
cannam@16 136 cerr << "ERROR: No such plugin as " << plugname << " in library"
cannam@16 137 << endl;
cannam@16 138 DLCLOSE(libraryHandle);
cannam@16 139 return 0;
cannam@16 140 } else {
cannam@16 141 plugnumber = 0;
cannam@16 142 }
cannam@16 143 }
cannam@16 144
cannam@16 145 descriptor = fn(plugnumber);
cannam@16 146 if (!descriptor) {
cannam@16 147 DLCLOSE(libraryHandle);
cannam@16 148 return 0;
cannam@16 149 }
cannam@16 150
cannam@16 151 SNDFILE *sndfile;
cannam@16 152 SF_INFO sfinfo;
cannam@16 153 memset(&sfinfo, 0, sizeof(SF_INFO));
cannam@16 154
cannam@16 155 sndfile = sf_open(wavname.c_str(), SFM_READ, &sfinfo);
cannam@16 156 if (!sndfile) {
cannam@16 157 cerr << "ERROR: Failed to open input file \"" << wavname << "\": "
cannam@16 158 << sf_strerror(sndfile) << endl;
cannam@16 159 DLCLOSE(libraryHandle);
cannam@16 160 return 1;
cannam@16 161 }
cannam@16 162
cannam@16 163 Vamp::PluginHostAdapter *plugin =
cannam@16 164 new Vamp::PluginHostAdapter(descriptor, sfinfo.samplerate);
cannam@16 165
cannam@16 166 cerr << "Running " << plugin->getName() << "..." << endl;
cannam@16 167
cannam@16 168 int blockSize = plugin->getPreferredBlockSize();
cannam@16 169 int stepSize = plugin->getPreferredStepSize();
cannam@16 170
cannam@16 171 cerr << "Preferred block size = " << blockSize << ", step size = "
cannam@16 172 << stepSize << endl;
cannam@16 173
cannam@16 174 if (blockSize == 0) blockSize = 1024;
cannam@16 175 if (stepSize == 0) stepSize = blockSize;
cannam@16 176
cannam@16 177 int channels = sfinfo.channels;
cannam@16 178
cannam@16 179 float *filebuf = new float[blockSize * channels];
cannam@16 180 float **plugbuf = new float*[channels];
cannam@16 181 for (int c = 0; c < channels; ++c) plugbuf[c] = new float[blockSize];
cannam@16 182
cannam@16 183 cerr << "Using block size = " << blockSize << ", step size = "
cannam@16 184 << stepSize << endl;
cannam@16 185
cannam@16 186 int minch = plugin->getMinChannelCount();
cannam@16 187 int maxch = plugin->getMaxChannelCount();
cannam@16 188 cerr << "Plugin accepts " << minch << " -> " << maxch << " channel(s)" << endl;
cannam@16 189
cannam@16 190 Vamp::Plugin::OutputList outputs = plugin->getOutputDescriptors();
cannam@16 191 Vamp::Plugin::OutputDescriptor od;
cannam@16 192
cannam@16 193 int output = 0;
cannam@16 194 if (argc == 4) output = atoi(argv[3]);
cannam@16 195
cannam@16 196 bool mix = false;
cannam@16 197
cannam@16 198 if (minch > channels || maxch < channels) {
cannam@16 199 if (minch == 1) {
cannam@16 200 cerr << "WARNING: Sound file has " << channels << " channels, mixing down to 1" << endl;
cannam@16 201 mix = true;
cannam@16 202 channels = 1;
cannam@16 203 } else {
cannam@16 204 cerr << "ERROR: Sound file has " << channels << " channels, out of range for plugin" << endl;
cannam@16 205 goto done;
cannam@16 206 }
cannam@16 207 }
cannam@16 208
cannam@16 209 if (outputs.empty()) {
cannam@16 210 cerr << "Plugin has no outputs!" << endl;
cannam@16 211 goto done;
cannam@16 212 }
cannam@16 213
cannam@16 214 if (int(outputs.size()) <= output) {
cannam@16 215 cerr << "Output " << output << " requested, but plugin has only " << outputs.size() << " output(s)" << endl;
cannam@16 216 goto done;
cannam@16 217 }
cannam@16 218
cannam@16 219 od = outputs[output];
cannam@16 220 cerr << "Output is " << od.name << endl;
cannam@16 221
cannam@16 222 plugin->initialise(channels, stepSize, blockSize);
cannam@16 223
cannam@16 224 for (size_t i = 0; i < sfinfo.frames; i += stepSize) {
cannam@16 225
cannam@16 226 int count;
cannam@16 227
cannam@16 228 if (sf_seek(sndfile, i, SEEK_SET) < 0) {
cannam@16 229 cerr << "ERROR: sf_seek failed: " << sf_strerror(sndfile) << endl;
cannam@16 230 break;
cannam@16 231 }
cannam@16 232
cannam@16 233 if ((count = sf_readf_float(sndfile, filebuf, blockSize)) < 0) {
cannam@16 234 cerr << "ERROR: sf_readf_float failed: " << sf_strerror(sndfile) << endl;
cannam@16 235 break;
cannam@16 236 }
cannam@16 237
cannam@16 238 for (int c = 0; c < channels; ++c) {
cannam@16 239 for (int j = 0; j < blockSize; ++j) {
cannam@16 240 plugbuf[c][j] = 0.0f;
cannam@16 241 }
cannam@16 242 }
cannam@16 243
cannam@16 244 for (int c = 0; c < sfinfo.channels; ++c) {
cannam@16 245 int tc = c;
cannam@16 246 if (mix) tc = 0;
cannam@16 247 for (int j = 0; j < blockSize && j < count; ++j) {
cannam@16 248 plugbuf[tc][j] += filebuf[j * channels + c];
cannam@16 249 }
cannam@16 250
cannam@16 251 if (plugin->getInputDomain() == Vamp::Plugin::FrequencyDomain) {
cannam@16 252 transformInput(plugbuf[tc], blockSize);
cannam@16 253 }
cannam@16 254 }
cannam@16 255
cannam@16 256 printFeatures
cannam@16 257 (i, sfinfo.samplerate, output, plugin->process
cannam@16 258 (plugbuf, Vamp::RealTime::frame2RealTime(i, sfinfo.samplerate)));
cannam@16 259 }
cannam@16 260
cannam@16 261 printFeatures(sfinfo.frames, sfinfo.samplerate, output,
cannam@16 262 plugin->getRemainingFeatures());
cannam@16 263
cannam@16 264 done:
cannam@16 265 delete plugin;
cannam@1 266
cannam@1 267 DLCLOSE(libraryHandle);
cannam@16 268 sf_close(sndfile);
cannam@1 269 return 0;
cannam@1 270 }
cannam@1 271
cannam@16 272 void
cannam@16 273 printFeatures(int frame, int sr, int output, Vamp::Plugin::FeatureSet features)
cannam@16 274 {
cannam@16 275 for (unsigned int i = 0; i < features[output].size(); ++i) {
cannam@16 276 Vamp::RealTime rt = Vamp::RealTime::frame2RealTime(frame, sr);
cannam@16 277 if (features[output][i].hasTimestamp) {
cannam@16 278 rt = features[output][i].timestamp;
cannam@16 279 }
cannam@16 280 cout << rt.toString() << ":";
cannam@16 281 for (unsigned int j = 0; j < features[output][i].values.size(); ++j) {
cannam@16 282 cout << " " << features[output][i].values[j];
cannam@16 283 }
cannam@16 284 cout << endl;
cannam@16 285 }
cannam@16 286 }
cannam@16 287
cannam@16 288 void
cannam@16 289 transformInput(float *buffer, size_t size)
cannam@16 290 {
cannam@16 291 double *inbuf = new double[size * 2];
cannam@16 292 double *outbuf = new double[size * 2];
cannam@16 293
cannam@16 294 // Copy across with Hanning window
cannam@16 295 for (size_t i = 0; i < size; ++i) {
cannam@16 296 inbuf[i] = double(buffer[i]) * (0.50 - 0.50 * cos(2 * M_PI * i / size));
cannam@16 297 inbuf[i + size] = 0.0;
cannam@16 298 }
cannam@16 299
cannam@16 300 for (size_t i = 0; i < size/2; ++i) {
cannam@16 301 double temp = inbuf[i];
cannam@16 302 inbuf[i] = inbuf[i + size/2];
cannam@16 303 inbuf[i + size/2] = temp;
cannam@16 304 }
cannam@16 305
cannam@16 306 fft(size, false, inbuf, inbuf + size, outbuf, outbuf + size);
cannam@16 307
cannam@16 308 for (size_t i = 0; i < size/2; ++i) {
cannam@16 309 buffer[i * 2] = outbuf[i];
cannam@16 310 buffer[i * 2 + 1] = outbuf[i + size];
cannam@16 311 }
cannam@16 312
cannam@16 313 delete inbuf;
cannam@16 314 delete outbuf;
cannam@16 315 }
cannam@16 316
cannam@16 317 void
cannam@16 318 fft(unsigned int n, bool inverse, double *ri, double *ii, double *ro, double *io)
cannam@16 319 {
cannam@16 320 if (!ri || !ro || !io) return;
cannam@16 321
cannam@16 322 unsigned int bits;
cannam@16 323 unsigned int i, j, k, m;
cannam@16 324 unsigned int blockSize, blockEnd;
cannam@16 325
cannam@16 326 double tr, ti;
cannam@16 327
cannam@16 328 if (n < 2) return;
cannam@16 329 if (n & (n-1)) return;
cannam@16 330
cannam@16 331 double angle = 2.0 * M_PI;
cannam@16 332 if (inverse) angle = -angle;
cannam@16 333
cannam@16 334 for (i = 0; ; ++i) {
cannam@16 335 if (n & (1 << i)) {
cannam@16 336 bits = i;
cannam@16 337 break;
cannam@16 338 }
cannam@16 339 }
cannam@16 340
cannam@16 341 static unsigned int tableSize = 0;
cannam@16 342 static int *table = 0;
cannam@16 343
cannam@16 344 if (tableSize != n) {
cannam@16 345
cannam@16 346 delete[] table;
cannam@16 347
cannam@16 348 table = new int[n];
cannam@16 349
cannam@16 350 for (i = 0; i < n; ++i) {
cannam@16 351
cannam@16 352 m = i;
cannam@16 353
cannam@16 354 for (j = k = 0; j < bits; ++j) {
cannam@16 355 k = (k << 1) | (m & 1);
cannam@16 356 m >>= 1;
cannam@16 357 }
cannam@16 358
cannam@16 359 table[i] = k;
cannam@16 360 }
cannam@16 361
cannam@16 362 tableSize = n;
cannam@16 363 }
cannam@16 364
cannam@16 365 if (ii) {
cannam@16 366 for (i = 0; i < n; ++i) {
cannam@16 367 ro[table[i]] = ri[i];
cannam@16 368 io[table[i]] = ii[i];
cannam@16 369 }
cannam@16 370 } else {
cannam@16 371 for (i = 0; i < n; ++i) {
cannam@16 372 ro[table[i]] = ri[i];
cannam@16 373 io[table[i]] = 0.0;
cannam@16 374 }
cannam@16 375 }
cannam@16 376
cannam@16 377 blockEnd = 1;
cannam@16 378
cannam@16 379 for (blockSize = 2; blockSize <= n; blockSize <<= 1) {
cannam@16 380
cannam@16 381 double delta = angle / (double)blockSize;
cannam@16 382 double sm2 = -sin(-2 * delta);
cannam@16 383 double sm1 = -sin(-delta);
cannam@16 384 double cm2 = cos(-2 * delta);
cannam@16 385 double cm1 = cos(-delta);
cannam@16 386 double w = 2 * cm1;
cannam@16 387 double ar[3], ai[3];
cannam@16 388
cannam@16 389 for (i = 0; i < n; i += blockSize) {
cannam@16 390
cannam@16 391 ar[2] = cm2;
cannam@16 392 ar[1] = cm1;
cannam@16 393
cannam@16 394 ai[2] = sm2;
cannam@16 395 ai[1] = sm1;
cannam@16 396
cannam@16 397 for (j = i, m = 0; m < blockEnd; j++, m++) {
cannam@16 398
cannam@16 399 ar[0] = w * ar[1] - ar[2];
cannam@16 400 ar[2] = ar[1];
cannam@16 401 ar[1] = ar[0];
cannam@16 402
cannam@16 403 ai[0] = w * ai[1] - ai[2];
cannam@16 404 ai[2] = ai[1];
cannam@16 405 ai[1] = ai[0];
cannam@16 406
cannam@16 407 k = j + blockEnd;
cannam@16 408 tr = ar[0] * ro[k] - ai[0] * io[k];
cannam@16 409 ti = ar[0] * io[k] + ai[0] * ro[k];
cannam@16 410
cannam@16 411 ro[k] = ro[j] - tr;
cannam@16 412 io[k] = io[j] - ti;
cannam@16 413
cannam@16 414 ro[j] += tr;
cannam@16 415 io[j] += ti;
cannam@16 416 }
cannam@16 417 }
cannam@16 418
cannam@16 419 blockEnd = blockSize;
cannam@16 420 }
cannam@16 421
cannam@16 422 if (inverse) {
cannam@16 423
cannam@16 424 double denom = (double)n;
cannam@16 425
cannam@16 426 for (i = 0; i < n; i++) {
cannam@16 427 ro[i] /= denom;
cannam@16 428 io[i] /= denom;
cannam@16 429 }
cannam@16 430 }
cannam@16 431 }
cannam@16 432
cannam@16 433