summaryrefslogtreecommitdiff
path: root/NT/src/vampHost.cpp
diff options
context:
space:
mode:
authorComment <tim@gray.(none)>2014-01-29 23:58:55 +0000
committerComment <tim@gray.(none)>2014-01-29 23:58:55 +0000
commit89961817f408e921de2c1be6197e2b1ee0f5df98 (patch)
tree983f41082fbe2877d9eed913d07784f157d5ced4 /NT/src/vampHost.cpp
parent90a237397507bda5a8194b9a7c9982454cc79718 (diff)
NT audio framework
Diffstat (limited to 'NT/src/vampHost.cpp')
-rw-r--r--NT/src/vampHost.cpp416
1 files changed, 416 insertions, 0 deletions
diff --git a/NT/src/vampHost.cpp b/NT/src/vampHost.cpp
new file mode 100644
index 0000000..a70e795
--- /dev/null
+++ b/NT/src/vampHost.cpp
@@ -0,0 +1,416 @@
+#include "vampHost.h"
+
+
+void vampHost::printFeatures(int frame, int sr, int output,
+ Plugin::FeatureSet features, ostream& out, bool useFrames)
+{
+ if (features[output].size()) {
+ cout << "." << features[output].size();
+ }
+ 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 << displayFrame;
+
+ if (features[output][i].hasDuration) {
+ displayFrame = RealTime::realTime2Frame
+ (features[output][i].duration, sr);
+ out << "," << displayFrame;
+ }
+
+ out << ":";
+
+ } else {
+
+ RealTime rt = RealTime::frame2RealTime(frame, sr);
+
+ if (features[output][i].hasTimestamp) {
+ rt = features[output][i].timestamp;
+ }
+
+ out << rt.toString();
+
+ if (features[output][i].hasDuration) {
+ rt = features[output][i].duration;
+ out<< "," << rt.toString();
+ }
+
+ out << ":";
+ }
+
+ for (unsigned int j = 0; j < features[output][i].values.size(); ++j) {
+ out<< " " << features[output][i].values[j];
+ }
+ out << " " << features[output][i].label;
+
+ out << endl;
+ }
+
+}
+
+
+
+void vampHost::rotorGetFeatures(int frame, int sr, int output,Plugin::FeatureSet features, vector<double>& out, double& progress)
+{
+ if (features[output].size()) {
+ cout << "." << features[output].size();
+ }
+ for (unsigned int i = 0; i < features[output].size(); ++i) {
+
+
+
+ int displayFrame = frame;
+
+ if (features[output][i].hasTimestamp) {
+ displayFrame = RealTime::realTime2Frame
+ (features[output][i].timestamp, sr);
+ }
+
+ cout << displayFrame;
+
+
+ cout << endl;
+ }
+
+}
+
+
+
+void vampHost::getTimestamps(int output,Plugin::FeatureSet features, vector<double>& out){
+
+ /*
+ vamp-simple-host qm-vamp-plugins:qm-tempotracker 01.wav
+
+ 0.046439908: 156.61 bpm
+ 0.429569160: 156.61 bpm
+ 0.812698412: 161.50 bpm
+ 1.184217686: 152.00 bpm
+
+
+ vamp-simple-host qm-vamp-plugins:qm-segmenter 01.wav
+
+ 0.000000000: 4 4
+ 23.800000000: 6 6
+ 44.600000000: 5 5
+ 55.000000000: 7 7
+ 72.800000000: 1 1
+ 90.600000000: 2 2
+ 109.200000000: 5 5
+ 116.000000000: 3 3
+ 143.800000000: 5 5
+ 153.400000000: 3 3
+ 163.000000000: 8 8
+
+ seems to be FP seconds then another metric
+ for now we can just take the first part
+
+ features[output][i].timestamp is of type RealTime: represents time values to nanosecond precision
+ int sec;
+ int nsec;
+ 1 sec = 10^9 nanosec
+
+ actually maybe this would be the way to go for rotor- avoiding rounding errors etc
+ for now - ideally will get a double representation
+
+ features[output][i].values is a vector of doubles + a description
+ WE DON'T CARE ABOUT ANYTHING <.01 seconds
+
+ static long realTime2Frame(const RealTime &r, unsigned int sampleRate);
+
+ get a vector of doubles out, using frames, presuming data has a timestamp
+
+
+ this is crashing with "Aborted (core dumped)"
+ if we check for timestamp
+
+ */
+
+ cout << "." << features[output].size();
+
+ //if (!features[output][0].hasTimestamp) {
+ // cerr << output << " channel, getTimestamps: error, featureset doesn't support timestamp" << endl;
+ //}_
+ //else {
+ for (unsigned int i = 0; i < features[output].size(); ++i) {
+ out.push_back( ((double)RealTime::realTime2Frame(features[output][i].timestamp, 1000))*.001f);
+ cout << "feature found.\n";
+ }
+ //}
+}
+
+bool vampHost::Analyser::init(const string &soname,const string &id,const int &_channels,const int &_bits,const int &_samples,const int &_rate,int _outputNo,const map<string,float> &params){
+
+ //stuff that only happens once
+ channels =_channels;
+ samples=_samples;
+ rate=_rate;
+ bits=_bits;
+ outputNo=_outputNo;
+ //output=_output;
+
+ //http://www.mega-nerd.com/libsndfile/api.html#note1
+ //libsndfile returns -1..1 for fp data
+ bytes=(bits>>3);
+ stride=channels*bytes;
+ scale=(1.0/pow(2.0,bits));
+
+ features.clear(); //in case of reuse
+ features[0.0]=feature();
+
+ loader = PluginLoader::getInstance();
+ key = loader->composePluginKey(soname, id);
+ plugin = loader->loadPlugin(key, _rate, PluginLoader::ADAPT_ALL_SAFE);
+ if (!plugin) {
+ cerr << ": ERROR: Failed to load plugin \"" << id
+ << "\" from library \"" << soname << "\"" << endl;
+ return false;
+ }
+
+ cerr << "Running plugin: \"" << plugin->getIdentifier() << "\"... Domain:";
+
+ if (plugin->getInputDomain() == Plugin::FrequencyDomain) {
+ cerr << "frequency" << endl;
+ }
+ else {
+
+ cerr << "time" << endl;
+
+ }
+
+ blockSize = plugin->getPreferredBlockSize();
+ 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;
+ }
+ overlapSize = blockSize - stepSize;
+ currentStep = 0;
+ finalStepsRemaining = max(1, (blockSize / stepSize) - 1); // at end of file, this many part-silent frames needed after we hit EOF
+
+ 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;
+
+ RealTime rt;
+ PluginWrapper *wrapper = 0;
+ RealTime adjustment = RealTime::zeroTime;
+
+ if (outputs.empty()) {
+ cerr << "ERROR: Plugin has no outputs!" << endl;
+ return false;
+ }
+
+ 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;
+ return false;
+ }
+ }
+ else {
+ if (int(outputs.size()) <= outputNo) {
+ cerr << "ERROR: Output " << outputNo << " requested, but plugin has only " << outputs.size() << " output(s)" << endl;
+ return false;
+ }
+ }
+ od = outputs[outputNo];
+ cerr << "Output number "<<outputNo<<": \"" << od.identifier << "\"" << endl;
+
+
+ for (auto i:params){
+ plugin->setParameter(i.first,i.second);
+ cerr << "Set plugin parameter: "<<i.first<<" : "<<i.second<<endl;
+ }
+
+ if (!plugin->initialise(channels, stepSize, blockSize)) {
+ cerr << "ERROR: Plugin initialise (channels = " << channels
+ << ", stepSize = " << stepSize << ", blockSize = "
+ << blockSize << ") failed." << endl;
+ return false;
+ }
+
+ cerr << "Vamphost Plugin initialised: (channels = " << channels
+ << ", stepSize = " << stepSize << ", blockSize = "
+ << blockSize << ")" << endl;
+
+ wrapper = dynamic_cast<PluginWrapper *>(plugin);
+ if (wrapper) {
+ // See documentation for
+ // PluginInputDomainAdapter::getTimestampAdjustment
+ PluginInputDomainAdapter *ida =wrapper->getWrapper<PluginInputDomainAdapter>();
+ if (ida) adjustment = ida->getTimestampAdjustment();
+ }
+
+ //everything is prepared to start consuming data in blocks
+
+ in_block=0;
+ blocks_processed=0;
+ currentStep=0;
+
+ featureNo=0;
+
+ return true;
+}
+void vampHost::Analyser::process_frame(uint8_t *data,int samples_in_frame){
+
+ int sample=0;
+
+ uint16_t *_data=(uint16_t*)data;
+ //process the whole frame which may be f>1<f blocks
+ //when the frame is finished leave the partial block for the next frame
+ while(sample<samples_in_frame) {
+ while(sample<samples_in_frame&&in_block<blockSize) {
+ for (int i=0;i<channels;i++) {
+ //unsigned int this_val=0;
+ // this_val+=data[(sample*stride)+(i*bytes)+j]<<((1-j)*8);
+ //}
+ //plugbuf[i][in_block]=((double)((int16_t)this_val))*scale;
+ plugbuf[i][in_block]=((float)_data[sample])*scale;
+ }
+ in_block++;
+ sample++;
+ }
+ if (in_block==blockSize) {
+ //block is ready to be processed
+ //cerr<<plugin->getIdentifier()<<" processed block "<<blocks_processed<<endl;
+
+ //I /think/ that the vamp plugin keeps processing through the plugbuf until it encounters 0s
+ rt = RealTime::frame2RealTime(currentStep * stepSize, rate); //48000); //setting different rate doesn't affect it
+
+
+ Plugin::FeatureSet feat=plugin->process(plugbuf, rt);
+
+ double t;
+
+ for (unsigned int i = 0; i < feat[outputNo].size(); ++i) {
+ feature f;
+ f.number=featureNo;
+ f.values=feat[outputNo][i].values;
+ //fix for plugins that don't set timestamp properly
+ t=((double)feat[outputNo][i].timestamp.sec)+(((double)feat[outputNo][i].timestamp.nsec)*.000000001);
+ if (t<.01) t=((rt.sec)+(rt.nsec)*.000000001);
+ features[t]=f;
+ featureNo++;
+ }
+
+ //if (feat[outputNo].size()>0) cerr<<"vamphost: "<<t<<":"<<features.size()<<" features"<<endl;
+
+ //shunt it down
+ for (int i=0;i<blockSize-stepSize;i++){
+ for (int j=0;j<channels;j++){
+ plugbuf[j][i]=plugbuf[j][i+stepSize];
+ }
+ }
+
+ //if (feat[outputNo].size()>0) cerr<<plugin->getIdentifier()<<" step:"<<currentStep<<" sample:"<<currentStep * stepSize<<" features:"<<features.size()<<endl;
+
+ in_block-=stepSize;
+ currentStep++; //WTF this number does not increase when called the 2nd way
+ }
+ }
+}
+void vampHost::Analyser::cleanup(){
+ //process final block
+ while(in_block<blockSize) {
+ for (int i=0;i<channels;i++) {
+ plugbuf[i][in_block]=0.0;
+ }
+ in_block++;
+ }
+
+ rt = RealTime::frame2RealTime(currentStep * stepSize, rate); // //setting different
+
+ Plugin::FeatureSet feat=plugin->process(plugbuf, rt);
+
+ for (unsigned int i = 0; i < feat[outputNo].size(); ++i) {
+ feature f;
+ f.number=featureNo;
+ f.values=feat[outputNo][i].values;
+ features[((double)feat[outputNo][i].timestamp.sec)+(((double)feat[outputNo][i].timestamp.nsec)*.000000001)]=f;
+ featureNo++;
+ }
+
+ feat=plugin->getRemainingFeatures();
+
+ for (unsigned int i = 0; i < feat[outputNo].size(); ++i) {
+ feature f;
+ f.number=featureNo;
+ f.values=feat[outputNo][i].values;
+ features[((double)feat[outputNo][i].timestamp.sec)+(((double)feat[outputNo][i].timestamp.nsec)*.000000001)]=f;
+ featureNo++;
+ }
+
+ //always make a final blank feature at the end
+ feature f;
+ f.number=featureNo;
+ f.values={};
+ features[((double)rt.sec)+(((double)rt.nsec)*.000000001f)]=f;
+
+ //cerr<<plugin->getIdentifier()<<" found "<<(features.size()-1)<<" features"<<endl;
+ //deal with left over data?
+ for (int c = 0; c < channels; ++c) {
+ delete[] plugbuf[c];
+ }
+ delete[] plugbuf;
+ delete plugin;
+}
+double vampHost::Analyser::get_value(const double &time) {
+ if (features.size()) {
+ auto i=features.upper_bound(time); //the first element in the container whose key is considered to go after k
+ if (i!=features.end()){
+ double uk=i->first;
+ double v1,v2;
+ v1=v2=0.0;
+ if (i->second.values.size()) v2=i->second.values[0];
+ i--;
+ double lk=i->first;
+ if (i->second.values.size()) v1=i->second.values[0];
+
+ return ((((time-lk)/(uk-lk))*(v2-v1))+v1);
+ }
+ }
+ return 0.0;
+}