initial commit

This commit is contained in:
chatlanin 2024-07-24 10:01:27 +03:00
commit 768471896a
46 changed files with 9834 additions and 0 deletions

4
.gitignore vendored Normal file
View File

@ -0,0 +1,4 @@
build
.cache
subprojects/*
!subprojects/hack.wrap

5
README.md Normal file
View File

@ -0,0 +1,5 @@
Это полностью переписаный на С++ vamp-plugin-sdk (https://vamp-plugins.org/code-doc/main.html)
Оптимизирован только для linux. Он не будет работать (только если чудом) на других системах.
В идеале хотелось бы привести это к единому заголовочному файлу и возможно это случится...
Если вы считаете нужным что-то добавить, убрать, поправить, переписать делайте это сами в своих форках и не спрашивайте меня ни о чем!

50
host/system.h Normal file
View File

@ -0,0 +1,50 @@
/* -*- c-basic-offset: 4 indent-tabs-mode: nil -*- vi:set ts=8 sts=4 sw=4: */
/*
Vamp
An API for audio analysis and feature extraction plugins.
Centre for Digital Music, Queen Mary, University of London.
Copyright 2006 Chris Cannam.
Permission is hereby granted, free of charge, to any person
obtaining a copy of this software and associated documentation
files (the "Software"), to deal in the Software without
restriction, including without limitation the rights to use, copy,
modify, merge, publish, distribute, sublicense, and/or sell copies
of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be
included in all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR
ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF
CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
Except as contained in this notice, the names of the Centre for
Digital Music; Queen Mary, University of London; and Chris Cannam
shall not be used in advertising or otherwise to promote the sale,
use or other dealings in this Software without prior written
authorization.
*/
#ifndef _SYSTEM_H_
#define _SYSTEM_H_
#include <dlfcn.h>
#define DLOPEN(a,b) dlopen((a).c_str(),(b))
#define DLSYM(a,b) dlsym((a),(b))
#define DLCLOSE(a) dlclose((a))
#define DLERROR() dlerror()
#define PLUGIN_SUFFIX "so"
#define HAVE_OPENDIR 1
#endif

40
host/test-c.c Normal file
View File

@ -0,0 +1,40 @@
#include <vamp-hostsdk/host-c.h>
#include <stdio.h>
int main(int argc, char **argv)
{
int i;
int libcount = vhGetLibraryCount();
printf("Vamp plugin libraries found:\n");
for (i = 0; i < libcount; ++i) {
printf("%d: %s\n", i, vhGetLibraryName(i));
}
printf("Going to try loading qm-vamp-plugins...\n");
int libindex = vhGetLibraryIndex("qm-vamp-plugins");
vhLibrary lib = vhLoadLibrary(libindex);
if (!lib) {
printf("Failure!\n");
return 1;
}
int plugincount = vhGetPluginCount(lib);
printf("Success: it contains %d plugins; they are:\n", plugincount);
for (i = 0; i < plugincount; ++i) {
const VampPluginDescriptor *descriptor = vhGetPluginDescriptor(lib, i);
if (!descriptor) {
printf("<unknown! failed to load>\n");
} else {
printf("%s\n", descriptor->identifier);
}
}
vhUnloadLibrary(lib);
return 0;
}

863
host/vamp-simple-host.cpp Normal file
View File

@ -0,0 +1,863 @@
/* -*- c-basic-offset: 4 indent-tabs-mode: nil -*- vi:set ts=8 sts=4 sw=4: */
/*
Vamp
An API for audio analysis and feature extraction plugins.
Centre for Digital Music, Queen Mary, University of London.
Copyright 2006 Chris Cannam, copyright 2007-2008 QMUL.
Permission is hereby granted, free of charge, to any person
obtaining a copy of this software and associated documentation
files (the "Software"), to deal in the Software without
restriction, including without limitation the rights to use, copy,
modify, merge, publish, distribute, sublicense, and/or sell copies
of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be
included in all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR
ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF
CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
Except as contained in this notice, the names of the Centre for
Digital Music; Queen Mary, University of London; and Chris Cannam
shall not be used in advertising or otherwise to promote the sale,
use or other dealings in this Software without prior written
authorization.
*/
/*
* This "simple" Vamp plugin host is no longer as simple as it was; it
* now has a lot of options and includes a lot of code to handle the
* various useful listing modes it supports.
*
* However, the runPlugin function still contains a reasonable
* implementation of a fairly generic Vamp plugin host capable of
* evaluating a given output on a given plugin for a sound file read
* via libsndfile.
*/
#include <vamp-hostsdk/PluginHostAdapter.h>
#include <vamp-hostsdk/PluginInputDomainAdapter.h>
#include <vamp-hostsdk/PluginLoader.h>
#include <iostream>
#include <fstream>
#include <set>
#include <sndfile.h>
#include <cstring>
#include <cstdlib>
#include "system.h"
#include <cmath>
using namespace std;
using Vamp::Plugin;
using Vamp::PluginHostAdapter;
using Vamp::RealTime;
using Vamp::HostExt::PluginLoader;
using Vamp::HostExt::PluginWrapper;
using Vamp::HostExt::PluginInputDomainAdapter;
#define HOST_VERSION "1.5"
enum Verbosity {
PluginIds,
PluginOutputIds,
PluginInformation,
PluginInformationDetailed
};
void printFeatures(int, int,
const Plugin::OutputDescriptor &, int,
const Plugin::FeatureSet &, ofstream *, bool frames);
void transformInput(float *, size_t);
void fft(unsigned int, bool, double *, double *, double *, double *);
void printPluginPath(bool verbose);
void printPluginCategoryList();
void enumeratePlugins(Verbosity);
void listPluginsInLibrary(string soname);
int runPlugin(string myname, string soname, string id, string output,
int outputNo, string inputFile, string outfilename, bool frames);
void usage(const char *name)
{
cerr << "\n"
<< name << ": A command-line host for Vamp audio analysis plugins.\n\n"
"Centre for Digital Music, Queen Mary, University of London.\n"
"Copyright 2006-2009 Chris Cannam and QMUL.\n"
"Freely redistributable; published under a BSD-style license.\n\n"
"Usage:\n\n"
" " << name << " [-s] pluginlibrary[." << PLUGIN_SUFFIX << "]:plugin[:output] file.wav [-o out.txt]\n"
" " << name << " [-s] pluginlibrary[." << PLUGIN_SUFFIX << "]:plugin file.wav [outputno] [-o out.txt]\n\n"
" -- Load plugin id \"plugin\" from \"pluginlibrary\" and run it on the\n"
" audio data in \"file.wav\", retrieving the named \"output\", or output\n"
" number \"outputno\" (the first output by default) and dumping it to\n"
" standard output, or to \"out.txt\" if the -o option is given.\n\n"
" \"pluginlibrary\" should be a library name, not a file path; the\n"
" standard Vamp library search path will be used to locate it. If\n"
" a file path is supplied, the directory part(s) will be ignored.\n\n"
" If the -s option is given, results will be labelled with the audio\n"
" sample frame at which they occur. Otherwise, they will be labelled\n"
" with time in seconds.\n\n"
" " << name << " -l\n"
" " << name << " --list\n\n"
" -- List the plugin libraries and Vamp plugins in the library search path\n"
" in a verbose human-readable format.\n\n"
" " << name << " -L\n"
" " << name << " --list-full\n\n"
" -- List all data reported by all the Vamp plugins in the library search\n"
" path in a very verbose human-readable format.\n\n"
" " << name << " --list-ids\n\n"
" -- List the plugins in the search path in a terse machine-readable format,\n"
" in the form vamp:soname:identifier.\n\n"
" " << name << " --list-outputs\n\n"
" -- List the outputs for plugins in the search path in a machine-readable\n"
" format, in the form vamp:soname:identifier:output.\n\n"
" " << name << " --list-by-category\n\n"
" -- List the plugins as a plugin index by category, in a machine-readable\n"
" format. The format may change in future releases.\n\n"
" " << name << " -p\n\n"
" -- Print out the Vamp library search path.\n\n"
" " << name << " -v\n\n"
" -- Display version information only.\n"
<< endl;
exit(2);
}
int main(int argc, char **argv)
{
char *scooter = argv[0];
char *name = 0;
while (scooter && *scooter) {
if (*scooter == '/' || *scooter == '\\') name = ++scooter;
else ++scooter;
}
if (!name || !*name) name = argv[0];
if (argc < 2) usage(name);
if (argc == 2) {
if (!strcmp(argv[1], "-v")) {
cout << "Simple Vamp plugin host version: " << HOST_VERSION << endl
<< "Vamp API version: " << VAMP_API_VERSION << endl
<< "Vamp SDK version: " << VAMP_SDK_VERSION << endl;
return 0;
} else if (!strcmp(argv[1], "-l") || !strcmp(argv[1], "--list")) {
printPluginPath(true);
enumeratePlugins(PluginInformation);
return 0;
} else if (!strcmp(argv[1], "-L") || !strcmp(argv[1], "--list-full")) {
enumeratePlugins(PluginInformationDetailed);
return 0;
} else if (!strcmp(argv[1], "-p")) {
printPluginPath(false);
return 0;
} else if (!strcmp(argv[1], "--list-ids")) {
enumeratePlugins(PluginIds);
return 0;
} else if (!strcmp(argv[1], "--list-outputs")) {
enumeratePlugins(PluginOutputIds);
return 0;
} else if (!strcmp(argv[1], "--list-by-category")) {
printPluginCategoryList();
return 0;
} else usage(name);
}
if (argc < 3) usage(name);
bool useFrames = false;
int base = 1;
if (!strcmp(argv[1], "-s")) {
useFrames = true;
base = 2;
}
string soname = argv[base];
string wavname = argv[base+1];
string plugid = "";
string output = "";
int outputNo = -1;
string outfilename;
if (argc >= base+3) {
int idx = base+2;
if (isdigit(*argv[idx])) {
outputNo = atoi(argv[idx++]);
}
if (argc == idx + 2) {
if (!strcmp(argv[idx], "-o")) {
outfilename = argv[idx+1];
} else usage(name);
} else if (argc != idx) {
(usage(name));
}
}
cerr << endl << name << ": Running..." << endl;
cerr << "Reading file: \"" << wavname << "\", writing to ";
if (outfilename == "") {
cerr << "standard output" << endl;
} else {
cerr << "\"" << outfilename << "\"" << endl;
}
string::size_type sep = soname.find(':');
if (sep != string::npos) {
plugid = soname.substr(sep + 1);
soname = soname.substr(0, sep);
sep = plugid.find(':');
if (sep != string::npos) {
output = plugid.substr(sep + 1);
plugid = plugid.substr(0, sep);
}
}
if (plugid == "") {
usage(name);
}
if (output != "" && outputNo != -1) {
usage(name);
}
if (output == "" && outputNo == -1) {
outputNo = 0;
}
return runPlugin(name, soname, plugid, output, outputNo,
wavname, outfilename, useFrames);
}
int runPlugin(string myname, string soname, string id,
string output, int outputNo, string wavname,
string outfilename, bool useFrames)
{
PluginLoader *loader = PluginLoader::getInstance();
PluginLoader::PluginKey key = loader->composePluginKey(soname, id);
SNDFILE *sndfile;
SF_INFO sfinfo;
memset(&sfinfo, 0, sizeof(SF_INFO));
sndfile = sf_open(wavname.c_str(), SFM_READ, &sfinfo);
if (!sndfile) {
cerr << myname << ": ERROR: Failed to open input file \""
<< wavname << "\": " << sf_strerror(sndfile) << endl;
return 1;
}
ofstream *out = 0;
if (outfilename != "") {
out = new ofstream(outfilename.c_str(), ios::out);
if (!*out) {
cerr << myname << ": ERROR: Failed to open output file \""
<< outfilename << "\" for writing" << endl;
delete out;
return 1;
}
}
Plugin *plugin = loader->loadPlugin
(key, sfinfo.samplerate, PluginLoader::ADAPT_ALL_SAFE);
if (!plugin) {
cerr << myname << ": ERROR: Failed to load plugin \"" << id
<< "\" from library \"" << soname << "\"" << endl;
sf_close(sndfile);
if (out) {
out->close();
delete out;
}
return 1;
}
cerr << "Running plugin: \"" << plugin->getIdentifier() << "\"..." << endl;
// Note that the following would be much simpler if we used a
// PluginBufferingAdapter as well -- i.e. if we had passed
// PluginLoader::ADAPT_ALL to loader->loadPlugin() above, instead
// of ADAPT_ALL_SAFE. Then we could simply specify our own block
// size, keep the step size equal to the block size, and ignore
// the plugin's bleatings. However, there are some issues with
// using a PluginBufferingAdapter that make the results sometimes
// technically different from (if effectively the same as) the
// un-adapted plugin, so we aren't doing that here. See the
// PluginBufferingAdapter documentation for details.
int blockSize = plugin->getPreferredBlockSize();
int stepSize = plugin->getPreferredStepSize();
if (blockSize == 0) {
blockSize = 1024;
}
if (stepSize == 0) {
if (plugin->getInputDomain() == Plugin::FrequencyDomain) {
stepSize = blockSize/2;
} else {
stepSize = blockSize;
}
} else if (stepSize > blockSize) {
cerr << "WARNING: stepSize " << stepSize << " > blockSize " << blockSize << ", resetting blockSize to ";
if (plugin->getInputDomain() == Plugin::FrequencyDomain) {
blockSize = stepSize * 2;
} else {
blockSize = stepSize;
}
cerr << blockSize << endl;
}
int overlapSize = blockSize - stepSize;
sf_count_t currentStep = 0;
int finalStepsRemaining = max(1, (blockSize / stepSize) - 1); // at end of file, this many part-silent frames needed after we hit EOF
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 + 2];
cerr << "Using block size = " << blockSize << ", step size = "
<< stepSize << endl;
// The channel queries here are for informational purposes only --
// a PluginChannelAdapter is being used automatically behind the
// scenes, and it will take case of any channel mismatch
int minch = plugin->getMinChannelCount();
int maxch = plugin->getMaxChannelCount();
cerr << "Plugin accepts " << minch << " -> " << maxch << " channel(s)" << endl;
cerr << "Sound file has " << channels << " (will mix/augment if necessary)" << endl;
Plugin::OutputList outputs = plugin->getOutputDescriptors();
Plugin::OutputDescriptor od;
Plugin::FeatureSet features;
int returnValue = 1;
int progress = 0;
RealTime rt;
PluginWrapper *wrapper = 0;
RealTime adjustment = RealTime::zeroTime;
if (outputs.empty()) {
cerr << "ERROR: Plugin has no outputs!" << endl;
goto done;
}
if (outputNo < 0) {
for (size_t oi = 0; oi < outputs.size(); ++oi) {
if (outputs[oi].identifier == output) {
outputNo = oi;
break;
}
}
if (outputNo < 0) {
cerr << "ERROR: Non-existent output \"" << output << "\" requested" << endl;
goto done;
}
} else {
if (int(outputs.size()) <= outputNo) {
cerr << "ERROR: Output " << outputNo << " requested, but plugin has only " << outputs.size() << " output(s)" << endl;
goto done;
}
}
od = outputs[outputNo];
cerr << "Output is: \"" << od.identifier << "\"" << endl;
if (!plugin->initialise(channels, stepSize, blockSize)) {
cerr << "ERROR: Plugin initialise (channels = " << channels
<< ", stepSize = " << stepSize << ", blockSize = "
<< blockSize << ") failed." << endl;
goto done;
}
wrapper = dynamic_cast<PluginWrapper *>(plugin);
if (wrapper) {
// See documentation for
// PluginInputDomainAdapter::getTimestampAdjustment
PluginInputDomainAdapter *ida =
wrapper->getWrapper<PluginInputDomainAdapter>();
if (ida) adjustment = ida->getTimestampAdjustment();
}
// Here we iterate over the frames, avoiding asking the numframes in case it's streaming input.
do {
int count;
if ((blockSize==stepSize) || (currentStep==0)) {
// read a full fresh block
if ((count = sf_readf_float(sndfile, filebuf, blockSize)) < 0) {
cerr << "ERROR: sf_readf_float failed: " << sf_strerror(sndfile) << endl;
break;
}
if (count != blockSize) --finalStepsRemaining;
} else {
// otherwise shunt the existing data down and read the remainder.
memmove(filebuf, filebuf + (stepSize * channels), overlapSize * channels * sizeof(float));
if ((count = sf_readf_float(sndfile, filebuf + (overlapSize * channels), stepSize)) < 0) {
cerr << "ERROR: sf_readf_float failed: " << sf_strerror(sndfile) << endl;
break;
}
if (count != stepSize) --finalStepsRemaining;
count += overlapSize;
}
for (int c = 0; c < channels; ++c) {
int j = 0;
while (j < count) {
plugbuf[c][j] = filebuf[j * sfinfo.channels + c];
++j;
}
while (j < blockSize) {
plugbuf[c][j] = 0.0f;
++j;
}
}
rt = RealTime::frame2RealTime(currentStep * stepSize, sfinfo.samplerate);
features = plugin->process(plugbuf, rt);
printFeatures
(RealTime::realTime2Frame(rt + adjustment, sfinfo.samplerate),
sfinfo.samplerate, od, outputNo, features, out, useFrames);
if (sfinfo.frames > 0){
int pp = progress;
progress = (int)((float(currentStep * stepSize) / sfinfo.frames) * 100.f + 0.5f);
if (progress != pp && out) {
cerr << "\r" << progress << "%";
}
}
++currentStep;
} while (finalStepsRemaining > 0);
if (out) cerr << "\rDone" << endl;
rt = RealTime::frame2RealTime(currentStep * stepSize, sfinfo.samplerate);
features = plugin->getRemainingFeatures();
printFeatures(RealTime::realTime2Frame(rt + adjustment, sfinfo.samplerate),
sfinfo.samplerate, od, outputNo, features, out, useFrames);
returnValue = 0;
done:
delete plugin;
if (out) {
out->close();
delete out;
}
sf_close(sndfile);
return returnValue;
}
static double
toSeconds(const RealTime &time)
{
return time.sec + double(time.nsec + 1) / 1000000000.0;
}
void
printFeatures(int frame, int sr,
const Plugin::OutputDescriptor &output, int outputNo,
const Plugin::FeatureSet &features, ofstream *out, bool useFrames)
{
static int featureCount = -1;
if (features.find(outputNo) == features.end()) return;
for (size_t i = 0; i < features.at(outputNo).size(); ++i) {
const Plugin::Feature &f = features.at(outputNo).at(i);
bool haveRt = false;
RealTime rt;
if (output.sampleType == Plugin::OutputDescriptor::VariableSampleRate) {
rt = f.timestamp;
haveRt = true;
} else if (output.sampleType == Plugin::OutputDescriptor::FixedSampleRate) {
int n = featureCount + 1;
if (f.hasTimestamp) {
n = int(round(toSeconds(f.timestamp) * output.sampleRate));
}
rt = RealTime::fromSeconds(double(n) / output.sampleRate);
haveRt = true;
featureCount = n;
}
if (useFrames) {
int displayFrame = frame;
if (haveRt) {
displayFrame = RealTime::realTime2Frame(rt, sr);
}
(out ? *out : cout) << displayFrame;
if (f.hasDuration) {
displayFrame = RealTime::realTime2Frame(f.duration, sr);
(out ? *out : cout) << "," << displayFrame;
}
(out ? *out : cout) << ":";
} else {
if (!haveRt) {
rt = RealTime::frame2RealTime(frame, sr);
}
(out ? *out : cout) << rt.toString();
if (f.hasDuration) {
rt = f.duration;
(out ? *out : cout) << "," << rt.toString();
}
(out ? *out : cout) << ":";
}
for (unsigned int j = 0; j < f.values.size(); ++j) {
(out ? *out : cout) << " " << f.values[j];
}
(out ? *out : cout) << " " << f.label;
(out ? *out : cout) << endl;
}
}
void
printPluginPath(bool verbose)
{
if (verbose) {
cout << "\nVamp plugin search path: ";
}
vector<string> path = PluginHostAdapter::getPluginPath();
for (size_t i = 0; i < path.size(); ++i) {
if (verbose) {
cout << "[" << path[i] << "]";
} else {
cout << path[i] << endl;
}
}
if (verbose) cout << endl;
}
static
string
header(string text, int level)
{
string out = '\n' + text + '\n';
for (size_t i = 0; i < text.length(); ++i) {
out += (level == 1 ? '=' : level == 2 ? '-' : '~');
}
out += '\n';
return out;
}
void
enumeratePlugins(Verbosity verbosity)
{
PluginLoader *loader = PluginLoader::getInstance();
if (verbosity == PluginInformation) {
cout << "\nVamp plugin libraries found in search path:" << endl;
}
vector<PluginLoader::PluginKey> plugins = loader->listPlugins();
typedef multimap<string, PluginLoader::PluginKey>
LibraryMap;
LibraryMap libraryMap;
for (size_t i = 0; i < plugins.size(); ++i) {
string path = loader->getLibraryPathForPlugin(plugins[i]);
libraryMap.insert(LibraryMap::value_type(path, plugins[i]));
}
string prevPath = "";
int index = 0;
for (LibraryMap::iterator i = libraryMap.begin();
i != libraryMap.end(); ++i) {
string path = i->first;
PluginLoader::PluginKey key = i->second;
if (path != prevPath) {
prevPath = path;
index = 0;
if (verbosity == PluginInformation) {
cout << "\n " << path << ":" << endl;
} else if (verbosity == PluginInformationDetailed) {
string::size_type ki = i->second.find(':');
string text = "Library \"" + i->second.substr(0, ki) + "\"";
cout << "\n" << header(text, 1);
}
}
Plugin *plugin = loader->loadPlugin(key, 48000);
if (plugin) {
char c = char('A' + index);
if (c > 'Z') c = char('a' + (index - 26));
PluginLoader::PluginCategoryHierarchy category =
loader->getPluginCategory(key);
string catstr;
if (!category.empty()) {
for (size_t ci = 0; ci < category.size(); ++ci) {
if (ci > 0) catstr += " > ";
catstr += category[ci];
}
}
if (verbosity == PluginInformation) {
cout << " [" << c << "] [v"
<< plugin->getVampApiVersion() << "] "
<< plugin->getName() << ", \""
<< plugin->getIdentifier() << "\"" << " ["
<< plugin->getMaker() << "]" << endl;
if (catstr != "") {
cout << " > " << catstr << endl;
}
if (plugin->getDescription() != "") {
cout << " - " << plugin->getDescription() << endl;
}
} else if (verbosity == PluginInformationDetailed) {
cout << header(plugin->getName(), 2);
cout << " - Identifier: "
<< key << endl;
cout << " - Plugin Version: "
<< plugin->getPluginVersion() << endl;
cout << " - Vamp API Version: "
<< plugin->getVampApiVersion() << endl;
cout << " - Maker: \""
<< plugin->getMaker() << "\"" << endl;
cout << " - Copyright: \""
<< plugin->getCopyright() << "\"" << endl;
cout << " - Description: \""
<< plugin->getDescription() << "\"" << endl;
cout << " - Input Domain: "
<< (plugin->getInputDomain() == Vamp::Plugin::TimeDomain ?
"Time Domain" : "Frequency Domain") << endl;
cout << " - Default Step Size: "
<< plugin->getPreferredStepSize() << endl;
cout << " - Default Block Size: "
<< plugin->getPreferredBlockSize() << endl;
cout << " - Minimum Channels: "
<< plugin->getMinChannelCount() << endl;
cout << " - Maximum Channels: "
<< plugin->getMaxChannelCount() << endl;
} else if (verbosity == PluginIds) {
cout << "vamp:" << key << endl;
}
Plugin::OutputList outputs =
plugin->getOutputDescriptors();
if (verbosity == PluginInformationDetailed) {
Plugin::ParameterList params = plugin->getParameterDescriptors();
for (size_t j = 0; j < params.size(); ++j) {
Plugin::ParameterDescriptor &pd(params[j]);
cout << "\nParameter " << j+1 << ": \"" << pd.name << "\"" << endl;
cout << " - Identifier: " << pd.identifier << endl;
cout << " - Description: \"" << pd.description << "\"" << endl;
if (pd.unit != "") {
cout << " - Unit: " << pd.unit << endl;
}
cout << " - Range: ";
cout << pd.minValue << " -> " << pd.maxValue << endl;
cout << " - Default: ";
cout << pd.defaultValue << endl;
if (pd.isQuantized) {
cout << " - Quantize Step: "
<< pd.quantizeStep << endl;
}
if (!pd.valueNames.empty()) {
cout << " - Value Names: ";
for (size_t k = 0; k < pd.valueNames.size(); ++k) {
if (k > 0) cout << ", ";
cout << "\"" << pd.valueNames[k] << "\"";
}
cout << endl;
}
}
if (outputs.empty()) {
cout << "\n** Note: This plugin reports no outputs!" << endl;
}
for (size_t j = 0; j < outputs.size(); ++j) {
Plugin::OutputDescriptor &od(outputs[j]);
cout << "\nOutput " << j+1 << ": \"" << od.name << "\"" << endl;
cout << " - Identifier: " << od.identifier << endl;
cout << " - Description: \"" << od.description << "\"" << endl;
if (od.unit != "") {
cout << " - Unit: " << od.unit << endl;
}
if (od.hasFixedBinCount) {
cout << " - Default Bin Count: " << od.binCount << endl;
}
if (!od.binNames.empty()) {
bool have = false;
for (size_t k = 0; k < od.binNames.size(); ++k) {
if (od.binNames[k] != "") {
have = true; break;
}
}
if (have) {
cout << " - Bin Names: ";
for (size_t k = 0; k < od.binNames.size(); ++k) {
if (k > 0) cout << ", ";
cout << "\"" << od.binNames[k] << "\"";
}
cout << endl;
}
}
if (od.hasKnownExtents) {
cout << " - Default Extents: ";
cout << od.minValue << " -> " << od.maxValue << endl;
}
if (od.isQuantized) {
cout << " - Quantize Step: "
<< od.quantizeStep << endl;
}
cout << " - Sample Type: "
<< (od.sampleType ==
Plugin::OutputDescriptor::OneSamplePerStep ?
"One Sample Per Step" :
od.sampleType ==
Plugin::OutputDescriptor::FixedSampleRate ?
"Fixed Sample Rate" :
"Variable Sample Rate") << endl;
if (od.sampleType !=
Plugin::OutputDescriptor::OneSamplePerStep) {
cout << " - Default Rate: "
<< od.sampleRate << endl;
}
cout << " - Has Duration: "
<< (od.hasDuration ? "Yes" : "No") << endl;
}
}
if (outputs.size() > 1 || verbosity == PluginOutputIds) {
for (size_t j = 0; j < outputs.size(); ++j) {
if (verbosity == PluginInformation) {
cout << " (" << j << ") "
<< outputs[j].name << ", \""
<< outputs[j].identifier << "\"" << endl;
if (outputs[j].description != "") {
cout << " - "
<< outputs[j].description << endl;
}
} else if (verbosity == PluginOutputIds) {
cout << "vamp:" << key << ":" << outputs[j].identifier << endl;
}
}
}
++index;
delete plugin;
}
}
if (verbosity == PluginInformation ||
verbosity == PluginInformationDetailed) {
cout << endl;
}
}
void
printPluginCategoryList()
{
PluginLoader *loader = PluginLoader::getInstance();
vector<PluginLoader::PluginKey> plugins = loader->listPlugins();
set<string> printedcats;
for (size_t i = 0; i < plugins.size(); ++i) {
PluginLoader::PluginKey key = plugins[i];
PluginLoader::PluginCategoryHierarchy category =
loader->getPluginCategory(key);
Plugin *plugin = loader->loadPlugin(key, 48000);
if (!plugin) continue;
string catstr = "";
if (category.empty()) catstr = '|';
else {
for (size_t j = 0; j < category.size(); ++j) {
catstr += category[j];
catstr += '|';
if (printedcats.find(catstr) == printedcats.end()) {
std::cout << catstr << std::endl;
printedcats.insert(catstr);
}
}
}
std::cout << catstr << key << ":::" << plugin->getName() << ":::" << plugin->getMaker() << ":::" << plugin->getDescription() << std::endl;
}
}

39
meson.build Normal file
View File

@ -0,0 +1,39 @@
project(
meson.current_source_dir().split('/').get(-1),
'cpp',
version : run_command('git', 'rev-parse', '--short', 'HEAD', check: false).stdout().strip(),
default_options : [
'warning_level=1',
'optimization=3',
'cpp_std=c++20'
])
add_project_arguments (
'-Wpedantic',
'-Wno-shadow',
'-Wno-unused-but-set-variable',
'-Wno-comment',
'-Wno-unused-parameter',
'-Wno-unused-value',
'-Wno-missing-field-initializers',
'-Wno-narrowing',
'-Wno-deprecated-enum-enum-conversion',
'-Wno-volatile',
'-Wno-format-security',
'-Wno-switch',
'-Wno-ignored-attributes',
'-Wno-unused-variable',
'-Wno-deprecated-enum-enum-conversion',
language: 'cpp'
)
#args = ['-lglfw', '-ldl', '-lGL', '-lpthread', '-lX11', '-lXxf86vm', '-lXrandr', '-lXi']
args = []
inc = []
deps = [
subproject('hack').get_variable('hack_dep'),
]
subdir('src')
subdir('test')

23
run.sh Executable file
View File

@ -0,0 +1,23 @@
#!/bin/zsh
PROJECT_NAME=$(basename $PWD)
run() {
command meson compile -C build
if [[ -z "$1" ]]; then
cd build
./test/$PROJECT_NAME
cd ..
else
meson test $1 -C build
fi
}
if [ -d "build" ]; then
run
else
command meson setup build
run
fi

59
src/meson.build Normal file
View File

@ -0,0 +1,59 @@
inc += include_directories('.')
headers = [
'vamp.h',
'vamp-hostsdk/Files.h',
'vamp-hostsdk/host-c.h',
'vamp-hostsdk/PluginBufferingAdapter.h',
'vamp-hostsdk/PluginChannelAdapter.h',
'vamp-hostsdk/PluginHostAdapter.h',
'vamp-hostsdk/PluginInputDomainAdapter.h',
'vamp-hostsdk/PluginLoader.h',
'vamp-hostsdk/PluginSummarisingAdapter.h',
'vamp-hostsdk/PluginWrapper.h',
'vamp-hostsdk/vamp-hostsdk.h',
'vamp-hostsdk/Window.h',
'vamp-sdk/ext/vamp_kiss_fft.h',
'vamp-sdk/ext/vamp_kiss_fft_guts.h',
'vamp-sdk/ext/vamp_kiss_fftr.h',
'vamp-sdk/FFT.h',
'vamp-sdk/Plugin.h',
'vamp-sdk/PluginAdapter.h',
'vamp-sdk/PluginBase.h',
'vamp-sdk/RealTime.h',
'vamp-sdk/vamp-sdk.h'
]
sources = [
'vamp-hostsdk/acsymbols.cpp',
'vamp-hostsdk/Files.cpp',
'vamp-hostsdk/host-c.cpp',
'vamp-hostsdk/PluginBufferingAdapter.cpp',
'vamp-hostsdk/PluginChannelAdapter.cpp',
'vamp-hostsdk/PluginHostAdapter.cpp',
'vamp-hostsdk/PluginInputDomainAdapter.cpp',
'vamp-hostsdk/PluginLoader.cpp',
'vamp-hostsdk/PluginSummarisingAdapter.cpp',
'vamp-hostsdk/PluginWrapper.cpp',
'vamp-sdk/acsymbols.cpp',
'vamp-sdk/FFT.cpp',
'vamp-sdk/PluginAdapter.cpp',
'vamp-sdk/RealTime.cpp'
]
lib = library(
meson.project_name(),
include_directories : inc,
sources: [headers, sources],
dependencies : deps,
cpp_args: args
)
deps += declare_dependency(
include_directories: inc,
link_with: lib,
)

195
src/vamp-hostsdk/Files.cpp Normal file
View File

@ -0,0 +1,195 @@
#include "vamp-hostsdk/PluginHostAdapter.h"
#include "vamp-hostsdk/Files.h"
#include <cctype> // tolower
#include <cstring>
#include <cstdlib>
#include <dirent.h>
#include <dlfcn.h>
#define PLUGIN_SUFFIX "so"
std::vector<std::string> Files::listLibraryFiles()
{
return listLibraryFilesMatching(Filter());
}
std::vector<std::string> Files::listLibraryFilesMatching(Filter filter)
{
std::vector<std::string> path = Vamp::PluginHostAdapter::getPluginPath();
std::vector<std::string> libraryFiles;
// we match case-insensitively, but only with ascii range
// characters (input strings are expected to be utf-8)
std::vector<std::string> libraryNames;
for (int j = 0; j < int(filter.libraryNames.size()); ++j) {
std::string n = filter.libraryNames[j];
for (size_t i = 0; i < n.length(); ++i) {
if (!(n[i] & 0x80)) {
n[i] = char(tolower(n[i]));
}
}
libraryNames.push_back(n);
}
for (size_t i = 0; i < path.size(); ++i) {
std::vector<std::string> files = listFiles(path[i], PLUGIN_SUFFIX);
for (std::vector<std::string>::iterator fi = files.begin();
fi != files.end(); ++fi) {
// we match case-insensitively, but only with ascii range
// characters (this string is expected to be utf-8)
std::string cleaned = *fi;
for (size_t j = 0; j < cleaned.length(); ++j) {
if (!(cleaned[j] & 0x80)) {
cleaned[j] = char(tolower(cleaned[j]));
}
}
// libraryName should be lacking an extension, as it is
// supposed to have come from the plugin key
std::string::size_type pi = cleaned.find('.');
if (pi != std::string::npos) {
cleaned = cleaned.substr(0, pi);
}
bool matched = false;
switch (filter.type) {
case Filter::All:
matched = true;
break;
case Filter::Matching:
for (int j = 0; j < int(libraryNames.size()); ++j) {
if (cleaned == libraryNames[j]) {
matched = true;
break;
}
}
break;
case Filter::NotMatching:
matched = true;
for (int j = 0; j < int(libraryNames.size()); ++j) {
if (cleaned == libraryNames[j]) {
matched = false;
break;
}
}
break;
}
if (!matched) continue;
std::string fullPath = path[i];
fullPath = splicePath(fullPath, *fi);
libraryFiles.push_back(fullPath);
}
}
return libraryFiles;
}
void* Files::loadLibrary(std::string path)
{
void *handle = 0;
handle = dlopen(path.c_str(), RTLD_LAZY | RTLD_LOCAL);
if (!handle) {
std::cerr << "Vamp::HostExt: Unable to load library \""
<< path << "\": " << dlerror() << std::endl;
}
return handle;
}
void Files::unloadLibrary(void *handle)
{
dlclose(handle);
}
void* Files::lookupInLibrary(void *handle, const char *symbol)
{
return (void *)dlsym(handle, symbol);
}
std::string Files::lcBasename(std::string path)
{
std::string basename(path);
std::string::size_type li = basename.rfind('/');
if (li != std::string::npos) basename = basename.substr(li + 1);
li = basename.find('.');
if (li != std::string::npos) basename = basename.substr(0, li);
// case-insensitive, but only with ascii range characters (this
// string is expected to be utf-8)
for (size_t i = 0; i < basename.length(); ++i) {
if (!(basename[i] & 0x80)) {
basename[i] = char(tolower(basename[i]));
}
}
return basename;
}
std::string Files::splicePath(std::string a, std::string b)
{
return a + "/" + b;
}
std::vector<std::string> Files::listFiles(std::string dir, std::string extension)
{
std::vector<std::string> files;
size_t extlen = extension.length();
DIR* d = opendir(dir.c_str());
if (!d) return files;
struct dirent* e = 0;
while ((e = readdir(d)))
{
size_t len = strlen(e->d_name);
if (len < extlen + 2 || e->d_name + len - extlen - 1 != "." + extension)
continue;
files.push_back(e->d_name);
}
closedir(d);
return files;
}
bool Files::isNonNative32Bit()
{
// Return true if we are running on a system for which we should
// use the VAMP_PATH_32 variable instead of VAMP_PATH. This will
// be the case if we are a 32-bit executable but the OS is
// natively 64-bit.
//
// This currently works only on Windows; other operating systems
// will use VAMP_PATH always.
if (sizeof(void *) == 8)
return false;
return false;
}
bool Files::getEnvUtf8(std::string variable, std::string &value)
{
value = "";
char *val = getenv(variable.c_str());
if (!val) {
return false;
}
value = val;
return true;
}

31
src/vamp-hostsdk/Files.h Normal file
View File

@ -0,0 +1,31 @@
#pragma once
#include <vector>
#include <string>
/**
* This is a private implementation class for the Vamp Host SDK.
*/
class Files
{
public:
static std::vector<std::string> listLibraryFiles();
struct Filter {
enum { All, Matching, NotMatching } type;
std::vector<std::string> libraryNames;
Filter() : type(All) { }
};
static std::vector<std::string> listLibraryFilesMatching(Filter);
static void *loadLibrary(std::string filename);
static void unloadLibrary(void *);
static void *lookupInLibrary(void *, const char *symbol);
static std::string lcBasename(std::string path);
static std::string splicePath(std::string a, std::string b);
static std::vector<std::string> listFiles(std::string dir, std::string ext);
static bool isNonNative32Bit();
static bool getEnvUtf8(std::string variable, std::string &value);
};

View File

@ -0,0 +1,722 @@
#include <vector>
#include <map>
#include "vamp-hostsdk/PluginBufferingAdapter.h"
#include "vamp-hostsdk/PluginInputDomainAdapter.h"
#include <iostream>
using std::cerr;
using std::endl;
using std::vector;
using std::map;
namespace Vamp {
namespace HostExt {
class PluginBufferingAdapter::Impl
{
public:
Impl(Plugin *plugin, float inputSampleRate);
~Impl();
void setPluginStepSize(size_t stepSize);
void setPluginBlockSize(size_t blockSize);
bool initialise(size_t channels, size_t stepSize, size_t blockSize);
void getActualStepAndBlockSizes(size_t &stepSize, size_t &blockSize);
OutputList getOutputDescriptors() const;
void setParameter(std::string, float);
void selectProgram(std::string);
void reset();
FeatureSet process(const float *const *inputBuffers, RealTime timestamp);
FeatureSet getRemainingFeatures();
protected:
class RingBuffer
{
public:
RingBuffer(int n) :
m_buffer(new float[n+1]), m_writer(0), m_reader(0), m_size(n+1) { }
virtual ~RingBuffer() { delete[] m_buffer; }
int getSize() const { return m_size-1; }
void reset() { m_writer = 0; m_reader = 0; }
int getReadSpace() const {
int writer = m_writer, reader = m_reader, space;
if (writer > reader) space = writer - reader;
else if (writer < reader) space = (writer + m_size) - reader;
else space = 0;
return space;
}
int getWriteSpace() const {
int writer = m_writer;
int reader = m_reader;
int space = (reader + m_size - writer - 1);
if (space >= m_size) space -= m_size;
return space;
}
int peek(float *destination, int n) const {
int available = getReadSpace();
if (n > available) {
for (int i = available; i < n; ++i) {
destination[i] = 0.f;
}
n = available;
}
if (n == 0) return n;
int reader = m_reader;
int here = m_size - reader;
const float *const bufbase = m_buffer + reader;
if (here >= n) {
for (int i = 0; i < n; ++i) {
destination[i] = bufbase[i];
}
} else {
for (int i = 0; i < here; ++i) {
destination[i] = bufbase[i];
}
float *const destbase = destination + here;
const int nh = n - here;
for (int i = 0; i < nh; ++i) {
destbase[i] = m_buffer[i];
}
}
return n;
}
int skip(int n) {
int available = getReadSpace();
if (n > available) {
n = available;
}
if (n == 0) return n;
int reader = m_reader;
reader += n;
while (reader >= m_size) reader -= m_size;
m_reader = reader;
return n;
}
int write(const float *source, int n) {
int available = getWriteSpace();
if (n > available) {
n = available;
}
if (n == 0) return n;
int writer = m_writer;
int here = m_size - writer;
float *const bufbase = m_buffer + writer;
if (here >= n) {
for (int i = 0; i < n; ++i) {
bufbase[i] = source[i];
}
} else {
for (int i = 0; i < here; ++i) {
bufbase[i] = source[i];
}
const int nh = n - here;
const float *const srcbase = source + here;
float *const buf = m_buffer;
for (int i = 0; i < nh; ++i) {
buf[i] = srcbase[i];
}
}
writer += n;
while (writer >= m_size) writer -= m_size;
m_writer = writer;
return n;
}
int zero(int n) {
int available = getWriteSpace();
if (n > available) {
n = available;
}
if (n == 0) return n;
int writer = m_writer;
int here = m_size - writer;
float *const bufbase = m_buffer + writer;
if (here >= n) {
for (int i = 0; i < n; ++i) {
bufbase[i] = 0.f;
}
} else {
for (int i = 0; i < here; ++i) {
bufbase[i] = 0.f;
}
const int nh = n - here;
for (int i = 0; i < nh; ++i) {
m_buffer[i] = 0.f;
}
}
writer += n;
while (writer >= m_size) writer -= m_size;
m_writer = writer;
return n;
}
protected:
float *m_buffer;
int m_writer;
int m_reader;
int m_size;
private:
RingBuffer(const RingBuffer &); // not provided
RingBuffer &operator=(const RingBuffer &); // not provided
};
Plugin *m_plugin;
size_t m_inputStepSize; // value passed to wrapper initialise()
size_t m_inputBlockSize; // value passed to wrapper initialise()
size_t m_setStepSize; // value passed to setPluginStepSize()
size_t m_setBlockSize; // value passed to setPluginBlockSize()
size_t m_stepSize; // value actually used to initialise plugin
size_t m_blockSize; // value actually used to initialise plugin
size_t m_channels;
vector<RingBuffer *> m_queue;
float **m_buffers;
float m_inputSampleRate;
long m_frame;
bool m_unrun;
mutable OutputList m_outputs;
mutable std::map<int, bool> m_rewriteOutputTimes;
std::map<int, int> m_fixedRateFeatureNos; // output no -> feature no
void processBlock(FeatureSet& allFeatureSets);
void adjustFixedRateFeatureTime(int outputNo, Feature &);
};
PluginBufferingAdapter::PluginBufferingAdapter(Plugin *plugin) :
PluginWrapper(plugin)
{
m_impl = new Impl(plugin, m_inputSampleRate);
}
PluginBufferingAdapter::~PluginBufferingAdapter()
{
delete m_impl;
}
size_t
PluginBufferingAdapter::getPreferredStepSize() const
{
return getPreferredBlockSize();
}
size_t
PluginBufferingAdapter::getPreferredBlockSize() const
{
return PluginWrapper::getPreferredBlockSize();
}
size_t
PluginBufferingAdapter::getPluginPreferredStepSize() const
{
return PluginWrapper::getPreferredStepSize();
}
size_t
PluginBufferingAdapter::getPluginPreferredBlockSize() const
{
return PluginWrapper::getPreferredBlockSize();
}
void
PluginBufferingAdapter::setPluginStepSize(size_t stepSize)
{
m_impl->setPluginStepSize(stepSize);
}
void
PluginBufferingAdapter::setPluginBlockSize(size_t blockSize)
{
m_impl->setPluginBlockSize(blockSize);
}
void
PluginBufferingAdapter::getActualStepAndBlockSizes(size_t &stepSize,
size_t &blockSize)
{
m_impl->getActualStepAndBlockSizes(stepSize, blockSize);
}
bool
PluginBufferingAdapter::initialise(size_t channels, size_t stepSize, size_t blockSize)
{
return m_impl->initialise(channels, stepSize, blockSize);
}
PluginBufferingAdapter::OutputList
PluginBufferingAdapter::getOutputDescriptors() const
{
return m_impl->getOutputDescriptors();
}
void
PluginBufferingAdapter::setParameter(std::string name, float value)
{
m_impl->setParameter(name, value);
}
void
PluginBufferingAdapter::selectProgram(std::string name)
{
m_impl->selectProgram(name);
}
void
PluginBufferingAdapter::reset()
{
m_impl->reset();
}
PluginBufferingAdapter::FeatureSet
PluginBufferingAdapter::process(const float *const *inputBuffers,
RealTime timestamp)
{
return m_impl->process(inputBuffers, timestamp);
}
PluginBufferingAdapter::FeatureSet
PluginBufferingAdapter::getRemainingFeatures()
{
return m_impl->getRemainingFeatures();
}
PluginBufferingAdapter::Impl::Impl(Plugin *plugin, float inputSampleRate) :
m_plugin(plugin),
m_inputStepSize(0),
m_inputBlockSize(0),
m_setStepSize(0),
m_setBlockSize(0),
m_stepSize(0),
m_blockSize(0),
m_channels(0),
m_queue(0),
m_buffers(0),
m_inputSampleRate(inputSampleRate),
m_frame(0),
m_unrun(true)
{
(void)getOutputDescriptors(); // set up m_outputs and m_rewriteOutputTimes
}
PluginBufferingAdapter::Impl::~Impl()
{
// the adapter will delete the plugin
for (size_t i = 0; i < m_channels; ++i) {
delete m_queue[i];
delete[] m_buffers[i];
}
delete[] m_buffers;
}
void
PluginBufferingAdapter::Impl::setPluginStepSize(size_t stepSize)
{
if (m_inputStepSize != 0) {
std::cerr << "PluginBufferingAdapter::setPluginStepSize: ERROR: Cannot be called after initialise()" << std::endl;
return;
}
m_setStepSize = stepSize;
}
void
PluginBufferingAdapter::Impl::setPluginBlockSize(size_t blockSize)
{
if (m_inputBlockSize != 0) {
std::cerr << "PluginBufferingAdapter::setPluginBlockSize: ERROR: Cannot be called after initialise()" << std::endl;
return;
}
m_setBlockSize = blockSize;
}
void
PluginBufferingAdapter::Impl::getActualStepAndBlockSizes(size_t &stepSize,
size_t &blockSize)
{
stepSize = m_stepSize;
blockSize = m_blockSize;
}
bool
PluginBufferingAdapter::Impl::initialise(size_t channels, size_t stepSize, size_t blockSize)
{
if (stepSize != blockSize) {
std::cerr << "PluginBufferingAdapter::initialise: input stepSize must be equal to blockSize for this adapter (stepSize = " << stepSize << ", blockSize = " << blockSize << ")" << std::endl;
return false;
}
m_channels = channels;
m_inputStepSize = stepSize;
m_inputBlockSize = blockSize;
// if the user has requested particular step or block sizes, use
// those; otherwise use the step and block sizes which the plugin
// prefers
m_stepSize = 0;
m_blockSize = 0;
if (m_setStepSize > 0) {
m_stepSize = m_setStepSize;
}
if (m_setBlockSize > 0) {
m_blockSize = m_setBlockSize;
}
if (m_stepSize == 0 && m_blockSize == 0) {
m_stepSize = m_plugin->getPreferredStepSize();
m_blockSize = m_plugin->getPreferredBlockSize();
}
bool freq = (m_plugin->getInputDomain() == Vamp::Plugin::FrequencyDomain);
// or sensible defaults if it has no preference
if (m_blockSize == 0) {
if (m_stepSize == 0) {
m_blockSize = 1024;
if (freq) {
m_stepSize = m_blockSize / 2;
} else {
m_stepSize = m_blockSize;
}
} else if (freq) {
m_blockSize = m_stepSize * 2;
} else {
m_blockSize = m_stepSize;
}
} else if (m_stepSize == 0) { // m_blockSize != 0 (that was handled above)
if (freq) {
m_stepSize = m_blockSize/2;
} else {
m_stepSize = m_blockSize;
}
}
// current implementation breaks if step is greater than block
if (m_stepSize > m_blockSize) {
size_t newBlockSize;
if (freq) {
newBlockSize = m_stepSize * 2;
} else {
newBlockSize = m_stepSize;
}
std::cerr << "PluginBufferingAdapter::initialise: WARNING: step size " << m_stepSize << " is greater than block size " << m_blockSize << ": cannot handle this in adapter; adjusting block size to " << newBlockSize << std::endl;
m_blockSize = newBlockSize;
}
// std::cerr << "PluginBufferingAdapter::initialise: NOTE: stepSize " << m_inputStepSize << " -> " << m_stepSize
// << ", blockSize " << m_inputBlockSize << " -> " << m_blockSize << std::endl;
m_buffers = new float *[m_channels];
for (size_t i = 0; i < m_channels; ++i) {
m_queue.push_back(new RingBuffer(int(m_blockSize + m_inputBlockSize)));
m_buffers[i] = new float[m_blockSize];
}
bool success = m_plugin->initialise(m_channels, m_stepSize, m_blockSize);
// std::cerr << "PluginBufferingAdapter::initialise: success = " << success << std::endl;
if (success) {
// Re-query outputs; properties such as bin count may have
// changed on initialise
m_outputs.clear();
(void)getOutputDescriptors();
}
return success;
}
PluginBufferingAdapter::OutputList
PluginBufferingAdapter::Impl::getOutputDescriptors() const
{
if (m_outputs.empty()) {
// std::cerr << "PluginBufferingAdapter::getOutputDescriptors: querying anew" << std::endl;
m_outputs = m_plugin->getOutputDescriptors();
}
PluginBufferingAdapter::OutputList outs = m_outputs;
for (int i = 0; i < int(outs.size()); ++i) {
switch (outs[i].sampleType) {
case OutputDescriptor::OneSamplePerStep:
outs[i].sampleType = OutputDescriptor::FixedSampleRate;
outs[i].sampleRate = m_inputSampleRate / float(m_stepSize);
m_rewriteOutputTimes[i] = true;
break;
case OutputDescriptor::FixedSampleRate:
if (outs[i].sampleRate == 0.f) {
outs[i].sampleRate = m_inputSampleRate / float(m_stepSize);
}
// We actually only need to rewrite output times for
// features that don't have timestamps already, but we
// can't tell from here whether our features will have
// timestamps or not
m_rewriteOutputTimes[i] = true;
break;
case OutputDescriptor::VariableSampleRate:
m_rewriteOutputTimes[i] = false;
break;
}
}
return outs;
}
void
PluginBufferingAdapter::Impl::setParameter(std::string name, float value)
{
m_plugin->setParameter(name, value);
// Re-query outputs; properties such as bin count may have changed
m_outputs.clear();
(void)getOutputDescriptors();
}
void
PluginBufferingAdapter::Impl::selectProgram(std::string name)
{
m_plugin->selectProgram(name);
// Re-query outputs; properties such as bin count may have changed
m_outputs.clear();
(void)getOutputDescriptors();
}
void
PluginBufferingAdapter::Impl::reset()
{
m_frame = 0;
m_unrun = true;
for (size_t i = 0; i < m_queue.size(); ++i) {
m_queue[i]->reset();
}
m_fixedRateFeatureNos.clear();
m_plugin->reset();
}
PluginBufferingAdapter::FeatureSet
PluginBufferingAdapter::Impl::process(const float *const *inputBuffers,
RealTime timestamp)
{
if (m_inputStepSize == 0) {
std::cerr << "PluginBufferingAdapter::process: ERROR: Plugin has not been initialised" << std::endl;
return FeatureSet();
}
FeatureSet allFeatureSets;
if (m_unrun) {
m_frame = RealTime::realTime2Frame(timestamp,
int(m_inputSampleRate + 0.5));
m_unrun = false;
}
// queue the new input
for (size_t i = 0; i < m_channels; ++i) {
int written = m_queue[i]->write(inputBuffers[i], int(m_inputBlockSize));
if (written < int(m_inputBlockSize) && i == 0) {
std::cerr << "WARNING: PluginBufferingAdapter::Impl::process: "
<< "Buffer overflow: wrote " << written
<< " of " << m_inputBlockSize
<< " input samples (for plugin step size "
<< m_stepSize << ", block size " << m_blockSize << ")"
<< std::endl;
}
}
// process as much as we can
while (m_queue[0]->getReadSpace() >= int(m_blockSize)) {
processBlock(allFeatureSets);
}
return allFeatureSets;
}
void
PluginBufferingAdapter::Impl::adjustFixedRateFeatureTime(int outputNo,
Feature &feature)
{
// cerr << "adjustFixedRateFeatureTime: from " << feature.timestamp;
double rate = m_outputs[outputNo].sampleRate;
if (rate == 0.0) {
rate = m_inputSampleRate / float(m_stepSize);
}
if (feature.hasTimestamp) {
double secs = feature.timestamp.sec;
secs += feature.timestamp.nsec / 1e9;
m_fixedRateFeatureNos[outputNo] = int(secs * rate + 0.5);
// cerr << " [secs = " << secs << ", no = " << m_fixedRateFeatureNos[outputNo] << "]";
}
feature.timestamp = RealTime::fromSeconds
(m_fixedRateFeatureNos[outputNo] / rate);
// cerr << " to " << feature.timestamp << " (rate = " << rate << ", hasTimestamp = " << feature.hasTimestamp << ")" << endl;
feature.hasTimestamp = true;
m_fixedRateFeatureNos[outputNo] = m_fixedRateFeatureNos[outputNo] + 1;
}
PluginBufferingAdapter::FeatureSet
PluginBufferingAdapter::Impl::getRemainingFeatures()
{
FeatureSet allFeatureSets;
// process remaining samples in queue
while (m_queue[0]->getReadSpace() >= int(m_blockSize)) {
processBlock(allFeatureSets);
}
// pad any last samples remaining and process
if (m_queue[0]->getReadSpace() > 0) {
for (size_t i = 0; i < m_channels; ++i) {
m_queue[i]->zero(int(m_blockSize) - m_queue[i]->getReadSpace());
}
processBlock(allFeatureSets);
}
// get remaining features
FeatureSet featureSet = m_plugin->getRemainingFeatures();
for (map<int, FeatureList>::iterator iter = featureSet.begin();
iter != featureSet.end(); ++iter) {
int outputNo = iter->first;
FeatureList featureList = iter->second;
for (size_t i = 0; i < featureList.size(); ++i) {
if (m_outputs[outputNo].sampleType ==
OutputDescriptor::FixedSampleRate) {
adjustFixedRateFeatureTime(outputNo, featureList[i]);
}
allFeatureSets[outputNo].push_back(featureList[i]);
}
}
return allFeatureSets;
}
void
PluginBufferingAdapter::Impl::processBlock(FeatureSet& allFeatureSets)
{
for (size_t i = 0; i < m_channels; ++i) {
m_queue[i]->peek(m_buffers[i], int(m_blockSize));
}
long frame = m_frame;
RealTime timestamp = RealTime::frame2RealTime
(frame, int(m_inputSampleRate + 0.5));
FeatureSet featureSet = m_plugin->process(m_buffers, timestamp);
PluginWrapper *wrapper = dynamic_cast<PluginWrapper *>(m_plugin);
RealTime adjustment;
if (wrapper) {
PluginInputDomainAdapter *ida =
wrapper->getWrapper<PluginInputDomainAdapter>();
if (ida) adjustment = ida->getTimestampAdjustment();
}
for (FeatureSet::iterator iter = featureSet.begin();
iter != featureSet.end(); ++iter) {
int outputNo = iter->first;
if (m_rewriteOutputTimes[outputNo]) {
FeatureList featureList = iter->second;
for (size_t i = 0; i < featureList.size(); ++i) {
switch (m_outputs[outputNo].sampleType) {
case OutputDescriptor::OneSamplePerStep:
// use our internal timestamp, always
featureList[i].timestamp = timestamp + adjustment;
featureList[i].hasTimestamp = true;
break;
case OutputDescriptor::FixedSampleRate:
adjustFixedRateFeatureTime(outputNo, featureList[i]);
break;
case OutputDescriptor::VariableSampleRate:
// plugin must set timestamp
break;
default:
break;
}
allFeatureSets[outputNo].push_back(featureList[i]);
}
} else {
for (size_t i = 0; i < iter->second.size(); ++i) {
allFeatureSets[outputNo].push_back(iter->second[i]);
}
}
}
// step forward
for (size_t i = 0; i < m_channels; ++i) {
m_queue[i]->skip(int(m_stepSize));
}
// increment internal frame counter each time we step forward
m_frame += m_stepSize;
}
}
}

View File

@ -0,0 +1,149 @@
#pragma once
#include "vamp-hostsdk/PluginWrapper.h"
namespace Vamp {
namespace HostExt {
/**
* \class PluginBufferingAdapter PluginBufferingAdapter.h <vamp-hostsdk/PluginBufferingAdapter.h>
*
* PluginBufferingAdapter is a Vamp plugin adapter that allows plugins
* to be used by a host supplying an audio stream in non-overlapping
* buffers of arbitrary size.
*
* A host using PluginBufferingAdapter may ignore the preferred step
* and block size reported by the plugin, and still expect the plugin
* to run. The value of blockSize and stepSize passed to initialise
* should be the size of the buffer which the host will supply; the
* stepSize should be equal to the blockSize.
*
* If the internal step size used for the plugin differs from that
* supplied by the host, the adapter will modify the sample type and
* rate specifications for the plugin outputs appropriately, and set
* timestamps on the output features for outputs that formerly used a
* different sample rate specification. This is necessary in order to
* obtain correct time stamping.
*
* In other respects, the PluginBufferingAdapter behaves identically
* to the plugin that it wraps. The wrapped plugin will be deleted
* when the wrapper is deleted.
*/
class PluginBufferingAdapter : public PluginWrapper
{
public:
/**
* Construct a PluginBufferingAdapter wrapping the given plugin.
* The adapter takes ownership of the plugin, which will be
* deleted when the adapter is deleted.
*/
PluginBufferingAdapter(Plugin *plugin);
virtual ~PluginBufferingAdapter();
/**
* Return the preferred step size for this adapter.
*
* Because of the way this adapter works, its preferred step size
* will always be the same as its preferred block size. This may
* or may not be the same as the preferred step size of the
* underlying plugin, which may be obtained by calling
* getPluginPreferredStepSize().
*/
size_t getPreferredStepSize() const;
/**
* Return the preferred block size for this adapter.
*
* This may or may not be the same as the preferred block size of
* the underlying plugin, which may be obtained by calling
* getPluginPreferredBlockSize().
*
* Note that this adapter may be initialised with any block size,
* not just its supposedly preferred one.
*/
size_t getPreferredBlockSize() const;
/**
* Initialise the adapter (and therefore the plugin) for the given
* number of channels. Initialise the adapter for the given step
* and block size, which must be equal.
*
* The step and block size used for the underlying plugin will
* depend on its preferences, or any values previously passed to
* setPluginStepSize and setPluginBlockSize.
*/
bool initialise(size_t channels, size_t stepSize, size_t blockSize);
/**
* Return the preferred step size of the plugin wrapped by this
* adapter.
*
* This is included mainly for informational purposes. This value
* is not likely to be a valid step size for the adapter itself,
* and it is not usually of any use in interpreting the results
* (because the adapter re-writes OneSamplePerStep outputs to
* FixedSampleRate so that the hop size no longer needs to be
* known beforehand in order to interpret them).
*/
size_t getPluginPreferredStepSize() const;
/**
* Return the preferred block size of the plugin wrapped by this
* adapter.
*
* This is included mainly for informational purposes.
*/
size_t getPluginPreferredBlockSize() const;
/**
* Set the step size that will be used for the underlying plugin
* when initialise() is called. If this is not set, the plugin's
* own preferred step size will be used. You will not usually
* need to call this function. If you do call it, it must be
* before the first call to initialise().
*/
void setPluginStepSize(size_t stepSize);
/**
* Set the block size that will be used for the underlying plugin
* when initialise() is called. If this is not set, the plugin's
* own preferred block size will be used. You will not usually
* need to call this function. If you do call it, it must be
* before the first call to initialise().
*/
void setPluginBlockSize(size_t blockSize);
/**
* Return the step and block sizes that were actually used when
* initialising the underlying plugin.
*
* This is included mainly for informational purposes. You will
* not usually need to call this function. If this is called
* before initialise(), it will return 0 for both values. If it
* is called after a failed call to initialise(), it will return
* the values that were used in the failed call to the plugin's
* initialise() function.
*/
void getActualStepAndBlockSizes(size_t &stepSize, size_t &blockSize);
void setParameter(std::string, float);
void selectProgram(std::string);
OutputList getOutputDescriptors() const;
void reset();
FeatureSet process(const float *const *inputBuffers, RealTime timestamp);
FeatureSet getRemainingFeatures();
protected:
class Impl;
Impl *m_impl;
};
}
}

View File

@ -0,0 +1,228 @@
#include "vamp-hostsdk/PluginChannelAdapter.h"
namespace Vamp {
namespace HostExt {
class PluginChannelAdapter::Impl
{
public:
Impl(Plugin *plugin);
~Impl();
bool initialise(size_t channels, size_t stepSize, size_t blockSize);
FeatureSet process(const float *const *inputBuffers, RealTime timestamp);
FeatureSet processInterleaved(const float *inputBuffers, RealTime timestamp);
protected:
Plugin *m_plugin;
size_t m_blockSize;
size_t m_inputChannels;
size_t m_pluginChannels;
float **m_buffer;
float **m_deinterleave;
const float **m_forwardPtrs;
};
PluginChannelAdapter::PluginChannelAdapter(Plugin *plugin) :
PluginWrapper(plugin)
{
m_impl = new Impl(plugin);
}
PluginChannelAdapter::~PluginChannelAdapter()
{
delete m_impl;
}
bool
PluginChannelAdapter::initialise(size_t channels, size_t stepSize, size_t blockSize)
{
return m_impl->initialise(channels, stepSize, blockSize);
}
PluginChannelAdapter::FeatureSet
PluginChannelAdapter::process(const float *const *inputBuffers,
RealTime timestamp)
{
return m_impl->process(inputBuffers, timestamp);
}
PluginChannelAdapter::FeatureSet
PluginChannelAdapter::processInterleaved(const float *inputBuffers,
RealTime timestamp)
{
return m_impl->processInterleaved(inputBuffers, timestamp);
}
PluginChannelAdapter::Impl::Impl(Plugin *plugin) :
m_plugin(plugin),
m_blockSize(0),
m_inputChannels(0),
m_pluginChannels(0),
m_buffer(0),
m_deinterleave(0),
m_forwardPtrs(0)
{
}
PluginChannelAdapter::Impl::~Impl()
{
// the adapter will delete the plugin
if (m_buffer) {
if (m_inputChannels > m_pluginChannels) {
delete[] m_buffer[0];
} else {
for (size_t i = 0; i < m_pluginChannels - m_inputChannels; ++i) {
delete[] m_buffer[i];
}
}
delete[] m_buffer;
m_buffer = 0;
}
if (m_deinterleave) {
for (size_t i = 0; i < m_inputChannels; ++i) {
delete[] m_deinterleave[i];
}
delete[] m_deinterleave;
m_deinterleave = 0;
}
if (m_forwardPtrs) {
delete[] m_forwardPtrs;
m_forwardPtrs = 0;
}
}
bool
PluginChannelAdapter::Impl::initialise(size_t channels, size_t stepSize, size_t blockSize)
{
m_blockSize = blockSize;
size_t minch = m_plugin->getMinChannelCount();
size_t maxch = m_plugin->getMaxChannelCount();
m_inputChannels = channels;
if (m_inputChannels < minch) {
m_forwardPtrs = new const float *[minch];
if (m_inputChannels > 1) {
// We need a set of zero-valued buffers to add to the
// forwarded pointers
m_buffer = new float*[minch - channels];
for (size_t i = 0; i < minch; ++i) {
m_buffer[i] = new float[blockSize];
for (size_t j = 0; j < blockSize; ++j) {
m_buffer[i][j] = 0.f;
}
}
}
m_pluginChannels = minch;
// std::cerr << "PluginChannelAdapter::initialise: expanding " << m_inputChannels << " to " << m_pluginChannels << " for plugin" << std::endl;
} else if (m_inputChannels > maxch) {
// We only need m_buffer if we are mixing down to a single
// channel -- otherwise we can just forward the same float* as
// passed in to process(), expecting the excess to be ignored
if (maxch == 1) {
m_buffer = new float *[1];
m_buffer[0] = new float[blockSize];
// std::cerr << "PluginChannelAdapter::initialise: mixing " << m_inputChannels << " to mono for plugin" << std::endl;
} else {
// std::cerr << "PluginChannelAdapter::initialise: reducing " << m_inputChannels << " to " << m_pluginChannels << " for plugin" << std::endl;
}
m_pluginChannels = maxch;
} else {
// std::cerr << "PluginChannelAdapter::initialise: accepting given number of channels (" << m_inputChannels << ")" << std::endl;
m_pluginChannels = m_inputChannels;
}
return m_plugin->initialise(m_pluginChannels, stepSize, blockSize);
}
PluginChannelAdapter::FeatureSet
PluginChannelAdapter::Impl::processInterleaved(const float *inputBuffers,
RealTime timestamp)
{
if (!m_deinterleave) {
m_deinterleave = new float *[m_inputChannels];
for (size_t i = 0; i < m_inputChannels; ++i) {
m_deinterleave[i] = new float[m_blockSize];
}
}
for (size_t i = 0; i < m_inputChannels; ++i) {
for (size_t j = 0; j < m_blockSize; ++j) {
m_deinterleave[i][j] = inputBuffers[j * m_inputChannels + i];
}
}
return process(m_deinterleave, timestamp);
}
PluginChannelAdapter::FeatureSet
PluginChannelAdapter::Impl::process(const float *const *inputBuffers,
RealTime timestamp)
{
// std::cerr << "PluginChannelAdapter::process: " << m_inputChannels << " -> " << m_pluginChannels << " channels" << std::endl;
if (m_inputChannels < m_pluginChannels) {
if (m_inputChannels == 1) {
for (size_t i = 0; i < m_pluginChannels; ++i) {
m_forwardPtrs[i] = inputBuffers[0];
}
} else {
for (size_t i = 0; i < m_inputChannels; ++i) {
m_forwardPtrs[i] = inputBuffers[i];
}
for (size_t i = m_inputChannels; i < m_pluginChannels; ++i) {
m_forwardPtrs[i] = m_buffer[i - m_inputChannels];
}
}
return m_plugin->process(m_forwardPtrs, timestamp);
} else if (m_inputChannels > m_pluginChannels) {
if (m_pluginChannels == 1) {
for (size_t j = 0; j < m_blockSize; ++j) {
m_buffer[0][j] = inputBuffers[0][j];
}
for (size_t i = 1; i < m_inputChannels; ++i) {
for (size_t j = 0; j < m_blockSize; ++j) {
m_buffer[0][j] += inputBuffers[i][j];
}
}
for (size_t j = 0; j < m_blockSize; ++j) {
m_buffer[0][j] /= float(m_inputChannels);
}
return m_plugin->process(m_buffer, timestamp);
} else {
return m_plugin->process(inputBuffers, timestamp);
}
} else {
return m_plugin->process(inputBuffers, timestamp);
}
}
}
}

View File

@ -0,0 +1,105 @@
#pragma once
#include "vamp-hostsdk/PluginWrapper.h"
namespace Vamp {
namespace HostExt {
/**
* \class PluginChannelAdapter PluginChannelAdapter.h <vamp-hostsdk/PluginChannelAdapter.h>
*
* PluginChannelAdapter is a Vamp plugin adapter that implements a
* policy for management of plugins that expect a different number of
* input channels from the number actually available in the source
* audio data.
*
* A host using PluginChannelAdapter may ignore the getMinChannelCount
* and getMaxChannelCount reported by the plugin, and still expect the
* plugin to run.
*
* PluginChannelAdapter implements the following policy:
*
* - If the plugin supports the provided number of channels directly,
* PluginChannelAdapter will just run the plugin as normal.
*
* - If the plugin only supports exactly one channel but more than
* one channel is provided, PluginChannelAdapter will use the mean of
* the channels. This ensures that the resulting values remain
* within the same magnitude range as expected for mono data.
*
* - If the plugin requires more than one channel but exactly one is
* provided, the provided channel will be duplicated across all the
* plugin input channels.
*
* If none of the above apply:
*
* - If the plugin requires more channels than are provided, the
* minimum acceptable number of channels will be produced by adding
* empty (zero valued) channels to those provided.
*
* - If the plugin requires fewer channels than are provided, the
* maximum acceptable number of channels will be produced by
* discarding the excess channels.
*
* Hosts requiring a different channel policy from the above will need
* to implement it themselves, instead of using PluginChannelAdapter.
*
* Note that PluginChannelAdapter does not override the minimum and
* maximum channel counts returned by the wrapped plugin. The host
* will need to be aware that it is using a PluginChannelAdapter, and
* be prepared to ignore these counts as necessary. (This contrasts
* with the approach used in PluginInputDomainAdapter, which aims to
* make the host completely unaware of which underlying input domain
* is in fact in use.)
*
* (The rationale for this is that a host may wish to use the
* PluginChannelAdapter but still discriminate in some way on the
* basis of the number of channels actually supported. For example, a
* simple stereo audio host may prefer to reject plugins that require
* more than two channels on the grounds that doesn't actually
* understand what they are for, rather than allow the channel adapter
* to make a potentially meaningless channel conversion for them.)
*
* In every respect other than its management of channels, the
* PluginChannelAdapter behaves identically to the plugin that it
* wraps. The wrapped plugin will be deleted when the wrapper is
* deleted.
*
* \note This class was introduced in version 1.1 of the Vamp plugin SDK.
*/
class PluginChannelAdapter : public PluginWrapper
{
public:
/**
* Construct a PluginChannelAdapter wrapping the given plugin.
* The adapter takes ownership of the plugin, which will be
* deleted when the adapter is deleted.
*/
PluginChannelAdapter(Plugin *plugin);
virtual ~PluginChannelAdapter();
bool initialise(size_t channels, size_t stepSize, size_t blockSize);
FeatureSet process(const float *const *inputBuffers, RealTime timestamp);
/**
* Call process(), providing interleaved audio data with the
* number of channels passed to initialise(). The adapter will
* de-interleave into temporary buffers as appropriate before
* calling process().
*
* \note This function was introduced in version 1.4 of the Vamp
* plugin SDK.
*/
FeatureSet processInterleaved(const float *inputBuffer, RealTime timestamp);
protected:
class Impl;
Impl *m_impl;
};
}
}

View File

@ -0,0 +1,424 @@
#include "vamp-hostsdk/PluginHostAdapter.h"
#include <cstdlib>
#include "vamp-hostsdk/Files.h"
namespace Vamp
{
PluginHostAdapter::PluginHostAdapter(const VampPluginDescriptor *descriptor,
float inputSampleRate) :
Plugin(inputSampleRate),
m_descriptor(descriptor)
{
// std::cerr << "PluginHostAdapter::PluginHostAdapter (plugin = " << descriptor->name << ")" << std::endl;
m_handle = m_descriptor->instantiate(m_descriptor, inputSampleRate);
if (!m_handle) {
// std::cerr << "WARNING: PluginHostAdapter: Plugin instantiation failed for plugin " << m_descriptor->name << std::endl;
}
}
PluginHostAdapter::~PluginHostAdapter()
{
// std::cerr << "PluginHostAdapter::~PluginHostAdapter (plugin = " << m_descriptor->name << ")" << std::endl;
if (m_handle) m_descriptor->cleanup(m_handle);
}
std::vector<std::string>
PluginHostAdapter::getPluginPath()
{
std::vector<std::string> path;
std::string envPath;
if (Files::isNonNative32Bit()) {
(void)Files::getEnvUtf8("VAMP_PATH_32", envPath);
} else {
(void)Files::getEnvUtf8("VAMP_PATH", envPath);
}
#ifdef _WIN32
#define PATH_SEPARATOR ';'
#define DEFAULT_VAMP_PATH "%ProgramFiles%\\Vamp Plugins"
#else
#define PATH_SEPARATOR ':'
#ifdef __APPLE__
#define DEFAULT_VAMP_PATH "$HOME/Library/Audio/Plug-Ins/Vamp:/Library/Audio/Plug-Ins/Vamp"
#else
#define DEFAULT_VAMP_PATH "$HOME/vamp:$HOME/.vamp:/usr/local/lib/vamp:/usr/lib/vamp"
#endif
#endif
if (envPath == "") {
envPath = DEFAULT_VAMP_PATH;
std::string home;
if (Files::getEnvUtf8("HOME", home)) {
std::string::size_type f;
while ((f = envPath.find("$HOME")) != std::string::npos &&
f < envPath.length()) {
envPath.replace(f, 5, home);
}
}
#ifdef _WIN32
std::string pfiles;
if (!Files::getEnvUtf8("ProgramFiles", pfiles)) {
pfiles = "C:\\Program Files";
}
std::string::size_type f;
while ((f = envPath.find("%ProgramFiles%")) != std::string::npos &&
f < envPath.length()) {
envPath.replace(f, 14, pfiles);
}
#endif
}
std::string::size_type index = 0, newindex = 0;
while ((newindex = envPath.find(PATH_SEPARATOR, index)) < envPath.size()) {
path.push_back(envPath.substr(index, newindex - index));
index = newindex + 1;
}
path.push_back(envPath.substr(index));
return path;
}
bool
PluginHostAdapter::initialise(size_t channels,
size_t stepSize,
size_t blockSize)
{
if (!m_handle) return false;
return m_descriptor->initialise
(m_handle,
(unsigned int)channels,
(unsigned int)stepSize,
(unsigned int)blockSize) ?
true : false;
}
void
PluginHostAdapter::reset()
{
if (!m_handle) {
// std::cerr << "PluginHostAdapter::reset: no handle" << std::endl;
return;
}
// std::cerr << "PluginHostAdapter::reset(" << m_handle << ")" << std::endl;
m_descriptor->reset(m_handle);
}
PluginHostAdapter::InputDomain
PluginHostAdapter::getInputDomain() const
{
if (m_descriptor->inputDomain == vampFrequencyDomain) {
return FrequencyDomain;
} else {
return TimeDomain;
}
}
unsigned int
PluginHostAdapter::getVampApiVersion() const
{
return m_descriptor->vampApiVersion;
}
std::string
PluginHostAdapter::getIdentifier() const
{
return m_descriptor->identifier;
}
std::string
PluginHostAdapter::getName() const
{
return m_descriptor->name;
}
std::string
PluginHostAdapter::getDescription() const
{
return m_descriptor->description;
}
std::string
PluginHostAdapter::getMaker() const
{
return m_descriptor->maker;
}
int
PluginHostAdapter::getPluginVersion() const
{
return m_descriptor->pluginVersion;
}
std::string
PluginHostAdapter::getCopyright() const
{
return m_descriptor->copyright;
}
PluginHostAdapter::ParameterList
PluginHostAdapter::getParameterDescriptors() const
{
ParameterList list;
for (unsigned int i = 0; i < m_descriptor->parameterCount; ++i) {
const VampParameterDescriptor *spd = m_descriptor->parameters[i];
ParameterDescriptor pd;
pd.identifier = spd->identifier;
pd.name = spd->name;
pd.description = spd->description;
pd.unit = spd->unit;
pd.minValue = spd->minValue;
pd.maxValue = spd->maxValue;
pd.defaultValue = spd->defaultValue;
pd.isQuantized = spd->isQuantized;
pd.quantizeStep = spd->quantizeStep;
if (pd.isQuantized && spd->valueNames) {
for (unsigned int j = 0; spd->valueNames[j]; ++j) {
pd.valueNames.push_back(spd->valueNames[j]);
}
}
list.push_back(pd);
}
return list;
}
float
PluginHostAdapter::getParameter(std::string param) const
{
if (!m_handle) return 0.0;
for (unsigned int i = 0; i < m_descriptor->parameterCount; ++i) {
if (param == m_descriptor->parameters[i]->identifier) {
return m_descriptor->getParameter(m_handle, i);
}
}
return 0.0;
}
void
PluginHostAdapter::setParameter(std::string param,
float value)
{
if (!m_handle) return;
for (unsigned int i = 0; i < m_descriptor->parameterCount; ++i) {
if (param == m_descriptor->parameters[i]->identifier) {
m_descriptor->setParameter(m_handle, i, value);
return;
}
}
}
PluginHostAdapter::ProgramList
PluginHostAdapter::getPrograms() const
{
ProgramList list;
for (unsigned int i = 0; i < m_descriptor->programCount; ++i) {
list.push_back(m_descriptor->programs[i]);
}
return list;
}
std::string
PluginHostAdapter::getCurrentProgram() const
{
if (!m_handle) return "";
int pn = m_descriptor->getCurrentProgram(m_handle);
if (pn < (int)m_descriptor->programCount) {
return m_descriptor->programs[pn];
} else {
return "";
}
}
void
PluginHostAdapter::selectProgram(std::string program)
{
if (!m_handle) return;
for (unsigned int i = 0; i < m_descriptor->programCount; ++i) {
if (program == m_descriptor->programs[i]) {
m_descriptor->selectProgram(m_handle, i);
return;
}
}
}
size_t
PluginHostAdapter::getPreferredStepSize() const
{
if (!m_handle) return 0;
return m_descriptor->getPreferredStepSize(m_handle);
}
size_t
PluginHostAdapter::getPreferredBlockSize() const
{
if (!m_handle) return 0;
return m_descriptor->getPreferredBlockSize(m_handle);
}
size_t
PluginHostAdapter::getMinChannelCount() const
{
if (!m_handle) return 0;
return m_descriptor->getMinChannelCount(m_handle);
}
size_t
PluginHostAdapter::getMaxChannelCount() const
{
if (!m_handle) return 0;
return m_descriptor->getMaxChannelCount(m_handle);
}
PluginHostAdapter::OutputList
PluginHostAdapter::getOutputDescriptors() const
{
OutputList list;
if (!m_handle) {
// std::cerr << "PluginHostAdapter::getOutputDescriptors: no handle " << std::endl;
return list;
}
unsigned int count = m_descriptor->getOutputCount(m_handle);
for (unsigned int i = 0; i < count; ++i) {
VampOutputDescriptor *sd = m_descriptor->getOutputDescriptor(m_handle, i);
OutputDescriptor d;
d.identifier = sd->identifier;
d.name = sd->name;
d.description = sd->description;
d.unit = sd->unit;
d.hasFixedBinCount = sd->hasFixedBinCount;
d.binCount = sd->binCount;
if (d.hasFixedBinCount && sd->binNames) {
for (unsigned int j = 0; j < sd->binCount; ++j) {
d.binNames.push_back(sd->binNames[j] ? sd->binNames[j] : "");
}
}
d.hasKnownExtents = sd->hasKnownExtents;
d.minValue = sd->minValue;
d.maxValue = sd->maxValue;
d.isQuantized = sd->isQuantized;
d.quantizeStep = sd->quantizeStep;
switch (sd->sampleType) {
case vampOneSamplePerStep:
d.sampleType = OutputDescriptor::OneSamplePerStep; break;
case vampFixedSampleRate:
d.sampleType = OutputDescriptor::FixedSampleRate; break;
case vampVariableSampleRate:
d.sampleType = OutputDescriptor::VariableSampleRate; break;
}
d.sampleRate = sd->sampleRate;
if (m_descriptor->vampApiVersion >= 2) {
d.hasDuration = sd->hasDuration;
} else {
d.hasDuration = false;
}
list.push_back(d);
m_descriptor->releaseOutputDescriptor(sd);
}
return list;
}
PluginHostAdapter::FeatureSet
PluginHostAdapter::process(const float *const *inputBuffers,
RealTime timestamp)
{
FeatureSet fs;
if (!m_handle) return fs;
int sec = timestamp.sec;
int nsec = timestamp.nsec;
VampFeatureList *features = m_descriptor->process(m_handle,
inputBuffers,
sec, nsec);
convertFeatures(features, fs);
m_descriptor->releaseFeatureSet(features);
return fs;
}
PluginHostAdapter::FeatureSet
PluginHostAdapter::getRemainingFeatures()
{
FeatureSet fs;
if (!m_handle) return fs;
VampFeatureList *features = m_descriptor->getRemainingFeatures(m_handle);
convertFeatures(features, fs);
m_descriptor->releaseFeatureSet(features);
return fs;
}
void
PluginHostAdapter::convertFeatures(VampFeatureList *features,
FeatureSet &fs)
{
if (!features) return;
unsigned int outputs = m_descriptor->getOutputCount(m_handle);
for (unsigned int i = 0; i < outputs; ++i) {
VampFeatureList &list = features[i];
if (list.featureCount > 0) {
Feature feature;
feature.values.reserve(list.features[0].v1.valueCount);
for (unsigned int j = 0; j < list.featureCount; ++j) {
feature.hasTimestamp = list.features[j].v1.hasTimestamp;
feature.timestamp = RealTime(list.features[j].v1.sec,
list.features[j].v1.nsec);
feature.hasDuration = false;
if (m_descriptor->vampApiVersion >= 2) {
unsigned int j2 = j + list.featureCount;
feature.hasDuration = list.features[j2].v2.hasDuration;
feature.duration = RealTime(list.features[j2].v2.durationSec,
list.features[j2].v2.durationNsec);
}
for (unsigned int k = 0; k < list.features[j].v1.valueCount; ++k) {
feature.values.push_back(list.features[j].v1.values[k]);
}
if (list.features[j].v1.label) {
feature.label = list.features[j].v1.label;
}
fs[i].push_back(feature);
if (list.features[j].v1.valueCount > 0) {
feature.values.clear();
}
if (list.features[j].v1.label) {
feature.label = "";
}
}
}
}
}
}

View File

@ -0,0 +1,75 @@
#pragma once
#include "vamp-sdk/Plugin.h"
#include "vamp.h"
#include <vector>
namespace Vamp {
/**
* \class PluginHostAdapter PluginHostAdapter.h <vamp-hostsdk/PluginHostAdapter.h>
*
* PluginHostAdapter is a wrapper class that a Vamp host can use to
* make the C-language VampPluginDescriptor object appear as a C++
* Vamp::Plugin object.
*
* The Vamp API is defined in vamp/vamp.h as a C API. The C++ objects
* used for convenience by plugins and hosts actually communicate
* using the C low-level API, but the details of this communication
* are handled seamlessly by the Vamp SDK implementation provided the
* plugin and host use the proper C++ wrapper objects.
*
* See also PluginAdapter, the plugin-side wrapper that makes a C++
* plugin object available using the C query API.
*/
class PluginHostAdapter : public Plugin
{
public:
PluginHostAdapter(const VampPluginDescriptor *descriptor,
float inputSampleRate);
virtual ~PluginHostAdapter();
static std::vector<std::string> getPluginPath();
bool initialise(size_t channels, size_t stepSize, size_t blockSize);
void reset();
InputDomain getInputDomain() const;
unsigned int getVampApiVersion() const;
std::string getIdentifier() const;
std::string getName() const;
std::string getDescription() const;
std::string getMaker() const;
int getPluginVersion() const;
std::string getCopyright() const;
ParameterList getParameterDescriptors() const;
float getParameter(std::string) const;
void setParameter(std::string, float);
ProgramList getPrograms() const;
std::string getCurrentProgram() const;
void selectProgram(std::string);
size_t getPreferredStepSize() const;
size_t getPreferredBlockSize() const;
size_t getMinChannelCount() const;
size_t getMaxChannelCount() const;
OutputList getOutputDescriptors() const;
FeatureSet process(const float *const *inputBuffers, RealTime timestamp);
FeatureSet getRemainingFeatures();
protected:
void convertFeatures(VampFeatureList *, FeatureSet &);
const VampPluginDescriptor *m_descriptor;
VampPluginHandle m_handle;
};
}

View File

@ -0,0 +1,482 @@
#include "vamp-hostsdk/PluginInputDomainAdapter.h"
#include "vamp-sdk/ext/vamp_kiss_fft.h"
#include "vamp-sdk/ext/vamp_kiss_fftr.h"
#include <cmath>
#include "Window.h"
#include <stdlib.h>
#include <stdio.h>
#include <math.h>
#include <string.h>
#include <limits.h>
namespace Vamp {
namespace HostExt {
class PluginInputDomainAdapter::Impl
{
public:
Impl(Plugin *plugin, float inputSampleRate);
~Impl();
bool initialise(size_t channels, size_t stepSize, size_t blockSize);
void reset();
size_t getPreferredStepSize() const;
size_t getPreferredBlockSize() const;
FeatureSet process(const float *const *inputBuffers, RealTime timestamp);
void setProcessTimestampMethod(ProcessTimestampMethod m);
ProcessTimestampMethod getProcessTimestampMethod() const;
RealTime getTimestampAdjustment() const;
WindowType getWindowType() const;
void setWindowType(WindowType type);
protected:
Plugin *m_plugin;
float m_inputSampleRate;
int m_channels;
int m_stepSize;
int m_blockSize;
float **m_freqbuf;
vamp_kiss_fft_scalar *m_ri;
WindowType m_windowType;
typedef Window<vamp_kiss_fft_scalar> W;
W *m_window;
ProcessTimestampMethod m_method;
int m_processCount;
float **m_shiftBuffers;
vamp_kiss_fftr_cfg m_cfg;
vamp_kiss_fft_cpx *m_cbuf;
FeatureSet processShiftingTimestamp(const float *const *inputBuffers, RealTime timestamp);
FeatureSet processShiftingData(const float *const *inputBuffers, RealTime timestamp);
size_t makeBlockSizeAcceptable(size_t) const;
W::WindowType convertType(WindowType t) const;
};
PluginInputDomainAdapter::PluginInputDomainAdapter(Plugin *plugin) :
PluginWrapper(plugin)
{
m_impl = new Impl(plugin, m_inputSampleRate);
}
PluginInputDomainAdapter::~PluginInputDomainAdapter()
{
delete m_impl;
}
bool
PluginInputDomainAdapter::initialise(size_t channels, size_t stepSize, size_t blockSize)
{
return m_impl->initialise(channels, stepSize, blockSize);
}
void
PluginInputDomainAdapter::reset()
{
m_impl->reset();
}
Plugin::InputDomain
PluginInputDomainAdapter::getInputDomain() const
{
return TimeDomain;
}
size_t
PluginInputDomainAdapter::getPreferredStepSize() const
{
return m_impl->getPreferredStepSize();
}
size_t
PluginInputDomainAdapter::getPreferredBlockSize() const
{
return m_impl->getPreferredBlockSize();
}
Plugin::FeatureSet
PluginInputDomainAdapter::process(const float *const *inputBuffers, RealTime timestamp)
{
return m_impl->process(inputBuffers, timestamp);
}
void
PluginInputDomainAdapter::setProcessTimestampMethod(ProcessTimestampMethod m)
{
m_impl->setProcessTimestampMethod(m);
}
PluginInputDomainAdapter::ProcessTimestampMethod
PluginInputDomainAdapter::getProcessTimestampMethod() const
{
return m_impl->getProcessTimestampMethod();
}
RealTime
PluginInputDomainAdapter::getTimestampAdjustment() const
{
return m_impl->getTimestampAdjustment();
}
PluginInputDomainAdapter::WindowType
PluginInputDomainAdapter::getWindowType() const
{
return m_impl->getWindowType();
}
void
PluginInputDomainAdapter::setWindowType(WindowType w)
{
m_impl->setWindowType(w);
}
PluginInputDomainAdapter::Impl::Impl(Plugin *plugin, float inputSampleRate) :
m_plugin(plugin),
m_inputSampleRate(inputSampleRate),
m_channels(0),
m_stepSize(0),
m_blockSize(0),
m_freqbuf(0),
m_ri(0),
m_windowType(HanningWindow),
m_window(0),
m_method(ShiftTimestamp),
m_processCount(0),
m_shiftBuffers(0),
m_cfg(0),
m_cbuf(0)
{
}
PluginInputDomainAdapter::Impl::~Impl()
{
// the adapter will delete the plugin
if (m_shiftBuffers) {
for (int c = 0; c < m_channels; ++c) {
delete[] m_shiftBuffers[c];
}
delete[] m_shiftBuffers;
}
if (m_channels > 0) {
for (int c = 0; c < m_channels; ++c) {
delete[] m_freqbuf[c];
}
delete[] m_freqbuf;
delete[] m_ri;
if (m_cfg) {
vamp_kiss_fftr_free(m_cfg);
m_cfg = 0;
delete[] m_cbuf;
m_cbuf = 0;
}
delete m_window;
}
}
// for some visual studii apparently
#ifndef M_PI
#define M_PI 3.14159265358979232846
#endif
bool
PluginInputDomainAdapter::Impl::initialise(size_t channels, size_t stepSize, size_t blockSize)
{
if (m_plugin->getInputDomain() == TimeDomain) {
m_stepSize = int(stepSize);
m_blockSize = int(blockSize);
m_channels = int(channels);
return m_plugin->initialise(channels, stepSize, blockSize);
}
if (blockSize < 2) {
std::cerr << "ERROR: PluginInputDomainAdapter::initialise: blocksize < 2 not supported" << std::endl;
return false;
}
if (blockSize % 2) {
std::cerr << "ERROR: PluginInputDomainAdapter::initialise: odd blocksize " << blockSize << " not supported" << std::endl;
return false;
}
if (m_channels > 0) {
for (int c = 0; c < m_channels; ++c) {
delete[] m_freqbuf[c];
}
delete[] m_freqbuf;
delete[] m_ri;
if (m_cfg) {
vamp_kiss_fftr_free(m_cfg);
m_cfg = 0;
delete[] m_cbuf;
m_cbuf = 0;
}
delete m_window;
}
m_stepSize = int(stepSize);
m_blockSize = int(blockSize);
m_channels = int(channels);
m_freqbuf = new float *[m_channels];
for (int c = 0; c < m_channels; ++c) {
m_freqbuf[c] = new float[m_blockSize + 2];
}
m_ri = new vamp_kiss_fft_scalar[m_blockSize];
m_window = new W(convertType(m_windowType), m_blockSize);
m_cfg = vamp_kiss_fftr_alloc(m_blockSize, false, 0, 0);
m_cbuf = new vamp_kiss_fft_cpx[m_blockSize/2+1];
m_processCount = 0;
return m_plugin->initialise(channels, stepSize, m_blockSize);
}
void
PluginInputDomainAdapter::Impl::reset()
{
m_processCount = 0;
m_plugin->reset();
}
size_t
PluginInputDomainAdapter::Impl::getPreferredStepSize() const
{
size_t step = m_plugin->getPreferredStepSize();
if (step == 0 && (m_plugin->getInputDomain() == FrequencyDomain)) {
step = getPreferredBlockSize() / 2;
}
return step;
}
size_t
PluginInputDomainAdapter::Impl::getPreferredBlockSize() const
{
size_t block = m_plugin->getPreferredBlockSize();
if (m_plugin->getInputDomain() == FrequencyDomain) {
if (block == 0) {
block = 1024;
} else {
block = makeBlockSizeAcceptable(block);
}
}
return block;
}
size_t
PluginInputDomainAdapter::Impl::makeBlockSizeAcceptable(size_t blockSize) const
{
if (blockSize < 2) {
std::cerr << "WARNING: PluginInputDomainAdapter::initialise: blocksize < 2 not" << std::endl
<< "supported, increasing from " << blockSize << " to 2" << std::endl;
blockSize = 2;
} else if (blockSize % 2) {
std::cerr << "WARNING: PluginInputDomainAdapter::initialise: odd blocksize not" << std::endl
<< "supported, increasing from " << blockSize << " to " << (blockSize+1) << std::endl;
blockSize = blockSize+1;
}
return blockSize;
}
RealTime
PluginInputDomainAdapter::Impl::getTimestampAdjustment() const
{
if (m_plugin->getInputDomain() == TimeDomain) {
return RealTime::zeroTime;
} else if (m_method == ShiftData || m_method == NoShift) {
return RealTime::zeroTime;
} else {
return RealTime::frame2RealTime
(m_blockSize/2, int(m_inputSampleRate + 0.5));
}
}
void
PluginInputDomainAdapter::Impl::setProcessTimestampMethod(ProcessTimestampMethod m)
{
m_method = m;
}
PluginInputDomainAdapter::ProcessTimestampMethod
PluginInputDomainAdapter::Impl::getProcessTimestampMethod() const
{
return m_method;
}
void
PluginInputDomainAdapter::Impl::setWindowType(WindowType t)
{
if (m_windowType == t) return;
m_windowType = t;
if (m_window) {
delete m_window;
m_window = new W(convertType(m_windowType), m_blockSize);
}
}
PluginInputDomainAdapter::WindowType
PluginInputDomainAdapter::Impl::getWindowType() const
{
return m_windowType;
}
PluginInputDomainAdapter::Impl::W::WindowType
PluginInputDomainAdapter::Impl::convertType(WindowType t) const
{
switch (t) {
case RectangularWindow:
return W::RectangularWindow;
case BartlettWindow:
return W::BartlettWindow;
case HammingWindow:
return W::HammingWindow;
case HanningWindow:
return W::HanningWindow;
case BlackmanWindow:
return W::BlackmanWindow;
case NuttallWindow:
return W::NuttallWindow;
case BlackmanHarrisWindow:
return W::BlackmanHarrisWindow;
default:
return W::HanningWindow;
}
}
Plugin::FeatureSet
PluginInputDomainAdapter::Impl::process(const float *const *inputBuffers,
RealTime timestamp)
{
if (m_plugin->getInputDomain() == TimeDomain) {
return m_plugin->process(inputBuffers, timestamp);
}
if (m_method == ShiftTimestamp || m_method == NoShift) {
return processShiftingTimestamp(inputBuffers, timestamp);
} else {
return processShiftingData(inputBuffers, timestamp);
}
}
Plugin::FeatureSet
PluginInputDomainAdapter::Impl::processShiftingTimestamp(const float *const *inputBuffers,
RealTime timestamp)
{
unsigned int roundedRate = 1;
if (m_inputSampleRate > 0.f) {
roundedRate = (unsigned int)round(m_inputSampleRate);
}
if (m_method == ShiftTimestamp) {
// we may need to add one nsec if timestamp +
// getTimestampAdjustment() rounds down
timestamp = timestamp + getTimestampAdjustment();
RealTime nsec(0, 1);
if (RealTime::realTime2Frame(timestamp, roundedRate) <
RealTime::realTime2Frame(timestamp + nsec, roundedRate)) {
timestamp = timestamp + nsec;
}
}
for (int c = 0; c < m_channels; ++c) {
m_window->cut(inputBuffers[c], m_ri);
for (int i = 0; i < m_blockSize/2; ++i) {
// FFT shift
vamp_kiss_fft_scalar value = m_ri[i];
m_ri[i] = m_ri[i + m_blockSize/2];
m_ri[i + m_blockSize/2] = value;
}
vamp_kiss_fftr(m_cfg, m_ri, m_cbuf);
for (int i = 0; i <= m_blockSize/2; ++i) {
m_freqbuf[c][i * 2] = float(m_cbuf[i].r);
m_freqbuf[c][i * 2 + 1] = float(m_cbuf[i].i);
}
}
return m_plugin->process(m_freqbuf, timestamp);
}
Plugin::FeatureSet
PluginInputDomainAdapter::Impl::processShiftingData(const float *const *inputBuffers,
RealTime timestamp)
{
if (m_processCount == 0) {
if (!m_shiftBuffers) {
m_shiftBuffers = new float *[m_channels];
for (int c = 0; c < m_channels; ++c) {
m_shiftBuffers[c] = new float[m_blockSize + m_blockSize/2];
}
}
for (int c = 0; c < m_channels; ++c) {
for (int i = 0; i < m_blockSize + m_blockSize/2; ++i) {
m_shiftBuffers[c][i] = 0.f;
}
}
}
for (int c = 0; c < m_channels; ++c) {
for (int i = m_stepSize; i < m_blockSize + m_blockSize/2; ++i) {
m_shiftBuffers[c][i - m_stepSize] = m_shiftBuffers[c][i];
}
for (int i = 0; i < m_blockSize; ++i) {
m_shiftBuffers[c][i + m_blockSize/2] = inputBuffers[c][i];
}
}
for (int c = 0; c < m_channels; ++c) {
m_window->cut(m_shiftBuffers[c], m_ri);
for (int i = 0; i < m_blockSize/2; ++i) {
// FFT shift
vamp_kiss_fft_scalar value = m_ri[i];
m_ri[i] = m_ri[i + m_blockSize/2];
m_ri[i + m_blockSize/2] = value;
}
vamp_kiss_fftr(m_cfg, m_ri, m_cbuf);
for (int i = 0; i <= m_blockSize/2; ++i) {
m_freqbuf[c][i * 2] = float(m_cbuf[i].r);
m_freqbuf[c][i * 2 + 1] = float(m_cbuf[i].i);
}
}
++m_processCount;
return m_plugin->process(m_freqbuf, timestamp);
}
}
}

View File

@ -0,0 +1,191 @@
#pragma once
#include "vamp-hostsdk/PluginWrapper.h"
namespace Vamp {
namespace HostExt {
/**
* \class PluginInputDomainAdapter PluginInputDomainAdapter.h <vamp-hostsdk/PluginInputDomainAdapter.h>
*
* PluginInputDomainAdapter is a Vamp plugin adapter that converts
* time-domain input into frequency-domain input for plugins that need
* it. This permits a host to use time- and frequency-domain plugins
* interchangeably without needing to handle the conversion itself.
*
* This adapter uses a basic windowed FFT (using Hann window by
* default) that supports power-of-two block sizes only. If a
* frequency domain plugin requests a non-power-of-two blocksize, the
* adapter will adjust it to a nearby power of two instead. Thus,
* getPreferredBlockSize() will always return a power of two if the
* wrapped plugin is a frequency domain one. If the plugin doesn't
* accept the adjusted power of two block size, initialise() will
* fail.
*
* The adapter provides no way for the host to discover whether the
* underlying plugin is actually a time or frequency domain plugin
* (except that if the preferred block size is not a power of two, it
* must be a time domain plugin).
*
* The FFT implementation is simple and self-contained, but unlikely
* to be the fastest available: a host can usually do better if it
* cares enough.
*
* The window shape for the FFT frame can be set using setWindowType
* and the current shape retrieved using getWindowType. (This was
* added in v2.3 of the SDK.)
*
* In every respect other than its input domain handling, the
* PluginInputDomainAdapter behaves identically to the plugin that it
* wraps. The wrapped plugin will be deleted when the wrapper is
* deleted.
*
* \note This class was introduced in version 1.1 of the Vamp plugin SDK.
*/
class PluginInputDomainAdapter : public PluginWrapper
{
public:
/**
* Construct a PluginInputDomainAdapter wrapping the given plugin.
* The adapter takes ownership of the plugin, which will be
* deleted when the adapter is deleted.
*/
PluginInputDomainAdapter(Plugin *plugin);
virtual ~PluginInputDomainAdapter();
bool initialise(size_t channels, size_t stepSize, size_t blockSize);
void reset();
InputDomain getInputDomain() const;
size_t getPreferredStepSize() const;
size_t getPreferredBlockSize() const;
FeatureSet process(const float *const *inputBuffers, RealTime timestamp);
/**
* ProcessTimestampMethod determines how the
* PluginInputDomainAdapter handles timestamps for the data passed
* to the process() function of the plugin it wraps, in the case
* where the plugin is expecting frequency-domain data.
*
* The Vamp specification requires that the timestamp passed to
* the plugin for frequency-domain input should be that of the
* centre of the processing block, rather than the start as is the
* case for time-domain input.
*
* Since PluginInputDomainAdapter aims to be transparent in use,
* it needs to handle this timestamp adjustment itself. However,
* some control is available over the method used for adjustment,
* by means of the ProcessTimestampMethod setting.
*
* If ProcessTimestampMethod is set to ShiftTimestamp (the
* default), then the data passed to the wrapped plugin will be
* calculated from the same input data block as passed to the
* wrapper, but the timestamp passed to the plugin will be
* advanced by half of the window size.
*
* If ProcessTimestampMethod is set to ShiftData, then the
* timestamp passed to the wrapped plugin will be the same as that
* passed to the process call of the wrapper, but the data block
* used to calculate the input will be shifted back (earlier) by
* half of the window size, with half a block of zero padding at
* the start of the first process call. This has the advantage of
* preserving the first half block of audio without any
* deterioration from window shaping.
*
* If ProcessTimestampMethod is set to NoShift, then no adjustment
* will be made and the timestamps will be incorrect.
*/
enum ProcessTimestampMethod {
ShiftTimestamp,
ShiftData,
NoShift
};
/**
* Set the method used for timestamp adjustment in plugins taking
* frequency-domain input. See the ProcessTimestampMethod
* documentation for details.
*
* This function must be called before the first call to
* process().
*/
void setProcessTimestampMethod(ProcessTimestampMethod);
/**
* Retrieve the method used for timestamp adjustment in plugins
* taking frequency-domain input. See the ProcessTimestampMethod
* documentation for details.
*/
ProcessTimestampMethod getProcessTimestampMethod() const;
/**
* Return the amount by which the timestamps supplied to process()
* are being incremented when they are passed to the plugin's own
* process() implementation.
*
* The Vamp API mandates that the timestamp passed to the plugin
* for time-domain input should be the time of the first sample in
* the block, but the timestamp passed for frequency-domain input
* should be the timestamp of the centre of the block.
*
* The PluginInputDomainAdapter adjusts its timestamps properly so
* that the plugin receives correct times, but in some
* circumstances (such as for establishing the correct timing of
* implicitly-timed features, i.e. features without their own
* timestamps) the host may need to be aware that this adjustment
* is taking place.
*
* If the plugin requires time-domain input or the
* PluginInputDomainAdapter is configured with its
* ProcessTimestampMethod set to ShiftData instead of
* ShiftTimestamp, then this function will return zero.
*
* The result of calling this function before initialise() has
* been called is undefined.
*/
RealTime getTimestampAdjustment() const;
/**
* The set of supported window shapes.
*/
enum WindowType {
RectangularWindow = 0,
BartlettWindow = 1, /// synonym for RectangularWindow
TriangularWindow = 1, /// synonym for BartlettWindow
HammingWindow = 2,
HanningWindow = 3, /// synonym for HannWindow
HannWindow = 3, /// synonym for HanningWindow
BlackmanWindow = 4,
NuttallWindow = 7,
BlackmanHarrisWindow = 8
};
/**
* Return the current window shape. The default is HanningWindow.
*/
WindowType getWindowType() const;
/**
* Set the current window shape.
*/
void setWindowType(WindowType type);
protected:
class Impl;
Impl *m_impl;
};
}
}

View File

@ -0,0 +1,567 @@
#include "vamp-hostsdk/PluginLoader.h"
#include "vamp-hostsdk/PluginInputDomainAdapter.h"
#include "vamp-hostsdk/PluginChannelAdapter.h"
#include "vamp-hostsdk/PluginBufferingAdapter.h"
#include "vamp-hostsdk/PluginHostAdapter.h"
#include "vamp.h"
#include "vamp-hostsdk/Files.h"
#include <fstream>
namespace Vamp {
namespace HostExt {
class PluginLoader::Impl
{
public:
Impl();
virtual ~Impl();
PluginKeyList listPlugins();
PluginKeyList listPluginsIn(std::vector<std::string>);
PluginKeyList listPluginsNotIn(std::vector<std::string>);
Plugin *loadPlugin(PluginKey key,
float inputSampleRate,
int adapterFlags);
PluginKey composePluginKey(std::string libraryName, std::string identifier);
PluginCategoryHierarchy getPluginCategory(PluginKey key);
std::string getLibraryPathForPlugin(PluginKey key);
static void setInstanceToClean(PluginLoader *instance);
protected:
class PluginDeletionNotifyAdapter : public PluginWrapper {
public:
PluginDeletionNotifyAdapter(Plugin *plugin, Impl *loader);
virtual ~PluginDeletionNotifyAdapter();
protected:
Impl *m_loader;
};
class InstanceCleaner {
public:
InstanceCleaner() : m_instance(0) { }
~InstanceCleaner() { delete m_instance; }
void setInstance(PluginLoader *instance) { m_instance = instance; }
protected:
PluginLoader *m_instance;
};
virtual void pluginDeleted(PluginDeletionNotifyAdapter *adapter);
std::map<PluginKey, std::string> m_pluginLibraryNameMap;
bool m_allPluginsEnumerated;
struct Enumeration {
enum { All, SinglePlugin, InLibraries, NotInLibraries } type;
PluginKey key;
std::vector<std::string> libraryNames;
Enumeration() : type(All) { }
};
std::vector<std::string> listLibraryFilesFor(Enumeration);
/// Populate m_pluginLibraryNameMap and return a list of the keys
/// that were added to it
std::vector<PluginKey> enumeratePlugins(Enumeration);
std::map<PluginKey, PluginCategoryHierarchy> m_taxonomy;
void generateTaxonomy();
std::map<Plugin *, void *> m_pluginLibraryHandleMap;
bool decomposePluginKey(PluginKey key,
std::string &libraryName, std::string &identifier);
static InstanceCleaner m_cleaner;
};
PluginLoader *
PluginLoader::m_instance = 0;
PluginLoader::Impl::InstanceCleaner
PluginLoader::Impl::m_cleaner;
PluginLoader::PluginLoader()
{
m_impl = new Impl();
}
PluginLoader::~PluginLoader()
{
delete m_impl;
}
PluginLoader *
PluginLoader::getInstance()
{
if (!m_instance) {
// The cleaner doesn't own the instance, because we leave the
// instance pointer in the base class for binary backwards
// compatibility reasons and to avoid waste
m_instance = new PluginLoader();
Impl::setInstanceToClean(m_instance);
}
return m_instance;
}
PluginLoader::PluginKeyList
PluginLoader::listPlugins()
{
return m_impl->listPlugins();
}
PluginLoader::PluginKeyList
PluginLoader::listPluginsIn(std::vector<std::string> libs)
{
return m_impl->listPluginsIn(libs);
}
PluginLoader::PluginKeyList
PluginLoader::listPluginsNotIn(std::vector<std::string> libs)
{
return m_impl->listPluginsNotIn(libs);
}
Plugin *
PluginLoader::loadPlugin(PluginKey key,
float inputSampleRate,
int adapterFlags)
{
return m_impl->loadPlugin(key, inputSampleRate, adapterFlags);
}
PluginLoader::PluginKey
PluginLoader::composePluginKey(std::string libraryName, std::string identifier)
{
return m_impl->composePluginKey(libraryName, identifier);
}
PluginLoader::PluginCategoryHierarchy
PluginLoader::getPluginCategory(PluginKey key)
{
return m_impl->getPluginCategory(key);
}
std::string
PluginLoader::getLibraryPathForPlugin(PluginKey key)
{
return m_impl->getLibraryPathForPlugin(key);
}
PluginLoader::Impl::Impl() :
m_allPluginsEnumerated(false)
{
}
PluginLoader::Impl::~Impl()
{
}
void
PluginLoader::Impl::setInstanceToClean(PluginLoader *instance)
{
m_cleaner.setInstance(instance);
}
PluginLoader::PluginKeyList
PluginLoader::Impl::listPlugins()
{
if (!m_allPluginsEnumerated) enumeratePlugins(Enumeration());
std::vector<PluginKey> plugins;
for (std::map<PluginKey, std::string>::const_iterator i =
m_pluginLibraryNameMap.begin();
i != m_pluginLibraryNameMap.end(); ++i) {
plugins.push_back(i->first);
}
return plugins;
}
PluginLoader::PluginKeyList
PluginLoader::Impl::listPluginsIn(std::vector<std::string> libs)
{
Enumeration enumeration;
enumeration.type = Enumeration::InLibraries;
enumeration.libraryNames = libs;
return enumeratePlugins(enumeration);
}
PluginLoader::PluginKeyList
PluginLoader::Impl::listPluginsNotIn(std::vector<std::string> libs)
{
Enumeration enumeration;
enumeration.type = Enumeration::NotInLibraries;
enumeration.libraryNames = libs;
return enumeratePlugins(enumeration);
}
std::vector<std::string>
PluginLoader::Impl::listLibraryFilesFor(Enumeration enumeration)
{
Files::Filter filter;
switch (enumeration.type) {
case Enumeration::All:
filter.type = Files::Filter::All;
break;
case Enumeration::SinglePlugin:
{
std::string libraryName, identifier;
if (!decomposePluginKey(enumeration.key, libraryName, identifier)) {
std::cerr << "WARNING: Vamp::HostExt::PluginLoader: "
<< "Invalid plugin key \"" << enumeration.key
<< "\" in enumerate" << std::endl;
return std::vector<std::string>();
}
filter.type = Files::Filter::Matching;
filter.libraryNames.clear();
filter.libraryNames.push_back(libraryName);
break;
}
case Enumeration::InLibraries:
filter.type = Files::Filter::Matching;
filter.libraryNames = enumeration.libraryNames;
break;
case Enumeration::NotInLibraries:
filter.type = Files::Filter::NotMatching;
filter.libraryNames = enumeration.libraryNames;
break;
}
return Files::listLibraryFilesMatching(filter);
}
std::vector<PluginLoader::PluginKey>
PluginLoader::Impl::enumeratePlugins(Enumeration enumeration)
{
std::string libraryName, identifier;
if (enumeration.type == Enumeration::SinglePlugin) {
decomposePluginKey(enumeration.key, libraryName, identifier);
}
std::vector<std::string> fullPaths = listLibraryFilesFor(enumeration);
// For these we should warn if a plugin can be loaded from a library
bool specific = (enumeration.type == Enumeration::SinglePlugin ||
enumeration.type == Enumeration::InLibraries);
std::vector<PluginKey> added;
for (size_t i = 0; i < fullPaths.size(); ++i) {
std::string fullPath = fullPaths[i];
void *handle = Files::loadLibrary(fullPath);
if (!handle) continue;
VampGetPluginDescriptorFunction fn =
(VampGetPluginDescriptorFunction)Files::lookupInLibrary
(handle, "vampGetPluginDescriptor");
if (!fn) {
if (specific) {
std::cerr << "Vamp::HostExt::PluginLoader: "
<< "No vampGetPluginDescriptor function found in library \""
<< fullPath << "\"" << std::endl;
}
Files::unloadLibrary(handle);
continue;
}
int index = 0;
const VampPluginDescriptor *descriptor = 0;
bool found = false;
while ((descriptor = fn(VAMP_API_VERSION, index))) {
++index;
if (identifier != "") {
if (descriptor->identifier != identifier) {
continue;
}
}
found = true;
PluginKey key = composePluginKey(fullPath, descriptor->identifier);
if (m_pluginLibraryNameMap.find(key) ==
m_pluginLibraryNameMap.end()) {
m_pluginLibraryNameMap[key] = fullPath;
}
added.push_back(key);
}
if (!found && specific) {
std::cerr << "Vamp::HostExt::PluginLoader: Plugin \""
<< identifier << "\" not found in library \""
<< fullPath << "\"" << std::endl;
}
Files::unloadLibrary(handle);
}
if (enumeration.type == Enumeration::All) {
m_allPluginsEnumerated = true;
}
return added;
}
PluginLoader::PluginKey
PluginLoader::Impl::composePluginKey(std::string libraryName, std::string identifier)
{
std::string basename = Files::lcBasename(libraryName);
return basename + ":" + identifier;
}
bool
PluginLoader::Impl::decomposePluginKey(PluginKey key,
std::string &libraryName,
std::string &identifier)
{
std::string::size_type ki = key.find(':');
if (ki == std::string::npos) {
return false;
}
libraryName = key.substr(0, ki);
identifier = key.substr(ki + 1);
return true;
}
PluginLoader::PluginCategoryHierarchy
PluginLoader::Impl::getPluginCategory(PluginKey plugin)
{
if (m_taxonomy.empty()) generateTaxonomy();
if (m_taxonomy.find(plugin) == m_taxonomy.end()) {
return PluginCategoryHierarchy();
}
return m_taxonomy[plugin];
}
std::string
PluginLoader::Impl::getLibraryPathForPlugin(PluginKey plugin)
{
if (m_pluginLibraryNameMap.find(plugin) == m_pluginLibraryNameMap.end()) {
if (m_allPluginsEnumerated) return "";
Enumeration enumeration;
enumeration.type = Enumeration::SinglePlugin;
enumeration.key = plugin;
enumeratePlugins(enumeration);
}
if (m_pluginLibraryNameMap.find(plugin) == m_pluginLibraryNameMap.end()) {
return "";
}
return m_pluginLibraryNameMap[plugin];
}
Plugin *
PluginLoader::Impl::loadPlugin(PluginKey key,
float inputSampleRate, int adapterFlags)
{
std::string libname, identifier;
if (!decomposePluginKey(key, libname, identifier)) {
std::cerr << "Vamp::HostExt::PluginLoader: Invalid plugin key \""
<< key << "\" in loadPlugin" << std::endl;
return 0;
}
std::string fullPath = getLibraryPathForPlugin(key);
if (fullPath == "") {
std::cerr << "Vamp::HostExt::PluginLoader: No library found in Vamp path for plugin \"" << key << "\"" << std::endl;
return 0;
}
void *handle = Files::loadLibrary(fullPath);
if (!handle) return 0;
VampGetPluginDescriptorFunction fn =
(VampGetPluginDescriptorFunction)Files::lookupInLibrary
(handle, "vampGetPluginDescriptor");
if (!fn) {
std::cerr << "Vamp::HostExt::PluginLoader: No vampGetPluginDescriptor function found in library \""
<< fullPath << "\"" << std::endl;
Files::unloadLibrary(handle);
return 0;
}
int index = 0;
const VampPluginDescriptor *descriptor = 0;
while ((descriptor = fn(VAMP_API_VERSION, index))) {
if (std::string(descriptor->identifier) == identifier) {
Vamp::PluginHostAdapter *plugin =
new Vamp::PluginHostAdapter(descriptor, inputSampleRate);
Plugin *adapter = new PluginDeletionNotifyAdapter(plugin, this);
m_pluginLibraryHandleMap[adapter] = handle;
if (adapterFlags & ADAPT_INPUT_DOMAIN) {
if (adapter->getInputDomain() == Plugin::FrequencyDomain) {
adapter = new PluginInputDomainAdapter(adapter);
}
}
if (adapterFlags & ADAPT_BUFFER_SIZE) {
adapter = new PluginBufferingAdapter(adapter);
}
if (adapterFlags & ADAPT_CHANNEL_COUNT) {
adapter = new PluginChannelAdapter(adapter);
}
return adapter;
}
++index;
}
std::cerr << "Vamp::HostExt::PluginLoader: Plugin \""
<< identifier << "\" not found in library \""
<< fullPath << "\"" << std::endl;
return 0;
}
void
PluginLoader::Impl::generateTaxonomy()
{
// cerr << "PluginLoader::Impl::generateTaxonomy" << endl;
std::vector<std::string> path = PluginHostAdapter::getPluginPath();
std::string libfragment = "/lib/";
std::vector<std::string> catpath;
std::string suffix = "cat";
for (std::vector<std::string>::iterator i = path.begin();
i != path.end(); ++i) {
// It doesn't matter that we're using literal forward-slash in
// this bit, as it's only relevant if the path contains
// "/lib/", which is only meaningful and only plausible on
// systems with forward-slash delimiters
std::string dir = *i;
std::string::size_type li = dir.find(libfragment);
if (li != std::string::npos) {
catpath.push_back
(dir.substr(0, li)
+ "/share/"
+ dir.substr(li + libfragment.length()));
}
catpath.push_back(dir);
}
char buffer[1024];
for (std::vector<std::string>::iterator i = catpath.begin();
i != catpath.end(); ++i) {
std::vector<std::string> files = Files::listFiles(*i, suffix);
for (std::vector<std::string>::iterator fi = files.begin();
fi != files.end(); ++fi) {
std::string filepath = Files::splicePath(*i, *fi);
std::ifstream is(filepath.c_str(), std::ifstream::in | std::ifstream::binary);
if (is.fail()) {
// cerr << "failed to open: " << filepath << endl;
continue;
}
// cerr << "opened: " << filepath << endl;
while (!!is.getline(buffer, 1024)) {
std::string line(buffer);
// cerr << "line = " << line << endl;
std::string::size_type di = line.find("::");
if (di == std::string::npos) continue;
std::string id = line.substr(0, di);
std::string encodedCat = line.substr(di + 2);
if (id.substr(0, 5) != "vamp:") continue;
id = id.substr(5);
while (encodedCat.length() >= 1 &&
encodedCat[encodedCat.length()-1] == '\r') {
encodedCat = encodedCat.substr(0, encodedCat.length()-1);
}
// cerr << "id = " << id << ", cat = " << encodedCat << endl;
PluginCategoryHierarchy category;
std::string::size_type ai;
while ((ai = encodedCat.find(" > ")) != std::string::npos) {
category.push_back(encodedCat.substr(0, ai));
encodedCat = encodedCat.substr(ai + 3);
}
if (encodedCat != "") category.push_back(encodedCat);
m_taxonomy[id] = category;
}
}
}
}
void
PluginLoader::Impl::pluginDeleted(PluginDeletionNotifyAdapter *adapter)
{
void *handle = m_pluginLibraryHandleMap[adapter];
if (!handle) return;
m_pluginLibraryHandleMap.erase(adapter);
for (auto h: m_pluginLibraryHandleMap) {
if (h.second == handle) {
// still in use
return;
}
}
Files::unloadLibrary(handle);
}
PluginLoader::Impl::PluginDeletionNotifyAdapter::PluginDeletionNotifyAdapter(Plugin *plugin,
Impl *loader) :
PluginWrapper(plugin),
m_loader(loader)
{
}
PluginLoader::Impl::PluginDeletionNotifyAdapter::~PluginDeletionNotifyAdapter()
{
// We need to delete the plugin before calling pluginDeleted, as
// the delete call may require calling through to the descriptor
// (for e.g. cleanup) but pluginDeleted may unload the required
// library for the call. To prevent a double deletion when our
// parent's destructor runs (after this one), be sure to set
// m_plugin to 0 after deletion.
delete m_plugin;
m_plugin = 0;
if (m_loader) m_loader->pluginDeleted(this);
}
}
}

View File

@ -0,0 +1,235 @@
#pragma once
#include <vector>
#include <string>
#include <map>
#include "vamp-hostsdk/PluginWrapper.h"
namespace Vamp {
class Plugin;
namespace HostExt {
/**
* \class PluginLoader PluginLoader.h <vamp-hostsdk/PluginLoader.h>
*
* Vamp::HostExt::PluginLoader is a convenience class for discovering
* and loading Vamp plugins using the typical plugin-path, library
* naming, and categorisation conventions described in the Vamp SDK
* documentation. This class is intended to greatly simplify the task
* of becoming a Vamp plugin host for any C++ application.
*
* Hosts are not required by the Vamp specification to use the same
* plugin search path and naming conventions as implemented by this
* class, and are certainly not required to use this actual class.
* But we do strongly recommend it.
*
* This class is not thread-safe; use it from a single application
* thread, or guard access to it with a mutex.
*
* \note This class was introduced in version 1.1 of the Vamp plugin SDK.
*/
class PluginLoader
{
public:
/**
* Obtain a pointer to the singleton instance of PluginLoader.
* Use this to obtain your loader object.
*/
static PluginLoader *getInstance();
/**
* PluginKey is a string type that is used to identify a plugin
* uniquely within the scope of "the current system". It consists
* of the lower-cased base name of the plugin library, a colon
* separator, and the identifier string for the plugin. It is
* only meaningful in the context of a given plugin path (the one
* returned by PluginHostAdapter::getPluginPath()).
*
* Use composePluginKey() to construct a plugin key from a known
* plugin library name and identifier.
*
* Note: the fact that the library component of the key is
* lower-cased implies that library names are matched
* case-insensitively by the PluginLoader class, regardless of the
* case sensitivity of the underlying filesystem. (Plugin
* identifiers _are_ case sensitive, however.) Also, it is not
* possible to portably extract a working library name from a
* plugin key, as the result may fail on case-sensitive
* filesystems. Use getLibraryPathForPlugin() instead.
*/
typedef std::string PluginKey;
/**
* PluginKeyList is a sequence of plugin keys, such as returned by
* listPlugins().
*/
typedef std::vector<PluginKey> PluginKeyList;
/**
* PluginCategoryHierarchy is a sequence of general->specific
* category names, as may be associated with a single plugin.
* This sequence describes the location of a plugin within a
* category forest, containing the human-readable names of the
* plugin's category tree root, followed by each of the nodes down
* to the leaf containing the plugin.
*
* \see getPluginCategory()
*/
typedef std::vector<std::string> PluginCategoryHierarchy;
/**
* Search for all available Vamp plugins, and return a list of
* them in the order in which they were found.
*/
PluginKeyList listPlugins();
/**
* Search for available Vamp plugins in libraries with the given
* library names, and return a list of them in the order in which
* they were found. Do not attempt to load any plugin libraries
* other than those named.
*
* The library names should be supplied without path or
* suffix. For example, use "vamp-example-plugins" to find plugins
* in /install/path/of/vamp-example-plugins.dll (or .so etc). This
* is the same concept of "library name" as appears in the plugin
* key: \see composePluginKey().
*/
PluginKeyList listPluginsIn(std::vector<std::string> libraryNames);
/**
* Search for available Vamp plugins in libraries other than those
* with the given library names, and return a list of them in the
* order in which they were found. Do not attempt to load any of
* the libraries named.
*
* The library names should be supplied without path or
* suffix. For example, use "vamp-example-plugins" to find plugins
* not appearing in /install/path/of/vamp-example-plugins.dll (or
* .so etc). This is the same concept of "library name" as appears
* in the plugin key: \see composePluginKey().
*/
PluginKeyList listPluginsNotIn(std::vector<std::string> libraryNames);
/**
* AdapterFlags contains a set of values that may be OR'd together
* to indicate in which circumstances PluginLoader should use a
* plugin adapter to make a plugin easier to use for a host that
* does not want to cater for complex features.
*
* The available flags are:
*
* ADAPT_INPUT_DOMAIN - If the plugin expects frequency domain
* input, wrap it in a PluginInputDomainAdapter that automatically
* converts the plugin to one that expects time-domain input.
* This enables a host to accommodate time- and frequency-domain
* plugins without needing to do any conversion itself.
*
* ADAPT_CHANNEL_COUNT - Wrap the plugin in a PluginChannelAdapter
* to handle any mismatch between the number of channels of audio
* the plugin can handle and the number available in the host.
* This enables a host to use plugins that may require the input
* to be mixed down to mono, etc., without having to worry about
* doing that itself.
*
* ADAPT_BUFFER_SIZE - Wrap the plugin in a PluginBufferingAdapter
* permitting the host to provide audio input using any block
* size, with no overlap, regardless of the plugin's preferred
* block size (suitable for hosts that read from non-seekable
* streaming media, for example). This adapter introduces some
* run-time overhead and also changes the semantics of the plugin
* slightly (see the PluginBufferingAdapter header documentation
* for details).
*
* ADAPT_ALL_SAFE - Perform all available adaptations that are
* meaningful for the plugin and "safe". Currently this means to
* ADAPT_INPUT_DOMAIN if the plugin wants FrequencyDomain input;
* ADAPT_CHANNEL_COUNT always; and ADAPT_BUFFER_SIZE never.
*
* ADAPT_ALL - Perform all available adaptations that are
* meaningful for the plugin.
*
* See PluginInputDomainAdapter, PluginChannelAdapter and
* PluginBufferingAdapter for more details of the classes that the
* loader may use if these flags are set.
*/
enum AdapterFlags {
ADAPT_INPUT_DOMAIN = 0x01,
ADAPT_CHANNEL_COUNT = 0x02,
ADAPT_BUFFER_SIZE = 0x04,
ADAPT_ALL_SAFE = 0x03,
ADAPT_ALL = 0xff
};
/**
* Load a Vamp plugin, given its identifying key. If the plugin
* could not be loaded, returns 0.
*
* The returned plugin should be deleted (using the standard C++
* delete keyword) after use.
*
* \param adapterFlags a bitwise OR of the values in the AdapterFlags
* enumeration, indicating under which circumstances an adapter should be
* used to wrap the original plugin. If adapterFlags is 0, no
* optional adapters will be used. Otherwise, the returned plugin
* may be of an adapter class type which will behave identically
* to the original plugin, apart from any particular features
* implemented by the adapter itself.
*
* \see AdapterFlags, PluginInputDomainAdapter, PluginChannelAdapter
*/
Plugin *loadPlugin(PluginKey key,
float inputSampleRate,
int adapterFlags = 0);
/**
* Given a Vamp plugin library name and plugin identifier, return
* the corresponding plugin key in a form suitable for passing in to
* loadPlugin().
*
* (Note that the reverse of this is not well-defined and is not
* offered in this API - consider using getLibraryPathForPlugin
* instead. See documentation for the PluginKey type for details.)
*
* \see PluginKey, getLibraryPathForPlugin, loadPlugin
*/
PluginKey composePluginKey(std::string libraryName,
std::string identifier);
/**
* Return the category hierarchy for a Vamp plugin, given its
* identifying key.
*
* If the plugin has no category information, return an empty
* hierarchy.
*
* \see PluginCategoryHierarchy
*/
PluginCategoryHierarchy getPluginCategory(PluginKey plugin);
/**
* Return the file path of the dynamic library from which the
* given plugin will be loaded (if available).
*/
std::string getLibraryPathForPlugin(PluginKey plugin);
protected:
PluginLoader();
virtual ~PluginLoader();
class Impl;
Impl *m_impl;
static PluginLoader *m_instance;
};
}
}

View File

@ -0,0 +1,948 @@
#include "vamp-hostsdk/PluginSummarisingAdapter.h"
#include <map>
#include <algorithm>
#include <cmath>
#include <climits>
using namespace std;
//#define DEBUG_PLUGIN_SUMMARISING_ADAPTER 1
//#define DEBUG_PLUGIN_SUMMARISING_ADAPTER_SEGMENT 1
namespace Vamp {
namespace HostExt {
class PluginSummarisingAdapter::Impl
{
public:
Impl(Plugin *plugin, float inputSampleRate);
~Impl();
bool initialise(size_t channels, size_t stepSize, size_t blockSize);
void reset();
FeatureSet process(const float *const *inputBuffers, RealTime timestamp);
FeatureSet getRemainingFeatures();
void setSummarySegmentBoundaries(const SegmentBoundaries &);
FeatureList getSummaryForOutput(int output,
SummaryType type,
AveragingMethod avg);
FeatureSet getSummaryForAllOutputs(SummaryType type,
AveragingMethod avg);
protected:
Plugin *m_plugin;
float m_inputSampleRate;
size_t m_stepSize;
size_t m_blockSize;
SegmentBoundaries m_boundaries;
typedef vector<float> ValueList;
struct Result { // smaller than Feature
RealTime time;
RealTime duration;
ValueList values; // bin number -> value
};
typedef vector<Result> ResultList;
struct OutputAccumulator {
int bins;
ResultList results;
OutputAccumulator() : bins(0) { }
};
typedef map<int, OutputAccumulator> OutputAccumulatorMap;
OutputAccumulatorMap m_accumulators; // output number -> accumulator
typedef map<RealTime, OutputAccumulator> SegmentAccumulatorMap;
typedef map<int, SegmentAccumulatorMap> OutputSegmentAccumulatorMap;
OutputSegmentAccumulatorMap m_segmentedAccumulators; // output -> segmented
typedef map<int, RealTime> OutputTimestampMap;
OutputTimestampMap m_prevTimestamps; // output number -> timestamp
OutputTimestampMap m_prevDurations; // output number -> durations
struct OutputBinSummary {
int count;
// extents
double minimum;
double maximum;
double sum;
// sample-average results
double median;
double mode;
double variance;
// continuous-time average results
double median_c;
double mode_c;
double mean_c;
double variance_c;
};
typedef map<int, OutputBinSummary> OutputSummary;
typedef map<RealTime, OutputSummary> SummarySegmentMap;
typedef map<int, SummarySegmentMap> OutputSummarySegmentMap;
OutputSummarySegmentMap m_summaries;
bool m_reduced;
RealTime m_endTime;
void accumulate(const FeatureSet &fs, RealTime, bool final);
void accumulate(int output, const Feature &f, RealTime, bool final);
void accumulateFinalDurations();
void findSegmentBounds(RealTime t, RealTime &start, RealTime &end);
void segment();
void reduce();
string getSummaryLabel(SummaryType type, AveragingMethod avg);
};
static RealTime INVALID_DURATION(INT_MIN, INT_MIN);
PluginSummarisingAdapter::PluginSummarisingAdapter(Plugin *plugin) :
PluginWrapper(plugin)
{
m_impl = new Impl(plugin, m_inputSampleRate);
}
PluginSummarisingAdapter::~PluginSummarisingAdapter()
{
delete m_impl;
}
bool
PluginSummarisingAdapter::initialise(size_t channels,
size_t stepSize, size_t blockSize)
{
return
PluginWrapper::initialise(channels, stepSize, blockSize) &&
m_impl->initialise(channels, stepSize, blockSize);
}
void
PluginSummarisingAdapter::reset()
{
m_impl->reset();
}
Plugin::FeatureSet
PluginSummarisingAdapter::process(const float *const *inputBuffers, RealTime timestamp)
{
return m_impl->process(inputBuffers, timestamp);
}
Plugin::FeatureSet
PluginSummarisingAdapter::getRemainingFeatures()
{
return m_impl->getRemainingFeatures();
}
void
PluginSummarisingAdapter::setSummarySegmentBoundaries(const SegmentBoundaries &b)
{
m_impl->setSummarySegmentBoundaries(b);
}
Plugin::FeatureList
PluginSummarisingAdapter::getSummaryForOutput(int output,
SummaryType type,
AveragingMethod avg)
{
return m_impl->getSummaryForOutput(output, type, avg);
}
Plugin::FeatureSet
PluginSummarisingAdapter::getSummaryForAllOutputs(SummaryType type,
AveragingMethod avg)
{
return m_impl->getSummaryForAllOutputs(type, avg);
}
PluginSummarisingAdapter::Impl::Impl(Plugin *plugin, float inputSampleRate) :
m_plugin(plugin),
m_inputSampleRate(inputSampleRate),
m_reduced(false)
{
}
PluginSummarisingAdapter::Impl::~Impl()
{
}
bool
PluginSummarisingAdapter::Impl::initialise(size_t, size_t stepSize, size_t blockSize)
{
m_stepSize = stepSize;
m_blockSize = blockSize;
return true;
}
void
PluginSummarisingAdapter::Impl::reset()
{
m_accumulators.clear();
m_segmentedAccumulators.clear();
m_prevTimestamps.clear();
m_prevDurations.clear();
m_summaries.clear();
m_reduced = false;
m_endTime = RealTime();
m_plugin->reset();
}
Plugin::FeatureSet
PluginSummarisingAdapter::Impl::process(const float *const *inputBuffers,
RealTime timestamp)
{
if (m_reduced) {
cerr << "WARNING: Cannot call PluginSummarisingAdapter::process() or getRemainingFeatures() after one of the getSummary methods" << endl;
}
FeatureSet fs = m_plugin->process(inputBuffers, timestamp);
accumulate(fs, timestamp, false);
m_endTime = timestamp +
RealTime::frame2RealTime(m_stepSize, int(m_inputSampleRate + 0.5));
#ifdef DEBUG_PLUGIN_SUMMARISING_ADAPTER
cerr << "timestamp = " << timestamp << ", end time becomes " << m_endTime
<< endl;
#endif
return fs;
}
Plugin::FeatureSet
PluginSummarisingAdapter::Impl::getRemainingFeatures()
{
if (m_reduced) {
cerr << "WARNING: Cannot call PluginSummarisingAdapter::process() or getRemainingFeatures() after one of the getSummary methods" << endl;
}
FeatureSet fs = m_plugin->getRemainingFeatures();
accumulate(fs, m_endTime, true);
return fs;
}
void
PluginSummarisingAdapter::Impl::setSummarySegmentBoundaries(const SegmentBoundaries &b)
{
m_boundaries = b;
#ifdef DEBUG_PLUGIN_SUMMARISING_ADAPTER
cerr << "PluginSummarisingAdapter::setSummarySegmentBoundaries: boundaries are:" << endl;
for (SegmentBoundaries::const_iterator i = m_boundaries.begin();
i != m_boundaries.end(); ++i) {
cerr << *i << " ";
}
cerr << endl;
#endif
}
Plugin::FeatureList
PluginSummarisingAdapter::Impl::getSummaryForOutput(int output,
SummaryType type,
AveragingMethod avg)
{
if (!m_reduced) {
accumulateFinalDurations();
segment();
reduce();
m_reduced = true;
}
bool continuous = (avg == ContinuousTimeAverage);
FeatureList fl;
for (SummarySegmentMap::const_iterator i = m_summaries[output].begin();
i != m_summaries[output].end(); ++i) {
Feature f;
f.hasTimestamp = true;
f.timestamp = i->first;
f.hasDuration = true;
SummarySegmentMap::const_iterator ii = i;
if (++ii == m_summaries[output].end()) {
f.duration = m_endTime - f.timestamp;
} else {
f.duration = ii->first - f.timestamp;
}
f.label = getSummaryLabel(type, avg);
for (OutputSummary::const_iterator j = i->second.begin();
j != i->second.end(); ++j) {
// these will be ordered by bin number, and no bin numbers
// will be missing except at the end (because of the way
// the accumulators were initially filled in accumulate())
const OutputBinSummary &summary = j->second;
double result = 0.f;
switch (type) {
case Minimum:
result = summary.minimum;
break;
case Maximum:
result = summary.maximum;
break;
case Mean:
if (continuous) {
result = summary.mean_c;
} else if (summary.count) {
result = summary.sum / summary.count;
}
break;
case Median:
if (continuous) result = summary.median_c;
else result = summary.median;
break;
case Mode:
if (continuous) result = summary.mode_c;
else result = summary.mode;
break;
case Sum:
result = summary.sum;
break;
case Variance:
if (continuous) result = summary.variance_c;
else result = summary.variance;
break;
case StandardDeviation:
if (continuous) result = sqrt(summary.variance_c);
else result = sqrt(summary.variance);
break;
case Count:
result = summary.count;
break;
case UnknownSummaryType:
break;
default:
break;
}
f.values.push_back(float(result));
}
fl.push_back(f);
}
return fl;
}
Plugin::FeatureSet
PluginSummarisingAdapter::Impl::getSummaryForAllOutputs(SummaryType type,
AveragingMethod avg)
{
if (!m_reduced) {
accumulateFinalDurations();
segment();
reduce();
m_reduced = true;
}
FeatureSet fs;
for (OutputSummarySegmentMap::const_iterator i = m_summaries.begin();
i != m_summaries.end(); ++i) {
fs[i->first] = getSummaryForOutput(i->first, type, avg);
}
return fs;
}
void
PluginSummarisingAdapter::Impl::accumulate(const FeatureSet &fs,
RealTime timestamp,
bool final)
{
for (FeatureSet::const_iterator i = fs.begin(); i != fs.end(); ++i) {
for (FeatureList::const_iterator j = i->second.begin();
j != i->second.end(); ++j) {
if (j->hasTimestamp) {
accumulate(i->first, *j, j->timestamp, final);
} else {
//!!! is this correct?
accumulate(i->first, *j, timestamp, final);
}
}
}
}
string
PluginSummarisingAdapter::Impl::getSummaryLabel(SummaryType type,
AveragingMethod avg)
{
string label;
string avglabel;
if (avg == SampleAverage) avglabel = ", sample average";
else avglabel = ", continuous-time average";
switch (type) {
case Minimum: label = "(minimum value)"; break;
case Maximum: label = "(maximum value)"; break;
case Mean: label = "(mean value" + avglabel + ")"; break;
case Median: label = "(median value" + avglabel + ")"; break;
case Mode: label = "(modal value" + avglabel + ")"; break;
case Sum: label = "(sum)"; break;
case Variance: label = "(variance" + avglabel + ")"; break;
case StandardDeviation: label = "(standard deviation" + avglabel + ")"; break;
case Count: label = "(count)"; break;
case UnknownSummaryType: label = "(unknown summary)"; break;
}
return label;
}
void
PluginSummarisingAdapter::Impl::accumulate(int output,
const Feature &f,
RealTime timestamp,
bool
#ifdef DEBUG_PLUGIN_SUMMARISING_ADAPTER
final
#endif
)
{
// What should happen if a feature's duration spans a segment
// boundary? I think we probably want to chop it, and pretend
// that it appears in both. A very long feature (e.g. key, if the
// whole audio is in a single key) might span many or all
// segments, and we want that to be reflected in the results
// (e.g. it is the modal key in all of those segments, not just
// the first). This is actually quite complicated to do.
// If features spanning a boundary should be chopped, then we need
// to have per-segment accumulators (and the feature value goes
// into both -- with a separate phase to split the accumulator up
// into segments).
#ifdef DEBUG_PLUGIN_SUMMARISING_ADAPTER
cerr << "output " << output << ": timestamp " << timestamp << ", prev timestamp " << m_prevTimestamps[output] << ", final " << final << endl;
#endif
// At each process step, accumulate() is called once for each
// feature on each output within that process's returned feature
// list, and with the timestamp passed in being that of the start
// of the process block.
// At the end (in getRemainingFeatures), accumulate() is called
// once for each feature on each output within the feature list
// returned by getRemainingFeatures, and with the timestamp being
// the same as the last process block and final set to true.
// (What if getRemainingFeatures doesn't return any features? We
// still need to ensure that the final duration is written. Need
// a separate function to close the durations.)
// At each call, we pull out the value for the feature and stuff
// it into the accumulator's appropriate values array; and we
// calculate the duration for the _previous_ feature, or pull it
// from the prevDurations array if the previous feature had a
// duration in its structure, and stuff that into the
// accumulator's appropriate durations array.
if (m_prevDurations.find(output) != m_prevDurations.end()) {
// Not the first time accumulate has been called for this
// output -- there has been a previous feature
RealTime prevDuration;
// Note that m_prevDurations[output] only contains the
// duration field that was contained in the previous feature.
// If it didn't have an explicit duration,
// m_prevDurations[output] should be INVALID_DURATION and we
// will have to calculate the duration from the previous and
// current timestamps.
if (m_prevDurations[output] != INVALID_DURATION) {
prevDuration = m_prevDurations[output];
#ifdef DEBUG_PLUGIN_SUMMARISING_ADAPTER
cerr << "Previous duration from previous feature: " << prevDuration << endl;
#endif
} else {
prevDuration = timestamp - m_prevTimestamps[output];
#ifdef DEBUG_PLUGIN_SUMMARISING_ADAPTER
cerr << "Previous duration from diff: " << timestamp << " - "
<< m_prevTimestamps[output] << endl;
#endif
}
#ifdef DEBUG_PLUGIN_SUMMARISING_ADAPTER
cerr << "output " << output << ": ";
cerr << "Pushing previous duration as " << prevDuration << endl;
#endif
m_accumulators[output].results
[m_accumulators[output].results.size() - 1]
.duration = prevDuration;
}
if (f.hasDuration) m_prevDurations[output] = f.duration;
else m_prevDurations[output] = INVALID_DURATION;
m_prevTimestamps[output] = timestamp;
if (f.hasDuration) {
RealTime et = timestamp;
et = et + f.duration;
if (et > m_endTime) m_endTime = et;
#ifdef DEBUG_PLUGIN_SUMMARISING_ADAPTER
cerr << "feature has duration, updating end time to " << m_endTime << endl;
#endif
}
Result result;
result.time = timestamp;
result.duration = INVALID_DURATION;
if (int(f.values.size()) > m_accumulators[output].bins) {
m_accumulators[output].bins = int(f.values.size());
}
for (int i = 0; i < int(f.values.size()); ++i) {
result.values.push_back(f.values[i]);
}
m_accumulators[output].results.push_back(result);
}
void
PluginSummarisingAdapter::Impl::accumulateFinalDurations()
{
for (OutputTimestampMap::iterator i = m_prevTimestamps.begin();
i != m_prevTimestamps.end(); ++i) {
int output = i->first;
int acount = int(m_accumulators[output].results.size());
if (acount == 0) continue;
#ifdef DEBUG_PLUGIN_SUMMARISING_ADAPTER
cerr << "output " << output << ": ";
#endif
if (m_prevDurations.find(output) != m_prevDurations.end() &&
m_prevDurations[output] != INVALID_DURATION) {
#ifdef DEBUG_PLUGIN_SUMMARISING_ADAPTER
cerr << "Pushing final duration from feature as " << m_prevDurations[output] << endl;
#endif
m_accumulators[output].results[acount - 1].duration =
m_prevDurations[output];
} else {
#ifdef DEBUG_PLUGIN_SUMMARISING_ADAPTER
cerr << "Pushing final duration from diff as " << m_endTime << " - " << m_prevTimestamps[output] << endl;
#endif
m_accumulators[output].results[acount - 1].duration =
m_endTime - m_prevTimestamps[output];
}
#ifdef DEBUG_PLUGIN_SUMMARISING_ADAPTER
cerr << "so duration for result no " << acount-1 << " is "
<< m_accumulators[output].results[acount-1].duration
<< endl;
#endif
}
}
void
PluginSummarisingAdapter::Impl::findSegmentBounds(RealTime t,
RealTime &start,
RealTime &end)
{
#ifdef DEBUG_PLUGIN_SUMMARISING_ADAPTER_SEGMENT
cerr << "findSegmentBounds: t = " << t << endl;
#endif
SegmentBoundaries::const_iterator i = upper_bound
(m_boundaries.begin(), m_boundaries.end(), t);
start = RealTime::zeroTime;
end = m_endTime;
if (i != m_boundaries.end()) {
end = *i;
}
if (i != m_boundaries.begin()) {
start = *--i;
}
#ifdef DEBUG_PLUGIN_SUMMARISING_ADAPTER_SEGMENT
cerr << "findSegmentBounds: " << t << " is in segment " << start << " -> " << end << endl;
#endif
}
void
PluginSummarisingAdapter::Impl::segment()
{
#ifdef DEBUG_PLUGIN_SUMMARISING_ADAPTER_SEGMENT
cerr << "segment: starting" << endl;
#endif
for (OutputAccumulatorMap::iterator i = m_accumulators.begin();
i != m_accumulators.end(); ++i) {
int output = i->first;
OutputAccumulator &source = i->second;
#ifdef DEBUG_PLUGIN_SUMMARISING_ADAPTER_SEGMENT
cerr << "segment: total results for output " << output << " = "
<< source.results.size() << endl;
#endif
// This is basically nonsense if the results have no values
// (i.e. their times and counts are the only things of
// interest)... but perhaps it's the user's problem if they
// ask for segmentation (or any summary at all) in that case
for (int n = 0; n < int(source.results.size()); ++n) {
// This result spans source.results[n].time to
// source.results[n].time + source.results[n].duration.
// We need to dispose it into segments appropriately
RealTime resultStart = source.results[n].time;
RealTime resultEnd = resultStart + source.results[n].duration;
#ifdef DEBUG_PLUGIN_SUMMARISING_ADAPTER_SEGMENT
cerr << "output: " << output << ", result start = " << resultStart << ", end = " << resultEnd << endl;
#endif
RealTime segmentStart = RealTime::zeroTime;
RealTime segmentEnd = resultEnd - RealTime(1, 0);
RealTime prevSegmentStart = segmentStart - RealTime(1, 0);
while (segmentEnd < resultEnd) {
#ifdef DEBUG_PLUGIN_SUMMARISING_ADAPTER_SEGMENT
cerr << "segment end " << segmentEnd << " < result end "
<< resultEnd << " (with result start " << resultStart << ")" << endl;
#endif
findSegmentBounds(resultStart, segmentStart, segmentEnd);
if (segmentStart == prevSegmentStart) {
// This can happen when we reach the end of the
// input, if a feature's end time overruns the
// input audio end time
break;
}
prevSegmentStart = segmentStart;
RealTime chunkStart = resultStart;
if (chunkStart < segmentStart) chunkStart = segmentStart;
RealTime chunkEnd = resultEnd;
if (chunkEnd > segmentEnd) chunkEnd = segmentEnd;
m_segmentedAccumulators[output][segmentStart].bins = source.bins;
Result chunk;
chunk.time = chunkStart;
chunk.duration = chunkEnd - chunkStart;
chunk.values = source.results[n].values;
#ifdef DEBUG_PLUGIN_SUMMARISING_ADAPTER_SEGMENT
cerr << "chunk for segment " << segmentStart << ": from " << chunk.time << ", duration " << chunk.duration << endl;
#endif
m_segmentedAccumulators[output][segmentStart].results
.push_back(chunk);
resultStart = chunkEnd;
}
}
}
}
struct ValueDurationFloatPair
{
float value;
float duration;
ValueDurationFloatPair() : value(0), duration(0) { }
ValueDurationFloatPair(float v, float d) : value(v), duration(d) { }
ValueDurationFloatPair &operator=(const ValueDurationFloatPair &p) {
value = p.value;
duration = p.duration;
return *this;
}
bool operator<(const ValueDurationFloatPair &p) const {
return value < p.value;
}
};
static double toSec(const RealTime &r)
{
return r.sec + double(r.nsec) / 1000000000.0;
}
void
PluginSummarisingAdapter::Impl::reduce()
{
for (OutputSegmentAccumulatorMap::iterator i =
m_segmentedAccumulators.begin();
i != m_segmentedAccumulators.end(); ++i) {
int output = i->first;
SegmentAccumulatorMap &segments = i->second;
for (SegmentAccumulatorMap::iterator j = segments.begin();
j != segments.end(); ++j) {
RealTime segmentStart = j->first;
OutputAccumulator &accumulator = j->second;
int sz = int(accumulator.results.size());
#ifdef DEBUG_PLUGIN_SUMMARISING_ADAPTER
cerr << "reduce: segment starting at " << segmentStart
<< " on output " << output << " has " << sz << " result(s)" << endl;
#endif
double totalDuration = 0.0;
//!!! is this right?
if (sz > 0) {
#ifdef DEBUG_PLUGIN_SUMMARISING_ADAPTER
cerr << "last time = " << accumulator.results[sz-1].time
<< ", duration = " << accumulator.results[sz-1].duration
<< " (step = " << m_stepSize << ", block = " << m_blockSize << ")"
<< endl;
#endif
totalDuration = toSec((accumulator.results[sz-1].time +
accumulator.results[sz-1].duration) -
segmentStart);
}
for (int bin = 0; bin < accumulator.bins; ++bin) {
// work on all values over time for a single bin
#ifdef DEBUG_PLUGIN_SUMMARISING_ADAPTER
cerr << "bin " << bin << ":" << endl;
#endif
OutputBinSummary summary;
summary.count = sz;
summary.minimum = 0.f;
summary.maximum = 0.f;
summary.median = 0.f;
summary.mode = 0.f;
summary.sum = 0.f;
summary.variance = 0.f;
summary.median_c = 0.f;
summary.mode_c = 0.f;
summary.mean_c = 0.f;
summary.variance_c = 0.f;
if (sz == 0) continue;
vector<ValueDurationFloatPair> valvec;
for (int k = 0; k < sz; ++k) {
while (int(accumulator.results[k].values.size()) <
accumulator.bins) {
accumulator.results[k].values.push_back(0.f);
}
}
for (int k = 0; k < sz; ++k) {
float value = accumulator.results[k].values[bin];
valvec.push_back
(ValueDurationFloatPair
(value,
float(toSec(accumulator.results[k].duration))));
}
sort(valvec.begin(), valvec.end());
summary.minimum = valvec[0].value;
summary.maximum = valvec[sz-1].value;
#ifdef DEBUG_PLUGIN_SUMMARISING_ADAPTER
cerr << "total duration = " << totalDuration << endl;
#endif
#ifdef DEBUG_PLUGIN_SUMMARISING_ADAPTER
/*
cerr << "value vector for medians:" << endl;
for (int k = 0; k < sz; ++k) {
cerr << "(" << valvec[k].value << "," << valvec[k].duration << ") ";
}
cerr << endl;
*/
#endif
if (sz % 2 == 1) {
summary.median = valvec[sz/2].value;
} else {
summary.median = (valvec[sz/2].value + valvec[sz/2 + 1].value) / 2;
}
double duracc = 0.0;
summary.median_c = valvec[sz-1].value;
for (int k = 0; k < sz; ++k) {
duracc += valvec[k].duration;
if (duracc > totalDuration/2) {
summary.median_c = valvec[k].value;
break;
}
}
#ifdef DEBUG_PLUGIN_SUMMARISING_ADAPTER
cerr << "median_c = " << summary.median_c << endl;
cerr << "median = " << summary.median << endl;
#endif
map<float, int> distribution;
#ifdef DEBUG_PLUGIN_SUMMARISING_ADAPTER
cerr << "summing (discrete): ";
#endif
for (int k = 0; k < sz; ++k) {
#ifdef DEBUG_PLUGIN_SUMMARISING_ADAPTER
cerr << accumulator.results[k].values[bin] << " ";
#endif
summary.sum += accumulator.results[k].values[bin];
distribution[accumulator.results[k].values[bin]] += 1;
}
#ifdef DEBUG_PLUGIN_SUMMARISING_ADAPTER
cerr << endl;
#endif
int md = 0;
for (map<float, int>::iterator di = distribution.begin();
di != distribution.end(); ++di) {
if (di->second > md) {
md = di->second;
summary.mode = di->first;
}
}
distribution.clear();
map<float, double> distribution_c;
for (int k = 0; k < sz; ++k) {
distribution_c[accumulator.results[k].values[bin]]
+= toSec(accumulator.results[k].duration);
}
double mrd = 0.0;
for (map<float, double>::iterator di = distribution_c.begin();
di != distribution_c.end(); ++di) {
if (di->second > mrd) {
#ifdef DEBUG_PLUGIN_SUMMARISING_ADAPTER
cerr << "element " << di->first << " spans time "
<< di->second << " and so is an improved mode "
<< "candidate over element " << summary.mode_c
<< " which spanned " << mrd << endl;
#endif
mrd = di->second;
summary.mode_c = di->first;
}
}
distribution_c.clear();
if (totalDuration > 0.0) {
double sum_c = 0.0;
#ifdef DEBUG_PLUGIN_SUMMARISING_ADAPTER
cerr << "summing (continuous): ";
#endif
for (int k = 0; k < sz; ++k) {
#ifdef DEBUG_PLUGIN_SUMMARISING_ADAPTER
cerr << accumulator.results[k].values[bin] << "*"
<< toSec(accumulator.results[k].duration) << " ";
#endif
double value = accumulator.results[k].values[bin]
* toSec(accumulator.results[k].duration);
sum_c += value;
}
#ifdef DEBUG_PLUGIN_SUMMARISING_ADAPTER
cerr << endl;
#endif
#ifdef DEBUG_PLUGIN_SUMMARISING_ADAPTER
cerr << "mean_c = " << sum_c << " / " << totalDuration << " = "
<< sum_c / totalDuration << " (sz = " << sz << ")" << endl;
#endif
summary.mean_c = sum_c / totalDuration;
for (int k = 0; k < sz; ++k) {
double value = accumulator.results[k].values[bin];
// * toSec(accumulator.results[k].duration);
summary.variance_c +=
(value - summary.mean_c) * (value - summary.mean_c)
* toSec(accumulator.results[k].duration);
}
// summary.variance_c /= summary.count;
summary.variance_c /= totalDuration;
}
double mean = summary.sum / summary.count;
#ifdef DEBUG_PLUGIN_SUMMARISING_ADAPTER
cerr << "mean = " << summary.sum << " / " << summary.count << " = "
<< summary.sum / summary.count << endl;
#endif
for (int k = 0; k < sz; ++k) {
float value = accumulator.results[k].values[bin];
summary.variance += (value - mean) * (value - mean);
}
summary.variance /= summary.count;
m_summaries[output][segmentStart][bin] = summary;
}
}
}
m_segmentedAccumulators.clear();
m_accumulators.clear();
}
}
}

View File

@ -0,0 +1,152 @@
#pragma once
#include "vamp-hostsdk/PluginWrapper.h"
#include <set>
namespace Vamp {
namespace HostExt {
/**
* \class PluginSummarisingAdapter PluginSummarisingAdapter.h <vamp-hostsdk/PluginSummarisingAdapter.h>
*
* PluginSummarisingAdapter is a Vamp plugin adapter that provides
* summarisation methods such as mean and median averages of output
* features, for use in any context where an available plugin produces
* individual values but the result that is actually needed is some
* sort of aggregate.
*
* To make use of PluginSummarisingAdapter, the host should configure,
* initialise and run the plugin through the adapter interface just as
* normal. Then, after the process and getRemainingFeatures methods
* have been properly called and processing is complete, the host may
* call getSummaryForOutput or getSummaryForAllOutputs to obtain
* summarised features: averages, maximum values, etc, depending on
* the SummaryType passed to the function.
*
* By default PluginSummarisingAdapter calculates a single summary of
* each output's feature across the whole duration of processed audio.
* A host needing summaries of sub-segments of the whole audio may
* call setSummarySegmentBoundaries before retrieving the summaries,
* providing a list of times such that one summary will be provided
* for each segment between two consecutive times.
*
* PluginSummarisingAdapter is straightforward rather than fast. It
* calculates all of the summary types for all outputs always, and
* then returns only the ones that are requested. It is designed on
* the basis that, for most features, summarising and storing
* summarised results is far cheaper than calculating the results in
* the first place. If this is not true for your particular feature,
* PluginSummarisingAdapter may not be the best approach for you.
*
* \note This class was introduced in version 2.0 of the Vamp plugin SDK.
*/
class PluginSummarisingAdapter : public PluginWrapper
{
public:
/**
* Construct a PluginSummarisingAdapter wrapping the given plugin.
* The adapter takes ownership of the plugin, which will be
* deleted when the adapter is deleted.
*/
PluginSummarisingAdapter(Plugin *plugin);
virtual ~PluginSummarisingAdapter();
bool initialise(size_t channels, size_t stepSize, size_t blockSize);
void reset();
FeatureSet process(const float *const *inputBuffers, RealTime timestamp);
FeatureSet getRemainingFeatures();
typedef std::set<RealTime> SegmentBoundaries;
/**
* Specify a series of segment boundaries, such that one summary
* will be returned for each of the contiguous intra-boundary
* segments. This function must be called before
* getSummaryForOutput or getSummaryForAllOutputs.
*
* Note that you cannot retrieve results with multiple different
* segmentations by repeatedly calling this function followed by
* one of the getSummary functions. The summaries are all
* calculated at the first call to any getSummary function, and
* once the summaries have been calculated, they remain
* calculated.
*/
void setSummarySegmentBoundaries(const SegmentBoundaries &);
enum SummaryType {
Minimum = 0,
Maximum = 1,
Mean = 2,
Median = 3,
Mode = 4,
Sum = 5,
Variance = 6,
StandardDeviation = 7,
Count = 8,
UnknownSummaryType = 999
};
/**
* AveragingMethod indicates how the adapter should handle
* average-based summaries of features whose results are not
* equally spaced in time.
*
* If SampleAverage is specified, summary types based on averages
* will be calculated by treating each result individually without
* regard to its time: for example, the mean will be the sum of
* all values divided by the number of values.
*
* If ContinuousTimeAverage is specified, each feature will be
* considered to have a duration, either as specified in the
* feature's duration field, or until the following feature: thus,
* for example, the mean will be the sum of the products of values
* and durations, divided by the total duration.
*
* Although SampleAverage is useful for many types of feature,
* ContinuousTimeAverage is essential for some situations, for
* example finding the result that spans the largest proportion of
* the input given a feature that emits a new result only when the
* value changes (the modal value integrated over time).
*/
enum AveragingMethod {
SampleAverage = 0,
ContinuousTimeAverage = 1
};
/**
* Return summaries of the features that were returned on the
* given output, using the given SummaryType and AveragingMethod.
*
* The plugin must have been fully run (process() and
* getRemainingFeatures() calls all made as appropriate) before
* this function is called.
*/
FeatureList getSummaryForOutput(int output,
SummaryType type,
AveragingMethod method = SampleAverage);
/**
* Return summaries of the features that were returned on all of
* the plugin's outputs, using the given SummaryType and
* AveragingMethod.
*
* The plugin must have been fully run (process() and
* getRemainingFeatures() calls all made as appropriate) before
* this function is called.
*/
FeatureSet getSummaryForAllOutputs(SummaryType type,
AveragingMethod method = SampleAverage);
protected:
class Impl;
Impl *m_impl;
};
}
}

View File

@ -0,0 +1,157 @@
#include "vamp-hostsdk/PluginWrapper.h"
namespace Vamp {
namespace HostExt {
PluginWrapper::PluginWrapper(Plugin *plugin) :
Plugin(plugin->getInputSampleRate()),
m_plugin(plugin)
{
}
PluginWrapper::~PluginWrapper()
{
delete m_plugin;
}
bool
PluginWrapper::initialise(size_t channels, size_t stepSize, size_t blockSize)
{
return m_plugin->initialise(channels, stepSize, blockSize);
}
void
PluginWrapper::reset()
{
m_plugin->reset();
}
Plugin::InputDomain
PluginWrapper::getInputDomain() const
{
return m_plugin->getInputDomain();
}
unsigned int
PluginWrapper::getVampApiVersion() const
{
return m_plugin->getVampApiVersion();
}
std::string
PluginWrapper::getIdentifier() const
{
return m_plugin->getIdentifier();
}
std::string
PluginWrapper::getName() const
{
return m_plugin->getName();
}
std::string
PluginWrapper::getDescription() const
{
return m_plugin->getDescription();
}
std::string
PluginWrapper::getMaker() const
{
return m_plugin->getMaker();
}
int
PluginWrapper::getPluginVersion() const
{
return m_plugin->getPluginVersion();
}
std::string
PluginWrapper::getCopyright() const
{
return m_plugin->getCopyright();
}
PluginBase::ParameterList
PluginWrapper::getParameterDescriptors() const
{
return m_plugin->getParameterDescriptors();
}
float
PluginWrapper::getParameter(std::string parameter) const
{
return m_plugin->getParameter(parameter);
}
void
PluginWrapper::setParameter(std::string parameter, float value)
{
m_plugin->setParameter(parameter, value);
}
PluginBase::ProgramList
PluginWrapper::getPrograms() const
{
return m_plugin->getPrograms();
}
std::string
PluginWrapper::getCurrentProgram() const
{
return m_plugin->getCurrentProgram();
}
void
PluginWrapper::selectProgram(std::string program)
{
m_plugin->selectProgram(program);
}
size_t
PluginWrapper::getPreferredStepSize() const
{
return m_plugin->getPreferredStepSize();
}
size_t
PluginWrapper::getPreferredBlockSize() const
{
return m_plugin->getPreferredBlockSize();
}
size_t
PluginWrapper::getMinChannelCount() const
{
return m_plugin->getMinChannelCount();
}
size_t PluginWrapper::getMaxChannelCount() const
{
return m_plugin->getMaxChannelCount();
}
Plugin::OutputList
PluginWrapper::getOutputDescriptors() const
{
return m_plugin->getOutputDescriptors();
}
Plugin::FeatureSet
PluginWrapper::process(const float *const *inputBuffers, RealTime timestamp)
{
return m_plugin->process(inputBuffers, timestamp);
}
Plugin::FeatureSet
PluginWrapper::getRemainingFeatures()
{
return m_plugin->getRemainingFeatures();
}
}
}

View File

@ -0,0 +1,88 @@
#pragma once
#include "vamp-sdk/Plugin.h"
namespace Vamp::HostExt {
/**
* \class PluginWrapper PluginWrapper.h <vamp-hostsdk/PluginWrapper.h>
*
* PluginWrapper is a simple base class for adapter plugins. It takes
* a pointer to a "to be wrapped" Vamp plugin on construction, and
* provides implementations of all the Vamp plugin methods that simply
* delegate through to the wrapped plugin. A subclass can therefore
* override only the methods that are meaningful for the particular
* adapter.
*
* \note This class was introduced in version 1.1 of the Vamp plugin SDK.
*/
class PluginWrapper : public Plugin
{
public:
virtual ~PluginWrapper();
bool initialise(size_t channels, size_t stepSize, size_t blockSize);
void reset();
InputDomain getInputDomain() const;
unsigned int getVampApiVersion() const;
std::string getIdentifier() const;
std::string getName() const;
std::string getDescription() const;
std::string getMaker() const;
int getPluginVersion() const;
std::string getCopyright() const;
ParameterList getParameterDescriptors() const;
float getParameter(std::string) const;
void setParameter(std::string, float);
ProgramList getPrograms() const;
std::string getCurrentProgram() const;
void selectProgram(std::string);
size_t getPreferredStepSize() const;
size_t getPreferredBlockSize() const;
size_t getMinChannelCount() const;
size_t getMaxChannelCount() const;
OutputList getOutputDescriptors() const;
FeatureSet process(const float *const *inputBuffers, RealTime timestamp);
FeatureSet getRemainingFeatures();
/**
* Return a pointer to the plugin wrapper of type WrapperType
* surrounding this wrapper's plugin, if present.
*
* This is useful in situations where a plugin is wrapped by
* multiple different wrappers (one inside another) and the host
* wants to call some wrapper-specific function on one of the
* layers without having to care about the order in which they are
* wrapped. For example, the plugin returned by
* PluginLoader::loadPlugin may have more than one wrapper; if the
* host wanted to query or fine-tune some property of one of them,
* it would be hard to do so without knowing the order of the
* wrappers. This function therefore gives direct access to the
* wrapper of a particular type.
*/
template <typename WrapperType>
WrapperType *getWrapper() {
WrapperType *w = dynamic_cast<WrapperType *>(this);
if (w) return w;
PluginWrapper *pw = dynamic_cast<PluginWrapper *>(m_plugin);
if (pw) return pw->getWrapper<WrapperType>();
return 0;
}
protected:
PluginWrapper(Plugin *plugin); // I take ownership of plugin
Plugin *m_plugin;
};
}

128
src/vamp-hostsdk/Window.h Normal file
View File

@ -0,0 +1,128 @@
#pragma once
#include "vamp-hostsdk/hostguard.h"
#include <cmath>
#include <cstdlib>
_VAMP_SDK_HOSTSPACE_BEGIN(Window.h)
template <typename T>
class Window
{
public:
enum WindowType {
RectangularWindow,
BartlettWindow,
HammingWindow,
HanningWindow,
BlackmanWindow,
NuttallWindow,
BlackmanHarrisWindow
};
/**
* Construct a windower of the given type.
*/
Window(WindowType type, size_t size) : m_type(type), m_size(size) { encache(); }
Window(const Window &w) : m_type(w.m_type), m_size(w.m_size) { encache(); }
Window &operator=(const Window &w) {
if (&w == this) return *this;
m_type = w.m_type;
m_size = w.m_size;
encache();
return *this;
}
virtual ~Window() { delete[] m_cache; }
void cut(T *src) const { cut(src, src); }
void cut(T *src, T *dst) const {
for (size_t i = 0; i < m_size; ++i) dst[i] = src[i] * m_cache[i];
}
template <typename T0, typename T1>
void cut(T0 *src, T1 *dst) const {
for (size_t i = 0; i < m_size; ++i) dst[i] = src[i] * m_cache[i];
}
T getArea() { return m_area; }
T getValue(size_t i) { return m_cache[i]; }
WindowType getType() const { return m_type; }
size_t getSize() const { return m_size; }
protected:
WindowType m_type;
size_t m_size;
T *m_cache;
T m_area;
void encache();
void cosinewin(T *, T, T, T, T);
};
template <typename T>
void Window<T>::encache()
{
int n = int(m_size);
T *mult = new T[n];
int i;
for (i = 0; i < n; ++i) mult[i] = 1.0;
switch (m_type) {
case RectangularWindow:
for (i = 0; i < n; ++i) {
mult[i] *= 0.5;
}
break;
case BartlettWindow:
for (i = 0; i < n/2; ++i) {
mult[i] *= (i / T(n/2));
mult[i + n/2] *= (1.0 - (i / T(n/2)));
}
break;
case HammingWindow:
cosinewin(mult, 0.54, 0.46, 0.0, 0.0);
break;
case HanningWindow:
cosinewin(mult, 0.50, 0.50, 0.0, 0.0);
break;
case BlackmanWindow:
cosinewin(mult, 0.42, 0.50, 0.08, 0.0);
break;
case NuttallWindow:
cosinewin(mult, 0.3635819, 0.4891775, 0.1365995, 0.0106411);
break;
case BlackmanHarrisWindow:
cosinewin(mult, 0.35875, 0.48829, 0.14128, 0.01168);
break;
}
m_cache = mult;
m_area = 0;
for (int i = 0; i < n; ++i) {
m_area += m_cache[i];
}
m_area /= n;
}
template <typename T>
void Window<T>::cosinewin(T *mult, T a0, T a1, T a2, T a3)
{
int n = int(m_size);
for (int i = 0; i < n; ++i) {
mult[i] *= (a0
- a1 * cos((2 * M_PI * i) / n)
+ a2 * cos((4 * M_PI * i) / n)
- a3 * cos((6 * M_PI * i) / n));
}
}
_VAMP_SDK_HOSTSPACE_END(Window.h)

View File

@ -0,0 +1,16 @@
/* These stubs are provided so that autoconf can check library
* versions using C symbols only */
extern void libvamphostsdk_v_2_9_present(void) { }
extern void libvamphostsdk_v_2_8_present(void) { }
extern void libvamphostsdk_v_2_7_1_present(void) { }
extern void libvamphostsdk_v_2_7_present(void) { }
extern void libvamphostsdk_v_2_6_present(void) { }
extern void libvamphostsdk_v_2_5_present(void) { }
extern void libvamphostsdk_v_2_4_present(void) { }
extern void libvamphostsdk_v_2_3_1_present(void) { }
extern void libvamphostsdk_v_2_3_present(void) { }
extern void libvamphostsdk_v_2_2_1_present(void) { }
extern void libvamphostsdk_v_2_2_present(void) { }
extern void libvamphostsdk_v_2_1_present(void) { }
extern void libvamphostsdk_v_2_0_present(void) { }

114
src/vamp-hostsdk/host-c.cpp Normal file
View File

@ -0,0 +1,114 @@
#include "vamp-hostsdk/host-c.h"
#include "vamp-hostsdk/Files.h"
#include <map>
#include <iostream>
#include <cstring>
using namespace std;
static vector<string> files;
static map<string, const char *> cnames;
static bool haveFiles = false;
struct vhLibrary_t {
vhLibrary_t(void *h, VampGetPluginDescriptorFunction f)
: handle(h), func(f), nplugins(0) { }
void *handle;
VampGetPluginDescriptorFunction func;
int nplugins;
};
static void initFilenames()
{
if (!haveFiles) {
files = Files::listLibraryFiles();
for (size_t i = 0; i < files.size(); ++i) {
cnames[files[i]] = strdup(Files::lcBasename(files[i]).c_str());
}
haveFiles = true;
}
}
int vhGetLibraryCount()
{
initFilenames();
return int(files.size());
}
const char *vhGetLibraryName(int index)
{
initFilenames();
if (index >= 0 && index < int(files.size())) {
return cnames[files[index]];
}
else return 0;
}
int vhGetLibraryIndex(const char *name)
{
for (size_t i = 0; i < files.size(); ++i) {
if (Files::lcBasename(name) == Files::lcBasename(files[i])) {
return i;
}
}
return -1;
}
vhLibrary vhLoadLibrary(int index)
{
initFilenames();
if (index < 0 || index >= int(files.size())) {
return 0;
}
string fullPath = files[index];
void *lib = Files::loadLibrary(fullPath);
if (!lib) return 0;
VampGetPluginDescriptorFunction func =
(VampGetPluginDescriptorFunction)Files::lookupInLibrary
(lib, "vampGetPluginDescriptor");
if (!func) {
cerr << "vhLoadLibrary: No vampGetPluginDescriptor function found in library \""
<< fullPath << "\"" << endl;
Files::unloadLibrary(lib);
return 0;
}
vhLibrary_t *vhl = new vhLibrary_t(lib, func);
while (vhl->func(VAMP_API_VERSION, vhl->nplugins)) {
++vhl->nplugins;
}
return vhl;
}
int vhGetPluginCount(vhLibrary library)
{
vhLibrary_t *vhl = static_cast<vhLibrary_t *>(library);
if (vhl) return vhl->nplugins;
else return 0;
}
const VampPluginDescriptor *vhGetPluginDescriptor(vhLibrary library,
int plugin)
{
vhLibrary_t *vhl = static_cast<vhLibrary_t *>(library);
if (vhl && plugin >= 0 && plugin < vhl->nplugins) {
return vhl->func(VAMP_API_VERSION, plugin);
} else {
return 0;
}
}
void vhUnloadLibrary(vhLibrary library)
{
vhLibrary_t *vhl = static_cast<vhLibrary_t *>(library);
if (vhl && vhl->handle) {
Files::unloadLibrary(vhl->handle);
}
delete vhl;
}

60
src/vamp-hostsdk/host-c.h Normal file
View File

@ -0,0 +1,60 @@
#pragma once
#include "vamp.h"
#ifdef __cplusplus
extern "C" {
#endif
typedef struct vhLibrary_t *vhLibrary;
/**
* Return the number of Vamp plugin libraries discovered in the
* installation path. This number will remain fixed after the first
* call -- plugins are only discovered once, the first time this
* function is called.
*/
extern int vhGetLibraryCount();
/**
* Return the library name (base soname) of the library with the given
* index, in the range 0..(vhGetLibraryCount()-1).
*/
extern const char *vhGetLibraryName(int library);
/**
* Return the library index for the given library name, or -1 if the
* name is not known.
*/
extern int vhGetLibraryIndex(const char *name);
/**
* Load the library with the given index. If the library cannot be
* loaded for any reason, the return value is 0; otherwise it is an
* opaque pointer suitable for passing to other functions in this API.
*/
extern vhLibrary vhLoadLibrary(int library);
/**
* Return the number of Vamp plugins in the given library.
*/
extern int vhGetPluginCount(vhLibrary library);
/**
* Return a Vamp plugin descriptor for a plugin in a given
* library. This simply calls the vampGetPluginDescriptor function in
* that library with the given plugin index and returns the
* result. See vamp/vamp.h for details about the plugin descriptor.
*/
extern const VampPluginDescriptor *vhGetPluginDescriptor(vhLibrary library,
int plugin);
/**
* Unload a plugin library. Do not do this while any of its plugins
* are still in use.
*/
extern void vhUnloadLibrary(vhLibrary);
#ifdef __cplusplus
}
#endif

View File

@ -0,0 +1,53 @@
/* -*- c-basic-offset: 4 indent-tabs-mode: nil -*- vi:set ts=8 sts=4 sw=4: */
/*
Vamp
An API for audio analysis and feature extraction plugins.
Centre for Digital Music, Queen Mary, University of London.
Copyright 2006 Chris Cannam.
Permission is hereby granted, free of charge, to any person
obtaining a copy of this software and associated documentation
files (the "Software"), to deal in the Software without
restriction, including without limitation the rights to use, copy,
modify, merge, publish, distribute, sublicense, and/or sell copies
of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be
included in all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR
ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF
CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
Except as contained in this notice, the names of the Centre for
Digital Music; Queen Mary, University of London; and Chris Cannam
shall not be used in advertising or otherwise to promote the sale,
use or other dealings in this Software without prior written
authorization.
*/
#ifndef _VAMP_HOSTSDK_SINGLE_INCLUDE_H_
#define _VAMP_HOSTSDK_SINGLE_INCLUDE_H_
#include "PluginBase.h"
#include "PluginBufferingAdapter.h"
#include "PluginChannelAdapter.h"
#include "Plugin.h"
#include "PluginHostAdapter.h"
#include "PluginInputDomainAdapter.h"
#include "PluginLoader.h"
#include "PluginSummarisingAdapter.h"
#include "PluginWrapper.h"
#include "RealTime.h"
#endif

213
src/vamp-sdk/FFT.cpp Normal file
View File

@ -0,0 +1,213 @@
#include "vamp-sdk/FFT.h"
#include <stdlib.h>
#include <math.h>
#include "vamp-sdk/ext/vamp_kiss_fft.h"
#include "vamp-sdk/ext/vamp_kiss_fftr.h"
namespace Vamp {
void FFT::forward(unsigned int un, const double* ri, const double* ii, double* ro, double* io)
{
int n(un);
vamp_kiss_fft_cfg c = vamp_kiss_fft_alloc(n, false, 0, 0);
vamp_kiss_fft_cpx *in = new vamp_kiss_fft_cpx[n];
vamp_kiss_fft_cpx *out = new vamp_kiss_fft_cpx[n];
for (int i = 0; i < n; ++i) {
in[i].r = ri[i];
in[i].i = 0;
}
if (ii) {
for (int i = 0; i < n; ++i) {
in[i].i = ii[i];
}
}
vamp_kiss_fft(c, in, out);
for (int i = 0; i < n; ++i) {
ro[i] = out[i].r;
io[i] = out[i].i;
}
vamp_kiss_fft_free(c);
delete[] in;
delete[] out;
}
void
FFT::inverse(unsigned int un,
const double *ri, const double *ii,
double *ro, double *io)
{
int n(un);
vamp_kiss_fft_cfg c = vamp_kiss_fft_alloc(n, true, 0, 0);
vamp_kiss_fft_cpx *in = new vamp_kiss_fft_cpx[n];
vamp_kiss_fft_cpx *out = new vamp_kiss_fft_cpx[n];
for (int i = 0; i < n; ++i) {
in[i].r = ri[i];
in[i].i = 0;
}
if (ii) {
for (int i = 0; i < n; ++i) {
in[i].i = ii[i];
}
}
vamp_kiss_fft(c, in, out);
double scale = 1.0 / double(n);
for (int i = 0; i < n; ++i) {
ro[i] = out[i].r * scale;
io[i] = out[i].i * scale;
}
vamp_kiss_fft_free(c);
delete[] in;
delete[] out;
}
class FFTComplex::D
{
public:
D(int n) :
m_n(n),
m_fconf(vamp_kiss_fft_alloc(n, false, 0, 0)),
m_iconf(vamp_kiss_fft_alloc(n, true, 0, 0)),
m_ci(new vamp_kiss_fft_cpx[m_n]),
m_co(new vamp_kiss_fft_cpx[m_n]) { }
~D() {
vamp_kiss_fftr_free(m_fconf);
vamp_kiss_fftr_free(m_iconf);
delete[] m_ci;
delete[] m_co;
}
void forward(const double *ci, double *co) {
for (int i = 0; i < m_n; ++i) {
m_ci[i].r = ci[i*2];
m_ci[i].i = ci[i*2+1];
}
vamp_kiss_fft(m_fconf, m_ci, m_co);
for (int i = 0; i < m_n; ++i) {
co[i*2] = m_co[i].r;
co[i*2+1] = m_co[i].i;
}
}
void inverse(const double *ci, double *co) {
for (int i = 0; i < m_n; ++i) {
m_ci[i].r = ci[i*2];
m_ci[i].i = ci[i*2+1];
}
vamp_kiss_fft(m_iconf, m_ci, m_co);
double scale = 1.0 / double(m_n);
for (int i = 0; i < m_n; ++i) {
co[i*2] = m_co[i].r * scale;
co[i*2+1] = m_co[i].i * scale;
}
}
private:
int m_n;
vamp_kiss_fft_cfg m_fconf;
vamp_kiss_fft_cfg m_iconf;
vamp_kiss_fft_cpx *m_ci;
vamp_kiss_fft_cpx *m_co;
};
FFTComplex::FFTComplex(unsigned int n) :
m_d(new D(n))
{
}
FFTComplex::~FFTComplex()
{
delete m_d;
}
void
FFTComplex::forward(const double *ci, double *co)
{
m_d->forward(ci, co);
}
void
FFTComplex::inverse(const double *ci, double *co)
{
m_d->inverse(ci, co);
}
class FFTReal::D
{
public:
D(int n) :
m_n(n),
m_fconf(vamp_kiss_fftr_alloc(n, false, 0, 0)),
m_iconf(vamp_kiss_fftr_alloc(n, true, 0, 0)),
m_ri(new vamp_kiss_fft_scalar[m_n]),
m_ro(new vamp_kiss_fft_scalar[m_n]),
m_freq(new vamp_kiss_fft_cpx[n/2+1]) { }
~D() {
vamp_kiss_fftr_free(m_fconf);
vamp_kiss_fftr_free(m_iconf);
delete[] m_ri;
delete[] m_ro;
delete[] m_freq;
}
void forward(const double *ri, double *co) {
for (int i = 0; i < m_n; ++i) {
// in case vamp_kiss_fft_scalar is float
m_ri[i] = ri[i];
}
vamp_kiss_fftr(m_fconf, m_ri, m_freq);
int hs = m_n/2 + 1;
for (int i = 0; i < hs; ++i) {
co[i*2] = m_freq[i].r;
co[i*2+1] = m_freq[i].i;
}
}
void inverse(const double *ci, double *ro) {
int hs = m_n/2 + 1;
for (int i = 0; i < hs; ++i) {
m_freq[i].r = ci[i*2];
m_freq[i].i = ci[i*2+1];
}
vamp_kiss_fftri(m_iconf, m_freq, m_ro);
double scale = 1.0 / double(m_n);
for (int i = 0; i < m_n; ++i) {
ro[i] = m_ro[i] * scale;
}
}
private:
int m_n;
vamp_kiss_fftr_cfg m_fconf;
vamp_kiss_fftr_cfg m_iconf;
vamp_kiss_fft_scalar *m_ri;
vamp_kiss_fft_scalar *m_ro;
vamp_kiss_fft_cpx *m_freq;
};
FFTReal::FFTReal(unsigned int n) :
m_d(new D(n))
{
}
FFTReal::~FFTReal()
{
delete m_d;
}
void
FFTReal::forward(const double *ri, double *co)
{
m_d->forward(ri, co);
}
void
FFTReal::inverse(const double *ci, double *ro)
{
m_d->inverse(ci, ro);
}
}

159
src/vamp-sdk/FFT.h Normal file
View File

@ -0,0 +1,159 @@
#pragma once
namespace Vamp {
/**
* A simple FFT implementation provided for convenience of plugin
* authors. This class provides one-shot (i.e. fixed table state is
* recalculated every time) double-precision complex-complex
* transforms. For repeated transforms from real time-domain data, use
* an FFTComplex or FFTReal object instead.
*
* Note: If the SDK has been compiled with the SINGLE_PRECISION_FFT
* flag, then all FFTs will use single precision internally. The
* default is double precision. The API uses doubles in either case.
*
* The forward transform is unscaled; the inverse transform is scaled
* by 1/n.
*/
class FFT
{
public:
/**
* Calculate a one-shot forward transform of size n.
* n must be a multiple of 2.
*
* ri and ii must point to the real and imaginary component arrays
* of the input. For real input, ii may be NULL.
*
* ro and io must point to enough space to receive the real and
* imaginary component arrays of the output.
*
* All input and output arrays are of size n.
*/
static void forward(unsigned int n,
const double *ri, const double *ii,
double *ro, double *io);
/**
* Calculate a one-shot inverse transform of size n.
* n must be a power of 2, greater than 1.
*
* ri and ii must point to the real and imaginary component arrays
* of the input. For real input, ii may be NULL.
*
* ro and io must point to enough space to receive the real and
* imaginary component arrays of the output. The output is scaled
* by 1/n. The output pointers may not be NULL, even if the output
* is expected to be real.
*
* All input and output arrays are of size n.
*/
static void inverse(unsigned int n,
const double *ri, const double *ii,
double *ro, double *io);
};
/**
* A simple FFT implementation provided for convenience of plugin
* authors. This class provides double-precision complex-complex
* transforms.
*
* Note: If the SDK has been compiled with the SINGLE_PRECISION_FFT
* flag, then all FFTs will use single precision internally. The
* default is double precision. The API uses doubles in either case.
*
* The forward transform is unscaled; the inverse transform is scaled
* by 1/n.
*/
class FFTComplex
{
public:
/**
* Prepare to calculate transforms of size n.
* n must be a multiple of 2.
*/
FFTComplex(unsigned int n);
~FFTComplex();
/**
* Calculate a forward transform of size n.
*
* ci must point to the interleaved complex input data of size n
* (that is, 2n doubles in total).
*
* co must point to enough space to receive an interleaved complex
* output array of size n (that is, 2n doubles in total).
*/
void forward(const double *ci, double *co);
/**
* Calculate an inverse transform of size n.
*
* ci must point to an interleaved complex input array of size n
* (that is, 2n doubles in total).
*
* co must point to enough space to receive the interleaved
* complex output data of size n (that is, 2n doubles in
* total). The output is scaled by 1/n.
*/
void inverse(const double *ci, double *co);
private:
class D;
D *m_d;
};
/**
* A simple FFT implementation provided for convenience of plugin
* authors. This class provides transforms between double-precision
* real time-domain and double-precision complex frequency-domain
* data.
*
* Note: If the SDK has been compiled with the SINGLE_PRECISION_FFT
* flag, then all FFTs will use single precision internally. The
* default is double precision. The API uses doubles in either case.
*
* The forward transform is unscaled; the inverse transform is scaled
* by 1/n.
*/
class FFTReal
{
public:
/**
* Prepare to calculate transforms of size n.
* n must be a multiple of 2.
*/
FFTReal(unsigned int n);
~FFTReal();
/**
* Calculate a forward transform of size n.
*
* ri must point to the real input data of size n.
*
* co must point to enough space to receive an interleaved complex
* output array of size n/2+1 (that is, n+2 doubles in total).
*/
void forward(const double *ri, double *co);
/**
* Calculate an inverse transform of size n.
*
* ci must point to an interleaved complex input array of size
* n/2+1 (that is, n+2 doubles in total).
*
* ro must point to enough space to receive the real output data
* of size n. The output is scaled by 1/n and only the real part
* is returned.
*/
void inverse(const double *ci, double *ro);
private:
class D;
D *m_d;
};
}

412
src/vamp-sdk/Plugin.h Normal file
View File

@ -0,0 +1,412 @@
#pragma once
#include <string>
#include <vector>
#include <map>
#include "PluginBase.h"
#include "RealTime.h"
namespace Vamp {
/**
* \class Plugin Plugin.h <vamp-sdk/Plugin.h>
*
* Vamp::Plugin is a base class for plugin instance classes
* that provide feature extraction from audio or related data.
*
* In most cases, the input will be audio and the output will be a
* stream of derived data at a lower sampling resolution than the
* input.
*
* Note that this class inherits several abstract methods from
* PluginBase. These must be implemented by the subclass.
*
*
* PLUGIN LIFECYCLE
*
* Feature extraction plugins are managed differently from real-time
* plugins (such as VST effects). The main difference is that the
* parameters for a feature extraction plugin are configured before
* the plugin is used, and do not change during use.
*
* 1. Host constructs the plugin, passing it the input sample rate.
* The plugin may do basic initialisation, but should not do anything
* computationally expensive at this point. You must make sure your
* plugin is cheap to construct, otherwise you'll seriously affect the
* startup performance of almost all hosts. If you have serious
* initialisation to do, the proper place is in initialise() (step 5).
*
* 2. Host may query the plugin's available outputs.
*
* 3. Host queries programs and parameter descriptors, and may set
* some or all of them. Parameters that are not explicitly set should
* take their default values as specified in the parameter descriptor.
* When a program is set, the parameter values may change and the host
* will re-query them to check.
*
* 4. Host queries the preferred step size, block size and number of
* channels. These may all vary depending on the parameter values.
* (Note however that you cannot make the number of distinct outputs
* dependent on parameter values.)
*
* 5. Plugin is properly initialised with a call to initialise. This
* fixes the step size, block size, and number of channels, as well as
* all of the parameter and program settings. If the values passed in
* to initialise do not match the plugin's advertised preferred values
* from step 4, the plugin may refuse to initialise and return false
* (although if possible it should accept the new values). Any
* computationally expensive setup code should take place here.
*
* 6. Host finally checks the number of values, resolution, extents
* etc per output (which may vary depending on the number of channels,
* step size and block size as well as the parameter values).
*
* 7. Host will repeatedly call the process method to pass in blocks
* of input data. This method may return features extracted from that
* data (if the plugin is causal).
*
* 8. Host will call getRemainingFeatures exactly once, after all the
* input data has been processed. This may return any non-causal or
* leftover features.
*
* 9. At any point after initialise was called, the host may
* optionally call the reset method and restart processing. (This
* does not mean it can change the parameters, which are fixed from
* initialise until destruction.)
*
* A plugin does not need to handle the case where setParameter or
* selectProgram is called after initialise has been called. It's the
* host's responsibility not to do that. Similarly, the plugin may
* safely assume that initialise is called no more than once.
*/
class Plugin : public PluginBase
{
public:
virtual ~Plugin() { }
/**
* Initialise a plugin to prepare it for use with the given number
* of input channels, step size (window increment, in sample
* frames) and block size (window size, in sample frames).
*
* The input sample rate should have been already specified at
* construction time.
*
* Return true for successful initialisation, false if the number
* of input channels, step size and/or block size cannot be
* supported.
*/
virtual bool initialise(size_t inputChannels,
size_t stepSize,
size_t blockSize) = 0;
/**
* Reset the plugin after use, to prepare it for another clean
* run. Not called for the first initialisation (i.e. initialise
* must also do a reset).
*/
virtual void reset() = 0;
enum InputDomain { TimeDomain, FrequencyDomain };
/**
* Get the plugin's required input domain.
*
* If this is TimeDomain, the samples provided to the process()
* function (below) will be in the time domain, as for a
* traditional audio processing plugin.
*
* If this is FrequencyDomain, the host will carry out a windowed
* FFT of size equal to the negotiated block size on the data
* before passing the frequency bin data in to process(). The
* input data for the FFT will be rotated so as to place the
* origin in the centre of the block.
* The plugin does not get to choose the window type -- the host
* will either let the user do so, or will use a Hanning window.
*/
virtual InputDomain getInputDomain() const = 0;
/**
* Get the preferred block size (window size -- the number of
* sample frames passed in each block to the process() function).
* This should be called before initialise().
*
* A plugin that can handle any block size may return 0. The
* final block size will be set in the initialise() call.
*/
virtual size_t getPreferredBlockSize() const { return 0; }
/**
* Get the preferred step size (window increment -- the distance
* in sample frames between the start frames of consecutive blocks
* passed to the process() function) for the plugin. This should
* be called before initialise().
*
* A plugin may return 0 if it has no particular interest in the
* step size. In this case, the host should make the step size
* equal to the block size if the plugin is accepting input in the
* time domain. If the plugin is accepting input in the frequency
* domain, the host may use any step size. The final step size
* will be set in the initialise() call.
*/
virtual size_t getPreferredStepSize() const { return 0; }
/**
* Get the minimum supported number of input channels.
*/
virtual size_t getMinChannelCount() const { return 1; }
/**
* Get the maximum supported number of input channels.
*/
virtual size_t getMaxChannelCount() const { return 1; }
struct OutputDescriptor
{
/**
* The name of the output, in computer-usable form. Should be
* reasonably short and without whitespace or punctuation, using
* the characters [a-zA-Z0-9_-] only.
* Example: "zero_crossing_count"
*/
std::string identifier;
/**
* The human-readable name of the output.
* Example: "Zero Crossing Counts"
*/
std::string name;
/**
* A human-readable short text describing the output. May be
* empty if the name has said it all already.
* Example: "The number of zero crossing points per processing block"
*/
std::string description;
/**
* The unit of the output, in human-readable form.
*/
std::string unit;
/**
* True if the output has the same number of values per sample
* for every output sample. Outputs for which this is false
* are unlikely to be very useful in a general-purpose host.
*/
bool hasFixedBinCount;
/**
* The number of values per result of the output. Undefined
* if hasFixedBinCount is false. If this is zero, the output
* is point data (i.e. only the time of each output is of
* interest, the value list will be empty).
*/
size_t binCount;
/**
* The (human-readable) names of each of the bins, if
* appropriate. This is always optional.
*/
std::vector<std::string> binNames;
/**
* True if the results in each output bin fall within a fixed
* numeric range (minimum and maximum values). Undefined if
* binCount is zero.
*/
bool hasKnownExtents;
/**
* Minimum value of the results in the output. Undefined if
* hasKnownExtents is false or binCount is zero.
*/
float minValue;
/**
* Maximum value of the results in the output. Undefined if
* hasKnownExtents is false or binCount is zero.
*/
float maxValue;
/**
* True if the output values are quantized to a particular
* resolution. Undefined if binCount is zero.
*/
bool isQuantized;
/**
* Quantization resolution of the output values (e.g. 1.0 if
* they are all integers). Undefined if isQuantized is false
* or binCount is zero.
*/
float quantizeStep;
enum SampleType {
/// Results from each process() align with that call's block start
OneSamplePerStep,
/// Results are evenly spaced in time (sampleRate specified below)
FixedSampleRate,
/// Results are unevenly spaced and have individual timestamps
VariableSampleRate
};
/**
* Positioning in time of the output results.
*/
SampleType sampleType;
/**
* Sample rate of the output results, as samples per second.
* Undefined if sampleType is OneSamplePerStep.
*
* If sampleType is VariableSampleRate and this value is
* non-zero, then it may be used to calculate a resolution for
* the output (i.e. the "duration" of each sample, in time,
* will be 1/sampleRate seconds). It's recommended to set
* this to zero if that behaviour is not desired.
*/
float sampleRate;
/**
* True if the returned results for this output are known to
* have a duration field.
*/
bool hasDuration;
OutputDescriptor() : // defaults for mandatory non-class-type members
hasFixedBinCount(false),
binCount(0),
hasKnownExtents(false),
minValue(0),
maxValue(0),
isQuantized(false),
quantizeStep(0),
sampleType(OneSamplePerStep),
sampleRate(0),
hasDuration(false) { }
};
typedef std::vector<OutputDescriptor> OutputList;
/**
* Get the outputs of this plugin. An output's index in this list
* is used as its numeric index when looking it up in the
* FeatureSet returned from the process() call.
*/
virtual OutputList getOutputDescriptors() const = 0;
struct Feature
{
/**
* True if an output feature has its own timestamp. This is
* mandatory if the output has VariableSampleRate, optional if
* the output has FixedSampleRate, and unused if the output
* has OneSamplePerStep.
*/
bool hasTimestamp;
/**
* Timestamp of the output feature. This is mandatory if the
* output has VariableSampleRate or if the output has
* FixedSampleRate and hasTimestamp is true, and unused
* otherwise.
*/
RealTime timestamp;
/**
* True if an output feature has a specified duration. This
* is optional if the output has VariableSampleRate or
* FixedSampleRate, and and unused if the output has
* OneSamplePerStep.
*/
bool hasDuration;
/**
* Duration of the output feature. This is mandatory if the
* output has VariableSampleRate or FixedSampleRate and
* hasDuration is true, and unused otherwise.
*/
RealTime duration;
/**
* Results for a single sample of this feature. If the output
* hasFixedBinCount, there must be the same number of values
* as the output's binCount count.
*/
std::vector<float> values;
/**
* Label for the sample of this feature.
*/
std::string label;
Feature() : // defaults for mandatory non-class-type members
hasTimestamp(false), hasDuration(false) { }
};
typedef std::vector<Feature> FeatureList;
typedef std::map<int, FeatureList> FeatureSet; // key is output no
/**
* Process a single block of input data.
*
* If the plugin's inputDomain is TimeDomain, inputBuffers will
* point to one array of floats per input channel, and each of
* these arrays will contain blockSize consecutive audio samples
* (the host will zero-pad as necessary). The timestamp in this
* case will be the real time in seconds of the start of the
* supplied block of samples.
*
* If the plugin's inputDomain is FrequencyDomain, inputBuffers
* will point to one array of floats per input channel, and each
* of these arrays will contain blockSize/2+1 consecutive pairs of
* real and imaginary component floats corresponding to bins
* 0..(blockSize/2) of the FFT output. That is, bin 0 (the first
* pair of floats) contains the DC output, up to bin blockSize/2
* which contains the Nyquist-frequency output. There will
* therefore be blockSize+2 floats per channel in total. The
* timestamp will be the real time in seconds of the centre of the
* FFT input window (i.e. the very first block passed to process
* might contain the FFT of half a block of zero samples and the
* first half-block of the actual data, with a timestamp of zero).
*
* Return any features that have become available after this
* process call. (These do not necessarily have to fall within
* the process block, except for OneSamplePerStep outputs.)
*/
virtual FeatureSet process(const float *const *inputBuffers,
RealTime timestamp) = 0;
/**
* After all blocks have been processed, calculate and return any
* remaining features derived from the complete input.
*/
virtual FeatureSet getRemainingFeatures() = 0;
/**
* Used to distinguish between Vamp::Plugin and other potential
* sibling subclasses of PluginBase. Do not reimplement this
* function in your subclass.
*/
virtual std::string getType() const { return "Feature Extraction Plugin"; }
/**
* Retrieve the input sample rate set on construction.
*/
float getInputSampleRate() const { return m_inputSampleRate; }
protected:
Plugin(float inputSampleRate) :
m_inputSampleRate(inputSampleRate) { }
float m_inputSampleRate;
};
}

View File

@ -0,0 +1,964 @@
#include "vamp-sdk/PluginAdapter.h"
#include <cstring>
#include <cstdlib>
#include <mutex>
using std::map;
using std::vector;
using std::string;
using std::cerr;
using std::endl;
using std::mutex;
using std::lock_guard;
namespace Vamp {
class PluginAdapterBase::Impl
{
public:
Impl(PluginAdapterBase *);
~Impl();
const VampPluginDescriptor *getDescriptor();
protected:
PluginAdapterBase *m_base;
static VampPluginHandle vampInstantiate(const VampPluginDescriptor *desc,
float inputSampleRate);
static void vampCleanup(VampPluginHandle handle);
static int vampInitialise(VampPluginHandle handle, unsigned int channels,
unsigned int stepSize, unsigned int blockSize);
static void vampReset(VampPluginHandle handle);
static float vampGetParameter(VampPluginHandle handle, int param);
static void vampSetParameter(VampPluginHandle handle, int param, float value);
static unsigned int vampGetCurrentProgram(VampPluginHandle handle);
static void vampSelectProgram(VampPluginHandle handle, unsigned int program);
static unsigned int vampGetPreferredStepSize(VampPluginHandle handle);
static unsigned int vampGetPreferredBlockSize(VampPluginHandle handle);
static unsigned int vampGetMinChannelCount(VampPluginHandle handle);
static unsigned int vampGetMaxChannelCount(VampPluginHandle handle);
static unsigned int vampGetOutputCount(VampPluginHandle handle);
static VampOutputDescriptor *vampGetOutputDescriptor(VampPluginHandle handle,
unsigned int i);
static void vampReleaseOutputDescriptor(VampOutputDescriptor *desc);
static VampFeatureList *vampProcess(VampPluginHandle handle,
const float *const *inputBuffers,
int sec,
int nsec);
static VampFeatureList *vampGetRemainingFeatures(VampPluginHandle handle);
static void vampReleaseFeatureSet(VampFeatureList *fs);
void checkOutputMap(Plugin *plugin);
void markOutputsChanged(Plugin *plugin);
void cleanup(Plugin *plugin);
unsigned int getOutputCount(Plugin *plugin);
VampOutputDescriptor *getOutputDescriptor(Plugin *plugin, unsigned int i);
VampFeatureList *process(Plugin *plugin,
const float *const *inputBuffers,
int sec, int nsec);
VampFeatureList *getRemainingFeatures(Plugin *plugin);
VampFeatureList *convertFeatures(Plugin *plugin,
const Plugin::FeatureSet &features);
// maps both plugins and descriptors to adapters
typedef map<const void *, Impl *> AdapterMap;
static AdapterMap *m_adapterMap;
static mutex &adapterMapMutex() {
// If this mutex was a global static, then it might be
// destroyed before the last adapter, and we would end up
// trying to lock an invalid mutex when removing an adapter
// from the adapter map. To ensure it outlasts the adapters,
// we need to ensure it is constructed before the construction
// of any of them is complete, since destruction order is
// reverse of construction. So we have to make sure this is
// called from the PluginAdapterBase::Impl constructor below.
static mutex m;
return m;
}
static Impl *lookupAdapter(VampPluginHandle);
mutex m_mutex; // guards all of the below
bool m_populated;
VampPluginDescriptor m_descriptor;
Plugin::ParameterList m_parameters;
Plugin::ProgramList m_programs;
typedef map<Plugin *, Plugin::OutputList *> OutputMap;
OutputMap m_pluginOutputs;
map<Plugin *, VampFeatureList *> m_fs;
map<Plugin *, vector<size_t> > m_fsizes;
map<Plugin *, vector<vector<size_t> > > m_fvsizes;
void resizeFS(Plugin *plugin, int n);
void resizeFL(Plugin *plugin, int n, size_t sz);
void resizeFV(Plugin *plugin, int n, int j, size_t sz);
};
PluginAdapterBase::PluginAdapterBase()
{
m_impl = new Impl(this);
}
PluginAdapterBase::~PluginAdapterBase()
{
delete m_impl;
}
const VampPluginDescriptor *
PluginAdapterBase::getDescriptor()
{
return m_impl->getDescriptor();
}
PluginAdapterBase::Impl::Impl(PluginAdapterBase *base) :
m_base(base),
m_populated(false)
{
#ifdef DEBUG_PLUGIN_ADAPTER
cerr << "PluginAdapterBase::Impl[" << this << "]::Impl" << endl;
#endif
(void)adapterMapMutex(); // see comment in adapterMapMutex function above
}
const VampPluginDescriptor *
PluginAdapterBase::Impl::getDescriptor()
{
#ifdef DEBUG_PLUGIN_ADAPTER
cerr << "PluginAdapterBase::Impl[" << this << "]::getDescriptor" << endl;
#endif
lock_guard<mutex> guard(m_mutex);
if (m_populated) return &m_descriptor;
Plugin *plugin = m_base->createPlugin(48000);
if (!plugin) {
cerr << "PluginAdapterBase::Impl::getDescriptor: Failed to create plugin" << endl;
return 0;
}
if (plugin->getVampApiVersion() != VAMP_API_VERSION) {
cerr << "Vamp::PluginAdapterBase::Impl::getDescriptor: ERROR: "
<< "API version " << plugin->getVampApiVersion()
<< " for\nplugin \"" << plugin->getIdentifier() << "\" "
<< "differs from version "
<< VAMP_API_VERSION << " for adapter.\n"
<< "This plugin is probably linked against a different version of the Vamp SDK\n"
<< "from the version it was compiled with. It will need to be re-linked correctly\n"
<< "before it can be used." << endl;
delete plugin;
return 0;
}
m_parameters = plugin->getParameterDescriptors();
m_programs = plugin->getPrograms();
m_descriptor.vampApiVersion = plugin->getVampApiVersion();
m_descriptor.identifier = strdup(plugin->getIdentifier().c_str());
m_descriptor.name = strdup(plugin->getName().c_str());
m_descriptor.description = strdup(plugin->getDescription().c_str());
m_descriptor.maker = strdup(plugin->getMaker().c_str());
m_descriptor.pluginVersion = plugin->getPluginVersion();
m_descriptor.copyright = strdup(plugin->getCopyright().c_str());
m_descriptor.parameterCount = m_parameters.size();
m_descriptor.parameters = (const VampParameterDescriptor **)
malloc(m_parameters.size() * sizeof(VampParameterDescriptor));
unsigned int i;
for (i = 0; i < m_parameters.size(); ++i) {
VampParameterDescriptor *desc = (VampParameterDescriptor *)
malloc(sizeof(VampParameterDescriptor));
desc->identifier = strdup(m_parameters[i].identifier.c_str());
desc->name = strdup(m_parameters[i].name.c_str());
desc->description = strdup(m_parameters[i].description.c_str());
desc->unit = strdup(m_parameters[i].unit.c_str());
desc->minValue = m_parameters[i].minValue;
desc->maxValue = m_parameters[i].maxValue;
desc->defaultValue = m_parameters[i].defaultValue;
desc->isQuantized = m_parameters[i].isQuantized;
desc->quantizeStep = m_parameters[i].quantizeStep;
desc->valueNames = 0;
if (desc->isQuantized && !m_parameters[i].valueNames.empty()) {
desc->valueNames = (const char **)
malloc((m_parameters[i].valueNames.size()+1) * sizeof(char *));
for (unsigned int j = 0; j < m_parameters[i].valueNames.size(); ++j) {
desc->valueNames[j] = strdup(m_parameters[i].valueNames[j].c_str());
}
desc->valueNames[m_parameters[i].valueNames.size()] = 0;
}
m_descriptor.parameters[i] = desc;
}
m_descriptor.programCount = m_programs.size();
m_descriptor.programs = (const char **)
malloc(m_programs.size() * sizeof(const char *));
for (i = 0; i < m_programs.size(); ++i) {
m_descriptor.programs[i] = strdup(m_programs[i].c_str());
}
if (plugin->getInputDomain() == Plugin::FrequencyDomain) {
m_descriptor.inputDomain = vampFrequencyDomain;
} else {
m_descriptor.inputDomain = vampTimeDomain;
}
m_descriptor.instantiate = vampInstantiate;
m_descriptor.cleanup = vampCleanup;
m_descriptor.initialise = vampInitialise;
m_descriptor.reset = vampReset;
m_descriptor.getParameter = vampGetParameter;
m_descriptor.setParameter = vampSetParameter;
m_descriptor.getCurrentProgram = vampGetCurrentProgram;
m_descriptor.selectProgram = vampSelectProgram;
m_descriptor.getPreferredStepSize = vampGetPreferredStepSize;
m_descriptor.getPreferredBlockSize = vampGetPreferredBlockSize;
m_descriptor.getMinChannelCount = vampGetMinChannelCount;
m_descriptor.getMaxChannelCount = vampGetMaxChannelCount;
m_descriptor.getOutputCount = vampGetOutputCount;
m_descriptor.getOutputDescriptor = vampGetOutputDescriptor;
m_descriptor.releaseOutputDescriptor = vampReleaseOutputDescriptor;
m_descriptor.process = vampProcess;
m_descriptor.getRemainingFeatures = vampGetRemainingFeatures;
m_descriptor.releaseFeatureSet = vampReleaseFeatureSet;
lock_guard<mutex> adapterMapGuard(adapterMapMutex());
if (!m_adapterMap) {
m_adapterMap = new AdapterMap;
}
(*m_adapterMap)[&m_descriptor] = this;
delete plugin;
m_populated = true;
return &m_descriptor;
}
PluginAdapterBase::Impl::~Impl()
{
#ifdef DEBUG_PLUGIN_ADAPTER
cerr << "PluginAdapterBase::Impl[" << this << "]::~Impl" << endl;
#endif
lock_guard<mutex> guard(m_mutex);
if (!m_populated) return;
free((void *)m_descriptor.identifier);
free((void *)m_descriptor.name);
free((void *)m_descriptor.description);
free((void *)m_descriptor.maker);
free((void *)m_descriptor.copyright);
for (unsigned int i = 0; i < m_descriptor.parameterCount; ++i) {
const VampParameterDescriptor *desc = m_descriptor.parameters[i];
free((void *)desc->identifier);
free((void *)desc->name);
free((void *)desc->description);
free((void *)desc->unit);
if (desc->valueNames) {
for (unsigned int j = 0; desc->valueNames[j]; ++j) {
free((void *)desc->valueNames[j]);
}
free((void *)desc->valueNames);
}
free((void *)desc);
}
free((void *)m_descriptor.parameters);
for (unsigned int i = 0; i < m_descriptor.programCount; ++i) {
free((void *)m_descriptor.programs[i]);
}
free((void *)m_descriptor.programs);
lock_guard<mutex> adapterMapGuard(adapterMapMutex());
if (m_adapterMap) {
m_adapterMap->erase(&m_descriptor);
if (m_adapterMap->empty()) {
delete m_adapterMap;
m_adapterMap = 0;
}
}
}
PluginAdapterBase::Impl *
PluginAdapterBase::Impl::lookupAdapter(VampPluginHandle handle)
{
#ifdef DEBUG_PLUGIN_ADAPTER
cerr << "PluginAdapterBase::Impl::lookupAdapter(" << handle << ")" << endl;
#endif
lock_guard<mutex> adapterMapGuard(adapterMapMutex());
if (!m_adapterMap) return 0;
AdapterMap::const_iterator i = m_adapterMap->find(handle);
if (i == m_adapterMap->end()) return 0;
return i->second;
}
VampPluginHandle
PluginAdapterBase::Impl::vampInstantiate(const VampPluginDescriptor *desc,
float inputSampleRate)
{
#ifdef DEBUG_PLUGIN_ADAPTER
cerr << "PluginAdapterBase::Impl::vampInstantiate(" << desc << ")" << endl;
#endif
lock_guard<mutex> adapterMapGuard(adapterMapMutex());
if (!m_adapterMap) {
m_adapterMap = new AdapterMap();
}
if (m_adapterMap->find(desc) == m_adapterMap->end()) {
cerr << "WARNING: PluginAdapterBase::Impl::vampInstantiate: Descriptor " << desc << " not in adapter map" << endl;
return 0;
}
Impl *adapter = (*m_adapterMap)[desc];
if (desc != &adapter->m_descriptor) return 0;
Plugin *plugin = adapter->m_base->createPlugin(inputSampleRate);
if (plugin) {
(*m_adapterMap)[plugin] = adapter;
}
#ifdef DEBUG_PLUGIN_ADAPTER
cerr << "PluginAdapterBase::Impl::vampInstantiate(" << desc << "): returning handle " << plugin << endl;
#endif
return plugin;
}
void
PluginAdapterBase::Impl::vampCleanup(VampPluginHandle handle)
{
#ifdef DEBUG_PLUGIN_ADAPTER
cerr << "PluginAdapterBase::Impl::vampCleanup(" << handle << ")" << endl;
#endif
Impl *adapter = lookupAdapter(handle);
if (!adapter) {
delete ((Plugin *)handle);
return;
}
adapter->cleanup(((Plugin *)handle));
}
int
PluginAdapterBase::Impl::vampInitialise(VampPluginHandle handle,
unsigned int channels,
unsigned int stepSize,
unsigned int blockSize)
{
#ifdef DEBUG_PLUGIN_ADAPTER
cerr << "PluginAdapterBase::Impl::vampInitialise(" << handle << ", " << channels << ", " << stepSize << ", " << blockSize << ")" << endl;
#endif
Impl *adapter = lookupAdapter(handle);
if (!adapter) return 0;
bool result = ((Plugin *)handle)->initialise(channels, stepSize, blockSize);
adapter->markOutputsChanged((Plugin *)handle);
return result ? 1 : 0;
}
void
PluginAdapterBase::Impl::vampReset(VampPluginHandle handle)
{
#ifdef DEBUG_PLUGIN_ADAPTER
cerr << "PluginAdapterBase::Impl::vampReset(" << handle << ")" << endl;
#endif
((Plugin *)handle)->reset();
}
float
PluginAdapterBase::Impl::vampGetParameter(VampPluginHandle handle,
int param)
{
#ifdef DEBUG_PLUGIN_ADAPTER
cerr << "PluginAdapterBase::Impl::vampGetParameter(" << handle << ", " << param << ")" << endl;
#endif
Impl *adapter = lookupAdapter(handle);
if (!adapter) return 0.0;
Plugin::ParameterList &list = adapter->m_parameters;
return ((Plugin *)handle)->getParameter(list[param].identifier);
}
void
PluginAdapterBase::Impl::vampSetParameter(VampPluginHandle handle,
int param, float value)
{
#ifdef DEBUG_PLUGIN_ADAPTER
cerr << "PluginAdapterBase::Impl::vampSetParameter(" << handle << ", " << param << ", " << value << ")" << endl;
#endif
Impl *adapter = lookupAdapter(handle);
if (!adapter) return;
Plugin::ParameterList &list = adapter->m_parameters;
((Plugin *)handle)->setParameter(list[param].identifier, value);
adapter->markOutputsChanged((Plugin *)handle);
}
unsigned int
PluginAdapterBase::Impl::vampGetCurrentProgram(VampPluginHandle handle)
{
#ifdef DEBUG_PLUGIN_ADAPTER
cerr << "PluginAdapterBase::Impl::vampGetCurrentProgram(" << handle << ")" << endl;
#endif
Impl *adapter = lookupAdapter(handle);
if (!adapter) return 0;
Plugin::ProgramList &list = adapter->m_programs;
string program = ((Plugin *)handle)->getCurrentProgram();
for (unsigned int i = 0; i < list.size(); ++i) {
if (list[i] == program) return i;
}
return 0;
}
void
PluginAdapterBase::Impl::vampSelectProgram(VampPluginHandle handle,
unsigned int program)
{
#ifdef DEBUG_PLUGIN_ADAPTER
cerr << "PluginAdapterBase::Impl::vampSelectProgram(" << handle << ", " << program << ")" << endl;
#endif
Impl *adapter = lookupAdapter(handle);
if (!adapter) return;
Plugin::ProgramList &list = adapter->m_programs;
((Plugin *)handle)->selectProgram(list[program]);
adapter->markOutputsChanged((Plugin *)handle);
}
unsigned int
PluginAdapterBase::Impl::vampGetPreferredStepSize(VampPluginHandle handle)
{
#ifdef DEBUG_PLUGIN_ADAPTER
cerr << "PluginAdapterBase::Impl::vampGetPreferredStepSize(" << handle << ")" << endl;
#endif
return ((Plugin *)handle)->getPreferredStepSize();
}
unsigned int
PluginAdapterBase::Impl::vampGetPreferredBlockSize(VampPluginHandle handle)
{
#ifdef DEBUG_PLUGIN_ADAPTER
cerr << "PluginAdapterBase::Impl::vampGetPreferredBlockSize(" << handle << ")" << endl;
#endif
return ((Plugin *)handle)->getPreferredBlockSize();
}
unsigned int
PluginAdapterBase::Impl::vampGetMinChannelCount(VampPluginHandle handle)
{
#ifdef DEBUG_PLUGIN_ADAPTER
cerr << "PluginAdapterBase::Impl::vampGetMinChannelCount(" << handle << ")" << endl;
#endif
return ((Plugin *)handle)->getMinChannelCount();
}
unsigned int
PluginAdapterBase::Impl::vampGetMaxChannelCount(VampPluginHandle handle)
{
#ifdef DEBUG_PLUGIN_ADAPTER
cerr << "PluginAdapterBase::Impl::vampGetMaxChannelCount(" << handle << ")" << endl;
#endif
return ((Plugin *)handle)->getMaxChannelCount();
}
unsigned int
PluginAdapterBase::Impl::vampGetOutputCount(VampPluginHandle handle)
{
#ifdef DEBUG_PLUGIN_ADAPTER
cerr << "PluginAdapterBase::Impl::vampGetOutputCount(" << handle << ")" << endl;
#endif
Impl *adapter = lookupAdapter(handle);
// cerr << "vampGetOutputCount: handle " << handle << " -> adapter "<< adapter << endl;
if (!adapter) return 0;
return adapter->getOutputCount((Plugin *)handle);
}
VampOutputDescriptor *
PluginAdapterBase::Impl::vampGetOutputDescriptor(VampPluginHandle handle,
unsigned int i)
{
#ifdef DEBUG_PLUGIN_ADAPTER
cerr << "PluginAdapterBase::Impl::vampGetOutputDescriptor(" << handle << ", " << i << ")" << endl;
#endif
Impl *adapter = lookupAdapter(handle);
// cerr << "vampGetOutputDescriptor: handle " << handle << " -> adapter "<< adapter << endl;
if (!adapter) return 0;
return adapter->getOutputDescriptor((Plugin *)handle, i);
}
void
PluginAdapterBase::Impl::vampReleaseOutputDescriptor(VampOutputDescriptor *desc)
{
#ifdef DEBUG_PLUGIN_ADAPTER
cerr << "PluginAdapterBase::Impl::vampReleaseOutputDescriptor(" << desc << ")" << endl;
#endif
if (desc->identifier) free((void *)desc->identifier);
if (desc->name) free((void *)desc->name);
if (desc->description) free((void *)desc->description);
if (desc->unit) free((void *)desc->unit);
if (desc->hasFixedBinCount && desc->binNames) {
for (unsigned int i = 0; i < desc->binCount; ++i) {
if (desc->binNames[i]) {
free((void *)desc->binNames[i]);
}
}
}
if (desc->binNames) free((void *)desc->binNames);
free((void *)desc);
}
VampFeatureList *
PluginAdapterBase::Impl::vampProcess(VampPluginHandle handle,
const float *const *inputBuffers,
int sec,
int nsec)
{
#ifdef DEBUG_PLUGIN_ADAPTER
cerr << "PluginAdapterBase::Impl::vampProcess(" << handle << ", " << sec << ", " << nsec << ")" << endl;
#endif
Impl *adapter = lookupAdapter(handle);
if (!adapter) return 0;
return adapter->process((Plugin *)handle, inputBuffers, sec, nsec);
}
VampFeatureList *
PluginAdapterBase::Impl::vampGetRemainingFeatures(VampPluginHandle handle)
{
#ifdef DEBUG_PLUGIN_ADAPTER
cerr << "PluginAdapterBase::Impl::vampGetRemainingFeatures(" << handle << ")" << endl;
#endif
Impl *adapter = lookupAdapter(handle);
if (!adapter) return 0;
return adapter->getRemainingFeatures((Plugin *)handle);
}
void
PluginAdapterBase::Impl::vampReleaseFeatureSet(VampFeatureList *)
{
#ifdef DEBUG_PLUGIN_ADAPTER
cerr << "PluginAdapterBase::Impl::vampReleaseFeatureSet" << endl;
#endif
}
void
PluginAdapterBase::Impl::cleanup(Plugin *plugin)
{
// at this point no mutex is held
lock_guard<mutex> adapterMapGuard(adapterMapMutex());
lock_guard<mutex> guard(m_mutex);
if (m_fs.find(plugin) != m_fs.end()) {
size_t outputCount = 0;
if (m_pluginOutputs[plugin]) {
outputCount = m_pluginOutputs[plugin]->size();
}
#ifdef DEBUG_PLUGIN_ADAPTER
cerr << "PluginAdapterBase::Impl::cleanup: " << outputCount << " output(s)" << endl;
#endif
VampFeatureList *list = m_fs[plugin];
for (unsigned int i = 0; i < outputCount; ++i) {
for (unsigned int j = 0; j < m_fsizes[plugin][i]; ++j) {
if (list[i].features[j].v1.label) {
free(list[i].features[j].v1.label);
}
if (list[i].features[j].v1.values) {
free(list[i].features[j].v1.values);
}
}
if (list[i].features) free(list[i].features);
}
if (list) free((void *)list);
m_fs.erase(plugin);
m_fsizes.erase(plugin);
m_fvsizes.erase(plugin);
}
if (m_pluginOutputs.find(plugin) != m_pluginOutputs.end()) {
delete m_pluginOutputs[plugin];
m_pluginOutputs.erase(plugin);
}
if (m_adapterMap) {
m_adapterMap->erase(plugin);
if (m_adapterMap->empty()) {
delete m_adapterMap;
m_adapterMap = 0;
}
}
delete ((Plugin *)plugin);
}
void
PluginAdapterBase::Impl::checkOutputMap(Plugin *plugin)
{
// must be called with m_mutex held
OutputMap::iterator i = m_pluginOutputs.find(plugin);
if (i == m_pluginOutputs.end() || !i->second) {
m_pluginOutputs[plugin] = new Plugin::OutputList
(plugin->getOutputDescriptors());
// cerr << "PluginAdapterBase::Impl::checkOutputMap: Have " << m_pluginOutputs[plugin]->size() << " outputs for plugin " << plugin->getIdentifier() << endl;
}
}
void
PluginAdapterBase::Impl::markOutputsChanged(Plugin *plugin)
{
lock_guard<mutex> guard(m_mutex);
OutputMap::iterator i = m_pluginOutputs.find(plugin);
// cerr << "PluginAdapterBase::Impl::markOutputsChanged" << endl;
if (i != m_pluginOutputs.end()) {
Plugin::OutputList *list = i->second;
m_pluginOutputs.erase(i);
delete list;
}
}
unsigned int
PluginAdapterBase::Impl::getOutputCount(Plugin *plugin)
{
lock_guard<mutex> guard(m_mutex);
checkOutputMap(plugin);
return m_pluginOutputs[plugin]->size();
}
VampOutputDescriptor *
PluginAdapterBase::Impl::getOutputDescriptor(Plugin *plugin,
unsigned int i)
{
lock_guard<mutex> guard(m_mutex);
checkOutputMap(plugin);
Plugin::OutputDescriptor &od =
(*m_pluginOutputs[plugin])[i];
VampOutputDescriptor *desc = (VampOutputDescriptor *)
malloc(sizeof(VampOutputDescriptor));
desc->identifier = strdup(od.identifier.c_str());
desc->name = strdup(od.name.c_str());
desc->description = strdup(od.description.c_str());
desc->unit = strdup(od.unit.c_str());
desc->hasFixedBinCount = od.hasFixedBinCount;
desc->binCount = od.binCount;
if (od.hasFixedBinCount && od.binCount > 0
// We would like to do "&& !od.binNames.empty()" here -- but we
// can't, because it will crash older versions of the host adapter
// which try to copy the names across whenever the bin count is
// non-zero, regardless of whether they exist or not
) {
desc->binNames = (const char **)
malloc(od.binCount * sizeof(const char *));
for (unsigned int i = 0; i < od.binCount; ++i) {
if (i < od.binNames.size()) {
desc->binNames[i] = strdup(od.binNames[i].c_str());
} else {
desc->binNames[i] = 0;
}
}
} else {
desc->binNames = 0;
}
desc->hasKnownExtents = od.hasKnownExtents;
desc->minValue = od.minValue;
desc->maxValue = od.maxValue;
desc->isQuantized = od.isQuantized;
desc->quantizeStep = od.quantizeStep;
switch (od.sampleType) {
case Plugin::OutputDescriptor::OneSamplePerStep:
desc->sampleType = vampOneSamplePerStep; break;
case Plugin::OutputDescriptor::FixedSampleRate:
desc->sampleType = vampFixedSampleRate; break;
case Plugin::OutputDescriptor::VariableSampleRate:
desc->sampleType = vampVariableSampleRate; break;
}
desc->sampleRate = od.sampleRate;
desc->hasDuration = od.hasDuration;
return desc;
}
VampFeatureList *
PluginAdapterBase::Impl::process(Plugin *plugin,
const float *const *inputBuffers,
int sec, int nsec)
{
// cerr << "PluginAdapterBase::Impl::process" << endl;
RealTime rt(sec, nsec);
// We don't want to hold the mutex during the actual process call,
// only while looking up the associated metadata
{
lock_guard<mutex> guard(m_mutex);
checkOutputMap(plugin);
}
return convertFeatures(plugin, plugin->process(inputBuffers, rt));
}
VampFeatureList *
PluginAdapterBase::Impl::getRemainingFeatures(Plugin *plugin)
{
// cerr << "PluginAdapterBase::Impl::getRemainingFeatures" << endl;
// We don't want to hold the mutex during the actual call, only
// while looking up the associated metadata
{
lock_guard<mutex> guard(m_mutex);
checkOutputMap(plugin);
}
return convertFeatures(plugin, plugin->getRemainingFeatures());
}
VampFeatureList *
PluginAdapterBase::Impl::convertFeatures(Plugin *plugin,
const Plugin::FeatureSet &features)
{
lock_guard<mutex> guard(m_mutex);
int lastN = -1;
int outputCount = 0;
if (m_pluginOutputs[plugin]) outputCount = m_pluginOutputs[plugin]->size();
resizeFS(plugin, outputCount);
VampFeatureList *fs = m_fs[plugin];
// cerr << "PluginAdapter(v2)::convertFeatures: NOTE: sizeof(Feature) == " << sizeof(Plugin::Feature) << ", sizeof(VampFeature) == " << sizeof(VampFeature) << ", sizeof(VampFeatureList) == " << sizeof(VampFeatureList) << endl;
for (Plugin::FeatureSet::const_iterator fi = features.begin();
fi != features.end(); ++fi) {
int n = fi->first;
// cerr << "PluginAdapterBase::Impl::convertFeatures: n = " << n << endl;
if (n >= int(outputCount)) {
cerr << "WARNING: PluginAdapterBase::Impl::convertFeatures: Too many outputs from plugin (" << n+1 << ", only should be " << outputCount << ")" << endl;
continue;
}
if (n > lastN + 1) {
for (int i = lastN + 1; i < n; ++i) {
fs[i].featureCount = 0;
}
}
const Plugin::FeatureList &fl = fi->second;
size_t sz = fl.size();
if (sz > m_fsizes[plugin][n]) resizeFL(plugin, n, sz);
fs[n].featureCount = sz;
for (size_t j = 0; j < sz; ++j) {
// cerr << "PluginAdapterBase::Impl::convertFeatures: j = " << j << endl;
VampFeature *feature = &fs[n].features[j].v1;
feature->hasTimestamp = fl[j].hasTimestamp;
feature->sec = fl[j].timestamp.sec;
feature->nsec = fl[j].timestamp.nsec;
feature->valueCount = fl[j].values.size();
VampFeatureV2 *v2 = &fs[n].features[j + sz].v2;
v2->hasDuration = fl[j].hasDuration;
v2->durationSec = fl[j].duration.sec;
v2->durationNsec = fl[j].duration.nsec;
if (feature->label) free(feature->label);
if (fl[j].label.empty()) {
feature->label = 0;
} else {
feature->label = strdup(fl[j].label.c_str());
}
if (feature->valueCount > m_fvsizes[plugin][n][j]) {
resizeFV(plugin, n, j, feature->valueCount);
}
for (unsigned int k = 0; k < feature->valueCount; ++k) {
// cerr << "PluginAdapterBase::Impl::convertFeatures: k = " << k << endl;
feature->values[k] = fl[j].values[k];
}
}
lastN = n;
}
if (lastN == -1) return 0;
if (int(outputCount) > lastN + 1) {
for (int i = lastN + 1; i < int(outputCount); ++i) {
fs[i].featureCount = 0;
}
}
// cerr << "PluginAdapter(v2)::convertFeatures: NOTE: have " << outputCount << " outputs" << endl;
// for (int i = 0; i < outputCount; ++i) {
// cerr << "PluginAdapter(v2)::convertFeatures: NOTE: output " << i << " has " << fs[i].featureCount << " features" << endl;
// }
return fs;
}
void
PluginAdapterBase::Impl::resizeFS(Plugin *plugin, int n)
{
// called with m_mutex held
#ifdef DEBUG_PLUGIN_ADAPTER
cerr << "PluginAdapterBase::Impl::resizeFS(" << plugin << ", " << n << ")" << endl;
#endif
int i = m_fsizes[plugin].size();
if (i >= n) return;
#ifdef DEBUG_PLUGIN_ADAPTER
cerr << "resizing from " << i << endl;
#endif
m_fs[plugin] = (VampFeatureList *)realloc
(m_fs[plugin], n * sizeof(VampFeatureList));
while (i < n) {
m_fs[plugin][i].featureCount = 0;
m_fs[plugin][i].features = 0;
m_fsizes[plugin].push_back(0);
m_fvsizes[plugin].push_back(vector<size_t>());
i++;
}
}
void
PluginAdapterBase::Impl::resizeFL(Plugin *plugin, int n, size_t sz)
{
// called with m_mutex held
#ifdef DEBUG_PLUGIN_ADAPTER
cerr << "PluginAdapterBase::Impl::resizeFL(" << plugin << ", " << n << ", "
<< sz << ")" << endl;
#endif
size_t i = m_fsizes[plugin][n];
if (i >= sz) return;
#ifdef DEBUG_PLUGIN_ADAPTER
cerr << "resizing from " << i << endl;
#endif
m_fs[plugin][n].features = (VampFeatureUnion *)realloc
(m_fs[plugin][n].features, 2 * sz * sizeof(VampFeatureUnion));
while (m_fsizes[plugin][n] < sz) {
m_fs[plugin][n].features[m_fsizes[plugin][n]].v1.hasTimestamp = 0;
m_fs[plugin][n].features[m_fsizes[plugin][n]].v1.valueCount = 0;
m_fs[plugin][n].features[m_fsizes[plugin][n]].v1.values = 0;
m_fs[plugin][n].features[m_fsizes[plugin][n]].v1.label = 0;
m_fs[plugin][n].features[m_fsizes[plugin][n] + sz].v2.hasDuration = 0;
m_fvsizes[plugin][n].push_back(0);
m_fsizes[plugin][n]++;
}
}
void
PluginAdapterBase::Impl::resizeFV(Plugin *plugin, int n, int j, size_t sz)
{
// called with m_mutex held
#ifdef DEBUG_PLUGIN_ADAPTER
cerr << "PluginAdapterBase::Impl::resizeFV(" << plugin << ", " << n << ", "
<< j << ", " << sz << ")" << endl;
#endif
size_t i = m_fvsizes[plugin][n][j];
if (i >= sz) return;
#ifdef DEBUG_PLUGIN_ADAPTER
cerr << "resizing from " << i << endl;
#endif
m_fs[plugin][n].features[j].v1.values = (float *)realloc
(m_fs[plugin][n].features[j].v1.values, sz * sizeof(float));
m_fvsizes[plugin][n][j] = sz;
}
PluginAdapterBase::Impl::AdapterMap *
PluginAdapterBase::Impl::m_adapterMap = 0;
}

View File

@ -0,0 +1,76 @@
#pragma once
#include <map>
#include "vamp.h"
#include "vamp-sdk/Plugin.h"
namespace Vamp {
/**
* \class PluginAdapterBase PluginAdapter.h <vamp-sdk/PluginAdapter.h>
*
* PluginAdapter and PluginAdapterBase provide a wrapper class that a
* plugin library can use to make its C++ Vamp::Plugin objects
* available through the Vamp C API.
*
* Almost all Vamp plugin libraries will want to make use of this. To
* do so, all they need to do is declare a PluginAdapter<T> for each
* plugin class T in their library. It's very simple, and you need to
* know absolutely nothing about how it works in order to use it.
* Just cut and paste from an existing plugin's discovery function.
* \see vampGetPluginDescriptor
*/
class PluginAdapterBase
{
public:
virtual ~PluginAdapterBase();
/**
* Return a VampPluginDescriptor describing the plugin that is
* wrapped by this adapter.
*/
const VampPluginDescriptor *getDescriptor();
protected:
PluginAdapterBase();
virtual Plugin *createPlugin(float inputSampleRate) = 0;
class Impl;
Impl *m_impl;
};
/**
* \class PluginAdapter PluginAdapter.h <vamp-sdk/PluginAdapter.h>
*
* PluginAdapter turns a PluginAdapterBase into a specific wrapper for
* a particular plugin implementation.
*
* See PluginAdapterBase.
*/
template <typename P>
class PluginAdapter : public PluginAdapterBase
{
public:
PluginAdapter() : PluginAdapterBase() { }
virtual ~PluginAdapter() { }
protected:
Plugin *createPlugin(float inputSampleRate) {
P *p = new P(inputSampleRate);
Plugin *plugin = dynamic_cast<Plugin *>(p);
if (!plugin) {
std::cerr << "ERROR: PluginAdapter::createPlugin: "
<< "Template type is not a plugin!"
<< std::endl;
delete p;
return 0;
}
return plugin;
}
};
}

218
src/vamp-sdk/PluginBase.h Normal file
View File

@ -0,0 +1,218 @@
#pragma once
#include <string>
#include <vector>
namespace Vamp {
/**
* A base class for plugins with optional configurable parameters,
* programs, etc. The Vamp::Plugin is derived from this, and
* individual Vamp plugins should derive from that.
*
* This class does not provide the necessary interfaces to instantiate
* or run a plugin. It only specifies an interface for retrieving
* those controls that the host may wish to show to the user for
* editing. It could meaningfully be subclassed by real-time plugins
* or other sorts of plugin as well as Vamp plugins.
*/
class PluginBase
{
public:
virtual ~PluginBase() { }
/**
* Get the Vamp API compatibility level of the plugin.
*/
virtual unsigned int getVampApiVersion() const { return 2; }
/**
* Get the computer-usable name of the plugin. This should be
* reasonably short and contain no whitespace or punctuation
* characters. It may only contain the characters [a-zA-Z0-9_-].
* This is the authoritative way for a program to identify a
* plugin within a given library.
*
* This text may be visible to the user, but it should not be the
* main text used to identify a plugin to the user (that will be
* the name, below).
*
* Example: "zero_crossings"
*/
virtual std::string getIdentifier() const = 0;
/**
* Get a human-readable name or title of the plugin. This
* should be brief and self-contained, as it may be used to
* identify the plugin to the user in isolation (i.e. without also
* showing the plugin's "identifier").
*
* Example: "Zero Crossings"
*/
virtual std::string getName() const = 0;
/**
* Get a human-readable description for the plugin, typically
* a line of text that may optionally be displayed in addition
* to the plugin's "name". May be empty if the name has said
* it all already.
*
* Example: "Detect and count zero crossing points"
*/
virtual std::string getDescription() const = 0;
/**
* Get the name of the author or vendor of the plugin in
* human-readable form. This should be a short identifying text,
* as it may be used to label plugins from the same source in a
* menu or similar.
*/
virtual std::string getMaker() const = 0;
/**
* Get the copyright statement or licensing summary for the
* plugin. This can be an informative text, without the same
* presentation constraints as mentioned for getMaker above.
*/
virtual std::string getCopyright() const = 0;
/**
* Get the version number of the plugin.
*/
virtual int getPluginVersion() const = 0;
struct ParameterDescriptor
{
/**
* The name of the parameter, in computer-usable form. Should
* be reasonably short, and may only contain the characters
* [a-zA-Z0-9_-].
*/
std::string identifier;
/**
* The human-readable name of the parameter.
*/
std::string name;
/**
* A human-readable short text describing the parameter. May be
* empty if the name has said it all already.
*/
std::string description;
/**
* The unit of the parameter, in human-readable form.
*/
std::string unit;
/**
* The minimum value of the parameter.
*/
float minValue;
/**
* The maximum value of the parameter.
*/
float maxValue;
/**
* The default value of the parameter. The plugin should
* ensure that parameters have this value on initialisation
* (i.e. the host is not required to explicitly set parameters
* if it wants to use their default values).
*/
float defaultValue;
/**
* True if the parameter values are quantized to a particular
* resolution.
*/
bool isQuantized;
/**
* Quantization resolution of the parameter values (e.g. 1.0
* if they are all integers). Undefined if isQuantized is
* false.
*/
float quantizeStep;
/**
* Names for the quantized values. If isQuantized is true,
* this may either be empty or contain one string for each of
* the quantize steps from minValue up to maxValue inclusive.
* Undefined if isQuantized is false.
*
* If these names are provided, they should be shown to the
* user in preference to the values themselves. The user may
* never see the actual numeric values unless they are also
* encoded in the names.
*/
std::vector<std::string> valueNames;
ParameterDescriptor() : // the defaults are invalid: you must set them
minValue(0),
maxValue(0),
defaultValue(0),
isQuantized(false),
quantizeStep(0) { }
};
typedef std::vector<ParameterDescriptor> ParameterList;
/**
* Get the controllable parameters of this plugin.
*/
virtual ParameterList getParameterDescriptors() const {
return ParameterList();
}
/**
* Get the value of a named parameter. The argument is the identifier
* field from that parameter's descriptor.
*/
virtual float getParameter(std::string) const { return 0.0; }
/**
* Set a named parameter. The first argument is the identifier field
* from that parameter's descriptor.
*/
virtual void setParameter(std::string, float) { }
typedef std::vector<std::string> ProgramList;
/**
* Get the program settings available in this plugin. A program
* is a named shorthand for a set of parameter values; changing
* the program may cause the plugin to alter the values of its
* published parameters (and/or non-public internal processing
* parameters). The host should re-read the plugin's parameter
* values after setting a new program.
*
* The programs must have unique names.
*/
virtual ProgramList getPrograms() const { return ProgramList(); }
/**
* Get the current program.
*/
virtual std::string getCurrentProgram() const { return ""; }
/**
* Select a program. (If the given program name is not one of the
* available programs, do nothing.)
*/
virtual void selectProgram(std::string) { }
/**
* Get the type of plugin. This is to be implemented by the
* immediate subclass, not by actual plugins. Do not attempt to
* implement this in plugin code.
*/
virtual std::string getType() const = 0;
};
}

199
src/vamp-sdk/RealTime.cpp Normal file
View File

@ -0,0 +1,199 @@
#include <iostream>
#include <limits.h>
#if (defined(__GNUC__)) && (__GNUC__ < 3)
#include <strstream>
#define stringstream strstream
#else
#include <sstream>
#endif
using std::cerr;
using std::endl;
#ifndef _WIN32
#include <sys/time.h>
#endif
#include "vamp-sdk/RealTime.h"
namespace Vamp {
// A RealTime consists of two ints that must be at least 32 bits each.
// A signed 32-bit int can store values exceeding +/- 2 billion. This
// means we can safely use our lower int for nanoseconds, as there are
// 1 billion nanoseconds in a second and we need to handle double that
// because of the implementations of addition etc that we use.
//
// The maximum valid RealTime on a 32-bit system is somewhere around
// 68 years: 999999999 nanoseconds longer than the classic Unix epoch.
#define ONE_BILLION 1000000000
RealTime::RealTime(int s, int n) :
sec(s), nsec(n)
{
while (nsec <= -ONE_BILLION && sec > INT_MIN) { nsec += ONE_BILLION; --sec; }
while (nsec >= ONE_BILLION && sec < INT_MAX) { nsec -= ONE_BILLION; ++sec; }
while (nsec > 0 && sec < 0) { nsec -= ONE_BILLION; ++sec; }
while (nsec < 0 && sec > 0) { nsec += ONE_BILLION; --sec; }
}
RealTime
RealTime::fromSeconds(double sec)
{
if (sec != sec) { // NaN
cerr << "ERROR: NaN/Inf passed to Vamp::RealTime::fromSeconds" << endl;
return RealTime::zeroTime;
} else if (sec >= 0) {
return RealTime(int(sec), int((sec - int(sec)) * ONE_BILLION + 0.5));
} else {
return -fromSeconds(-sec);
}
}
RealTime
RealTime::fromMilliseconds(int msec)
{
return RealTime(msec / 1000, (msec % 1000) * 1000000);
}
#ifndef _WIN32
RealTime
RealTime::fromTimeval(const struct timeval &tv)
{
return RealTime(int(tv.tv_sec), int(tv.tv_usec * 1000));
}
#endif
std::ostream &operator<<(std::ostream &out, const RealTime &rt)
{
if (rt < RealTime::zeroTime) {
out << "-";
} else {
out << " ";
}
int s = (rt.sec < 0 ? -rt.sec : rt.sec);
int n = (rt.nsec < 0 ? -rt.nsec : rt.nsec);
out << s << ".";
int nn(n);
if (nn == 0) out << "00000000";
else while (nn < (ONE_BILLION / 10)) {
out << "0";
nn *= 10;
}
out << n << "R";
return out;
}
std::string
RealTime::toString() const
{
std::stringstream out;
out << *this;
std::string s = out.str();
// remove trailing R
return s.substr(0, s.length() - 1);
}
std::string
RealTime::toText(bool fixedDp) const
{
if (*this < RealTime::zeroTime) return "-" + (-*this).toText(fixedDp);
std::stringstream out;
if (sec >= 3600) {
out << (sec / 3600) << ":";
}
if (sec >= 60) {
int minutes = (sec % 3600) / 60;
if (sec >= 3600 && minutes < 10) out << "0";
out << minutes << ":";
}
if (sec >= 10) {
out << ((sec % 60) / 10);
}
out << (sec % 10);
int ms = msec();
if (ms != 0) {
out << ".";
out << (ms / 100);
ms = ms % 100;
if (ms != 0) {
out << (ms / 10);
ms = ms % 10;
} else if (fixedDp) {
out << "0";
}
if (ms != 0) {
out << ms;
} else if (fixedDp) {
out << "0";
}
} else if (fixedDp) {
out << ".000";
}
std::string s = out.str();
return s;
}
RealTime
RealTime::operator/(int d) const
{
int secdiv = sec / d;
int secrem = sec % d;
double nsecdiv = (double(nsec) + ONE_BILLION * double(secrem)) / d;
return RealTime(secdiv, int(nsecdiv + 0.5));
}
double
RealTime::operator/(const RealTime &r) const
{
double lTotal = double(sec) * ONE_BILLION + double(nsec);
double rTotal = double(r.sec) * ONE_BILLION + double(r.nsec);
if (rTotal == 0) return 0.0;
else return lTotal/rTotal;
}
long
RealTime::realTime2Frame(const RealTime &time, unsigned int sampleRate)
{
if (time < zeroTime) return -realTime2Frame(-time, sampleRate);
double s = time.sec + double(time.nsec) / ONE_BILLION;
return long(s * sampleRate + 0.5);
}
RealTime
RealTime::frame2RealTime(long frame, unsigned int sampleRate)
{
if (frame < 0) return -frame2RealTime(-frame, sampleRate);
int sec = int(frame / long(sampleRate));
frame -= sec * long(sampleRate);
int nsec = (int)((double(frame) / double(sampleRate)) * ONE_BILLION + 0.5);
// Use ctor here instead of setting data members directly to
// ensure nsec > ONE_BILLION is handled properly. It's extremely
// unlikely, but not impossible.
return RealTime(sec, nsec);
}
const RealTime RealTime::zeroTime(0,0);
}

114
src/vamp-sdk/RealTime.h Normal file
View File

@ -0,0 +1,114 @@
#pragma once
#include <iostream>
#include <string>
struct timeval;
namespace Vamp {
/**
* \class RealTime RealTime.h <vamp-sdk/RealTime.h>
*
* RealTime represents time values to nanosecond precision
* with accurate arithmetic and frame-rate conversion functions.
*/
struct RealTime
{
int sec;
int nsec;
int usec() const { return nsec / 1000; }
int msec() const { return nsec / 1000000; }
RealTime(): sec(0), nsec(0) {}
RealTime(int s, int n);
RealTime(const RealTime &r) :
sec(r.sec), nsec(r.nsec) { }
static RealTime fromSeconds(double sec);
static RealTime fromMilliseconds(int msec);
#ifndef _WIN32
static RealTime fromTimeval(const struct timeval &);
#endif
RealTime &operator=(const RealTime &r) {
sec = r.sec; nsec = r.nsec; return *this;
}
RealTime operator+(const RealTime &r) const {
return RealTime(sec + r.sec, nsec + r.nsec);
}
RealTime operator-(const RealTime &r) const {
return RealTime(sec - r.sec, nsec - r.nsec);
}
RealTime operator-() const {
return RealTime(-sec, -nsec);
}
bool operator <(const RealTime &r) const {
if (sec == r.sec) return nsec < r.nsec;
else return sec < r.sec;
}
bool operator >(const RealTime &r) const {
if (sec == r.sec) return nsec > r.nsec;
else return sec > r.sec;
}
bool operator==(const RealTime &r) const {
return (sec == r.sec && nsec == r.nsec);
}
bool operator!=(const RealTime &r) const {
return !(r == *this);
}
bool operator>=(const RealTime &r) const {
if (sec == r.sec) return nsec >= r.nsec;
else return sec >= r.sec;
}
bool operator<=(const RealTime &r) const {
if (sec == r.sec) return nsec <= r.nsec;
else return sec <= r.sec;
}
RealTime operator/(int d) const;
/**
* Return the ratio of two times.
*/
double operator/(const RealTime &r) const;
/**
* Return a human-readable debug-type string to full precision
* (probably not a format to show to a user directly)
*/
std::string toString() const;
/**
* Return a user-readable string to the nearest millisecond
* in a form like HH:MM:SS.mmm
*/
std::string toText(bool fixedDp = false) const;
/**
* Convert a RealTime into a sample frame at the given sample rate.
*/
static long realTime2Frame(const RealTime &r, unsigned int sampleRate);
/**
* Convert a sample frame at the given sample rate into a RealTime.
*/
static RealTime frame2RealTime(long frame, unsigned int sampleRate);
static const RealTime zeroTime;
};
std::ostream &operator<<(std::ostream &out, const RealTime &rt);
}

View File

@ -0,0 +1,16 @@
/* These stubs are provided so that autoconf can check library
* versions using C symbols only */
extern void libvampsdk_v_2_9_present(void) { }
extern void libvampsdk_v_2_8_present(void) { }
extern void libvampsdk_v_2_7_1_present(void) { }
extern void libvampsdk_v_2_7_present(void) { }
extern void libvampsdk_v_2_6_present(void) { }
extern void libvampsdk_v_2_5_present(void) { }
extern void libvampsdk_v_2_4_present(void) { }
extern void libvampsdk_v_2_3_1_present(void) { }
extern void libvampsdk_v_2_3_present(void) { }
extern void libvampsdk_v_2_2_1_present(void) { }
extern void libvampsdk_v_2_2_present(void) { }
extern void libvampsdk_v_2_1_present(void) { }
extern void libvampsdk_v_2_0_present(void) { }

View File

@ -0,0 +1,443 @@
#pragma once
#include <stdlib.h>
#include <stdio.h>
#include <math.h>
#include <string.h>
#include "vamp-sdk/ext/vamp_kiss_fft_guts.h"
// #ifndef _VAMP_IN_PLUGINSDK
// # ifndef _VAMP_IN_HOSTSDK
// # error "Do not use the Vamp SDK mangled KissFFT header except through the Vamp SDK API"
// # endif
// #endif
#ifdef VAMP_KISSFFT_USE_CPP_LINKAGE
#define VAMP_KISS_FFT_MALLOC ::malloc
#define VAMP_KISS_FFT_FREE ::free
#else
#ifdef __cplusplus
extern "C" {
#define VAMP_KISS_FFT_MALLOC malloc
#define VAMP_KISS_FFT_FREE free
#endif
#endif
#define vamp_kiss_fft_scalar double
typedef struct {
vamp_kiss_fft_scalar r;
vamp_kiss_fft_scalar i;
} vamp_kiss_fft_cpx;
struct vamp_kiss_fft_state{
int nfft;
int inverse;
int factors[2*MAXFACTORS];
vamp_kiss_fft_cpx twiddles[1];
};
typedef struct vamp_kiss_fft_state* vamp_kiss_fft_cfg;
static void kf_bfly2(vamp_kiss_fft_cpx * Fout, const size_t fstride, const vamp_kiss_fft_cfg st, int m)
{
vamp_kiss_fft_cpx* Fout2;
vamp_kiss_fft_cpx* tw1 = st->twiddles;
vamp_kiss_fft_cpx t;
Fout2 = Fout + m;
do
{
C_FIXDIV(*Fout,2);
C_FIXDIV(*Fout2,2);
C_MUL(t, *Fout2, *tw1);
tw1 += fstride;
C_SUB(*Fout2 , *Fout, t);
C_ADDTO(*Fout , t);
++Fout2;
++Fout;
}
while (--m);
}
static void kf_bfly4(vamp_kiss_fft_cpx* Fout, const size_t fstride, const vamp_kiss_fft_cfg st, const size_t m)
{
vamp_kiss_fft_cpx *tw1,*tw2,*tw3;
vamp_kiss_fft_cpx scratch[6];
size_t k = m;
const size_t m2=2*m;
const size_t m3=3*m;
tw3 = tw2 = tw1 = st->twiddles;
do
{
C_FIXDIV(*Fout, 4);
C_FIXDIV(Fout[m], 4);
C_FIXDIV(Fout[m2], 4);
C_FIXDIV(Fout[m3], 4);
C_MUL(scratch[0], Fout[m], *tw1 );
C_MUL(scratch[1], Fout[m2], *tw2 );
C_MUL(scratch[2], Fout[m3], *tw3 );
C_SUB(scratch[5], *Fout, scratch[1]);
C_ADDTO(*Fout, scratch[1]);
C_ADD(scratch[3], scratch[0], scratch[2]);
C_SUB(scratch[4], scratch[0], scratch[2]);
C_SUB(Fout[m2], *Fout, scratch[3]);
tw1 += fstride;
tw2 += fstride * 2;
tw3 += fstride * 3;
C_ADDTO(*Fout, scratch[3]);
if (st->inverse)
{
Fout[m].r = scratch[5].r - scratch[4].i;
Fout[m].i = scratch[5].i + scratch[4].r;
Fout[m3].r = scratch[5].r + scratch[4].i;
Fout[m3].i = scratch[5].i - scratch[4].r;
}
else
{
Fout[m].r = scratch[5].r + scratch[4].i;
Fout[m].i = scratch[5].i - scratch[4].r;
Fout[m3].r = scratch[5].r - scratch[4].i;
Fout[m3].i = scratch[5].i + scratch[4].r;
}
++Fout;
}
while (--k);
}
static void kf_bfly3(vamp_kiss_fft_cpx* Fout, const size_t fstride, const vamp_kiss_fft_cfg st, size_t m)
{
size_t k=m;
const size_t m2 = 2*m;
vamp_kiss_fft_cpx *tw1,*tw2;
vamp_kiss_fft_cpx scratch[5];
vamp_kiss_fft_cpx epi3;
epi3 = st->twiddles[fstride*m];
tw1=tw2=st->twiddles;
do
{
C_FIXDIV(*Fout, 3);
C_FIXDIV(Fout[m], 3);
C_FIXDIV(Fout[m2], 3);
C_MUL(scratch[1], Fout[m], *tw1);
C_MUL(scratch[2], Fout[m2], *tw2);
C_ADD(scratch[3], scratch[1], scratch[2]);
C_SUB(scratch[0], scratch[1], scratch[2]);
tw1 += fstride;
tw2 += fstride*2;
Fout[m].r = Fout->r - HALF_OF(scratch[3].r);
Fout[m].i = Fout->i - HALF_OF(scratch[3].i);
C_MULBYSCALAR(scratch[0], epi3.i);
C_ADDTO(*Fout,scratch[3]);
Fout[m2].r = Fout[m].r + scratch[0].i;
Fout[m2].i = Fout[m].i - scratch[0].r;
Fout[m].r -= scratch[0].i;
Fout[m].i += scratch[0].r;
++Fout;
}
while (--k);
}
static void kf_bfly5(vamp_kiss_fft_cpx* Fout, const size_t fstride, const vamp_kiss_fft_cfg st, int m)
{
vamp_kiss_fft_cpx* Fout0;
vamp_kiss_fft_cpx* Fout1;
vamp_kiss_fft_cpx* Fout2;
vamp_kiss_fft_cpx* Fout3;
vamp_kiss_fft_cpx* Fout4;
int u;
vamp_kiss_fft_cpx scratch[13];
vamp_kiss_fft_cpx* twiddles = st->twiddles;
vamp_kiss_fft_cpx* tw;
vamp_kiss_fft_cpx ya,yb;
ya = twiddles[fstride * m];
yb = twiddles[fstride * 2 * m];
Fout0 = Fout;
Fout1 = Fout0+m;
Fout2 = Fout0+2*m;
Fout3 = Fout0+3*m;
Fout4 = Fout0+4*m;
tw = st->twiddles;
for ( u=0; u<m; ++u )
{
C_FIXDIV(*Fout0, 5);
C_FIXDIV(*Fout1, 5);
C_FIXDIV(*Fout2, 5);
C_FIXDIV(*Fout3, 5);
C_FIXDIV(*Fout4, 5);
scratch[0] = *Fout0;
C_MUL(scratch[1], *Fout1, tw[u * fstride]);
C_MUL(scratch[2], *Fout2, tw[2 * u * fstride]);
C_MUL(scratch[3], *Fout3, tw[3 * u * fstride]);
C_MUL(scratch[4], *Fout4, tw[4 * u * fstride]);
C_ADD(scratch[7], scratch[1], scratch[4]);
C_SUB(scratch[10], scratch[1], scratch[4]);
C_ADD(scratch[8], scratch[2], scratch[3]);
C_SUB(scratch[9], scratch[2], scratch[3]);
Fout0->r += scratch[7].r + scratch[8].r;
Fout0->i += scratch[7].i + scratch[8].i;
scratch[5].r = scratch[0].r + S_MUL(scratch[7].r,ya.r) + S_MUL(scratch[8].r,yb.r);
scratch[5].i = scratch[0].i + S_MUL(scratch[7].i,ya.r) + S_MUL(scratch[8].i,yb.r);
scratch[6].r = S_MUL(scratch[10].i,ya.i) + S_MUL(scratch[9].i,yb.i);
scratch[6].i = -S_MUL(scratch[10].r,ya.i) - S_MUL(scratch[9].r,yb.i);
C_SUB(*Fout1,scratch[5],scratch[6]);
C_ADD(*Fout4,scratch[5],scratch[6]);
scratch[11].r = scratch[0].r + S_MUL(scratch[7].r,yb.r) + S_MUL(scratch[8].r,ya.r);
scratch[11].i = scratch[0].i + S_MUL(scratch[7].i,yb.r) + S_MUL(scratch[8].i,ya.r);
scratch[12].r = - S_MUL(scratch[10].i,yb.i) + S_MUL(scratch[9].i,ya.i);
scratch[12].i = S_MUL(scratch[10].r,yb.i) - S_MUL(scratch[9].r,ya.i);
C_ADD(*Fout2,scratch[11],scratch[12]);
C_SUB(*Fout3,scratch[11],scratch[12]);
++Fout0;
++Fout1;
++Fout2;
++Fout3;
++Fout4;
}
}
/* perform the butterfly for one stage of a mixed radix FFT */
static void kf_bfly_generic(vamp_kiss_fft_cpx* Fout, const size_t fstride, const vamp_kiss_fft_cfg st, int m, int p)
{
int u,k,q1,q;
vamp_kiss_fft_cpx * twiddles = st->twiddles;
vamp_kiss_fft_cpx t;
int Norig = st->nfft;
vamp_kiss_fft_cpx * scratch = (vamp_kiss_fft_cpx*)VAMP_KISS_FFT_TMP_ALLOC(sizeof(vamp_kiss_fft_cpx)*p);
for ( u=0; u<m; ++u )
{
k=u;
for (q1 = 0; q1 < p; ++q1)
{
scratch[q1] = Fout[k];
C_FIXDIV(scratch[q1], p);
k += m;
}
k = u;
for (q1 = 0; q1 < p; ++q1)
{
int twidx=0;
Fout[k] = scratch[0];
for (q = 1; q < p; ++q)
{
twidx += int(fstride * k);
if (twidx >= Norig) twidx -= Norig;
C_MUL(t,scratch[q], twiddles[twidx]);
C_ADDTO(Fout[k], t);
}
k += m;
}
}
VAMP_KISS_FFT_TMP_FREE(scratch);
}
static
void kf_work(vamp_kiss_fft_cpx* Fout, const vamp_kiss_fft_cpx* f, const size_t fstride, int in_stride, int* factors, const vamp_kiss_fft_cfg st)
{
vamp_kiss_fft_cpx * Fout_beg=Fout;
const int p=*factors++; /* the radix */
const int m=*factors++; /* stage's fft length/p */
const vamp_kiss_fft_cpx * Fout_end = Fout + p*m;
#ifdef _OPENMP
// use openmp extensions at the
// top-level (not recursive)
if (fstride==1 && p<=5)
{
int k;
// execute the p different work units in different threads
# pragma omp parallel for
for (k=0;k<p;++k)
kf_work( Fout +k*m, f+ fstride*in_stride*k,fstride*p,in_stride,factors,st);
// all threads have joined by this point
switch (p) {
case 2: kf_bfly2(Fout,fstride,st,m); break;
case 3: kf_bfly3(Fout,fstride,st,m); break;
case 4: kf_bfly4(Fout,fstride,st,m); break;
case 5: kf_bfly5(Fout,fstride,st,m); break;
default: kf_bfly_generic(Fout,fstride,st,m,p); break;
}
return;
}
#endif
if (m==1) {
do{
Fout->r = f->r;
Fout->i = f->i;
f += fstride*in_stride;
}while(++Fout != Fout_end );
}else{
do{
// recursive call:
// DFT of size m*p performed by doing
// p instances of smaller DFTs of size m,
// each one takes a decimated version of the input
kf_work( Fout , f, fstride*p, in_stride, factors,st);
f += fstride*in_stride;
}while( (Fout += m) != Fout_end );
}
Fout=Fout_beg;
// recombine the p smaller DFTs
switch (p) {
case 2: kf_bfly2(Fout,fstride,st,m); break;
case 3: kf_bfly3(Fout,fstride,st,m); break;
case 4: kf_bfly4(Fout,fstride,st,m); break;
case 5: kf_bfly5(Fout,fstride,st,m); break;
default: kf_bfly_generic(Fout,fstride,st,m,p); break;
}
}
/* facbuf is populated by p1,m1,p2,m2, ...
where
p[i] * m[i] = m[i-1]
m0 = n */
static
void kf_factor(int n,int * facbuf)
{
int p=4;
double floor_sqrt;
floor_sqrt = floor( sqrt((double)n) );
/*factor out powers of 4, powers of 2, then any remaining primes */
do {
while (n % p) {
switch (p) {
case 4: p = 2; break;
case 2: p = 3; break;
default: p += 2; break;
}
if (p > floor_sqrt)
p = n; /* no more factors, skip to end */
}
n /= p;
*facbuf++ = p;
*facbuf++ = n;
} while (n > 1);
}
/*
*
* User-callable function to allocate all necessary storage space for the fft.
*
* The return value is a contiguous block of memory, allocated with malloc. As such,
* It can be freed with free(), rather than a kiss_fft-specific function.
* */
inline vamp_kiss_fft_cfg vamp_kiss_fft_alloc(int nfft,int inverse_fft,void * mem,size_t * lenmem )
{
vamp_kiss_fft_cfg st=NULL;
size_t memneeded = sizeof(struct vamp_kiss_fft_state)
+ sizeof(vamp_kiss_fft_cpx)*(nfft-1); /* twiddle factors*/
if ( lenmem==NULL ) {
st = ( vamp_kiss_fft_cfg)VAMP_KISS_FFT_MALLOC( memneeded );
}else{
if (mem != NULL && *lenmem >= memneeded)
st = (vamp_kiss_fft_cfg)mem;
*lenmem = memneeded;
}
if (st) {
int i;
st->nfft=nfft;
st->inverse = inverse_fft;
for (i=0;i<nfft;++i) {
const double pi=3.141592653589793238462643383279502884197169399375105820974944;
double phase = -2*pi*i / nfft;
if (st->inverse)
phase *= -1;
kf_cexp(st->twiddles+i, phase );
}
kf_factor(nfft,st->factors);
}
return st;
}
inline void vamp_kiss_fft_free(void *mem)
{
free(mem);
}
inline void vamp_kiss_fft_stride(vamp_kiss_fft_cfg st,const vamp_kiss_fft_cpx *fin,vamp_kiss_fft_cpx *fout,int in_stride)
{
if (fin == fout) {
//NOTE: this is not really an in-place FFT algorithm.
//It just performs an out-of-place FFT into a temp buffer
vamp_kiss_fft_cpx * tmpbuf = (vamp_kiss_fft_cpx*)VAMP_KISS_FFT_TMP_ALLOC( sizeof(vamp_kiss_fft_cpx)*st->nfft);
kf_work(tmpbuf,fin,1,in_stride, st->factors,st);
memcpy(fout,tmpbuf,sizeof(vamp_kiss_fft_cpx)*st->nfft);
VAMP_KISS_FFT_TMP_FREE(tmpbuf);
}else{
kf_work( fout, fin, 1,in_stride, st->factors,st );
}
}
inline void vamp_kiss_fft(vamp_kiss_fft_cfg cfg,const vamp_kiss_fft_cpx *fin,vamp_kiss_fft_cpx *fout)
{
vamp_kiss_fft_stride(cfg,fin,fout,1);
}
inline void vamp_kiss_fft_cleanup(void)
{
// nothing needed any more
}
inline int vamp_kiss_fft_next_fast_size(int n)
{
while(1) {
int m=n;
while ( (m%2) == 0 ) m/=2;
while ( (m%3) == 0 ) m/=3;
while ( (m%5) == 0 ) m/=5;
if (m<=1)
break; /* n is completely factorable by twos, threes, and fives */
n++;
}
return n;
}
/* for real ffts, we need an even size */
#define vamp_kiss_fftr_next_fast_size_real(n) (vamp_kiss_fft_next_fast_size( ((n)+1)>>1)<<1)
#ifndef VAMP_KISSFFT_USE_CPP_LINKAGE
#ifdef __cplusplus
}
#endif
#endif

View File

@ -0,0 +1,74 @@
#pragma once
#include <limits.h>
#define MAXFACTORS 32
/* e.g. an fft of length 128 has 4 factors
as far as kissfft is concerned
4*4*4*2
*/
/*
Explanation of macros dealing with complex math:
C_MUL(m,a,b) : m = a*b
C_FIXDIV( c , div ) : if a fixed point impl., c /= div. noop otherwise
C_SUB( res, a,b) : res = a - b
C_SUBFROM( res , a) : res -= a
C_ADDTO( res , a) : res += a
* */
#define S_MUL(a,b) ( (a)*(b) )
#define C_MUL(m,a,b) \
do{ (m).r = (a).r*(b).r - (a).i*(b).i;\
(m).i = (a).r*(b).i + (a).i*(b).r; }while(0)
#define C_FIXDIV(c,div) /* NOOP */
#define C_MULBYSCALAR( c, s ) \
do{ (c).r *= (s);\
(c).i *= (s); }while(0)
#define CHECK_OVERFLOW_OP(a,op,b) /* noop */
#define C_ADD( res, a,b)\
do { \
CHECK_OVERFLOW_OP((a).r,+,(b).r)\
CHECK_OVERFLOW_OP((a).i,+,(b).i)\
(res).r=(a).r+(b).r; (res).i=(a).i+(b).i; \
}while(0)
#define C_SUB( res, a,b)\
do { \
CHECK_OVERFLOW_OP((a).r,-,(b).r)\
CHECK_OVERFLOW_OP((a).i,-,(b).i)\
(res).r=(a).r-(b).r; (res).i=(a).i-(b).i; \
}while(0)
#define C_ADDTO( res , a)\
do { \
CHECK_OVERFLOW_OP((res).r,+,(a).r)\
CHECK_OVERFLOW_OP((res).i,+,(a).i)\
(res).r += (a).r; (res).i += (a).i;\
}while(0)
#define C_SUBFROM( res , a)\
do {\
CHECK_OVERFLOW_OP((res).r,-,(a).r)\
CHECK_OVERFLOW_OP((res).i,-,(a).i)\
(res).r -= (a).r; (res).i -= (a).i; \
}while(0)
#define VAMP_KISS_FFT_COS(phase) (vamp_kiss_fft_scalar) cos(phase)
#define VAMP_KISS_FFT_SIN(phase) (vamp_kiss_fft_scalar) sin(phase)
#define HALF_OF(x) ((x)*.5)
#define kf_cexp(x,phase) \
do{ \
(x)->r = VAMP_KISS_FFT_COS(phase);\
(x)->i = VAMP_KISS_FFT_SIN(phase);\
}while(0)
#define VAMP_KISS_FFT_TMP_ALLOC(nbytes) VAMP_KISS_FFT_MALLOC(nbytes)
#define VAMP_KISS_FFT_TMP_FREE(ptr) VAMP_KISS_FFT_FREE(ptr)
/* a debugging function */
#define pcpx(c)\
fprintf(stderr,"%g + %gi\n",(double)((c)->r),(double)((c)->i) )

View File

@ -0,0 +1,161 @@
#pragma once
#include "vamp_kiss_fft.h"
#ifndef VAMP_KISSFFT_USE_CPP_LINKAGE
#ifdef __cplusplus
extern "C" {
#endif
#endif
/* Real optimized version can save about 45% cpu time vs. complex fft of a real seq. */
typedef struct vamp_kiss_fftr_state *vamp_kiss_fftr_cfg;
struct vamp_kiss_fftr_state{
vamp_kiss_fft_cfg substate;
vamp_kiss_fft_cpx * tmpbuf;
vamp_kiss_fft_cpx * super_twiddles;
};
inline vamp_kiss_fftr_cfg vamp_kiss_fftr_alloc(int nfft,int inverse_fft,void * mem,size_t * lenmem)
{
int i;
vamp_kiss_fftr_cfg st = NULL;
size_t subsize, memneeded;
if (nfft & 1) {
fprintf(stderr,"Real FFT optimization must be even.\n");
return NULL;
}
nfft >>= 1;
vamp_kiss_fft_alloc (nfft, inverse_fft, NULL, &subsize);
memneeded = sizeof(struct vamp_kiss_fftr_state) + subsize + sizeof(vamp_kiss_fft_cpx) * ( nfft * 3 / 2);
if (lenmem == NULL) {
st = (vamp_kiss_fftr_cfg) VAMP_KISS_FFT_MALLOC (memneeded);
} else {
if (*lenmem >= memneeded)
st = (vamp_kiss_fftr_cfg) mem;
*lenmem = memneeded;
}
if (!st)
return NULL;
st->substate = (vamp_kiss_fft_cfg) (st + 1); /*just beyond vamp_kiss_fftr_state struct */
st->tmpbuf = (vamp_kiss_fft_cpx *) (((char *) st->substate) + subsize);
st->super_twiddles = st->tmpbuf + nfft;
vamp_kiss_fft_alloc(nfft, inverse_fft, st->substate, &subsize);
for (i = 0; i < nfft/2; ++i) {
double phase =
-3.14159265358979323846264338327 * ((double) (i+1) / nfft + .5);
if (inverse_fft)
phase *= -1;
kf_cexp (st->super_twiddles+i,phase);
}
return st;
}
inline void vamp_kiss_fftr_free(void *mem)
{
free(mem);
}
inline void vamp_kiss_fftr(vamp_kiss_fftr_cfg st,const vamp_kiss_fft_scalar *timedata,vamp_kiss_fft_cpx *freqdata)
{
/* input buffer timedata is stored row-wise */
int k,ncfft;
vamp_kiss_fft_cpx fpnk,fpk,f1k,f2k,tw,tdc;
if ( st->substate->inverse) {
fprintf(stderr,"kiss fft usage error: improper alloc\n");
exit(1);
}
ncfft = st->substate->nfft;
/*perform the parallel fft of two real signals packed in real,imag*/
vamp_kiss_fft( st->substate , (const vamp_kiss_fft_cpx*)timedata, st->tmpbuf );
/* The real part of the DC element of the frequency spectrum in st->tmpbuf
* contains the sum of the even-numbered elements of the input time sequence
* The imag part is the sum of the odd-numbered elements
*
* The sum of tdc.r and tdc.i is the sum of the input time sequence.
* yielding DC of input time sequence
* The difference of tdc.r - tdc.i is the sum of the input (dot product) [1,-1,1,-1...
* yielding Nyquist bin of input time sequence
*/
tdc.r = st->tmpbuf[0].r;
tdc.i = st->tmpbuf[0].i;
C_FIXDIV(tdc,2);
CHECK_OVERFLOW_OP(tdc.r ,+, tdc.i);
CHECK_OVERFLOW_OP(tdc.r ,-, tdc.i);
freqdata[0].r = tdc.r + tdc.i;
freqdata[ncfft].r = tdc.r - tdc.i;
freqdata[ncfft].i = freqdata[0].i = 0;
for ( k=1;k <= ncfft/2 ; ++k ) {
fpk = st->tmpbuf[k];
fpnk.r = st->tmpbuf[ncfft-k].r;
fpnk.i = - st->tmpbuf[ncfft-k].i;
C_FIXDIV(fpk,2);
C_FIXDIV(fpnk,2);
C_ADD( f1k, fpk , fpnk );
C_SUB( f2k, fpk , fpnk );
C_MUL( tw , f2k , st->super_twiddles[k-1]);
freqdata[k].r = HALF_OF(f1k.r + tw.r);
freqdata[k].i = HALF_OF(f1k.i + tw.i);
freqdata[ncfft-k].r = HALF_OF(f1k.r - tw.r);
freqdata[ncfft-k].i = HALF_OF(tw.i - f1k.i);
}
}
inline void vamp_kiss_fftri(vamp_kiss_fftr_cfg st,const vamp_kiss_fft_cpx *freqdata,vamp_kiss_fft_scalar *timedata)
{
/* input buffer timedata is stored row-wise */
int k, ncfft;
if (st->substate->inverse == 0) {
fprintf (stderr, "kiss fft usage error: improper alloc\n");
exit (1);
}
ncfft = st->substate->nfft;
st->tmpbuf[0].r = freqdata[0].r + freqdata[ncfft].r;
st->tmpbuf[0].i = freqdata[0].r - freqdata[ncfft].r;
C_FIXDIV(st->tmpbuf[0],2);
for (k = 1; k <= ncfft / 2; ++k) {
vamp_kiss_fft_cpx fk, fnkc, fek, fok, tmp;
fk = freqdata[k];
fnkc.r = freqdata[ncfft - k].r;
fnkc.i = -freqdata[ncfft - k].i;
C_FIXDIV( fk , 2 );
C_FIXDIV( fnkc , 2 );
C_ADD (fek, fk, fnkc);
C_SUB (tmp, fk, fnkc);
C_MUL (fok, tmp, st->super_twiddles[k-1]);
C_ADD (st->tmpbuf[k], fek, fok);
C_SUB (st->tmpbuf[ncfft - k], fek, fok);
st->tmpbuf[ncfft - k].i *= -1;
}
vamp_kiss_fft (st->substate, st->tmpbuf, (vamp_kiss_fft_cpx *) timedata);
}
#ifndef VAMP_KISSFFT_USE_CPP_LINKAGE
#ifdef __cplusplus
}
#endif
#endif
#ifdef VAMP_KISSFFT_USE_CPP_LINKAGE
#define VAMP_KISSFFT_USED_CPP_LINKAGE 1
#endif

7
src/vamp-sdk/vamp-sdk.h Normal file
View File

@ -0,0 +1,7 @@
#pragma once
#include "PluginBase.h"
#include "Plugin.h"
#include "RealTime.h"
#include "FFT.h"

352
src/vamp.h Normal file
View File

@ -0,0 +1,352 @@
#ifndef VAMP_HEADER_INCLUDED
#define VAMP_HEADER_INCLUDED
#ifdef __cplusplus
extern "C" {
#endif
/**
* Plugin API version. This is incremented when a change is made that
* changes the binary layout of the descriptor records. When this
* happens, there should be a mechanism for retaining compatibility
* with older hosts and/or plugins.
*
* See also the vampApiVersion field in the plugin descriptor, and the
* hostApiVersion argument to the vampGetPluginDescriptor function.
*/
#define VAMP_API_VERSION 2
/**
* C language API for Vamp plugins.
*
* This is the formal plugin API for Vamp. Plugin authors may prefer
* to use the C++ classes provided in the Vamp plugin SDK, instead of
* using this API directly. There is an adapter class provided that
* makes C++ plugins available using this C API with relatively little
* work, and the C++ headers are more thoroughly documented.
*
* IMPORTANT: The comments in this file summarise the purpose of each
* of the declared fields and functions, but do not provide a complete
* guide to their permitted values and expected usage. Please refer
* to the C++ headers in the Vamp plugin SDK for further details and
* plugin lifecycle documentation.
*/
typedef struct _VampParameterDescriptor
{
/** Computer-usable name of the parameter. Must not change. [a-zA-Z0-9_-] */
const char *identifier;
/** Human-readable name of the parameter. May be translatable. */
const char *name;
/** Human-readable short text about the parameter. May be translatable. */
const char *description;
/** Human-readable unit of the parameter. */
const char *unit;
/** Minimum value. */
float minValue;
/** Maximum value. */
float maxValue;
/** Default value. Plugin is responsible for setting this on initialise. */
float defaultValue;
/** 1 if parameter values are quantized to a particular resolution. */
int isQuantized;
/** Quantization resolution, if isQuantized. */
float quantizeStep;
/** Human-readable names of the values, if isQuantized. May be NULL. */
const char **valueNames;
} VampParameterDescriptor;
typedef enum
{
/** Each process call returns results aligned with call's block start. */
vampOneSamplePerStep,
/** Returned results are evenly spaced at samplerate specified below. */
vampFixedSampleRate,
/** Returned results have their own individual timestamps. */
vampVariableSampleRate
} VampSampleType;
typedef struct _VampOutputDescriptor
{
/** Computer-usable name of the output. Must not change. [a-zA-Z0-9_-] */
const char *identifier;
/** Human-readable name of the output. May be translatable. */
const char *name;
/** Human-readable short text about the output. May be translatable. */
const char *description;
/** Human-readable name of the unit of the output. */
const char *unit;
/** 1 if output has equal number of values for each returned result. */
int hasFixedBinCount;
/** Number of values per result, if hasFixedBinCount. */
unsigned int binCount;
/** Names of returned value bins, if hasFixedBinCount. May be NULL. */
const char **binNames;
/** 1 if each returned value falls within the same fixed min/max range. */
int hasKnownExtents;
/** Minimum value for a returned result in any bin, if hasKnownExtents. */
float minValue;
/** Maximum value for a returned result in any bin, if hasKnownExtents. */
float maxValue;
/** 1 if returned results are quantized to a particular resolution. */
int isQuantized;
/** Quantization resolution for returned results, if isQuantized. */
float quantizeStep;
/** Time positioning method for returned results (see VampSampleType). */
VampSampleType sampleType;
/** Sample rate of returned results, if sampleType is vampFixedSampleRate.
"Resolution" of result, if sampleType is vampVariableSampleRate. */
float sampleRate;
/** 1 if the returned results for this output are known to have a
duration field.
This field is new in Vamp API version 2; it must not be tested
for plugins that report an older API version in their plugin
descriptor.
*/
int hasDuration;
} VampOutputDescriptor;
typedef struct _VampFeature
{
/** 1 if the feature has a timestamp (i.e. if vampVariableSampleRate). */
int hasTimestamp;
/** Seconds component of timestamp. */
int sec;
/** Nanoseconds component of timestamp. */
int nsec;
/** Number of values. Must be binCount if hasFixedBinCount. */
unsigned int valueCount;
/** Values for this returned sample. */
float *values;
/** Label for this returned sample. May be NULL. */
char *label;
} VampFeature;
typedef struct _VampFeatureV2
{
/** 1 if the feature has a duration. */
int hasDuration;
/** Seconds component of duratiion. */
int durationSec;
/** Nanoseconds component of duration. */
int durationNsec;
} VampFeatureV2;
typedef union _VampFeatureUnion
{
// sizeof(featureV1) >= sizeof(featureV2) for backward compatibility
VampFeature v1;
VampFeatureV2 v2;
} VampFeatureUnion;
typedef struct _VampFeatureList
{
/** Number of features in this feature list. */
unsigned int featureCount;
/** Features in this feature list. May be NULL if featureCount is
zero.
If present, this array must contain featureCount feature
structures for a Vamp API version 1 plugin, or 2*featureCount
feature unions for a Vamp API version 2 plugin.
The features returned by an API version 2 plugin must consist
of the same feature structures as in API version 1 for the
first featureCount array elements, followed by featureCount
unions that contain VampFeatureV2 structures (or NULL pointers
if no V2 feature structures are present).
*/
VampFeatureUnion *features;
} VampFeatureList;
typedef enum
{
vampTimeDomain,
vampFrequencyDomain
} VampInputDomain;
typedef void *VampPluginHandle;
typedef struct _VampPluginDescriptor
{
/** API version with which this descriptor is compatible. */
unsigned int vampApiVersion;
/** Computer-usable name of the plugin. Must not change. [a-zA-Z0-9_-] */
const char *identifier;
/** Human-readable name of the plugin. May be translatable. */
const char *name;
/** Human-readable short text about the plugin. May be translatable. */
const char *description;
/** Human-readable name of plugin's author or vendor. */
const char *maker;
/** Version number of the plugin. */
int pluginVersion;
/** Human-readable summary of copyright or licensing for plugin. */
const char *copyright;
/** Number of parameter inputs. */
unsigned int parameterCount;
/** Fixed descriptors for parameter inputs. */
const VampParameterDescriptor **parameters;
/** Number of programs. */
unsigned int programCount;
/** Fixed names for programs. */
const char **programs;
/** Preferred input domain for audio input (time or frequency). */
VampInputDomain inputDomain;
/** Create and return a new instance of this plugin. */
VampPluginHandle (*instantiate)(const struct _VampPluginDescriptor *,
float inputSampleRate);
/** Destroy an instance of this plugin. */
void (*cleanup)(VampPluginHandle);
/** Initialise an instance following parameter configuration. */
int (*initialise)(VampPluginHandle,
unsigned int inputChannels,
unsigned int stepSize,
unsigned int blockSize);
/** Reset an instance, ready to use again on new input data. */
void (*reset)(VampPluginHandle);
/** Get a parameter value. */
float (*getParameter)(VampPluginHandle, int);
/** Set a parameter value. May only be called before initialise. */
void (*setParameter)(VampPluginHandle, int, float);
/** Get the current program (if programCount > 0). */
unsigned int (*getCurrentProgram)(VampPluginHandle);
/** Set the current program. May only be called before initialise. */
void (*selectProgram)(VampPluginHandle, unsigned int);
/** Get the plugin's preferred processing window increment in samples. */
unsigned int (*getPreferredStepSize)(VampPluginHandle);
/** Get the plugin's preferred processing window size in samples. */
unsigned int (*getPreferredBlockSize)(VampPluginHandle);
/** Get the minimum number of input channels this plugin can handle. */
unsigned int (*getMinChannelCount)(VampPluginHandle);
/** Get the maximum number of input channels this plugin can handle. */
unsigned int (*getMaxChannelCount)(VampPluginHandle);
/** Get the number of feature outputs (distinct sets of results). */
unsigned int (*getOutputCount)(VampPluginHandle);
/** Get a descriptor for a given feature output. Returned pointer
is valid only until next call to getOutputDescriptor for this
handle, or releaseOutputDescriptor for this descriptor. Host
must call releaseOutputDescriptor after use. */
VampOutputDescriptor *(*getOutputDescriptor)(VampPluginHandle,
unsigned int);
/** Destroy a descriptor for a feature output. */
void (*releaseOutputDescriptor)(VampOutputDescriptor *);
/** Process an input block and return a set of features. Returned
pointer is valid only until next call to process,
getRemainingFeatures, or cleanup for this handle, or
releaseFeatureSet for this feature set. Host must call
releaseFeatureSet after use. */
VampFeatureList *(*process)(VampPluginHandle,
const float *const *inputBuffers,
int sec,
int nsec);
/** Return any remaining features at the end of processing. */
VampFeatureList *(*getRemainingFeatures)(VampPluginHandle);
/** Release a feature set returned from process or getRemainingFeatures. */
void (*releaseFeatureSet)(VampFeatureList *);
} VampPluginDescriptor;
/** Get the descriptor for a given plugin index in this library.
Return NULL if the index is outside the range of valid indices for
this plugin library.
The hostApiVersion argument tells the library code the highest
Vamp API version supported by the host. The function should
return a plugin descriptor compatible with the highest API version
supported by the library that is no higher than that supported by
the host. Provided the descriptor has the correct vampApiVersion
field for its actual compatibility level, the host should be able
to do the right thing with it: use it if possible, discard it
otherwise.
This is the only symbol that a Vamp plugin actually needs to
export from its shared object; all others can be hidden. See the
accompanying documentation for notes on how to achieve this with
certain compilers.
*/
const VampPluginDescriptor *vampGetPluginDescriptor
(unsigned int hostApiVersion, unsigned int index);
/** Function pointer type for vampGetPluginDescriptor. */
typedef const VampPluginDescriptor *(*VampGetPluginDescriptorFunction)
(unsigned int, unsigned int);
#ifdef __cplusplus
}
#endif
#endif

7
subprojects/hack.wrap Normal file
View File

@ -0,0 +1,7 @@
[wrap-git]
url = https://gitcast.ru/chatlanin/hack.git
revision = master
[provide]
hack = hack_dep

208
test/main.cpp Normal file
View File

@ -0,0 +1,208 @@
#include "hack/logger/logger.hpp"
#include "vamp-hostsdk/PluginInputDomainAdapter.h"
#include "vamp-hostsdk/PluginBufferingAdapter.h"
#include "vamp-sdk/Plugin.h"
class MyPlugin : public Vamp::Plugin
{
public:
MyPlugin(float inputSampleRate);
virtual ~MyPlugin();
std::string getIdentifier() const;
std::string getName() const;
std::string getDescription() const;
std::string getMaker() const;
int getPluginVersion() const;
std::string getCopyright() const;
InputDomain getInputDomain() const;
size_t getPreferredBlockSize() const;
size_t getPreferredStepSize() const;
size_t getMinChannelCount() const;
size_t getMaxChannelCount() const;
ParameterList getParameterDescriptors() const;
float getParameter(std::string identifier) const;
void setParameter(std::string identifier, float value);
ProgramList getPrograms() const;
std::string getCurrentProgram() const;
void selectProgram(std::string name);
OutputList getOutputDescriptors() const;
bool initialise(size_t channels, size_t stepSize, size_t blockSize);
void reset();
FeatureSet process(const float *const *inputBuffers,
Vamp::RealTime timestamp);
FeatureSet getRemainingFeatures();
};
MyPlugin::MyPlugin(float inputSampleRate) : Plugin(inputSampleRate)
{
}
MyPlugin::~MyPlugin()
{
}
std::string MyPlugin::getIdentifier() const
{
return "myplugin";
}
std::string MyPlugin::getName() const
{
return "My Plugin";
}
std::string MyPlugin::getDescription() const
{
return "";
}
std::string MyPlugin::getMaker() const
{
return "";
}
int MyPlugin::getPluginVersion() const
{
return 1;
}
std::string MyPlugin::getCopyright() const
{
return "";
}
MyPlugin::InputDomain MyPlugin::getInputDomain() const
{
return TimeDomain;
}
size_t MyPlugin::getPreferredBlockSize() const
{
return 0;
}
size_t MyPlugin::getPreferredStepSize() const
{
return 0;
}
size_t MyPlugin::getMinChannelCount() const
{
return 1;
}
size_t MyPlugin::getMaxChannelCount() const
{
return 1;
}
MyPlugin::ParameterList MyPlugin::getParameterDescriptors() const
{
ParameterList list;
ParameterDescriptor d;
d.identifier = "parameter";
d.name = "Some Parameter";
d.description = "";
d.unit = "";
d.minValue = 0;
d.maxValue = 10;
d.defaultValue = 5;
d.isQuantized = false;
list.push_back(d);
return list;
}
float MyPlugin::getParameter(std::string identifier) const
{
if (identifier == "parameter")
return 5;
return 0;
}
void MyPlugin::setParameter(std::string identifier, float value)
{
if (identifier == "parameter") {
}
}
MyPlugin::ProgramList MyPlugin::getPrograms() const
{
ProgramList list;
return list;
}
std::string MyPlugin::getCurrentProgram() const
{
return "";
}
void MyPlugin::selectProgram(std::string name)
{
}
MyPlugin::OutputList MyPlugin::getOutputDescriptors() const
{
OutputList list;
OutputDescriptor d;
d.identifier = "output";
d.name = "My Output";
d.description = "";
d.unit = "";
d.hasFixedBinCount = true;
d.binCount = 1;
d.hasKnownExtents = false;
d.isQuantized = false;
d.sampleType = OutputDescriptor::OneSamplePerStep;
d.hasDuration = false;
list.push_back(d);
return list;
}
bool MyPlugin::initialise(size_t channels, size_t stepSize, size_t blockSize)
{
if (channels < getMinChannelCount() || channels > getMaxChannelCount()) return false;
return true;
}
void MyPlugin::reset()
{
}
MyPlugin::FeatureSet MyPlugin::process(const float *const *inputBuffers, Vamp::RealTime timestamp)
{
return FeatureSet();
}
MyPlugin::FeatureSet MyPlugin::getRemainingFeatures()
{
return FeatureSet();
}
auto main() -> int
{
float samplerate = 10.f;
MyPlugin* ch = new MyPlugin(samplerate);
Vamp::HostExt::PluginInputDomainAdapter* ia = new Vamp::HostExt::PluginInputDomainAdapter(ch);
ia->setProcessTimestampMethod(Vamp::HostExt::PluginInputDomainAdapter::ShiftData);
Vamp::HostExt::PluginBufferingAdapter* adapter = new Vamp::HostExt::PluginBufferingAdapter(ia);
int blocksize = adapter->getPreferredBlockSize();
if (!adapter->initialise(1, blocksize, blocksize))
hack::error()("Не инициализировался адаптер");
hack::log()("is ok");
}

8
test/meson.build Normal file
View File

@ -0,0 +1,8 @@
executable(
meson.project_name(),
'main.cpp',
dependencies : deps,
cpp_args: args,
include_directories : inc
)