From 4e2395d52f9da8efbf7385519f4d7d11a7498f90 Mon Sep 17 00:00:00 2001 From: Tim Redfern Date: Wed, 6 Mar 2013 17:26:24 +0000 Subject: mysterious --- rotord/01.xml | 47 ++-------- rotord/rotor.cpp | 186 ++++++++++++++++++++++++++++++++++++++- rotord/rotor.h | 258 ++++++++++++++++++++++++++++++++++++------------------ rotord/rotord.cbp | 9 ++ rotord/xmlIO.cpp | 8 +- 5 files changed, 377 insertions(+), 131 deletions(-) (limited to 'rotord') diff --git a/rotord/01.xml b/rotord/01.xml index dec3a05..e6c8697 100644 --- a/rotord/01.xml +++ b/rotord/01.xml @@ -1,48 +1,13 @@ Off and on template ©Rotor 2013 - music track to analyse - beat (sawtooth) - excitement + beats - performance take 1 - + segmenter - performance take 2 - + outputs 0 except when signal first passes a new integer: then 1 + signal to analyse - 1 - - - - - - - - - - - - - - 2 - - - - 1 - - - - - - cycle switch - - - - scale - - - - - + outputs data when changed + signal to output diff --git a/rotord/rotor.cpp b/rotord/rotor.cpp index 8360824..4164baa 100644 --- a/rotord/rotor.cpp +++ b/rotord/rotor.cpp @@ -1,13 +1,21 @@ #include "rotor.h" -using namespace Rotor; +//float equality +bool fequal(const float u,const float v){ + if (abs(u-v)<.001) return true; + else return false; +}; + +using namespace Rotor; +/* string soname="qm-vamp-plugins"; string id="qm-tempotracker"; string myname=""; string output=""; int outputNo=0; + */ void Render_context::runTask() { while (!isCancelled()) { @@ -34,6 +42,29 @@ void Render_context::add_queue(int item) { mutex.unlock(); } +bool Signal_input::connect(Node* source) { + if (source->output_type=="signal") { + connection=source; + return true; + } + else return false; +}; + +bool Signal_output::render(const float duration, const float framerate,string &xml_out){ + cerr << "Rotor: Signal_output rendering " << duration << " seconds at " << framerate << " frames per second" << endl; + float step=1.0f/framerate; + float v=0.0f; + for (float f=0.0f;f"+ofToString(u)+"\n"); + v=u; + } + } + return true; +} + Command_response Render_context::session_command(const std::vector& command){ //method,id,command1,{command2,}{body} //here we allow the controlling server to communicate with running tasks @@ -95,6 +126,11 @@ Command_response Render_context::session_command(const std::vector& string graph_filename=command[3]; if (load_graph(graph_filename)) { response.description="Rotor: loaded graph "+command[3]+"\n"; + string xmlstring; + xml.copyXmlToString(xmlstring); + response.description+=xmlstring; + //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? } else { response.status=HTTPResponse::HTTP_INTERNAL_SERVER_ERROR; //~/sources/poco-1.4.6-all/Net/include/Poco/Net/HTTPResponse.h @@ -109,9 +145,153 @@ Command_response Render_context::session_command(const std::vector& } } } + if (command[2]=="signal") { + if (command[0]=="GET") { //generate xml from 1st signal output + if (state==IDLE) { + //direct call for testing + float framerate=0.0f; + if (command.size()>2) { + framerate=ofToFloat(command[3]); + } + string signal_xml; + if (graph.signal_render(framerate,signal_xml)){ + response.description=signal_xml; + } + else { + response.status=HTTPResponse::HTTP_INTERNAL_SERVER_ERROR; + response.description="Rotor: could not render output signal\n"; + } + } + else { + response.status=HTTPResponse::HTTP_NOT_FOUND; + response.description="Signal output not found\n"; + } + } + else { + response.status=HTTPResponse::HTTP_SERVICE_UNAVAILABLE; + response.description="Rotor: context busy\n"; + } + } return response; } -bool Render_context::load_graph(string graph_filename){ - return true; +//how to do this? +//aim - load nodes from xml +//easily maintain the xml tree +//be able to save it or print it +//--if the list of nodes is a flat vector - don't need to store ID with the node - just refer to the index # of the node while loading? +//--if instead it refers to the index# in the unordered map - can handle deletion etc - have to check if a node exists when linking +//--also can a node link to one 'later' ? it may have to? in which case there's a 2 step process - 1 load the nodes - 2 link them +//maybe + + +// a generic way to create a named node- each node is an object- will be loadable from .so +// eventually need to figure out to load .sos and enumerate classes +// in the short term either +// - hand logic that recognises nodes and creates inputs etc +// - not sure how else? + +//its a factory? +//http://stackoverflow.com/questions/4007382/how-to-create-class-objects-dynamically +//http://www.codeproject.com/Articles/3734/Different-ways-of-implementing-factories +//http://stackoverflow.com/questions/13292856/create-derived-class-in-base-class-based-on-parameter +//http://en.wikipedia.org/wiki/Factory_method_pattern + +//what is a good way to pass parameters to objects? +//get all attributes of node and pass them as creation parameters? +//could just pass them all as strings, let the object choose what to do itself? +//YES + +bool Render_context::load_graph(string filename){ + printf("loading %s\n",filename.c_str()); + if(xml.loadFile(filename) ){ + graph=Graph(xml.getAttribute("patchbay","ID","",0),xml.getValue("patchbay","",0)); + if(xml.pushTag("patchbay")) { + int n1=xml.getNumTags("node"); + for (int i1=0;i1 settings; + vector attrs; + xml.getAttributeNames("node",attrs,i1); + for (auto& attr: attrs) { + settings[attr]=xml.getAttribute("node",attr,"",i1); + //cerr << "Got attribute: " << attr << ":" << xml.getAttribute("node",attr,"",i1) << endl; + } + settings["description"]=xml.getValue("node","",i1); + Node* node=factory.create(settings); + if (node) { + cerr << "Rotor: created '" << xml.getAttribute("node","type","",i1) << "'" << endl; + string nodeID=xml.getAttribute("node","ID","",i1); + graph.nodes[nodeID]=node; + if(xml.pushTag("node",i1)) { + int n2=xml.getNumTags("signal_input"); + for (int i2=0;i2create_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(graph.nodes[fromID])){ + cerr << "Rotor: graph loader cannot connect input " << i2 << " of node '" << nodeID << "' to node '" << fromID << "'" << endl; + return false; + } + else cerr << "Rotor: linked input " << i2 << " of node '" << nodeID << "' to node '" << fromID << "'" << endl; + } + else cerr << "Rotor: linking input " << i2 << " of node: '" << nodeID << "', cannot find target '" << fromID << "'" << endl; + } + xml.popTag(); + } + } + else { + cerr << "Rotor: graph loader cannot find node '" << xml.getAttribute("node","type","",i1) << "'" << endl; + return false; + } + } + xml.popTag(); + } + /* + model_name=XML.getAttribute("map4","model","none",0); + model_x=ofToFloat(XML.getAttribute("map4","x","none",0)); + model_y=ofToFloat(XML.getAttribute("map4","y","none",0)); + model_z=ofToFloat(XML.getAttribute("map4","z","none",0)); + if(XML.pushTag("map4")) { + numViews=XML.getNumTags("view"); + if(numViews) { + views=new viewpoint[numViews]; + for (int i=0;ikeys; + XML.getAttributeNames("settings", keys, 0); + mapsettings; + for (int k=0;k signal_inputs; -vector image_inputs; - -//in input -int input_id; - - -&image get_ouput() -{ - for (int i=0;iget_output(); ///methinks this is neater? you can check pointer for NULL, can't be ref -} +we traverse the graph as recursive function calls until we satisfy all dependencies NO NODE HAS MORE THAN ONE OUTPUT WE DON'T LINK TO AN OUTPUT OBJECT WE LINK TO THE NODE - GET_OUTPUT IS THE RENDER FUNCTION +??the only node with more than 1 output is audio? +??lets rethink this +??audio analysis nodes can be seperate - they can all load from the same audio file - were gonna have to process each pass +??output splitter? channel splitter? these can be done as 1 object per channel? +??I think so + settings - how do we deal with settings being controllable signal inputs can have a gui representation as well other gui items don't have an input @@ -45,29 +26,16 @@ maybe we should look at time in int (frames) - - what does this imply is it easier to have a function like: bool Same_frame(float time1, float time2); -2 main questions -authentication - how -authentication to renderer or just session? -files - where -generated images & movies where? - -nb where a signal enters a channel comp input - it is duplicated - -next - Poco create thread - -1st - create thread and return id - create method to kill it +nb where a signal enters a channel comp input - it is duplicated - so nodes should cache a value (more for time effects) sql stuff +NB best way to use is: interface uploads audio and makes thumbnail: graph determines what kind of audio analysis is used by referring to plugins +jesus where is it hanging in the frigging debugger -messaging - http factory includes a poco notication center -when it creates the rotor session it registers it -session messages are posted thus and consumed +GOOD GOOD GOOD -best way to control the rendering process? work queue isn't bad as it means we keep concurrency -also need to: -find out what the renderer is doing -get progress +next - build signal_output and make a working chain with dummy data */ @@ -114,6 +82,7 @@ namespace Rotor { //forward declaration class Node; + class Render_status{ public: int id; @@ -130,40 +99,11 @@ namespace Rotor { std::string description; Poco::Net::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) { - state=IDLE; - }; - void runTask(); - void add_queue(int item); - Command_response session_command(const std::vector& command); - 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 - int load_audio(string filename); - Render_requirements get_requirements(); - int load_video(int num,string filename); //can be performance or clip - - private: - int state; - float progress; //for a locking process: audio analysis or rendering - std::deque work_queue; - Poco::Mutex mutex; //lock for access from parent thread - std::string audio_filename; - vampHost::QMAnalyser audio_analyser; - xmlIO xml; - }; class Input{ public: + Input(const string &_desc): description(_desc){}; Node* connection; - + string description; }; class Image_input: public Input{ public: @@ -171,19 +111,42 @@ namespace Rotor { }; class Signal_input: public Input{ public: - + bool connect(Node* source); + Signal_input(const string &_desc): Input(_desc){}; + }; class Node{ public: + Node(){}; + virtual ~Node() {}; + Node(map &settings){ + cerr << "Node:"; + for (map::iterator it=settings.begin();it!=settings.end();++it) { + cerr << it->first << "," << it->second << " "; + } + cerr << endl; + description=settings["description"];type=settings["type"];output_type=settings["output"];ID=settings["ID"]; + }; UUID uid; //every usable node has a UUID int id; - vector inputs; //simple node has signal inputs and outputs - void get_output(float time); - void gather_inputs(float time) { + vector inputs; //simple node has signal inputs and outputs + void create_signal_input(const string &description) {inputs.push_back(new Signal_input(description));}; + void gather_inputs(const float &time) { for (uint i=0;iget_output(time); + if (inputs[i]->connection) inputs[i]->connection->get_output(time); + } + } + float get_output(const float &time) { //default is to pass through first input, if disconnected returns 0 + cerr << "getting output for " << type << "," << ID << endl; + if (inputs.size()) { + if (inputs[0]->connection) return inputs[0]->connection->get_output(time); } + return 0.0f; } + string description; + string type; + string output_type; + string ID; }; class Image{ char* data; @@ -191,7 +154,7 @@ namespace Rotor { class Image_node: public Node{ public: vector image_inputs; //image node also has image inputs and outputs - void gather_inputs(float time) { + void gather_inputs(const float &time) { Node::gather_inputs(time); for (uint i=0;iget_output(time); @@ -209,15 +172,144 @@ namespace Rotor { private: float image_time; }; + //actual nodes------------------------------------------------- + class Audio_analysis: public Node { + public: + Audio_analysis(){}; + Audio_analysis(map &settings) { + cerr << "Audio analysis:"; + for (map::iterator it=settings.begin();it!=settings.end();++it) { + cerr << it->first << "," << it->second << " "; + } + cerr << endl; + description=settings["description"];type=settings["type"];output_type=settings["output"];ID=settings["ID"]; + }; + float get_output(const float &time) { + float t=time; + return t; + } + }; + class Is_new_integer: public Node { + //does this require knowing what the framerate is? + //for now, assume 25 + //what to cache? for now, don't cache + public: + Is_new_integer(){}; + Is_new_integer(map &settings) { + cerr << "Is new integer:"; + for (map::iterator it=settings.begin();it!=settings.end();++it) { + cerr << it->first << "," << it->second << " "; + } + cerr << endl; + description=settings["description"];type=settings["type"];output_type=settings["output"];ID=settings["ID"]; + }; + float get_output(const float &time) { + if (((int)Node::get_output(time))>((int)Node::get_output(time-.04))) return 1.0f; + else return 0.0f; + } + }; + class Signal_output: public Node { + public: + Signal_output(){}; + Signal_output(map &settings) { + cerr << "Signal output:"; + for (map::iterator it=settings.begin();it!=settings.end();++it) { + cerr << it->first << "," << it->second << " "; + } + cerr << endl; + description=settings["description"];type=settings["type"];output_type=settings["output"];ID=settings["ID"]; + }; + + bool render(const float duration, const float framerate,string &xml_out); + }; + //------------------------------------------------------------------- + class Node_factory{ + public: + Node_factory(); + template + T* clone(T proto,map &settings) { + cerr << "Factory settings: "; + for (map::iterator it=settings.begin();it!=settings.end();++it) { + cerr << it->first << "," << it->second << " "; + } + cerr << endl; + return new T(settings); + } + void add_type(string type,Node proto){ + type_map[type]=proto; + }; + Node* create(map &settings){ + if (type_map.find(settings["type"])!=type_map.end()) { + return clone(type_map[settings["type"]],settings); + } + else return NULL; + }; + private: + map type_map; + }; class Graph{ public: - UUID uid; //every version of a graph has a UUID + Graph(){framerate=25.0f;duration=10.0f;}; + Graph(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 nodes; + Node* find(const string &type){ + for (std::unordered_map::iterator it=nodes.begin();it!=nodes.end();++it) { + if (it->second->type==type) return it->second; + } + return NULL; + }; + bool signal_render(const float _fr,string &signal_xml) { + if (_fr>.001) framerate=_fr; + if (find("signal_output")) { + Signal_output *signal_output=dynamic_cast(find("signal_output")); + return signal_output->render(duration,framerate,signal_xml); + } + else return false; + } + private: + Node_factory factory; + float framerate; + float duration; + }; + 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) { + state=IDLE; + }; + void runTask(); + void add_queue(int item); + Command_response session_command(const std::vector& command); + 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 + int load_audio(string filename); + Render_requirements get_requirements(); + int load_video(int num,string filename); //can be performance or clip + private: - std::unordered_map nodes; + int state; + float progress; //for a locking process: audio analysis or rendering + std::deque work_queue; + Poco::Mutex mutex; //lock for access from parent thread + std::string audio_filename; + vampHost::QMAnalyser audio_analyser; + xmlIO xml; + Graph graph; + Node_factory factory; }; } /* coding style Types begin with a capital, use underscore as a seperator +instances begin with a small letter */ diff --git a/rotord/rotord.cbp b/rotord/rotord.cbp index 5baac29..4f964e9 100644 --- a/rotord/rotord.cbp +++ b/rotord/rotord.cbp @@ -49,13 +49,22 @@ + + + + + + + + + diff --git a/rotord/xmlIO.cpp b/rotord/xmlIO.cpp index d83380c..3a7ec61 100755 --- a/rotord/xmlIO.cpp +++ b/rotord/xmlIO.cpp @@ -70,9 +70,9 @@ void xmlIO::clear(){ //--------------------------------------------------------- bool xmlIO::loadFile(const string& xmlFile){ - string fullXmlFile = ofToDataPath(xmlFile); + //string fullXmlFile = ofToDataPath(xmlFile); - bool loadOkay = doc.LoadFile(fullXmlFile); + bool loadOkay = doc.LoadFile(xmlFile); //theo removed bool check as it would //return false if the file exists but was @@ -88,8 +88,8 @@ bool xmlIO::loadFile(const string& xmlFile){ //--------------------------------------------------------- bool xmlIO::saveFile(const string& xmlFile){ - string fullXmlFile = ofToDataPath(xmlFile); - return doc.SaveFile(fullXmlFile); + //string fullXmlFile = ofToDataPath(xmlFile); + return doc.SaveFile(xmlFile); } //--------------------------------------------------------- -- cgit v1.2.3