summaryrefslogtreecommitdiff
path: root/rotord
diff options
context:
space:
mode:
Diffstat (limited to 'rotord')
-rwxr-xr-xrotord/rotor.h76
-rwxr-xr-xrotord/rotord.cpp31
-rw-r--r--rotord/vampHost.cpp226
-rw-r--r--rotord/vampHost.h16
4 files changed, 299 insertions, 50 deletions
diff --git a/rotord/rotor.h b/rotord/rotor.h
index 594465d..8d4b712 100755
--- a/rotord/rotor.h
+++ b/rotord/rotor.h
@@ -85,17 +85,24 @@ get progress
#include "Poco/Mutex.h"
#include "Poco/Random.h"
#include "Poco/AutoPtr.h"
+#include "Poco/File.h"
#include <iostream>
using Poco::UUID;
using Poco::UUIDGenerator;
-#define ROTOR_READY 0
-#define ROTOR_ANALYSING_AUDIO 1
-#define ROTOR_CREATING_PREVIEW 2
-#define ROTOR_RENDERING 3
-
namespace Rotor {
+#define IDLE 0
+#define ANALYSING_AUDIO 1
+#define AUDIO_READY 2
+#define CREATING_PREVIEW 3
+#define PREVIEW_READY 4
+#define RENDERING 5
+#define RENDER_READY 6
+
+#define ANALYSE_AUDIO 0
+#define PREVIEW 1
+#define RENDER 2
//forward declaration
class Node;
@@ -110,41 +117,66 @@ namespace Rotor {
int num_performances;
int num_clips;
};
+ class Command_response{
+ public:
+ Command_response() { status=HTTPResponse::HTTP_OK; }
+ string description;
+ HTTPResponse::HTTPStatus status;
+ };
class Render_context: public Poco::Task { //Poco task object
//manages a 'patchbay'
//high level interfaces for the wizard
//and low level interface onto the graph
public:
- Render_context(const std::string& name): Task(name) {
+ Render_context(const std::string& name): Task(name) {
+ state=IDLE;
};
void runTask() {
while (!isCancelled()) {
mutex.lock();
if (work_queue.size()){
- std::string out=name()+": "+work_queue[0]+"\n";
- printf(out.c_str());
+ int cmd=work_queue[0];
work_queue.pop_front();
+ if (cmd==ANALYSE_AUDIO) {
+
+ }
}
mutex.unlock();
sleep(100);
}
printf("Rotor: stopping thread\n");
}
- void add_queue(std::string item) {
+ void add_queue(int item) {
mutex.lock();
work_queue.push_back(item);
mutex.unlock();
}
- std::string session_command(const std::vector<std::string>& command){
- string response;
+ Command_response session_command(const std::vector<std::string>& command){
+ //method,id,command1,{command2,}{body}
+ //here we allow the controlling server to communicate with running tasks
+ Command_response response;
mutex.lock();
- if (command[1]=="audio") {
- if (command[0]=="PUT") {
- //get audio file location and initiate analysis
- if (command.size()>1) {
- //pass message to task
- work_queue.push_back(command[2]);
- response="<status>1</status>\n";
+ if (command[2]=="audio") {
+ if (command[0]=="PUT") { //get audio file location and initiate analysis
+ if (command.size()>2) {
+ if (state==IDLE) {
+ //check file exists
+ Poco::File f=Poco::File(command[3]);
+ if (f.exists()) {
+ //pass to worker thread ??if engine is ready?? ??what if engine has finished but results aren't read??
+ audio_filename=command[3]; //for now, store session variables in memory
+ work_queue.push_back(ANALYSE_AUDIO);
+ response.description="<status>Starting audio analysis: "+command[3]+"</status>\n";
+ }
+ else {
+ response.status=HTTPResponse::HTTP_NOT_FOUND;
+ response.description="<status>File "+command[3]+" not found</status>\n";
+ }
+ }
+ else {
+ response.status=HTTPResponse::HTTP_BAD_REQUEST;
+ response.description="<status>Rotor: session busy</status>\n";
+ }
}
}
}
@@ -161,11 +193,11 @@ namespace Rotor {
int load_video(int num,string filename); //can be performance or clip
private:
- int status;
+ int state;
float progress; //for a locking process: audio analysis or rendering
- std::deque<std::string> work_queue;
-
- Poco::Mutex mutex; //access to notification queue
+ std::deque<int> work_queue;
+ Poco::Mutex mutex; //lock for access from parent thread
+ std::string audio_filename;
};
class Input{
public:
diff --git a/rotord/rotord.cpp b/rotord/rotord.cpp
index c4103e3..21ce639 100755
--- a/rotord/rotord.cpp
+++ b/rotord/rotord.cpp
@@ -69,7 +69,7 @@ void AudioAnalyserHandler::handleRequest(HTTPServerRequest& request,HTTPServerRe
ostr << "</head>";
ostr << "<body><p style=\"text-align: center; "
"font-size: 48px;\">";
- vampHost::runPlugin("",settings.soname,settings.filtername, "",0, settings.inputFile, ostr,true);
+ vampHost::runPlugin("",settings.soname,settings.filtername, "",0, settings.inputFile, ostr,true);
ostr << "</p></body></html>";
}
@@ -187,36 +187,27 @@ HTTPRequestHandler* RotorRequestHandlerFactory::createRequestHandler(const HTTPS
content="<status>Rotor: render context invoked with no command</status>\n";
}
}
- else {
- //in a way the rest of this stuff happens inside the thread
- //its not elegant to decipher the message just to assemble another message to decipher elsewhere!
- //pass a message object with the command type and contents selected?
- //every action will have a seperate work function in the render_context
- //some of these functions have to return data immediately- maybe a queue isn't the ideal model
- //what's the best way to seperate these?
- //could have a seperate mutex for the graph
- //all the get methods want to get something from the render context and express it in xml.
- //what is the best way...
- //should the processing be done inside the render context - I think so
-
- //++++ processing inside object
- //---- processing occurring in different places
-
- //the idea of command queueing won't work
-
+ else { //session modifier command- to be passed to render context
+ //some commands need to return error codes
+ //ie where the audio file isn't found
+ //on the other hand, some commands need to know state of the renderer?
std::string s;
std::ostringstream os;
os<<request.stream().rdbuf();
s=os.str();
- vector<string> sc;
+ vector<string> sc; //method,id,command1,{command2,}{body}
sc.push_back(request.getMethod());
for (auto& i: command){
sc.push_back(i);
}
sc.push_back(s);
- content=((Poco::AutoPtr<Rotor::Render_context>)task)->session_command(command);
+ Rotor::Command_response response=((Poco::AutoPtr<Rotor::Render_context>)task)->session_command(sc);
+
+ content=response.description;
+ status=response.status;
+
}
}
}
diff --git a/rotord/vampHost.cpp b/rotord/vampHost.cpp
index 54ce77b..c7e5691 100644
--- a/rotord/vampHost.cpp
+++ b/rotord/vampHost.cpp
@@ -7,7 +7,7 @@ int vampHost::runPlugin(string myname, string soname, string id, string output,
PluginLoader *loader = PluginLoader::getInstance();
PluginLoader::PluginKey key = loader->composePluginKey(soname, id);
-
+
SNDFILE *sndfile;
SF_INFO sfinfo;
memset(&sfinfo, 0, sizeof(SF_INFO));
@@ -118,7 +118,7 @@ int vampHost::runPlugin(string myname, string soname, string id, string output,
if (int(outputs.size()) <= outputNo) {
cerr << "ERROR: Output " << outputNo << " requested, but plugin has only " << outputs.size() << " output(s)" << endl;
goto done;
- }
+ }
}
od = outputs[outputNo];
@@ -139,7 +139,7 @@ int vampHost::runPlugin(string myname, string soname, string id, string output,
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 {
@@ -259,5 +259,221 @@ void vampHost::printFeatures(int frame, int sr, int output,
out << endl;
}
-
-} \ No newline at end of file
+
+}
+
+int vampHost::QMBeats::process(const string inputFile){
+ //vampHost::runPlugin("",settings.soname,settings.filtername, "",0, settings.inputFile, ostr,true);
+ //would run the plugin, outputting progress to cerr and the data to ostr
+ //
+ //we want to run a specific plugin, outputting progress to a mutex-protected passed variable
+ //and ultimately passing the data back as a string
+ //get the progress as a float
+ //how to handle errors?
+ string soname="qm-vamp-plugins";
+ string filtername="qm-tempotracker";
+
+ 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(inputFile.c_str(), SFM_READ, &sfinfo);
+ if (!sndfile) {
+ cerr << myname << ": ERROR: Failed to open input file \""
+ << inputFile << "\": " << sf_strerror(sndfile) << endl;
+ 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);
+ 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);
+
+ vampHost::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) {
+ cerr << "\r" << _progress << "%";
+ }
+ mutex.lock();
+ progress = (float(currentStep * stepSize) / sfinfo.frames) * 100.f;
+ mutex.unlock();
+ }
+
+ ++currentStep;
+
+ } while (finalStepsRemaining > 0);
+
+ cerr << "\rDone" << endl;
+
+ rt = RealTime::frame2RealTime(currentStep * stepSize, sfinfo.samplerate);
+
+ vampHost::printFeatures(RealTime::realTime2Frame(rt + adjustment, sfinfo.samplerate),sfinfo.samplerate, outputNo,plugin->getRemainingFeatures(), out, useFrames);
+
+ returnValue = 0;
+
+done:
+ delete plugin;
+ sf_close(sndfile);
+ return returnValue;
+
+ return 0;
+}
diff --git a/rotord/vampHost.h b/rotord/vampHost.h
index 03a375e..d4ae1ac 100644
--- a/rotord/vampHost.h
+++ b/rotord/vampHost.h
@@ -2,6 +2,8 @@
#include <vamp-hostsdk/PluginInputDomainAdapter.h>
#include <vamp-hostsdk/PluginLoader.h>
+#include "Poco/Mutex.h"
+
#include <iostream>
#include <fstream>
#include <set>
@@ -26,7 +28,7 @@ using Vamp::HostExt::PluginInputDomainAdapter;
#define HOST_VERSION "1.5"
namespace vampHost {
-
+
class Settings{
public:
Settings(string _so="",string _filter="",string _input="") {
@@ -38,9 +40,17 @@ namespace vampHost {
string filtername;
string inputFile;
};
+ class QMBeats{
+ public:
+ int process(const string soundfile);
+ float getProgress();
+ private:
+ float progress;
+ Poco::Mutex mutex; //lock for progress data
+ };
-
+ string getQMBeats(const string soundfile);
void printFeatures(int, int, int, Plugin::FeatureSet, ostream &, bool frames);
int runPlugin(string myname, string soname, string id, string output,int outputNo, string inputFile, ostream& out, bool frames);
-} \ No newline at end of file
+}