diff options
| author | Tim Redfern <tim@herge.(none)> | 2013-04-04 18:03:14 +0100 |
|---|---|---|
| committer | Tim Redfern <tim@herge.(none)> | 2013-04-04 18:03:14 +0100 |
| commit | 332c7c24301700ca0a4ceb104051bcd3a3b3bc4b (patch) | |
| tree | 5d4d0c12c749bc1af40effae6e3d943149fa06ae /rotord | |
| parent | ef217eb0c2450e50a25e6ae2aee36178fcdd54c7 (diff) | |
analyser loader
Diffstat (limited to 'rotord')
| -rw-r--r-- | rotord/01.xml | 2 | ||||
| -rw-r--r-- | rotord/rotor.cpp | 101 | ||||
| -rwxr-xr-x | rotord/rotor.h | 74 | ||||
| -rw-r--r-- | rotord/vampHost.cpp | 20 | ||||
| -rw-r--r-- | rotord/vampHost.h | 9 |
5 files changed, 129 insertions, 77 deletions
diff --git a/rotord/01.xml b/rotord/01.xml index 830c3a3..e351184 100644 --- a/rotord/01.xml +++ b/rotord/01.xml @@ -1,6 +1,6 @@ <?xml version="1.0" encoding="ISO-8859-1"?> <patchbay ID="0f7aa258-7c2f-11e2-abbd-133252267708">Off and on template ©Rotor 2013 - <node ID="01" type="audio_analysis" soname="qm-vamp-plugins" id="beats" output="signal">beats + <node ID="01" type="audio_analysis" soname="qm-vamp-plugins" id="qm-tempotracker" output="signal">beats </node> <node ID="02" type="audio_analysis" soname="qm-vamp-plugins" id="qm-segmenter" output="signal">segmenter </node> diff --git a/rotord/rotor.cpp b/rotord/rotor.cpp index f7c017d..cd288e3 100644 --- a/rotord/rotor.cpp +++ b/rotord/rotor.cpp @@ -21,9 +21,13 @@ void Render_context::runTask() { mutex.unlock(); if (cmd==ANALYSE_AUDIO) { state=ANALYSING_AUDIO; - vector<base_audio_processor*> proc; - proc.push_back(audio_thumb); - if (load_audio(audio_filename,proc)) { + vector<Base_audio_processor*> processors; + processors.push_back(audio_thumb); + vector<Node*> analysers=graph.find_nodes("audio_analysis"); + for (auto a: analysers) { + processors.push_back(a); + } + if (load_audio(audio_filename,processors)) { state=AUDIO_READY; } else state=IDLE; @@ -118,9 +122,9 @@ Command_response Render_context::session_command(const std::vector<std::string>& } if (command[2]=="graph") { if (command[0]=="GET") { - if (xml.bDocLoaded) { + if (graph.loaded) { response.status=HTTPResponse::HTTP_OK; - xml.copyXmlToString(response.description); + response.description=graph.toString(); } else { response.description="<status>Rotor: graph not loaded</status>\n"; @@ -134,14 +138,14 @@ Command_response Render_context::session_command(const std::vector<std::string>& Poco::File f=Poco::File(command[3]); if (f.exists()) { string graph_filename=command[3]; - if (load_graph(graph_filename)) { + if (graph.load(graph_filename)) { response.status=HTTPResponse::HTTP_OK; //response.description="<status context='"+command[1]+"'>Rotor: loaded graph "+command[3]+"</status>\n"; - string xmlstring; - xml.copyXmlToString(xmlstring); - response.description=xmlstring; + response.description=graph.toString(); //the graph could actually contain an xml object and we could just print it here? //or could our nodes even be subclassed from xml nodes? + //the graph or the audio could load first- have to analyse the audio with vamp after the graph is loaded + //for now the graph must load 1st } else { response.status=HTTPResponse::HTTP_INTERNAL_SERVER_ERROR; //~/sources/poco-1.4.6-all/Net/include/Poco/Net/HTTPResponse.h @@ -248,13 +252,10 @@ Command_response Render_context::session_command(const std::vector<std::string>& return response; } - - - //http://blog.tomaka17.com/2012/03/libavcodeclibavformat-tutorial/ //great to use c++11 features -bool Render_context::load_audio(const string &filename,vector<base_audio_processor*> processors){ +bool Render_context::load_audio(const string &filename,vector<Base_audio_processor*> processors){ av_register_all(); @@ -325,14 +326,12 @@ bool Render_context::load_audio(const string &filename,vector<base_audio_process std::cout << "The data is in format " <<codecContext->sample_fmt<< " (aka "<< av_get_sample_fmt_name(codecContext->sample_fmt) << ") "<<std::endl; for (auto p: processors) { - p->init(codecContext->channels,16,samples); + p->init(codecContext->channels,16,samples,codecContext->sample_rate); } AVPacket packet; av_init_packet(&packet); int sample_processed=0; - - bool diag=true; while (true) { @@ -361,11 +360,7 @@ bool Render_context::load_audio(const string &filename,vector<base_audio_process //av_get_channel_layout_string (char *buf, int buf_size, int nb_channels, uint64_t channel_layout) - if (diag) { - cerr << "first frame: "<<bytes << ", "<<frame->nb_samples<<" samples in "<<av_get_sample_fmt_name(frame->format)<<" format with channel layout "<<frame->channel_layout<< std::endl; - diag=false; - } - + //now we can pass the data to the processor(s) for (auto p: processors) { sample_processed=p->process_frame(frame->data[0],frame->nb_samples); @@ -408,11 +403,19 @@ bool Render_context::load_audio(const string &filename,vector<base_audio_process return true; } - -bool Render_context::load_graph(string &filename){ - printf("loading %s\n",filename.c_str()); +const string Graph::toString(){ + string xmlgraph; + if (loaded) { + xml.copyXmlToString(xmlgraph); + return xmlgraph; + } + else return ""; +} +bool Graph::load(string &filename){ + loaded=false; + printf("loading graph: %s\n",filename.c_str()); if(xml.loadFile(filename) ){ - graph=Graph(xml.getAttribute("patchbay","ID","",0),xml.getValue("patchbay","",0)); + init(xml.getAttribute("patchbay","ID","",0),xml.getValue("patchbay","",0)); if(xml.pushTag("patchbay")) { int n1=xml.getNumTags("node"); for (int i1=0;i1<n1;i1++){ @@ -428,14 +431,14 @@ bool Render_context::load_graph(string &filename){ if (node) { cerr << "Rotor: created '" << xml.getAttribute("node","type","",i1) << "'" << endl; string nodeID=xml.getAttribute("node","ID","",i1); - graph.nodes[nodeID]=node; + nodes[nodeID]=node; if(xml.pushTag("node",i1)) { int n2=xml.getNumTags("signal_input"); for (int i2=0;i2<n2;i2++){ - graph.nodes[nodeID]->create_signal_input(xml.getValue("signal_input","",i2)); + nodes[nodeID]->create_signal_input(xml.getValue("signal_input","",i2)); string fromID=xml.getAttribute("signal_input","from","",i2); - if (graph.nodes.find(fromID)!=graph.nodes.end()) { - if (!graph.nodes[nodeID]->inputs[i2]->connect((Signal_node*)graph.nodes[fromID])){ + if (nodes.find(fromID)!=nodes.end()) { + if (!nodes[nodeID]->inputs[i2]->connect((Signal_node*)nodes[fromID])){ cerr << "Rotor: graph loader cannot connect input " << i2 << " of node '" << nodeID << "' to node '" << fromID << "'" << endl; return false; } @@ -453,6 +456,7 @@ bool Render_context::load_graph(string &filename){ } xml.popTag(); } + loaded=true; return true; } else return false; @@ -465,15 +469,11 @@ Node_factory::Node_factory(){ add_type("==",new Is_new_integer()); add_type("signal_output",new Signal_output()); } -void audio_thumbnailer::init(int _channels,int _bits,int _samples) { +void Audio_thumbnailer::init(int _channels,int _bits,int _samples,int _rate) { //base_audio_processor::init(_channels,_bits,_samples); channels=_channels; bits=_bits; samples=_samples; - - cerr << "init audio thumbnailer with "<<channels<<" channels, "<<samples<<" samples of "<< bits<<" bits."<<endl; - - samples_per_column=samples/width; column=0; //point thumbnail bitmap out_sample=0; //sample in whole track @@ -483,7 +483,7 @@ void audio_thumbnailer::init(int _channels,int _bits,int _samples) { samples=0; accum=0.0; } -int audio_thumbnailer::process_frame(uint8_t *_data,int samples_in_frame){ +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 @@ -501,17 +501,7 @@ int audio_thumbnailer::process_frame(uint8_t *_data,int samples_in_frame){ this_val+=_data[(sample*stride)+(i*bytes)+j]<<(j*8); } //convert from integer data format - i.e s16p - to audio signal in -1..1 range - //don't know how many bytes we are dealing with necessarily? - //double val=((double)(this_val-offset))*scale; - - // - //cerr << this_val << " "; - // - - - //if (this_val>offset) this_val=-(this_val-(offset*2)); - - //this_val -=offset; + //presume 16 bits for now... double val=((double)((int16_t)this_val))*scale; accum+=val*val; samples++; @@ -539,7 +529,7 @@ int audio_thumbnailer::process_frame(uint8_t *_data,int samples_in_frame){ } return out_sample; } -string audio_thumbnailer::print(){ +string Audio_thumbnailer::print(){ string output; for (int j=0;j<height;j++) { for (int i=0;i<width;i++) { @@ -549,3 +539,20 @@ string audio_thumbnailer::print(){ } return output; } +void Audio_analysis::init(int _channels,int _bits,int _samples, int _rate) { + //need these to make sense of data + channels=_channels; + bits=_bits; + samples=_samples; + + analyser.init(soname,id,_rate); + + //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) { + //the standard vamp process takes a file + return 1; +} diff --git a/rotord/rotor.h b/rotord/rotor.h index 436de10..11e1170 100755 --- a/rotord/rotor.h +++ b/rotord/rotor.h @@ -222,18 +222,31 @@ namespace Rotor { private: float image_time; }; + class Base_audio_processor: public Signal_node { + public: + virtual int process_frame(uint8_t *data,int samples)=0; + virtual void init(int _channels,int _bits,int _samples,int _rate)=0; + int channels,bits,samples,rate; + }; //actual nodes------------------------------------------------- - class Audio_analysis: public Signal_node { + class Audio_analysis: public Base_audio_processor { public: Audio_analysis(){}; Audio_analysis(map<string,string> &settings) { base_settings(settings); + soname=check(settings,"soname"); + id=check(settings,"id"); }; Audio_analysis* clone(map<string,string> &_settings) { return new Audio_analysis(_settings);}; + void init(int _channels,int _bits,int _samples,int _rate); + int process_frame(uint8_t *data,int samples_in_frame); float get_output(const float &time) { float t=time; return t; } + private: + string soname,id; + vampHost::Analyser analyser; }; class Signal_divide: public Signal_node { //divides incoming signal by a fixed amount @@ -308,52 +321,61 @@ namespace Rotor { }; class Graph{ public: - Graph(){framerate=25.0f;duration=10.0f;}; - Graph(const string& _uid,const string& _desc){ uid=_uid;description=_desc;framerate=25.0f;duration=10.0f;}; + Graph(){framerate=25.0f;duration=10.0f;loaded = false;}; + Graph(const string& _uid,const string& _desc){init(_uid,_desc);}; + void init(const string& _uid,const string& _desc){ uid=_uid;description=_desc;framerate=25.0f;duration=10.0f;}; string uid; //every version of a graph has a UUID, no particular need to actually read its data(?) //?? is it faster than using strings?? string description; std::unordered_map<string,Node*> nodes; - Node* find(const string &type){ + vector<Node*> find_nodes(const string &type){ + vector<Node*> found; + for (std::unordered_map<string,Node*>::iterator it=nodes.begin();it!=nodes.end();++it) { + if (it->second->type==type) found.push_back(it->second); + } + return found; + }; + Node* find_node(const string &type){ for (std::unordered_map<string,Node*>::iterator it=nodes.begin();it!=nodes.end();++it) { if (it->second->type==type) return it->second; } - return NULL; + return nullptr; //can be tested against }; bool signal_render(const float _fr,string &signal_xml) { if (_fr>.001) framerate=_fr; - if (find("signal_output")) { - Signal_output *signal_output=dynamic_cast<Signal_output*>(find("signal_output")); + if (find_node("signal_output")) { + Signal_output *signal_output=dynamic_cast<Signal_output*>(find_node("signal_output")); return signal_output->render(duration,framerate,signal_xml); } else return false; } + int load(Poco::UUID uid); + bool load(string &graph_filename); + UUID save(); //save to DB, returns UUID of saved graph + bool loaded; + const string toString(); private: Node_factory factory; float framerate; float duration; + xmlIO xml; }; - class base_audio_processor { - public: - virtual int process_frame(uint8_t *data,int samples)=0; - virtual void init(int _channels,int _bits,int _samples)=0; - int channels,bits,samples; - }; - class audio_thumbnailer: public base_audio_processor { + class Audio_thumbnailer: public Base_audio_processor { //how to deal with the fact that frames don't correspond with pixels? //buffer the data somehow //draw pixels based on rms value public: - audio_thumbnailer(){ - height=48; - width=128; //fit + Audio_thumbnailer(){ + height=32; + width=64; //fit data=new uint8_t[height*width]; memset(data,0,height*width); - } - ~audio_thumbnailer(){ + }; + ~Audio_thumbnailer(){ delete[] data; - } - void init(int _channels,int _bits,int _samples); + }; + Audio_thumbnailer* clone(map<string,string> &_settings) { return new Audio_thumbnailer();}; + void init(int _channels,int _bits,int _samples,int _rate); int process_frame(uint8_t *data,int samples_in_frame); string print(); uint8_t *data; @@ -369,7 +391,7 @@ namespace Rotor { //and low level interface onto the graph public: Render_context(const std::string& name): Task(name) { - audio_thumb=new audio_thumbnailer(); + audio_thumb=new Audio_thumbnailer(); state=IDLE; }; void runTask(); @@ -378,10 +400,7 @@ namespace Rotor { Render_status get_status(); void cancel(); //interrupt locking process int make_preview(int nodeID, float time); //starts a frame preview - returns status code - how to retrieve? - int load_graph(Poco::UUID uid); - bool load_graph(string &graph_filename); //should eventually be as above - UUID save_graph(); //returns UUID of saved graph - bool load_audio(const string &filename,vector<base_audio_processor*> processors); + bool load_audio(const string &filename,vector<Base_audio_processor*> processors); Render_requirements get_requirements(); int load_video(int num,string &filename); //can be performance or clip @@ -392,9 +411,8 @@ namespace Rotor { std::deque<int> work_queue; Poco::Mutex mutex; //lock for access from parent thread std::string audio_filename; - audio_thumbnailer *audio_thumb; + Audio_thumbnailer *audio_thumb; vampHost::QMAnalyser audio_analyser; - xmlIO xml; Graph graph; Node_factory factory; }; diff --git a/rotord/vampHost.cpp b/rotord/vampHost.cpp index 947e49a..b9b0781 100644 --- a/rotord/vampHost.cpp +++ b/rotord/vampHost.cpp @@ -587,4 +587,22 @@ float vampHost::QMAnalyser::get_progress(){ p=progress; mutex.unlock(); return p; -}
\ No newline at end of file +} +void vampHost::Analyser::init(const string &soname,const string &id,const int &rate){ + + loader = PluginLoader::getInstance(); + key = loader->composePluginKey(soname, id); + Plugin *plugin = loader->loadPlugin(key, rate, PluginLoader::ADAPT_ALL_SAFE); + if (!plugin) { + cerr << ": ERROR: Failed to load plugin \"" << id + << "\" from library \"" << soname << "\"" << endl; + return; + } + + cerr << "Running plugin: \"" << plugin->getIdentifier() << "\"..." << endl; + + delete plugin; + + return; +} + diff --git a/rotord/vampHost.h b/rotord/vampHost.h index dfff408..de6ae02 100644 --- a/rotord/vampHost.h +++ b/rotord/vampHost.h @@ -53,6 +53,15 @@ namespace vampHost { float progress; Poco::Mutex mutex; //lock for progress data }; + class Analyser{ + public: + void init(const string &soname,const string &id,const int &rate); + vector<float> beats; + private: + PluginLoader *loader; + PluginLoader::PluginKey key; + Plugin *plugin; + }; string getQMBeats(const string soundfile); void printFeatures(int, int, int, Plugin::FeatureSet, ostream &, bool frames); |
