summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorTim Redfern <tim@herge.(none)>2013-02-15 17:02:12 +0000
committerTim Redfern <tim@herge.(none)>2013-02-15 17:02:12 +0000
commit09c827e0b4aa62401ac1ed84107bd5570b2580be (patch)
tree15ab8ba28f6f569aa0f2c1b897c8a5340c8c66aa
parent9f746d958099458dc11afdf410fb0b24ee510cac (diff)
adding vamp
-rw-r--r--rotord/Makefile2
-rw-r--r--rotord/rotord.cpp16
-rw-r--r--rotord/rotord.h11
-rw-r--r--rotord/system.h75
-rw-r--r--rotord/vampHost.cpp281
-rw-r--r--rotord/vampHost.h34
6 files changed, 415 insertions, 4 deletions
diff --git a/rotord/Makefile b/rotord/Makefile
index 9098836..d828a1c 100644
--- a/rotord/Makefile
+++ b/rotord/Makefile
@@ -2,7 +2,7 @@
MY_CFLAGS =
# The linker options.
-MY_LIBS = -lPocoNet -lPocoXML -lPocoUtil -lPocoFoundation
+MY_LIBS = -lPocoNet -lPocoXML -lPocoUtil -lPocoFoundation -lvamp-hostsdk -lsndfile
# The pre-processor options used by the cpp (man cpp for more).
CPPFLAGS = -Wall
diff --git a/rotord/rotord.cpp b/rotord/rotord.cpp
index 965d290..315e856 100644
--- a/rotord/rotord.cpp
+++ b/rotord/rotord.cpp
@@ -43,10 +43,20 @@ HTTPRequestHandler* RotorRequestHandlerFactory::createRequestHandler(const HTTPS
app.logger().information("Request from "+ request.clientAddress().toString()+" "+ostr.str()+" segments: "+out);
- if (segments.size() == 0)
+ if (segments.size() == 0) {
return new RotorRequestHandler(_format);
- else
- return 0;
+ }
+ else if (segments[0]=="vamp") {
+ // vamp/plugin/filter/filename
+ // how do deal with error condition?
+ //string audioData=runPlugin(string myname, string soname, string id,
+ // string output, int outputNo, string wavname,
+ // string outfilename, bool useFrames);
+ return 0;
+ }
+ else {
+ return 0;
+ }
}
diff --git a/rotord/rotord.h b/rotord/rotord.h
index 41c97d4..6def0a8 100644
--- a/rotord/rotord.h
+++ b/rotord/rotord.h
@@ -38,6 +38,8 @@ using Poco::Util::OptionSet;
using Poco::Util::OptionCallback;
using Poco::Util::HelpFormatter;
+#include "vampHost.h"
+
class RotorRequestHandler: public HTTPRequestHandler
{
public:
@@ -47,6 +49,15 @@ class RotorRequestHandler: public HTTPRequestHandler
std::string _format;
};
+class RotorAudioAnalyserHandler: public HTTPRequestHandler
+{
+ public:
+ RotorAudioAnalyserHandler(const std::string& format);
+ void handleRequest(HTTPServerRequest& request,HTTPServerResponse& response);
+ private:
+ std::string _format;
+};
+
class RotorRequestHandlerFactory: public HTTPRequestHandlerFactory
{
public:
diff --git a/rotord/system.h b/rotord/system.h
new file mode 100644
index 0000000..15aa8c1
--- /dev/null
+++ b/rotord/system.h
@@ -0,0 +1,75 @@
+/* -*- 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_
+
+#ifdef _WIN32
+
+#include <windows.h>
+
+#define DLOPEN(a,b) LoadLibrary((a).c_str())
+#define DLSYM(a,b) GetProcAddress((HINSTANCE)(a),(b))
+#define DLCLOSE(a) FreeLibrary((HINSTANCE)(a))
+#define DLERROR() ""
+
+#define PLUGIN_SUFFIX "dll"
+
+#else
+
+#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()
+
+#ifdef __APPLE__
+
+#define PLUGIN_SUFFIX "dylib"
+#define HAVE_OPENDIR 1
+
+#else
+
+#define PLUGIN_SUFFIX "so"
+#define HAVE_OPENDIR 1
+
+#endif /* __APPLE__ */
+
+#endif /* ! _WIN32 */
+
+#endif
+
diff --git a/rotord/vampHost.cpp b/rotord/vampHost.cpp
new file mode 100644
index 0000000..8423c54
--- /dev/null
+++ b/rotord/vampHost.cpp
@@ -0,0 +1,281 @@
+#include "vampHost.h"
+
+int vampHost::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;
+
+ 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);
+
+ printFeatures
+ (RealTime::realTime2Frame(rt + adjustment, sfinfo.samplerate),
+ sfinfo.samplerate, outputNo, plugin->process(plugbuf, rt),
+ out, useFrames);
+
+ if (sfinfo.frames > 0){
+ int pp = progress;
+ progress = lrintf((float(currentStep * stepSize) / sfinfo.frames) * 100.f);
+ if (progress != pp && out) {
+ cerr << "\r" << progress << "%";
+ }
+ }
+
+ ++currentStep;
+
+ } while (finalStepsRemaining > 0);
+
+ if (out) cerr << "\rDone" << endl;
+
+ rt = RealTime::frame2RealTime(currentStep * stepSize, sfinfo.samplerate);
+
+ printFeatures(RealTime::realTime2Frame(rt + adjustment, sfinfo.samplerate),
+ sfinfo.samplerate, outputNo,
+ plugin->getRemainingFeatures(), out, useFrames);
+
+ returnValue = 0;
+
+done:
+ delete plugin;
+ if (out) {
+ out->close();
+ delete out;
+ }
+ sf_close(sndfile);
+ return returnValue;
+}
+
+void vampHost::printFeatures(int frame, int sr, int output,
+ Plugin::FeatureSet features, ofstream *out, bool useFrames)
+{
+ for (unsigned int i = 0; i < features[output].size(); ++i) {
+
+ if (useFrames) {
+
+ int displayFrame = frame;
+
+ if (features[output][i].hasTimestamp) {
+ displayFrame = RealTime::realTime2Frame
+ (features[output][i].timestamp, sr);
+ }
+
+ (out ? *out : cout) << displayFrame;
+
+ if (features[output][i].hasDuration) {
+ displayFrame = RealTime::realTime2Frame
+ (features[output][i].duration, sr);
+ (out ? *out : cout) << "," << displayFrame;
+ }
+
+ (out ? *out : cout) << ":";
+
+ } else {
+
+ RealTime rt = RealTime::frame2RealTime(frame, sr);
+
+ if (features[output][i].hasTimestamp) {
+ rt = features[output][i].timestamp;
+ }
+
+ (out ? *out : cout) << rt.toString();
+
+ if (features[output][i].hasDuration) {
+ rt = features[output][i].duration;
+ (out ? *out : cout) << "," << rt.toString();
+ }
+
+ (out ? *out : cout) << ":";
+ }
+
+ for (unsigned int j = 0; j < features[output][i].values.size(); ++j) {
+ (out ? *out : cout) << " " << features[output][i].values[j];
+ }
+ (out ? *out : cout) << " " << features[output][i].label;
+
+ (out ? *out : cout) << endl;
+ }
+} \ No newline at end of file
diff --git a/rotord/vampHost.h b/rotord/vampHost.h
new file mode 100644
index 0000000..e46520c
--- /dev/null
+++ b/rotord/vampHost.h
@@ -0,0 +1,34 @@
+#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"
+
+namespace vampHost {
+
+
+ void printFeatures(int, int, int, Plugin::FeatureSet, ofstream *, bool frames);
+ int runPlugin(string myname, string soname, string id, string output,int outputNo, string inputFile, string outfilename, bool frames);
+
+} \ No newline at end of file