From c5085b5a31c1e25bf83ae910710996863531f8b2 Mon Sep 17 00:00:00 2001 From: Tim Redfern Date: Fri, 30 Aug 2013 18:27:08 +0100 Subject: value output from audio analysis --- rotord/src/graph.cpp | 12 ++++ rotord/src/nodes_audio_analysis.cpp | 135 ++++++++++++++++++++++++++++++++++++ rotord/src/nodes_audio_analysis.h | 26 +++++-- rotord/src/nodes_maths.h | 6 +- rotord/src/nodes_transform.h | 6 -- rotord/src/rendercontext.cpp | 25 +++++++ rotord/src/rotor.cpp | 118 ------------------------------- rotord/src/rotor.h | 2 + rotord/src/vampHost.cpp | 17 +++-- rotord/src/vampHost.h | 9 ++- 10 files changed, 221 insertions(+), 135 deletions(-) create mode 100644 rotord/src/nodes_audio_analysis.cpp (limited to 'rotord/src') diff --git a/rotord/src/graph.cpp b/rotord/src/graph.cpp index 03192f3..3d0a88b 100644 --- a/rotord/src/graph.cpp +++ b/rotord/src/graph.cpp @@ -33,6 +33,18 @@ bool Graph::signal_render(string &signal_xml,const float framerate) { return false; } */ +bool Graph::print_features(xmlIO &XML,string &node){ + if (nodes.find(node)!=nodes.end()){ + if (dynamic_cast(nodes[node])){ + XML.addValue("features",dynamic_cast(nodes[node])->get_features()); + return true; + } + XML.addValue("error","node /"+node+"/ is not an Audio processor"); + return false; + } + XML.addValue("error","could not find node /"+node+"/"); + return false; +} bool Graph::preview(xmlIO &XML,string &node,string &_format,int frame,int w,int h){ if (nodes.find(node)!=nodes.end()){ float t=frame/framerate; diff --git a/rotord/src/nodes_audio_analysis.cpp b/rotord/src/nodes_audio_analysis.cpp new file mode 100644 index 0000000..925858f --- /dev/null +++ b/rotord/src/nodes_audio_analysis.cpp @@ -0,0 +1,135 @@ +#include "nodes_audio_analysis.h" + +namespace Rotor{ + bool Audio_thumbnailer::init(int _channels,int _bits,int _samples,int _rate) { + //base_audio_processor::init(_channels,_bits,_samples); + channels=_channels; + bits=_bits; + samples=_samples; + samples_per_column=samples/width; + column=0; //point thumbnail bitmap + out_sample=0; //sample in whole track + offset=0x1<<(bits-1); //signed audio + scale=1.0f/offset; + sample=0; + samples=0; + accum=0.0; + return true; + } + int Audio_thumbnailer::process_frame(uint8_t *_data,int samples_in_frame){ + //begin by processing remaining samples + //samples per column could be larger than a frame! (probably is) + //but all we are doing is averaging + int bytes=(bits>>3); + int stride=channels*bytes; + int in_sample=0; + while (in_sample>1; + for (int i=0;iwrite((char*)data,width*height); + //tring output; + /* + for (int j=0;jclose(); + delete enc; + return output.str(); + } + void Audio_thumbnailer::print_vector(xmlIO XML){ + string vdata; + for (int i=0;i0) vdata+=","; + vdata+=ofToString(vectordata[i]); + } + XML.addValue("data",vdata); + } + bool Audio_analysis::init(int _channels,int _bits,int _samples, int _rate) { + //need these to make sense of data + channels=_channels; + bits=_bits; + samples=_samples; + + return analyser.init(soname,id,_channels,_bits,_samples,_rate,outputNo,params); + + + //attempt to load vamp plugin and prepare to receive frames of data + //should the audio analysis contain a vamphost or should it inherit? + //maybe neater to contain it in terms of headers etc + + } + int Audio_analysis::process_frame(uint8_t *data,int samples_in_frame) { + analyser.process_frame(data,samples_in_frame); + return 1; + } + void Audio_analysis::cleanup() { + analyser.cleanup(); + //print_features(); + } + string Audio_analysis::get_features(){ + string data; + for (auto i: analyser.features) { + data=data+" ["+ofToString(i.second.number)+":"+ofToString(i.first); + if (i.second.values.size()) { + data+=" ("; + bool first=true; + for (auto j: i.second.values) { + if (first){ + first=false; + } + else data+=","; + data=data+ofToString(j); + } + data+=") "; + } + data+="]"; + } + return data; + } +} \ No newline at end of file diff --git a/rotord/src/nodes_audio_analysis.h b/rotord/src/nodes_audio_analysis.h index 9252db4..fba4bcc 100644 --- a/rotord/src/nodes_audio_analysis.h +++ b/rotord/src/nodes_audio_analysis.h @@ -5,12 +5,17 @@ #include "vampHost.h" namespace Rotor { +#define VAMPHOST_Timeline 1 +#define VAMPHOST_Timesteps 2 +#define VAMPHOST_Valueline 3 +#define VAMPHOST_Values 4 class Audio_analysis: public Audio_processor { public: Audio_analysis(){ //create_attribute("soname","Plugin library to use","Plugin library","vamp-example-plugins",{"horiz","vert","horizR","vertR"}); //create_attribute("id","ID of Plugin to use","Plugin ID","percussiononsets",{"horiz","vert","horizR","vertR"}); create_attribute("analyser","Analyser Plugin to use","Analyser plugin","barbeattracker",{"barbeattracker","segmenter"}); + create_attribute("mode","Data output mode","Mode","timeline",{"timeline","timesteps","valueline","values"}); create_parameter("outputNo","number","Plugin output to use","Output number",0.0f); title="Audio analysis"; description="Analyse audio and output"; @@ -31,19 +36,32 @@ namespace Rotor { void set_parameter(const std::string &key,const std::string &value){params[key]=ofToFloat(value);}; int process_frame(uint8_t *data,int samples_in_frame); const float output(const Time_spec &time) { - if (analyser.features.size()) { + if (analyser.features.size()) { auto i=analyser.features.upper_bound(time.time); //the first element in the container whose key is considered to go after k if (i!=analyser.features.end()){ float uk=i->first; + float v1,v2; + v1=v2=0.0f; + if (i->second.values.size()) v2=i->second.values[0]; i--; float lk=i->first; - int ln=i->second; - return (((time.time-lk)/(uk-lk))+ln); + int ln=i->second.number; + if (i->second.values.size()) v1=i->second.values[0]; + switch (attributes["mode"]->intVal){ + case VAMPHOST_Timeline: + return (((time.time-lk)/(uk-lk))+ln); + case VAMPHOST_Timesteps: + return (float)ln; + case VAMPHOST_Valueline: + return (((time.time-lk)/(v2-v1))+v1); + case VAMPHOST_Values: + return v1; + } } } return 0.0f; } - void print_features(); + string get_features(); void print_summary(){ cerr<<"vamp plugin "<value/in; + break; } } } diff --git a/rotord/src/nodes_transform.h b/rotord/src/nodes_transform.h index bb3a04f..fd6b7be 100644 --- a/rotord/src/nodes_transform.h +++ b/rotord/src/nodes_transform.h @@ -25,11 +25,6 @@ namespace Rotor { create_parameter("scale","number","Scale about origin","Scale",1.0f); create_attribute("filter","Filtering mode","Filter mode","linear",{"nearest","linear","area","cubic","lanczos"}); }; - Transformer(map &settings):Transformer() { - base_settings(settings); - }; - ~Transformer(){ - }; Image *transform(Image *in){ if (in){ //INTER_NEAREST - a nearest-neighbor interpolation @@ -85,7 +80,6 @@ namespace Rotor { trans_mat=getAffineTransform( srcTri, dstTri ); warpAffine( in->rgb, inter.rgb, trans_mat, inter.rgb.size(), filtmode, cv::BORDER_WRAP); - // Compute rotation matrix // cv::Point centre = cv::Point( oX*image.w, oY*image.h ); diff --git a/rotord/src/rendercontext.cpp b/rotord/src/rendercontext.cpp index 7d85cce..3f20923 100644 --- a/rotord/src/rendercontext.cpp +++ b/rotord/src/rendercontext.cpp @@ -342,6 +342,31 @@ void Render_context::session_command(const Session_command& command,xmlIO& XML,H XML.addValue("error","Bad request"); } } + if (command.commands[1]=="features") { + if (command.method=="GET") { + if(state==IDLE){ + //parse json to get preview spec, return XML? this is a mess + string features_node=command.commands[2]; + if (graph.print_features(XML,features_node)) { + status=HTTPResponse::HTTP_OK; + } + else { + status=HTTPResponse::HTTP_BAD_REQUEST; + logger.error("ERROR: Could not print features for node "+features_node); + } + } + else { + status=HTTPResponse::HTTP_BAD_REQUEST; + logger.error("ERROR: Session busy"); + XML.addValue("error","Session busy"); + } + } + else { + status=HTTPResponse::HTTP_BAD_REQUEST; + logger.error("ERROR: Bad request"); + XML.addValue("error","Bad request"); + } + } if (command.commands[1]=="render") { if (command.method=="GET") { if(state==RENDERING){ diff --git a/rotord/src/rotor.cpp b/rotord/src/rotor.cpp index f1a2605..3246240 100755 --- a/rotord/src/rotor.cpp +++ b/rotord/src/rotor.cpp @@ -77,123 +77,6 @@ float Parameter::get(const Time_spec& time){ //gets input and updates variable } return value; } -bool Audio_thumbnailer::init(int _channels,int _bits,int _samples,int _rate) { - //base_audio_processor::init(_channels,_bits,_samples); - channels=_channels; - bits=_bits; - samples=_samples; - samples_per_column=samples/width; - column=0; //point thumbnail bitmap - out_sample=0; //sample in whole track - offset=0x1<<(bits-1); //signed audio - scale=1.0f/offset; - sample=0; - samples=0; - accum=0.0; - return true; -} -int Audio_thumbnailer::process_frame(uint8_t *_data,int samples_in_frame){ - //begin by processing remaining samples - //samples per column could be larger than a frame! (probably is) - //but all we are doing is averaging - int bytes=(bits>>3); - int stride=channels*bytes; - int in_sample=0; - while (in_sample>1; - for (int i=0;iwrite((char*)data,width*height); - //tring output; - /* - for (int j=0;jclose(); - delete enc; - return output.str(); -} -void Audio_thumbnailer::print_vector(xmlIO XML){ - string vdata; - for (int i=0;i0) vdata+=","; - vdata+=ofToString(vectordata[i]); - } - XML.addValue("data",vdata); -} -bool Audio_analysis::init(int _channels,int _bits,int _samples, int _rate) { - //need these to make sense of data - channels=_channels; - bits=_bits; - samples=_samples; - - return analyser.init(soname,id,_channels,_bits,_samples,_rate,outputNo,params); - - - //attempt to load vamp plugin and prepare to receive frames of data - //should the audio analysis contain a vamphost or should it inherit? - //maybe neater to contain it in terms of headers etc - -} -int Audio_analysis::process_frame(uint8_t *data,int samples_in_frame) { - analyser.process_frame(data,samples_in_frame); - return 1; -} -void Audio_analysis::cleanup() { - analyser.cleanup(); - //print_features(); -} -void Audio_analysis::print_features(){ - for (auto i: analyser.features) { - cerr<<" ["<composePluginKey(soname, id); @@ -763,7 +763,10 @@ void vampHost::Analyser::process_frame(uint8_t *data,int samples_in_frame){ Plugin::FeatureSet feat=plugin->process(plugbuf, rt); for (unsigned int i = 0; i < feat[outputNo].size(); ++i) { - features[((float)feat[outputNo][i].timestamp.sec)+(((float)feat[outputNo][i].timestamp.nsec)*.000000001)]=featureNo; + feature f; + f.number=featureNo; + f.values=feat[outputNo][i].values; + features[((float)feat[outputNo][i].timestamp.sec)+(((float)feat[outputNo][i].timestamp.nsec)*.000000001)]=f; featureNo++; } @@ -794,14 +797,20 @@ void vampHost::Analyser::cleanup(){ Plugin::FeatureSet feat=plugin->process(plugbuf, rt); for (unsigned int i = 0; i < feat[outputNo].size(); ++i) { - features[((float)feat[outputNo][i].timestamp.sec)+(((float)feat[outputNo][i].timestamp.nsec)*.000000001)]=featureNo; + feature f; + f.number=featureNo; + f.values=feat[outputNo][i].values; + features[((float)feat[outputNo][i].timestamp.sec)+(((float)feat[outputNo][i].timestamp.nsec)*.000000001)]=f; featureNo++; } feat=plugin->getRemainingFeatures(); for (unsigned int i = 0; i < feat[outputNo].size(); ++i) { - features[((float)feat[outputNo][i].timestamp.sec)+(((float)feat[outputNo][i].timestamp.nsec)*.000000001)]=featureNo; + feature f; + f.number=featureNo; + f.values=feat[outputNo][i].values; + features[((float)feat[outputNo][i].timestamp.sec)+(((float)feat[outputNo][i].timestamp.nsec)*.000000001)]=f; featureNo++; } diff --git a/rotord/src/vampHost.h b/rotord/src/vampHost.h index d1dfc81..241ffce 100644 --- a/rotord/src/vampHost.h +++ b/rotord/src/vampHost.h @@ -32,7 +32,11 @@ using Vamp::HostExt::PluginInputDomainAdapter; #define HOST_VERSION "1.5" namespace vampHost { - + struct feature{ + feature():number(0){}; + int number; + vector values; + }; class Settings{ public: Settings(string _so="",string _filter="",string _input="") { @@ -60,7 +64,8 @@ namespace vampHost { void process_frame(uint8_t *data,int samples_in_frame); void cleanup(); - map features; + //map features; + map features; //map //this is the best way to store features: because map allows to search for the key below and above the present time -- cgit v1.2.3