initial commit
This commit is contained in:
commit
768471896a
4
.gitignore
vendored
Normal file
4
.gitignore
vendored
Normal file
@ -0,0 +1,4 @@
|
||||
build
|
||||
.cache
|
||||
subprojects/*
|
||||
!subprojects/hack.wrap
|
5
README.md
Normal file
5
README.md
Normal file
@ -0,0 +1,5 @@
|
||||
Это полностью переписаный на С++ vamp-plugin-sdk (https://vamp-plugins.org/code-doc/main.html)
|
||||
Оптимизирован только для linux. Он не будет работать (только если чудом) на других системах.
|
||||
|
||||
В идеале хотелось бы привести это к единому заголовочному файлу и возможно это случится...
|
||||
Если вы считаете нужным что-то добавить, убрать, поправить, переписать делайте это сами в своих форках и не спрашивайте меня ни о чем!
|
50
host/system.h
Normal file
50
host/system.h
Normal 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
40
host/test-c.c
Normal 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
863
host/vamp-simple-host.cpp
Normal 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
39
meson.build
Normal 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
23
run.sh
Executable 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
59
src/meson.build
Normal 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
195
src/vamp-hostsdk/Files.cpp
Normal 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
31
src/vamp-hostsdk/Files.h
Normal 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);
|
||||
};
|
722
src/vamp-hostsdk/PluginBufferingAdapter.cpp
Normal file
722
src/vamp-hostsdk/PluginBufferingAdapter.cpp
Normal 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;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
149
src/vamp-hostsdk/PluginBufferingAdapter.h
Normal file
149
src/vamp-hostsdk/PluginBufferingAdapter.h
Normal 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;
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
}
|
228
src/vamp-hostsdk/PluginChannelAdapter.cpp
Normal file
228
src/vamp-hostsdk/PluginChannelAdapter.cpp
Normal 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);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
105
src/vamp-hostsdk/PluginChannelAdapter.h
Normal file
105
src/vamp-hostsdk/PluginChannelAdapter.h
Normal 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;
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
}
|
424
src/vamp-hostsdk/PluginHostAdapter.cpp
Normal file
424
src/vamp-hostsdk/PluginHostAdapter.cpp
Normal 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 = "";
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
75
src/vamp-hostsdk/PluginHostAdapter.h
Normal file
75
src/vamp-hostsdk/PluginHostAdapter.h
Normal 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;
|
||||
};
|
||||
|
||||
}
|
482
src/vamp-hostsdk/PluginInputDomainAdapter.cpp
Normal file
482
src/vamp-hostsdk/PluginInputDomainAdapter.cpp
Normal 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);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
191
src/vamp-hostsdk/PluginInputDomainAdapter.h
Normal file
191
src/vamp-hostsdk/PluginInputDomainAdapter.h
Normal 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;
|
||||
};
|
||||
|
||||
}
|
||||
}
|
567
src/vamp-hostsdk/PluginLoader.cpp
Normal file
567
src/vamp-hostsdk/PluginLoader.cpp
Normal 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);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
235
src/vamp-hostsdk/PluginLoader.h
Normal file
235
src/vamp-hostsdk/PluginLoader.h
Normal 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;
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
}
|
948
src/vamp-hostsdk/PluginSummarisingAdapter.cpp
Normal file
948
src/vamp-hostsdk/PluginSummarisingAdapter.cpp
Normal 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();
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
||||
}
|
152
src/vamp-hostsdk/PluginSummarisingAdapter.h
Normal file
152
src/vamp-hostsdk/PluginSummarisingAdapter.h
Normal 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;
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
}
|
157
src/vamp-hostsdk/PluginWrapper.cpp
Normal file
157
src/vamp-hostsdk/PluginWrapper.cpp
Normal 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();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
88
src/vamp-hostsdk/PluginWrapper.h
Normal file
88
src/vamp-hostsdk/PluginWrapper.h
Normal 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
128
src/vamp-hostsdk/Window.h
Normal 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)
|
16
src/vamp-hostsdk/acsymbols.cpp
Normal file
16
src/vamp-hostsdk/acsymbols.cpp
Normal 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
114
src/vamp-hostsdk/host-c.cpp
Normal 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
60
src/vamp-hostsdk/host-c.h
Normal 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
|
53
src/vamp-hostsdk/vamp-hostsdk.h
Normal file
53
src/vamp-hostsdk/vamp-hostsdk.h
Normal 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
213
src/vamp-sdk/FFT.cpp
Normal 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
159
src/vamp-sdk/FFT.h
Normal 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
412
src/vamp-sdk/Plugin.h
Normal 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;
|
||||
};
|
||||
|
||||
}
|
964
src/vamp-sdk/PluginAdapter.cpp
Normal file
964
src/vamp-sdk/PluginAdapter.cpp
Normal 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;
|
||||
|
||||
}
|
76
src/vamp-sdk/PluginAdapter.h
Normal file
76
src/vamp-sdk/PluginAdapter.h
Normal 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
218
src/vamp-sdk/PluginBase.h
Normal 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
199
src/vamp-sdk/RealTime.cpp
Normal 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
114
src/vamp-sdk/RealTime.h
Normal 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);
|
||||
|
||||
}
|
16
src/vamp-sdk/acsymbols.cpp
Normal file
16
src/vamp-sdk/acsymbols.cpp
Normal 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) { }
|
443
src/vamp-sdk/ext/vamp_kiss_fft.h
Normal file
443
src/vamp-sdk/ext/vamp_kiss_fft.h
Normal 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
|
74
src/vamp-sdk/ext/vamp_kiss_fft_guts.h
Normal file
74
src/vamp-sdk/ext/vamp_kiss_fft_guts.h
Normal 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) )
|
161
src/vamp-sdk/ext/vamp_kiss_fftr.h
Normal file
161
src/vamp-sdk/ext/vamp_kiss_fftr.h
Normal 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
7
src/vamp-sdk/vamp-sdk.h
Normal 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
352
src/vamp.h
Normal 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
7
subprojects/hack.wrap
Normal 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
208
test/main.cpp
Normal 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
8
test/meson.build
Normal file
@ -0,0 +1,8 @@
|
||||
executable(
|
||||
meson.project_name(),
|
||||
'main.cpp',
|
||||
dependencies : deps,
|
||||
cpp_args: args,
|
||||
include_directories : inc
|
||||
)
|
||||
|
Loading…
Reference in New Issue
Block a user