#ifndef ROTOR_H #define ROTOR_H //definitions of base classes and types for rendering graph #include #include #include #include #include #include #include "Poco/Net/HTTPResponse.h" #include "Poco/Logger.h" #include "Poco/Path.h" #include "Poco/Base64Encoder.h" #include "xmlIO.h" #include "utils.h" #include "cvimage.h" #include "libavwrapper.h" namespace Rotor { //forward declarations class Node; class Signal_node; class Image_node; class Parameter; class Time_spec{ public: Time_spec(){}; Time_spec(float _time,float _framerate,float _duration){ time=_time; framerate=_framerate; duration=_duration;}; float time; //num/denom ? float framerate; float duration; 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) { time=_time; framerate=_framerate; duration=_duration; w=_w; h=_h;}; Frame_spec(int _frame,float _framerate,float _duration,int _w,int _h) { time=((float)_frame)/_framerate; framerate=_framerate; duration=_duration; w=_w; h=_h;}; 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; }; 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: virtual Node* clone(map &_settings)=0; //pure virtual virtual ~Node(){ duplicate_inputs=false; }; 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); }; 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,"")); } } for (auto p: parameters){ if (find_setting(settings,p.first,"")!="") { parameters[p.first]->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 *get_preview(const Frame_spec &frame); 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(){}; int channels,bits,samples,rate; }; //actual nodes------------------------------------------------- class Time: public Signal_node { public: Time(){ title="Time"; description="Outputs the time in seconds as a signal"; }; Time(map &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 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: }; class Video_cycler: public Image_node { public: Video_cycler(){ create_image_input("Image input","Image input"); create_signal_input("Selector","Selector input"); 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){ 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("colours","palette list of web colours","Colours","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["colours"]->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=255-((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 Transform: public Image_node { //what is the best coordinate system to use? //origin: corner or centre //units: pixel or fractional //aspect: scaled or homogenous public: Transform(){ create_image_input("image input","Image input"); create_parameter("transformX","number","X transformation","Transform X",0.0f); create_parameter("transformY","number","Y transformation","Transform X",0.0f); create_parameter("originX","number","X transformation origin","Origin X",0.5f); create_parameter("originY","number","Y transformation origin","Origin Y",0.5f); create_parameter("rotation","number","Rotation about origin","Rotation",0.0f); create_parameter("scale","number","Scale about origin","Scale",1.0f); title="Transform"; description="Apply 2D transformation"; }; Transform(map &settings):Transform() { base_settings(settings); }; ~Transform(){ }; Transform* clone(map &_settings) { return new Transform(_settings);}; Image *output(const Frame_spec &frame){ Image *in=image_inputs[0]->get(frame); if (in){ float tX=parameters["transformX"]->value; float tY=parameters["transformY"]->value; float oX=parameters["originX"]->value; float oY=parameters["originY"]->value; float r=parameters["rotation"]->value; float s=parameters["scale"]->value; //do opencv transform cv::Point2f srcTri[3], dstTri[3]; cv::Mat rot_mat(2,3,CV_32FC1); cv::Mat trans_mat(2,3,CV_32FC1); Image inter; inter.setup(in->w,in->h); // Compute matrix by creating triangle and transforming //is there a better way - combine the 2? Just a bit of geometry srcTri[0].x=0; srcTri[0].y=0; srcTri[1].x=in->w-1; srcTri[1].y=0; srcTri[2].x=0; srcTri[2].y=in->h-1; for (int i=0;i<3;i++){ dstTri[i].x=srcTri[i].x+(tX*in->w); dstTri[i].y=srcTri[i].y+(tY*in->h); } trans_mat=getAffineTransform( srcTri, dstTri ); warpAffine( in->rgb, inter.rgb, trans_mat, inter.rgb.size(), cv::INTER_LINEAR, cv::BORDER_WRAP); // Compute rotation matrix // cv::Point centre = cv::Point( oX*in->w, oY*in->h ); rot_mat = getRotationMatrix2D( centre, r, s ); // Do the transformation // warpAffine( inter.rgb, image.rgb, rot_mat, image.rgb.size(), cv::INTER_LINEAR, cv::BORDER_WRAP); //BORDER_WRAP //INTER_NEAREST - a nearest-neighbor interpolation //INTER_LINEAR - a bilinear interpolation (used by default) //INTER_AREA - resampling using pixel area relation. It may be a preferred method for image decimation, as it gives moire’-free results. But when the image is zoomed, it is similar to the INTER_NEAREST method. //INTER_CUBIC - a bicubic interpolation over 4x4 pixel neighborhood //INTER_LANCZOS4 - a Lanczos interpolation over 8x8 pixel neighborhood return ℑ } return nullptr; } private: //todo - quality settings }; 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.05f,0.0f,1.0f); title="Difference matte"; description="Create an alpha channel using a background reference picture"; }; Difference_matte(map &settings):Difference_matte() { base_settings(settings); }; ~Difference_matte(){}; 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){ //copy incoming image **writable Image *in2=image_inputs[1]->get(frame); if (in2) { cv::cvtColor(in1->rgb,greyfg,CV_RGB2GRAY); cv::cvtColor(in2->rgb,greybg,CV_RGB2GRAY); //cv::absDiff(greybg,greyfg,greyDiff); //adaptiveThreshold(greyDiff,parameters["threshold"].value,20,true,false); //int blockSize, int offset=0,bool invert=false, bool gauss=false); image.alpha_merge(*in2); return ℑ } //if there aren't 2 image inputs connected just return the first return in1; } return nullptr; } private: cv::Mat greyfg,greybg,greyDiff; }; #define VIDEOFRAMES_still 1 #define VIDEOFRAMES_blend 2 class Video_loader: public Image_node { public: Video_loader(){ create_parameter("speed","number","video playback speed","Speed",1.0f,0.0f,0.0f); create_parameter("framerate","number","framerate override","Frame rate",0.0f,0.0f,0.0f); create_attribute("filename","name of video file to load","File name",""); create_attribute("mode","frame mode","Mode","still",{"still","blend"}); title="Video loader"; description="Loads a video file"; }; Video_loader(map &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 mode; 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){ return image_inputs[0]->get(frame); }; 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 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; }; void list_nodes(xmlIO XML){ int i=0; for (auto& type: type_map) { //c++11 if (type.second->description!="") { //blank description = internal/ testing node 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(); i++; } } } 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); }; void init(const string& _uid,const string& _desc){ uid=_uid; description=_desc; duration=20.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 string &audio_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 set_resolution(int w,int h); bool loaded; float duration; const string toString(); xmlIO xml; 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); }; ~Audio_thumbnailer(){ delete[] data; }; 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(); uint8_t *data; int height,width,samples_per_column; int column,out_sample,sample,samples; int offset; double scale,accum; }; } #endif