#ifndef ROTOR_H #define ROTOR_H //definitions of base classes and types for rendering graph #include #include #include #include #include #include #include #include "Poco/Net/HTTPResponse.h" #include "Poco/Logger.h" #include "Poco/File.h" #include "Poco/Path.h" #include "Poco/Base64Encoder.h" #include "Poco/FileStream.h" #include "Poco/CountingStream.h" #include "Poco/StreamCopier.h" #include "xmlIO.h" #include "utils.h" #include "cvimage.h" #include "libavwrapper.h" //using namespace cv; namespace Rotor { //forward declarations class Node; class Signal_node; class Image_node; class Parameter; class Audio_frame{ public: Audio_frame(uint16_t *_samples,int _channels,int _numsamples){ samples=_samples; channels=_channels; numsamples=_numsamples; } uint16_t *samples; int channels,numsamples; }; class Time_spec{ public: Time_spec(){}; Time_spec(float _time,float _framerate,float _duration,Audio_frame *_audio=nullptr){ time=_time; framerate=_framerate; duration=_duration; audio=_audio;}; float time; //num/denom ? float framerate; float duration; Audio_frame *audio; Time_spec lastframe() const{ return Time_spec(time-(1.0f/framerate),framerate,duration); } int frame(){ return (int)((time*framerate)+0.5); //rounded to the nearest frame } }; class Frame_spec: public Time_spec{ public: Frame_spec(float _time,float _framerate,float _duration,int _w,int _h,Audio_frame *_audio=nullptr) { time=_time; framerate=_framerate; duration=_duration; w=_w; h=_h;audio=_audio;}; Frame_spec(int _frame,float _framerate,float _duration,int _w,int _h,Audio_frame *_audio=nullptr) { time=((float)_frame)/_framerate; framerate=_framerate; duration=_duration; w=_w; h=_h;audio=_audio;}; int h,w; Frame_spec lastframe(){ return Frame_spec(time-(1.0f/framerate),framerate,duration,w,h); } }; class Colour{ public: Colour(){ r=g=b=0; } Colour(int c){ r=c&0xFF; g=(c&0xFF00)>>8; b=(c&0xFF0000)>>16; } Colour(std::string s){ r=(uint8_t)ofHexToChar(s.substr(0,2)); g=(uint8_t)ofHexToChar(s.substr(2,2)); b=(uint8_t)ofHexToChar(s.substr(4,2)); } float Rfloat(){ return ((float)r)/255.0f; } float Gfloat(){ return ((float)g)/255.0f; } float Bfloat(){ return ((float)b)/255.0f; } uint8_t r,g,b; }; class Command_response{ public: Command_response() { status=Poco::Net::HTTPResponse::HTTP_OK; } std::string description; Poco::Net::HTTPResponse::HTTPStatus status; }; class Input{ public: Input(const string &_desc,const string &_title): connection(nullptr),description(_desc),title(_title){}; Node* connection; string description; string title; }; class Image_input: public Input{ public: virtual ~Image_input(){}; bool connect(Node *source); Image_input(const string &_desc,const string &_title,Node* _connect): Input(_desc,_title){ connect(_connect); }; Image* get(const Frame_spec& time); }; class Signal_input: public Input{ public: virtual ~Signal_input(){}; bool connect(Node *source); Signal_input(const string &_desc,const string &_title,Node* _connect): Input(_desc,_title){ connect(_connect); }; float get(const Time_spec& time); }; class Parameter: public Signal_input{ public: virtual ~Parameter(){}; void init(const float &_val){ value=_val; } Parameter(const string &_type,const string &_desc,const string &_title,float _value,float _min,float _max,Node* _connect): Signal_input(_desc,_title,_connect),value(_value),min(_min),max(_max),type(_type){}; float value,min,max; float get(const Time_spec& time); string type; }; class Attribute{ //description of a static attribute which can be an enumerated string array public: virtual ~Attribute(){}; Attribute(const string &_desc,const string &_title,const string &_value,std::vector _vals={}): description(_desc),title(_title),value(_value),intVal(0){ vals=_vals; init(_value); }; void init(const string &_key){ //inits int value from set::string vals index value=_key; std::vector::iterator it=it = find(vals.begin(),vals.end(),value); if (it!=vals.end()){ intVal = std::distance(vals.begin(),it)+1; //using 1-index for enums } else intVal=0; } string value,description,title; std::vector vals; int intVal; }; class Node{ public: Node(){duplicate_inputs=false;}; virtual Node* clone(map &_settings)=0; //pure virtual virtual ~Node(){ }; vector inputs; //simple node can have signal inputs, output depends on node type unordered_map parameters; //linked parameters can convert from settings to inputs unordered_map attributes; void create_signal_input(const string &_desc,const string &_title,Node* _connect=nullptr ) { inputs.push_back(new Signal_input(_desc,_title,_connect)); }; void create_parameter(const string &_name,const string &_type,const string &_desc,const string &_title,float _value=1.0f,float _min=0.0f,float _max=0.0f,Node* _connect=nullptr) { parameters[_name]=new Parameter(_type,_desc,_title,_value,_min,_max,_connect); }; void create_attribute(const string &_attr,const string &_desc,const string &_title,const string &_value,std::vector _vals={}) { attributes[_attr]=new Attribute(_desc,_title,_value,_vals); }; void create_attribute(string *alias,const string &_attr,const string &_desc,const string &_title,const string &_value,std::vector _vals={}) { attributes[_attr]=new Attribute(_desc,_title,_value,_vals); alias=&(attributes[_attr]->value); }; void create_attribute(int *alias,const string &_attr,const string &_desc,const string &_title,const string &_value,std::vector _vals={}) { attributes[_attr]=new Attribute(_desc,_title,_value,_vals); alias=&(attributes[_attr]->intVal); }; string description; string type; string ID; string title; bool duplicate_inputs; string find_setting(map &settings,string key,string def=""){ if (settings.find(key)!=settings.end()) return settings[key]; else return def;}; float find_setting(map &settings,string key,float def){ if (settings.find(key)!=settings.end()) return ofToFloat(settings[key]); else return def;}; int find_setting(map &settings,string key,int def){ if (settings.find(key)!=settings.end()) return ofToInt(settings[key]); else return def;}; void base_settings(map &settings) { description=find_setting(settings,"description"); type=find_setting(settings,"type"); ID=find_setting(settings,"ID"); title=find_setting(settings,"title"); for (auto a: attributes){ if (find_setting(settings,a.first,"")!="") { attributes[a.first]->init(find_setting(settings,a.first,"")); cerr<<"setting attribute '"<intVal<<")"<init(find_setting(settings,p.first,0.0f)); cerr<<"setting parameter '"<get(time); } } virtual void set_parameter(const std::string &key,const std::string &value){}; }; class Signal_node: public Node{ public: virtual ~Signal_node(){}; const float get_output(const Time_spec &time) { update(time); return output(time); }; virtual const float output(const Time_spec &time) { return 0.0f; }; }; class Image_node: public Node{ public: virtual ~Image_node(){}; vector image_inputs; //image node also has image inputs and outputs void create_image_input(const string &_title,const string &_desc,Node* _connect=nullptr) { image_inputs.push_back(new Image_input(_desc,_title,_connect)); }; Image *get_output(const Frame_spec &frame) { image.setup(frame.w,frame.h); update((Time_spec)frame); return output(frame); } virtual const Image *output(const Frame_spec &frame)=0; Image image; private: float image_time; //? could be used to detect image reuse? }; class Audio_processor: public Signal_node { public: virtual Audio_processor(){}; virtual ~Audio_processor(){}; virtual int process_frame(uint8_t *data,int samples)=0; virtual bool init(int _channels,int _bits,int _samples,int _rate)=0; virtual void cleanup()=0; virtual void print_summary(){}; virtual string get_features(){}; int channels,bits,samples,rate; }; class LUT { LUT(){ lut=nullptr; }; ~LUT(){if (lut) { delete[] lut;} }; void generate(float black_in,float white_in,float black_out,float white_out,float gamma){ //can check here if anything has changed if (lut) delete[] lut; lut=new unsigned char[256]; float fltmax=(255.0f/256.0f); for (int i=0;i<256;i++){ lut[i]=(unsigned char)(((pow(min(fltmax,max(0.0f,(((((float)i)/256.0f)-black_in)/(white_in-black_in)))),(1.0/gamma))*(white_out-black_out))+black_out)*255.0f); } } void apply(const cv::Mat& in,cv::Mat &out){ //facility to apply to other images for inherited classes out.create(in.rows,in.cols,in.type()); for (int i=0;i &settings): Time() { base_settings(settings); }; Time* clone(map &_settings) { return new Time(_settings);}; const float output(const Time_spec &time) { return time.time; } }; class Track_time: public Signal_node { public: Track_time(){ title="Track time"; description="Outputs the fraction of the track as a signal"; }; Track_time(map &settings): Track_time() { base_settings(settings); }; Track_time* clone(map &_settings) { return new Track_time(_settings);}; const float output(const Time_spec &time) { return time.time/time.duration; } }; class At_track_time: public Signal_node { public: At_track_time(){ create_signal_input("signal","Signal Input"); create_parameter("time","number","Track time to evaluate","Time",0.0f); title="@Track time"; description="Gets input from a different point in the track"; }; At_track_time(map &settings): At_track_time() { base_settings(settings); }; At_track_time* clone(map &_settings) { return new At_track_time(_settings);}; const float output(const Time_spec &time) { Time_spec t=Time_spec(parameters["time"]->value*time.duration,time.framerate,time.duration); return inputs[0]->get(t); } }; class Signal_output: public Signal_node { public: Signal_output(){ create_signal_input("signal","Signal Input"); title="Signal output"; description="Outputs a signal to xml for testing"; }; Signal_output(map &settings): Signal_output() { base_settings(settings); }; Signal_output* clone(map &_settings) { return new Signal_output(_settings);}; bool render(const float duration, const float framerate,string &xml_out); const float output(const Time_spec &time) { return inputs[0]->get(time); } }; class Testcard: public Image_node { public: Testcard(){ //internal testing node only }; Testcard(map &settings): Testcard() { base_settings(settings); }; ~Testcard(){}; Testcard* clone(map &_settings) { return new Testcard(_settings);}; Image *output(const Frame_spec &frame){ float hs=(255.0f/frame.h); for (int i=0;iAdata[i*frame.w+j]=(uint8_t)255; //image->Zdata[i*frame.w+j]=(uint16_t)512; //1.0 in fixed point 8.8 bits } } return ℑ } private: }; class Invert: public Image_node { public: Invert(){ create_image_input("Image to invert","Image input"); create_parameter("invert","number","Invert when greater than 0.0","Negative",1.0f,0.0f,1.0f); title="Negative"; description="Inverts the input picture"; }; Invert(map &settings) :Invert() { base_settings(settings); }; ~Invert(){}; Invert* clone(map &_settings) { return new Invert(_settings);}; Image *output(const Frame_spec &frame){ Image *in=image_inputs[0]->get(frame); if (in) { if (parameters["invert"]->value>0.0f){ for (int i=0;iw*in->h*3;i++) { image.RGBdata[i]=255-in->RGBdata[i]; } return ℑ } return in; } return nullptr; } private: }; #define CYCLER_cut 1 #define CYCLER_mix 2 class Video_cycler: public Image_node { public: Video_cycler(){ create_image_input("Image input","Image input"); create_signal_input("Selector","Selector input"); create_attribute("mode","Cycling mode {cut|mix}","Mode","cut",{"cut","mix"}); title="Video cycler"; description="Cycles through video inputs according to selector signal"; duplicate_inputs=true; } Video_cycler(map &settings):Video_cycler() { base_settings(settings); }; ~Video_cycler(){}; bool load(const string &filename); Image *output(const Frame_spec &frame){ if (attributes["mode"]->intVal==CYCLER_mix&&image_inputs.size()>1){ int im1=((int)inputs[0]->get((Time_spec)frame))%image_inputs.size(); int im2=(im1+1)%image_inputs.size(); float f=fmod(inputs[0]->get((Time_spec)frame),1.0f); Image *in1=image_inputs[im1]->get(frame); if (in1){ Image *in2=image_inputs[im2]->get(frame); if (in2){ image=(*in1); image*=(1.0f-f); Image i2=(*in2); i2*=f; image+=i2; return ℑ } return in1; } return nullptr; } return image_inputs[((int)inputs[0]->get((Time_spec)frame))%image_inputs.size()]->get(frame); } Video_cycler* clone(map &_settings) { return new Video_cycler(_settings);}; }; class Signal_colour: public Image_node { public: Signal_colour(){ create_signal_input("Selector","Selector input"); create_attribute("palette","palette list of web colours","Colour palette","000000"); title="Signal colour"; description="Cycles through a palette of background colours according to selector signal"; }; Signal_colour(map &settings):Signal_colour() { base_settings(settings); for (int i=0;ivalue.size()/6;i++){ palette.push_back(Colour(attributes["palette"]->value.substr(i*6,6))); } prevcol=-1; }; ~Signal_colour(){}; Image *output(const Frame_spec &frame){ if (palette.size()) { int col=((int)inputs[0]->get((Time_spec)frame))%palette.size(); //if (col!=prevcol){ //how about when starting a new render? for (int i=0;i &_settings) { return new Signal_colour(_settings);}; private: vector palette; int prevcol; }; class Signal_greyscale: public Image_node { //Draws signal bars in greyscale public: Signal_greyscale(){ create_signal_input("Signal","Signal input"); title="Signal greyscale"; description="Renders signal level as greyscale background"; }; Signal_greyscale(map &settings):Signal_greyscale() { base_settings(settings); prevcol=-1; }; ~Signal_greyscale(){}; Image *output(const Frame_spec &frame){ uint8_t col=((uint8_t)(inputs[0]->get((Time_spec)frame)*255.0f)); if (col!=prevcol){ //how about when starting a new render? for (int i=0;i &_settings) { return new Signal_greyscale(_settings);}; private: uint8_t prevcol; }; #define ARITHMETIC_plus 1 #define ARITHMETIC_minus 2 #define ARITHMETIC_multiply 3 #define ARITHMETIC_divide 4 #define ARITHMETIC_modulo 5 class Image_arithmetic: public Image_node { public: Image_arithmetic(){ create_image_input("image input","Image input"); create_parameter("value","number","Value or signal for operation","Value",1.0f); create_attribute("operator","operator for image","Operator","+",{"+","-","*","/"}); title="Image arithmetic"; description="Performs arithmetic on an image with a signal or value"; }; Image_arithmetic(map &settings):Image_arithmetic() { base_settings(settings); } ~Image_arithmetic(){}; Image *output(const Frame_spec &frame){ Image *in=image_inputs[0]->get(frame); if (in){ switch (attributes["operator"]->intVal) { case ARITHMETIC_plus: image=(*in); //could be poss without copy? image+=parameters["value"]->value; break; case ARITHMETIC_minus: image=(*in); image-=parameters["value"]->value; break; case ARITHMETIC_multiply: image=(*in); image*=parameters["value"]->value; break; case ARITHMETIC_divide: image=(*in); image/=parameters["value"]->value; break; } } return ℑ } Image_arithmetic* clone(map &_settings) { return new Image_arithmetic(_settings);}; private: }; #define BLEND_blend 1 #define BLEND_screen 2 #define BLEND_multiply 3 #define BLEND_alpha 4 #define BLEND_wrap 5 #define BLEND_xor 6 class Blend: public Image_node { public: Blend(){ create_image_input("image input 1","Image input 1"); create_image_input("image input 2","Image input 2"); create_parameter("amount","number","amount to blend input 2","Blend amount",0.5f,0.0f,1.0f); create_attribute("mode","Blend mode","Blend mode","blend",{"blend","screen","multiply","alpha","wrap","xor"}); title ="Blend"; description="Blend images in various modes"; }; Blend(map &settings):Blend() { base_settings(settings); }; ~Blend(){}; Blend* clone(map &_settings) { return new Blend(_settings);}; Image *output(const Frame_spec &frame){ Image *in1=image_inputs[0]->get(frame); if (in1){ Image *in2=image_inputs[1]->get(frame); if (in2) { image=*(in1); switch(attributes["mode"]->intVal){ case BLEND_screen: image+=(*in2); break; case BLEND_multiply: image*=(*in2); break; case BLEND_xor: image^=(*in2); break; case BLEND_alpha: image=image.alpha_blend(*in2); break; case BLEND_wrap: image=image.add_wrap(*in2); break; case BLEND_blend: //has to be last because of initialser of *in? go figure image*=(1.0f-parameters["amount"]->value); Image *in=(*in2)*parameters["amount"]->value; image+=(*in); delete in; break; } return ℑ } //if there aren't 2 image inputs connected just return the first return in1; } return nullptr; } private: }; #define MIRROR_horiz 1 #define MIRROR_vert 2 #define MIRROR_horizR 3 #define MIRROR_vertR 4 class Mirror: public Image_node { public: Mirror(){ create_image_input("image input","Image input"); create_attribute("mode","Mirror mode","Mirror mode","horiz",{"horiz","vert","horizR","vertR"}); title="Mirror"; description="Mirror video across a central axis"; }; Mirror(map &settings):Mirror() { base_settings(settings); }; ~Mirror(){ }; Mirror* clone(map &_settings) { return new Mirror(_settings);}; Image *output(const Frame_spec &frame){ Image *in=image_inputs[0]->get(frame); if (in){ //copy incoming image **writable image=(*in); //could be more efficient here by only copying once switch (attributes["mode"]->intVal) { case MIRROR_horiz: for (int i=0;i &settings):Monochrome() { base_settings(settings); }; ~Monochrome(){ }; Monochrome* clone(map &_settings) { return new Monochrome(_settings);}; Image *output(const Frame_spec &frame){ Image *in=image_inputs[0]->get(frame); if (in){ for (int i=0;iRGBdata[(((j*image.w)+i)*3)+l]]; for (int k=0;k<3;k++) image.RGBdata[(((j*image.w)+i)*3)+k]=luma; } } return ℑ } return nullptr; } private: }; class Alpha_merge: public Image_node { public: Alpha_merge(){ create_image_input("image input","Image input"); create_image_input("alpha input","Alpha input"); title="Alpha merge"; description="Alpha merge two images"; }; Alpha_merge(map &settings):Alpha_merge() { base_settings(settings); }; ~Alpha_merge(){}; Alpha_merge* clone(map &_settings) { return new Alpha_merge(_settings);}; Image *output(const Frame_spec &frame){ Image *in1=image_inputs[0]->get(frame); if (in1){ //copy incoming image **writable Image *in2=image_inputs[1]->get(frame); if (in2) { image=(*in1); image.alpha_merge(*in2); return ℑ } //if there aren't 2 image inputs connected just return the first return in1; } return nullptr; } private: }; class Difference_matte: public Image_node { public: Difference_matte(){ create_image_input("image input","Image input"); create_image_input("background input","Background input"); create_parameter("threshold","number","Difference threshold","Threshold",0.2f,0.0f,1.0f); create_parameter("feather","number","Feather width","Feather",0.1f,0.0f,1.0f); create_parameter("weight_h","number","H component weight","Weight H",0.5f,0.0f,1.0f); create_parameter("weight_s","number","S component weight","Weight S",0.5f,0.0f,1.0f); create_parameter("weight_v","number","V component weight","Weight V",0.5f,0.0f,1.0f); create_parameter("blursize","number","Blur size","Blur size",2.0f,0.0f,10.0f); create_attribute("mode","Output {image|alpha}","output mode","alpha",{"image","alpha"}); title="Difference matte"; description="Create an alpha channel using a background reference picture"; LUT=nullptr; }; Difference_matte(map &settings):Difference_matte() { base_settings(settings); }; ~Difference_matte(){if (LUT) delete[] LUT;}; Difference_matte* clone(map &_settings) { return new Difference_matte(_settings);}; Image *output(const Frame_spec &frame){ Image *in1=image_inputs[0]->get(frame); if (in1){ Image *in2=image_inputs[1]->get(frame); if (in2) { generate_LUT(); /* cv::cvtColor(in1->rgb,greyfg,CV_RGB2GRAY); cv::cvtColor(in2->rgb,greybg,CV_RGB2GRAY); cv::absdiff(greyfg,greybg,greyDiff); //parameters["threshold"]->value cv::threshold(greyDiff,mask,parameters["threshold"]->value,255,CV_THRESH_BINARY); //int block_size=3, double param1=5); //int blockSize, int offset=0,bool invert=false, bool gauss=false); //cv::adaptiveThreshold(greyDiff,mask,255,CV_ADAPTIVE_THRESH_GAUSSIAN_C,CV_THRESH_BINARY, 3,5); //int block_size=3, double param1=5); //int blockSize, int offset=0,bool invert=false, bool gauss=false); */ cv::cvtColor(in1->rgb, hsv1, CV_RGB2HSV); cv::cvtColor(in2->rgb, hsv2, CV_RGB2HSV); mask.create(frame.h,frame.w,CV_8UC1); lutmask.create(frame.h,frame.w,CV_8UC1); //get euclidean distance in HSV space int dist,d; uint8_t m; float weights[3] = {parameters["weight_h"]->value,parameters["weight_s"]->value,parameters["weight_v"]->value}; float weight_total=255.0f/pow(pow(weights[0]*255,2)+pow(weights[1]*255,2)+pow(weights[2]*255,2),0.5); for (int i=0;ivalue/2.0)*2)+1,1.0); //nb this doesn't do the intended: create 'continuously variable' blur cv::GaussianBlur(mask,filtmask,cvSize(ksize,ksize),parameters["blursize"]->value); for (int i=0;ivalue=="image"){ cv::cvtColor(lutmask, image.rgb, CV_GRAY2RGB); } else image.alpha_from_cv(lutmask); return ℑ } //if there aren't 2 image inputs connected just return the first return in1; } return nullptr; } void generate_LUT(){ //can check here if anything has changed //cerr<<"generating LUT: threshold "<value<<", feather "<value<value-(parameters["feather"]->value*0.5f)); float maxf=min(1.0f,parameters["threshold"]->value+(parameters["feather"]->value*0.5f)); for (int i=0;i<256;i++){ LUT[i]=(uint8_t)(min(1.0f,max(0.0f,((((float)i)/255.0f)-minf)/(maxf-minf)))*255.0f); // cerr<<((int)LUT[i])<<" "; } //cerr< &settings): Video_loader() { base_settings(settings); isLoaded=false; if (attributes["filename"]->value!="") { load(find_setting(settings,"media_path","")+attributes["filename"]->value); } lastframe=0; }; ~Video_loader(){}; bool load(const string &filename); Image *output(const Frame_spec &frame); Video_loader* clone(map &_settings) { return new Video_loader(_settings);}; bool isLoaded; private: libav::decoder player; int lastframe; }; class Video_output: public Image_node { public: Video_output(){ create_image_input("image to output","Image input"); title="Video output"; description="Outputs to video from here"; }; Video_output(map &settings):Video_output() { base_settings(settings); }; ~Video_output(){ }; Image *output(const Frame_spec &frame){ Image *in=image_inputs[0]->get(frame); if (in){ //make copy of the image, for feedback //optimise? image=(*in); return ℑ } return nullptr; }; Video_output* clone(map &_settings) { return new Video_output(_settings);}; bool render(const float duration, const float framerate,const string &output_filename,const string &audio_filename,float& progress,int w,int h); private: }; class Video_feedback: public Image_node { public: Video_feedback(){ title="Video feedback"; description="Repeats output of the last frame"; feedback=nullptr; }; Video_feedback(map &settings):Video_feedback() { base_settings(settings); }; ~Video_feedback(){ }; void set_feedback(Image *iptr){ feedback=iptr; } Image *output(const Frame_spec &frame){ if (feedback->RGBdata){ return feedback; } image.setup(frame.w,frame.h); image.clear(); return ℑ }; Video_feedback* clone(map &_settings) { return new Video_feedback(_settings);}; private: Image *feedback; }; //------------------------------------------------------------------- class Node_factory{ public: Node_factory(); ~Node_factory(){ for (auto t:type_map) delete t.second; } void add_type(string type,Node* proto){ type_map[type]=proto; }; Node *create(map &settings){ if (settings.find("type")!=settings.end()) { if (type_map.find(settings["type"])!=type_map.end()) { return type_map[settings["type"]]->clone(settings); } } return NULL; }; bool list_node(const string &t,xmlIO XML){ for (auto& type: type_map) { if (type.first==t) { list_node(type,XML); return true; } } XML.addValue("error","Node /"+t+"/ not found"); }; void list_node(std::pair, Rotor::Node*> &type,xmlIO XML,int i=0){ XML.addTag("node"); XML.addAttribute("node","type",type.first,i); XML.addAttribute("node","inputs",type.second->duplicate_inputs?"expandable":"fixed",i); XML.addAttribute("node","title",type.second->title,i); XML.addAttribute("node","description",type.second->description,i); if (dynamic_cast (type.second)!=nullptr) XML.addAttribute("node","output","signal",i); if (dynamic_cast (type.second)!=nullptr) XML.addAttribute("node","output","image",i); XML.pushTag("node",i); //if (type.second->description!="") { // XML.addTag("description"); // XML.setValue("description",type.second->description,0); //} int j=0; for (auto& input: type.second->inputs) { XML.addTag("signal_input"); XML.addAttribute("signal_input","title",input->title,j); XML.addAttribute("signal_input","description",input->description,j); j++; } j=0; if (dynamic_cast (type.second)!=nullptr) { for (auto& input: (dynamic_cast(type.second))->image_inputs) { XML.addTag("image_input"); XML.addAttribute("image_input","title",input->title,j); XML.addAttribute("image_input","description",input->description,j); j++; } } j=0; for (auto& parameter: type.second->parameters) { XML.addTag("parameter"); XML.addAttribute("parameter","name",parameter.first,j); XML.addAttribute("parameter","type",parameter.second->type,j); XML.addAttribute("parameter","title",parameter.second->title,j); XML.addAttribute("parameter","description",parameter.second->description,j); XML.addAttribute("parameter","value",parameter.second->value,j); XML.addAttribute("parameter","min",parameter.second->min,j); XML.addAttribute("parameter","max",parameter.second->max,j); j++; } j=0; for (auto& attribute: type.second->attributes) { XML.addTag("attribute"); XML.addAttribute("attribute","name",attribute.first,j); XML.addAttribute("attribute","title",attribute.second->title,j); XML.addAttribute("attribute","description",attribute.second->description,j); XML.addAttribute("attribute","value",attribute.second->value,j); if (attribute.second->vals.size()){ //document attribute enumeration XML.addAttribute("attribute","type","enum",j); XML.pushTag("attribute",j); int k=0; for (auto val: attribute.second->vals){ XML.addTag("option"); XML.addAttribute("option","value",val,k); k++; } XML.popTag(); } else XML.addAttribute("attribute","type","string",j); j++; } XML.popTag(); } void list_nodes(xmlIO XML){ int i=0; for (auto& type: type_map) { if (type.second->description!="") { //blank description = internal/ testing node list_node(type,XML,i); i++; } } } void list_nodes(Json::Value &JSON){ JSON["nodeslist"]=Json::arrayValue; for (auto& type: type_map) { if (type.second->description!="") { //blank description = internal/ testing node Json::Value node; node["type"]=type.first; node["title"]=type.second->title; node["inputs"]=type.second->duplicate_inputs?"expandable":"fixed"; if (dynamic_cast (type.second)!=nullptr) node["output"]="signal"; if (dynamic_cast (type.second)!=nullptr) node["output"]="image"; node["description"]=type.second->description; if (type.second->inputs.size()){ node["signal_inputs"]=Json::arrayValue; for (auto& input: type.second->inputs) { Json::Value signal_input; signal_input["title"]=input->title; signal_input["description"]=input->description; node["signal_inputs"].append(signal_input); } } if (dynamic_cast (type.second)!=nullptr) { if ((dynamic_cast(type.second))->image_inputs.size()){ node["image_inputs"]=Json::arrayValue; for (auto& input: (dynamic_cast(type.second))->image_inputs) { Json::Value image_input; image_input["title"]=input->title; image_input["description"]=input->description; node["image_inputs"].append(image_input); } } } if (type.second->parameters.size()){ node["parameters"]=Json::arrayValue; for (auto& param: type.second->parameters) { Json::Value parameter; parameter["name"]=param.first; parameter["type"]=param.second->type; parameter["title"]=param.second->title; parameter["description"]=param.second->description; parameter["value"]=param.second->value; parameter["min"]=param.second->min; parameter["max"]=param.second->max; node["parameters"].append(parameter); } } if (type.second->attributes.size()){ node["attributes"]=Json::arrayValue; for (auto& attr: type.second->attributes) { Json::Value attribute; attribute["name"]=attr.first; attribute["title"]=attr.second->title; attribute["description"]=attr.second->description; attribute["value"]=attr.second->value; if (attr.second->vals.size()){ //document attribute enumeration attribute["type"]="enum"; attribute["options"]=Json::arrayValue; for (auto val: attr.second->vals){ attribute["options"].append(val); } } else attribute["type"]="string"; node["attributes"].append(attribute); } } JSON["nodeslist"].append(node); } } } private: unordered_map type_map; }; class Graph{ public: Graph(){duration=20.0f;loaded = false;outW=640;outH=360;}; Graph(const string& _uid,const string& _desc){ init(_uid,_desc); audio_loaded=false; }; void init(const string& _uid,const string& _desc){ uid=_uid; description=_desc; duration=20.0f; framerate=25.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; vector find_nodes(const string &type); //could be a way of finding a set based on capabilities? Node* find_node(const string &type); bool signal_render(string &signal_xml,const float framerate); bool video_render(const string &output_filename,const float framerate,float& progress); bool load(string data,string media_path); bool loadFile(string &filename,string media_path); bool parseXml(string media_path); bool parseJson(string &data,string &media_path); bool set_resolution(int w,int h); bool preview(xmlIO &XML,string &node,string &format,int frame,int w,int h); bool check_audio(string audio,string path); bool print_features(xmlIO &XML,string &node); bool loaded; float duration; float framerate; const string toString(); xmlIO xml; bool audio_loaded; string audio_filename; private: Node_factory factory; int outW,outH; }; class Audio_thumbnailer: public Audio_processor { public: Audio_thumbnailer(){ height=128; width=512; //fit data=new uint8_t[height*width]; memset(data,0,height*width); vectordata =new float[width]; }; ~Audio_thumbnailer(){ delete[] data; delete[] vectordata; }; Audio_thumbnailer* clone(map &_settings) { return new Audio_thumbnailer();}; bool init(int _channels,int _bits,int _samples,int _rate); void cleanup(){}; int process_frame(uint8_t *data,int samples_in_frame); string print(); void print_vector(xmlIO XML); uint8_t *data; float *vectordata; int height,width,samples_per_column; int column,out_sample,sample,samples; int offset; double scale,accum; }; } #endif