# HG changeset patch # User cannam # Date 1144423930 0 # Node ID 61887dda7fe0c12bbc1ef9f1f61ba8d11761d803 # Parent 6c5466fbea903092d57eb01fd80b4dd3e067e54a * Make the host capable of doing something useful! diff -r 6c5466fbea90 -r 61887dda7fe0 Makefile --- a/Makefile Thu Apr 06 17:23:41 2006 +0000 +++ b/Makefile Fri Apr 07 15:32:10 2006 +0000 @@ -17,7 +17,7 @@ # Libraries required for the host at link time # -HOST_LIBS = -Lvamp-sdk -lvamp-sdk -ldl +HOST_LIBS = -Lvamp-sdk -lvamp-sdk -lsndfile -ldl # Libraries required for the plugin. Note that we can (and actively # want to) statically link with libstdc++, because our plugin exposes diff -r 6c5466fbea90 -r 61887dda7fe0 README --- a/README Thu Apr 06 17:23:41 2006 +0000 +++ b/README Fri Apr 07 15:32:10 2006 +0000 @@ -72,8 +72,9 @@ * host -The simplest possible Vamp host -- this doesn't even process anything -yet, just loads the plugins and prints out their names. +A simple command-line Vamp host, capable of loading a plugin and using +it to process a complete audio file, with its default parameters. +Requires libsndfile. Building the SDK diff -r 6c5466fbea90 -r 61887dda7fe0 host/simplehost.cpp --- a/host/simplehost.cpp Thu Apr 06 17:23:41 2006 +0000 +++ b/host/simplehost.cpp Fri Apr 07 15:32:10 2006 +0000 @@ -7,6 +7,7 @@ Centre for Digital Music, Queen Mary, University of London. Copyright 2006 Chris Cannam. + FFT code from Don Cross's public domain FFT implementation. Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation @@ -34,68 +35,390 @@ authorization. */ -#include - +#include "PluginHostAdapter.h" #include "vamp.h" -#include "PluginHostAdapter.h" +#include +#include #include "system.h" +using std::cout; +using std::cerr; +using std::endl; +using std::string; + +void printFeatures(int, int, int, Vamp::Plugin::FeatureSet); +void transformInput(float *, size_t); +void fft(unsigned int, bool, double *, double *, double *, double *); + /* - A very simple Vamp plugin host. So far all this does is load the - plugin library given on the command line and dump out the names of - the plugins found in it. + A very simple Vamp plugin host. Given the name of a plugin + library and the name of a sound file on the command line, it loads + the first plugin in the library and runs it on the sound file, + dumping the plugin's first output to stdout. */ int main(int argc, char **argv) { - if (argc != 2) { - std::cerr << "Usage: " << argv[0] << " pluginlibrary.so" << std::endl; + if (argc < 2 || argc > 4) { + cerr << "Usage: " << argv[0] << " pluginlibrary.so[:plugin] [file.wav] [outputno]" << endl; return 2; } - std::cerr << std::endl << argv[0] << ": Running..." << std::endl; + cerr << endl << argv[0] << ": Running..." << endl; - std::string soname = argv[1]; + string soname = argv[1]; + string plugname = ""; + string wavname; + if (argc >= 3) wavname = argv[2]; + + if (soname.find(":") >= 0) { + plugname = soname.substr(soname.find(":") + 1); + soname = soname.substr(0, soname.find(":")); + } void *libraryHandle = DLOPEN(soname, RTLD_LAZY); if (!libraryHandle) { - std::cerr << argv[0] << ": Failed to open plugin library " - << soname << ": " << DLERROR() << std::endl; + cerr << argv[0] << ": Failed to open plugin library " + << soname << ": " << DLERROR() << endl; return 1; } - std::cout << argv[0] << ": Opened plugin library " << soname << std::endl; + cerr << argv[0] << ": Opened plugin library " << soname << endl; VampGetPluginDescriptorFunction fn = (VampGetPluginDescriptorFunction) DLSYM(libraryHandle, "vampGetPluginDescriptor"); if (!fn) { - std::cerr << argv[0] << ": No Vamp descriptor function in library " - << soname << std::endl; + cerr << argv[0] << ": No Vamp descriptor function in library " + << soname << endl; DLCLOSE(libraryHandle); return 1; } - std::cout << argv[0] << ": Found plugin descriptor function" << std::endl; + cerr << argv[0] << ": Found plugin descriptor function" << endl; int index = 0; + int plugnumber = -1; const VampPluginDescriptor *descriptor = 0; while ((descriptor = fn(index))) { - Vamp::PluginHostAdapter adapter(descriptor, 48000); - std::cout << argv[0] << ": Plugin " << (index+1) - << " is \"" << adapter.getName() << "\"" << std::endl; + Vamp::PluginHostAdapter plugin(descriptor, 48000); + cerr << argv[0] << ": Plugin " << (index+1) + << " is \"" << plugin.getName() << "\"" << endl; + + if (plugin.getName() == plugname) plugnumber = index; ++index; } - std::cout << argv[0] << ": Done\n" << std::endl; + cerr << argv[0] << ": Done\n" << endl; + + if (wavname == "") { + DLCLOSE(libraryHandle); + return 0; + } + + if (plugnumber < 0) { + if (plugname != "") { + cerr << "ERROR: No such plugin as " << plugname << " in library" + << endl; + DLCLOSE(libraryHandle); + return 0; + } else { + plugnumber = 0; + } + } + + descriptor = fn(plugnumber); + if (!descriptor) { + DLCLOSE(libraryHandle); + return 0; + } + + SNDFILE *sndfile; + SF_INFO sfinfo; + memset(&sfinfo, 0, sizeof(SF_INFO)); + + sndfile = sf_open(wavname.c_str(), SFM_READ, &sfinfo); + if (!sndfile) { + cerr << "ERROR: Failed to open input file \"" << wavname << "\": " + << sf_strerror(sndfile) << endl; + DLCLOSE(libraryHandle); + return 1; + } + + Vamp::PluginHostAdapter *plugin = + new Vamp::PluginHostAdapter(descriptor, sfinfo.samplerate); + + cerr << "Running " << plugin->getName() << "..." << endl; + + int blockSize = plugin->getPreferredBlockSize(); + int stepSize = plugin->getPreferredStepSize(); + + cerr << "Preferred block size = " << blockSize << ", step size = " + << stepSize << endl; + + if (blockSize == 0) blockSize = 1024; + if (stepSize == 0) stepSize = blockSize; + + int channels = sfinfo.channels; + + float *filebuf = new float[blockSize * channels]; + float **plugbuf = new float*[channels]; + for (int c = 0; c < channels; ++c) plugbuf[c] = new float[blockSize]; + + cerr << "Using block size = " << blockSize << ", step size = " + << stepSize << endl; + + int minch = plugin->getMinChannelCount(); + int maxch = plugin->getMaxChannelCount(); + cerr << "Plugin accepts " << minch << " -> " << maxch << " channel(s)" << endl; + + Vamp::Plugin::OutputList outputs = plugin->getOutputDescriptors(); + Vamp::Plugin::OutputDescriptor od; + + int output = 0; + if (argc == 4) output = atoi(argv[3]); + + bool mix = false; + + if (minch > channels || maxch < channels) { + if (minch == 1) { + cerr << "WARNING: Sound file has " << channels << " channels, mixing down to 1" << endl; + mix = true; + channels = 1; + } else { + cerr << "ERROR: Sound file has " << channels << " channels, out of range for plugin" << endl; + goto done; + } + } + + if (outputs.empty()) { + cerr << "Plugin has no outputs!" << endl; + goto done; + } + + if (int(outputs.size()) <= output) { + cerr << "Output " << output << " requested, but plugin has only " << outputs.size() << " output(s)" << endl; + goto done; + } + + od = outputs[output]; + cerr << "Output is " << od.name << endl; + + plugin->initialise(channels, stepSize, blockSize); + + for (size_t i = 0; i < sfinfo.frames; i += stepSize) { + + int count; + + if (sf_seek(sndfile, i, SEEK_SET) < 0) { + cerr << "ERROR: sf_seek failed: " << sf_strerror(sndfile) << endl; + break; + } + + if ((count = sf_readf_float(sndfile, filebuf, blockSize)) < 0) { + cerr << "ERROR: sf_readf_float failed: " << sf_strerror(sndfile) << endl; + break; + } + + for (int c = 0; c < channels; ++c) { + for (int j = 0; j < blockSize; ++j) { + plugbuf[c][j] = 0.0f; + } + } + + for (int c = 0; c < sfinfo.channels; ++c) { + int tc = c; + if (mix) tc = 0; + for (int j = 0; j < blockSize && j < count; ++j) { + plugbuf[tc][j] += filebuf[j * channels + c]; + } + + if (plugin->getInputDomain() == Vamp::Plugin::FrequencyDomain) { + transformInput(plugbuf[tc], blockSize); + } + } + + printFeatures + (i, sfinfo.samplerate, output, plugin->process + (plugbuf, Vamp::RealTime::frame2RealTime(i, sfinfo.samplerate))); + } + + printFeatures(sfinfo.frames, sfinfo.samplerate, output, + plugin->getRemainingFeatures()); + +done: + delete plugin; DLCLOSE(libraryHandle); + sf_close(sndfile); return 0; } +void +printFeatures(int frame, int sr, int output, Vamp::Plugin::FeatureSet features) +{ + for (unsigned int i = 0; i < features[output].size(); ++i) { + Vamp::RealTime rt = Vamp::RealTime::frame2RealTime(frame, sr); + if (features[output][i].hasTimestamp) { + rt = features[output][i].timestamp; + } + cout << rt.toString() << ":"; + for (unsigned int j = 0; j < features[output][i].values.size(); ++j) { + cout << " " << features[output][i].values[j]; + } + cout << endl; + } +} + +void +transformInput(float *buffer, size_t size) +{ + double *inbuf = new double[size * 2]; + double *outbuf = new double[size * 2]; + + // Copy across with Hanning window + for (size_t i = 0; i < size; ++i) { + inbuf[i] = double(buffer[i]) * (0.50 - 0.50 * cos(2 * M_PI * i / size)); + inbuf[i + size] = 0.0; + } + + for (size_t i = 0; i < size/2; ++i) { + double temp = inbuf[i]; + inbuf[i] = inbuf[i + size/2]; + inbuf[i + size/2] = temp; + } + + fft(size, false, inbuf, inbuf + size, outbuf, outbuf + size); + + for (size_t i = 0; i < size/2; ++i) { + buffer[i * 2] = outbuf[i]; + buffer[i * 2 + 1] = outbuf[i + size]; + } + + delete inbuf; + delete outbuf; +} + +void +fft(unsigned int n, bool inverse, double *ri, double *ii, double *ro, double *io) +{ + if (!ri || !ro || !io) return; + + unsigned int bits; + unsigned int i, j, k, m; + unsigned int blockSize, blockEnd; + + double tr, ti; + + if (n < 2) return; + if (n & (n-1)) return; + + double angle = 2.0 * M_PI; + if (inverse) angle = -angle; + + for (i = 0; ; ++i) { + if (n & (1 << i)) { + bits = i; + break; + } + } + + static unsigned int tableSize = 0; + static int *table = 0; + + if (tableSize != n) { + + delete[] table; + + table = new int[n]; + + for (i = 0; i < n; ++i) { + + m = i; + + for (j = k = 0; j < bits; ++j) { + k = (k << 1) | (m & 1); + m >>= 1; + } + + table[i] = k; + } + + tableSize = n; + } + + if (ii) { + for (i = 0; i < n; ++i) { + ro[table[i]] = ri[i]; + io[table[i]] = ii[i]; + } + } else { + for (i = 0; i < n; ++i) { + ro[table[i]] = ri[i]; + io[table[i]] = 0.0; + } + } + + blockEnd = 1; + + for (blockSize = 2; blockSize <= n; blockSize <<= 1) { + + double delta = angle / (double)blockSize; + double sm2 = -sin(-2 * delta); + double sm1 = -sin(-delta); + double cm2 = cos(-2 * delta); + double cm1 = cos(-delta); + double w = 2 * cm1; + double ar[3], ai[3]; + + for (i = 0; i < n; i += blockSize) { + + ar[2] = cm2; + ar[1] = cm1; + + ai[2] = sm2; + ai[1] = sm1; + + for (j = i, m = 0; m < blockEnd; j++, m++) { + + ar[0] = w * ar[1] - ar[2]; + ar[2] = ar[1]; + ar[1] = ar[0]; + + ai[0] = w * ai[1] - ai[2]; + ai[2] = ai[1]; + ai[1] = ai[0]; + + k = j + blockEnd; + tr = ar[0] * ro[k] - ai[0] * io[k]; + ti = ar[0] * io[k] + ai[0] * ro[k]; + + ro[k] = ro[j] - tr; + io[k] = io[j] - ti; + + ro[j] += tr; + io[j] += ti; + } + } + + blockEnd = blockSize; + } + + if (inverse) { + + double denom = (double)n; + + for (i = 0; i < n; i++) { + ro[i] /= denom; + io[i] /= denom; + } + } +} + + diff -r 6c5466fbea90 -r 61887dda7fe0 vamp-sdk/PluginAdapter.cpp --- a/vamp-sdk/PluginAdapter.cpp Thu Apr 06 17:23:41 2006 +0000 +++ b/vamp-sdk/PluginAdapter.cpp Fri Apr 07 15:32:10 2006 +0000 @@ -578,7 +578,7 @@ int i = m_fsizes[plugin].size(); if (i >= n) return; - std::cerr << "resizing from " << i << std::endl; +// std::cerr << "resizing from " << i << std::endl; m_fs[plugin] = (VampFeatureList *)realloc (m_fs[plugin], n * sizeof(VampFeatureList)); @@ -595,13 +595,13 @@ void PluginAdapterBase::resizeFL(Plugin *plugin, int n, size_t sz) { - std::cerr << "PluginAdapterBase::resizeFL(" << plugin << ", " << n << ", " - << sz << ")" << std::endl; +// std::cerr << "PluginAdapterBase::resizeFL(" << plugin << ", " << n << ", " +// << sz << ")" << std::endl; size_t i = m_fsizes[plugin][n]; if (i >= sz) return; - std::cerr << "resizing from " << i << std::endl; +// std::cerr << "resizing from " << i << std::endl; m_fs[plugin][n].features = (VampFeature *)realloc (m_fs[plugin][n].features, sz * sizeof(VampFeature)); @@ -618,14 +618,13 @@ void PluginAdapterBase::resizeFV(Plugin *plugin, int n, int j, size_t sz) { - - std::cerr << "PluginAdapterBase::resizeFV(" << plugin << ", " << n << ", " - << j << ", " << sz << ")" << std::endl; +// std::cerr << "PluginAdapterBase::resizeFV(" << plugin << ", " << n << ", " +// << j << ", " << sz << ")" << std::endl; size_t i = m_fvsizes[plugin][n][j]; if (i >= sz) return; - std::cerr << "resizing from " << i << std::endl; +// std::cerr << "resizing from " << i << std::endl; m_fs[plugin][n].features[j].values = (float *)realloc (m_fs[plugin][n].features[j].values, sz * sizeof(float));