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