diff options
Diffstat (limited to 'rotord/rotor.h')
| -rwxr-xr-x | rotord/rotor.h | 1391 |
1 files changed, 0 insertions, 1391 deletions
diff --git a/rotord/rotor.h b/rotord/rotor.h deleted file mode 100755 index f922fcf..0000000 --- a/rotord/rotor.h +++ /dev/null @@ -1,1391 +0,0 @@ -#ifndef ROTOR_H -#define ROTOR_H - -/* -nodes can have many inputs but only 1 output - -image nodes that use an image as input can pass on the incoming image only if its unchanged. - -TODO - parameter class that automatically links variable to correctly named inputs -TODO - use try.. catch and dynamic_cast to verify node connections rather than checking 'type' tag - -TODO - put the boilerplate code for checking inputs into the base class, finally call checked_output - -http://stackoverflow.com/questions/5261658/how-to-seek-in-ffmpeg-c-c -*/ - -#include <unordered_map> -#include <deque> -#include <math.h> -#include <memory> -#include <sys/time.h> -#include <iostream> - -#include "Poco/Net/HTTPServer.h" -#include "Poco/Net/HTTPResponse.h" -#include "Poco/UUID.h" -#include "Poco/UUIDGenerator.h" -#include "Poco/Notification.h" -#include "Poco/NotificationCenter.h" -#include "Poco/Observer.h" -#include "Poco/ThreadPool.h" -#include "Poco/Thread.h" -#include "Poco/Task.h" -#include "Poco/Runnable.h" -#include "Poco/Mutex.h" -#include "Poco/Random.h" -#include "Poco/AutoPtr.h" -#include "Poco/File.h" -#include "Poco/Base64Encoder.h" -#include "Poco/Path.h" -#include "Poco/StringTokenizer.h" -#include "Poco/Logger.h" - - -using Poco::UUID; -using Poco::UUIDGenerator; -using Poco::Net::HTTPResponse; -using Poco::Logger; - -/* -extern "C" { - #include <libavcodec/avcodec.h> - #include <libavformat/avformat.h> - #include <libavutil/opt.h> - #include <libavutil/channel_layout.h> - #include <libavutil/common.h> - #include <libavutil/imgutils.h> - #include <libavutil/mathematics.h> - #include <libavutil/samplefmt.h> - - #include <libavutil/dict.h> - //#include <libavutil/dict.c> stops the compiler error but causes a linker error. does libavcodec need to be statically linked? - #include <libavutil/imgutils.h> - #include <libavutil/samplefmt.h> - //#include <libavutil/timestamp.h> -} -*/ - - -#define AUDIO_INBUF_SIZE 20480 -#define AUDIO_REFILL_THRESH 4096 - -#include "xmlIO.h" -#include "utils.h" //fequal -#include "libavwrapper.h" -#include "cvimage.h" - -namespace Rotor { - #define IDLE 0 - #define ANALYSING_AUDIO 1 - #define AUDIO_READY 2 - #define CREATING_PREVIEW 3 - #define PREVIEW_READY 4 - #define RENDERING 5 - #define RENDER_READY 6 - - #define ANALYSE_AUDIO 1 - #define PREVIEW 2 - #define RENDER 3 - - //forward declaration - class Node; - class Signal_node; - class Image_node; - class Parameter_input; - - //http://blog.tomaka17.com/2012/03/libavcodeclibavformat-tutorial/ - /* struct Packet { - explicit Packet(AVFormatContext* ctxt = nullptr) { - av_init_packet(&packet); - packet.data = nullptr; - packet.size=0; - if (ctxt) reset(ctxt); - } - - Packet(Packet&& other) : packet(std::move(other.packet)) { - other.packet.data = nullptr; - } - - ~Packet() { - if (packet.data) - av_free_packet(&packet); - } - - void reset(AVFormatContext* ctxt) { - if (packet.data) - av_free_packet(&packet); - if (av_read_frame(ctxt, &packet) < 0) - packet.data = nullptr; - } - - AVPacket packet; - }; - */ - class Time_spec{ - public: - Time_spec(){}; - Time_spec(float _time,float _framerate,float _duration){ time=_time; framerate=_framerate; duration=_duration;}; - float time; - float framerate; - float duration; - Time_spec lastframe() const{ - return Time_spec(time-(1.0f/framerate),framerate,duration); - } - }; - 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;}; - //Frame_spec(time,_framerate,_duration,_w,_h);}; - - //float time; //this hould probably be implemented with a num/denom scheme eventually for accuracy - //float framerate; - int h,w; - //Frame_spec lastframe(){ - // return Frame_spec(time-(1.0f/framerate),framerate,w,h); - //} - int frame(){ - return (int)((time*framerate)+0.5); //rounded to the nearest frame - } - }; - 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)); - } - uint8_t r,g,b; - }; - - class Render_status{ - public: - int id; - float progress; - }; - class Render_requirements{ - public: - int num_performances; - int num_clips; - }; - 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): connection(nullptr),description(_desc){}; - Node* connection; - string description; - }; - class Image_input: public Input{ - public: - bool connect(Image_node *source); - Image_input(const string &_desc): Input(_desc){}; - }; - class Signal_input: public Input{ - public: - bool connect(Signal_node *source); - Signal_input(const string &_desc): Input(_desc){}; - }; - class Parameter_input: public Signal_input{ - public: - Parameter_input(const string &_param,const string &_desc): Signal_input(_desc),receiver(nullptr),parameter(_param){}; - float *receiver; - void update(const Time_spec& time); - string parameter; - }; - class Node{ - public: - virtual Node* clone(map<string,string> &_settings)=0; - virtual ~Node(){}; - UUID uid; //every usable node has a UUID - int id; - vector<Signal_input*> inputs; //simple node can have signal inputs, output depends on node type - vector<Parameter_input*> parameter_inputs; //linked parameters can convert from settings to inputs - void create_signal_input(const string &description) {inputs.push_back(new Signal_input(description));}; - void create_parameter_input(const string ¶meter,const string &description) {parameter_inputs.push_back(new Parameter_input(parameter,description));}; - string description; - string type; - string output_type; - string ID; - string find_setting(map<string,string> &settings,string key,string def=""){ if (settings.find(key)!=settings.end()) return settings[key]; else return def;}; - float find_setting(map<string,string> &settings,string key,float def){ if (settings.find(key)!=settings.end()) return ofToFloat(settings[key]); else return def;}; - int find_setting(map<string,string> &settings,string key,int def){ if (settings.find(key)!=settings.end()) return ofToInt(settings[key]); else return def;}; - void base_settings(map<string,string> &settings) { - description=find_setting(settings,"description"); - type=find_setting(settings,"type"); - output_type=find_setting(settings,"output"); - ID=find_setting(settings,"ID"); - } - virtual void set_parameter(const std::string &key,const std::string &value){}; - virtual void link_params(){}; //TODO make param classes that link automatically - void update_params(const Time_spec& time); - }; - class Signal_node: public Node{ - public: - virtual ~Signal_node(){}; - const float get_output(const Time_spec &time) { update_params(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_input*> image_inputs; //image node also has image inputs and outputs - void create_image_input(const string &description) {image_inputs.push_back(new Image_input(description));}; - Image *get_output(const Frame_spec &frame) { update_params((Time_spec)frame); return output(frame); }; - virtual const Image *output(const Frame_spec &frame)=0; - Image *get_preview(const Frame_spec &frame); - Image *image; //this can be privately allocated or just passed on as the node see fit - private: - float image_time; - }; - class Base_audio_processor: public Signal_node { - public: - virtual ~Base_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(){}; - Time(map<string,string> &settings) { - base_settings(settings); - }; - Time* clone(map<string,string> &_settings) { return new Time(_settings);}; - const float output(const Time_spec &time) { - return time.time; - } - }; - class Track_time: public Signal_node { - public: - Track_time(){}; - Track_time(map<string,string> &settings) { - base_settings(settings); - }; - Track_time* clone(map<string,string> &_settings) { return new Track_time(_settings);}; - const float output(const Time_spec &time) { - return time.time/time.duration; - } - }; -#define COMPARISON_Equal 1 -#define COMPARISON_Not_equal 2 -#define COMPARISON_Greater 3 -#define COMPARISON_Less 4 -#define COMPARISON_Greater_or_equal 5 -#define COMPARISON_Less_or_equal 6 - class Comparison: public Signal_node { - public: - Comparison(){}; - Comparison(map<string,string> &settings) { - base_settings(settings); - value=find_setting(settings,"value",0.0f); - string _op=find_setting(settings,"operator","=="); - if (_op=="==") op=COMPARISON_Equal; - if (_op=="!=") op=COMPARISON_Not_equal; - if (_op==">") op=COMPARISON_Greater; - if (_op=="<") op=COMPARISON_Less; - if (_op==">=") op=COMPARISON_Greater_or_equal; - if (_op=="<=") op=COMPARISON_Less_or_equal; - } - void link_params() { - for (auto p:parameter_inputs){ - if (p->parameter=="value") p->receiver=&value; - } - }; - Comparison* clone(map<string,string> &_settings) { return new Comparison(_settings);}; - const float output(const Time_spec &time) { - if (inputs.size()) { //there should there be a way to specify number of inputs in the code rather than in xml - if (inputs[0]->connection) { - float in= (((Signal_node*)inputs[0]->connection)->get_output(time)); - switch (op) { - case COMPARISON_Equal: - return fequal(value,in)?1.0f:0.0f; - break; - case COMPARISON_Not_equal: - return fequal(value,in)?0.0f:1.0f; - break; - case COMPARISON_Greater: - return fgreater(value,in)?1.0f:0.0f; - break; - case COMPARISON_Less: - return fless(value,in)?1.0f:0.0f; - break; - case COMPARISON_Greater_or_equal: - return fgreater_or_equal(value,in)?1.0f:0.0f; - break; - case COMPARISON_Less_or_equal: - return fless_or_equal(value,in)?1.0f:0.0f; - break; - } - } - } - return 0.0f; - } - int op; - float value; - }; -#define ARITHMETIC_plus 1 -#define ARITHMETIC_minus 2 -#define ARITHMETIC_multiply 3 -#define ARITHMETIC_divide 4 -#define ARITHMETIC_modulo 5 - class Arithmetic: public Signal_node { - public: - Arithmetic(){}; - Arithmetic(map<string,string> &settings) { - base_settings(settings); - value=find_setting(settings,"value",0.0f); - string _op=find_setting(settings,"operator","+"); - if (_op=="+") op=ARITHMETIC_plus; - if (_op=="-") op=ARITHMETIC_minus; - if (_op=="*") op=ARITHMETIC_multiply; - if (_op=="/") op=ARITHMETIC_divide; - if (_op=="%") op=ARITHMETIC_modulo; - } - void link_params() { - for (auto p:parameter_inputs){ - p->receiver=nullptr; - if (p->parameter=="value") p->receiver=&value; - } - }; - Arithmetic* clone(map<string,string> &_settings) { return new Arithmetic(_settings);}; - const float output(const Time_spec &time) { - if (inputs.size()) { //there should there be a way to specify number of inputs in the code rather than in xml - if (inputs[0]->connection) { - float in= (((Signal_node*)inputs[0]->connection)->get_output(time)); - switch (op) { - case ARITHMETIC_plus: - return in+value; - break; - case ARITHMETIC_minus: - return in-value; - break; - case ARITHMETIC_multiply: - return in*value; - break; - case ARITHMETIC_divide: - return in/value; - break; - case ARITHMETIC_modulo: - return fmod(in,value); - break; - } - } - } - return 0.0f; - } - int op; - float value; - }; - class Signal_divide: public Signal_node { - public: - Signal_divide(){}; - Signal_divide(map<string,string> &settings) { - base_settings(settings); - divide_amount=ofToFloat(find_setting(settings,"amount")); - for (auto p:parameter_inputs){ - if (p->parameter=="amount") p->receiver=÷_amount; - } - }; - Signal_divide* clone(map<string,string> &_settings) { return new Signal_divide(_settings);}; - const float output(const Time_spec &time) { - if (inputs.size()) { //there should there be a way to specify number of inputs in the code rather than in xml - if (inputs[0]->connection) { - return (((Signal_node*)inputs[0]->connection)->get_output(time))/divide_amount; - } - } - return 0.0f; - } - float divide_amount; - }; - class Is_new_integer: public Signal_node { - public: - Is_new_integer(){}; - Is_new_integer(map<string,string> &settings) { - base_settings(settings); - }; - Is_new_integer* clone(map<string,string> &_settings) { return new Is_new_integer(_settings);}; - const float output(const Time_spec &time) { - if (inputs[0]->connection) { - float s1=(((Signal_node*)(inputs[0]->connection))->get_output(time)); - float s2=(((Signal_node*)(inputs[0]->connection))->get_output(time.lastframe())); - if (((int)s1)>((int)s2)) { - return 1.0f; - } - } - return 0.0f; - } - }; - class On_off: public Signal_node { - public: - On_off(){}; - On_off(map<string,string> &settings) { - base_settings(settings); - }; - On_off* clone(map<string,string> &_settings) { return new On_off(_settings);}; - const float output(const Time_spec &time) { - if (inputs[0]->connection) { - float s1=(((Signal_node*)(inputs[0]->connection))->get_output(time)); - if ((int)s1%2) return 1.0f; - } - return 0.0f; - } - }; - //pseudo random repeatable hash function - //http://create.stephan-brumme.com/fnv-hash/ - const uint32_t Prime = 0x01000193; // 16777619 - const uint32_t Seed = 0x811C9DC5; // 2166136261 - /// hash a byte - inline uint32_t fnv1a(unsigned char oneByte, uint32_t hash = Seed) - { - return (oneByte ^ hash) * Prime; - } - /// hash a short (two bytes) - inline uint32_t fnv1a(unsigned short twoBytes, uint32_t hash = Seed) - { - const unsigned char* ptr = (const unsigned char*) &twoBytes; - hash = fnv1a(*ptr++, hash); - return fnv1a(*ptr , hash); - } - /// hash a 32 bit integer (four bytes) - inline uint32_t fnv1a(uint32_t fourBytes, uint32_t hash = Seed) - { - const unsigned char* ptr = (const unsigned char*) &fourBytes; - hash = fnv1a(*ptr++, hash); - hash = fnv1a(*ptr++, hash); - hash = fnv1a(*ptr++, hash); - return fnv1a(*ptr , hash); - } - class Random: public Signal_node { - public: - Random(){}; - Random(map<string,string> &settings) { - base_settings(settings); - seed=(Seed+find_setting(settings,"seed",0)); - cerr<<"random:: seed "<<seed<<" ("<<Seed<<")"<<endl; - }; - Random* clone(map<string,string> &_settings) { return new Random(_settings);}; - const float output(const Time_spec &time) { - if (inputs.size()) { - if (inputs[0]->connection) { - - //hash the integer part and add the fractional part back on - float o=(((Signal_node*)inputs[0]->connection)->get_output(time)); - uint32_t m=(int)o; - return ((float)(fnv1a(m,seed)%((uint32_t)time.duration)))+(o-m); - } - } - return 0.0f; - } - uint32_t seed; - private: - }; - class Signal_output: public Signal_node { - public: - Signal_output(){}; - Signal_output(map<string,string> &settings) { - base_settings(settings); - }; - Signal_output* clone(map<string,string> &_settings) { return new Signal_output(_settings);}; - bool render(const float duration, const float framerate,string &xml_out); - const float output(const Time_spec &time) { - if (inputs[0]->connection) { - return ((Signal_node*)(inputs[0]->connection))->get_output(time); - } - else return 0.0f; - } - }; - class Testcard: public Image_node { - public: - Testcard(){image=nullptr;}; - Testcard(map<string,string> &settings) { - base_settings(settings); - image=new Image(); - }; - ~Testcard(){ if (image) delete image;}; - Testcard* clone(map<string,string> &_settings) { return new Testcard(_settings);}; - Image *output(const Frame_spec &frame){ - if (image->setup(frame.w,frame.h)) { - - } - //always create testcard - //float ws=(255.0f/frame.w); - float hs=(255.0f/frame.h); - for (int i=0;i<frame.h;i++){ - for (int j=0;j<frame.w;j++){ - image->RGBdata[(i*frame.w+j)*3]=(uint8_t)((int)((i+(frame.time*25.0f)*hs))%255); - image->RGBdata[((i*frame.w+j)*3)+1]=(uint8_t)((int)((j+(frame.time*100.0f)*hs))%255); - image->RGBdata[((i*frame.w+j)*3)+2]=(uint8_t)(0); - //image->Adata[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 image; - } - private: - Image *image; //is an image generator - }; - class Invert: public Image_node { - public: - Invert(){image=nullptr;}; - Invert(map<string,string> &settings) { - base_settings(settings); - image=new Image(); - }; - ~Invert(){ if (image) delete image;}; - Invert* clone(map<string,string> &_settings) { return new Invert(_settings);}; - Image *output(const Frame_spec &frame){ - if (image_inputs.size()) { - if (image_inputs[0]->connection){ - if (inputs[0]->connection) { - if (fgreater_or_equal(1.0f,(((Signal_node*)inputs[0]->connection)->get_output((Time_spec)frame)))) { - Image *in=(((Image_node*)image_inputs[0]->connection)->get_output(frame)); - if (in){ - image->setup(frame.w,frame.h); - for (int i=0;i<in->w*in->h*3;i++) { - image->RGBdata[i]=255-in->RGBdata[i]; - } - return image; - } - } - } - return (((Image_node*)image_inputs[0]->connection)->get_output(frame)); - } - - } - if (image_inputs[0]->connection) { - return image; - } - return nullptr; - } - private: - Image *image; //is an image generator - //bool invert; - }; - class Video_output: public Image_node { - public: - Video_output(){}; - Video_output(map<string,string> &settings) { - base_settings(settings); - }; - ~Video_output(){ }; - Image *output(const Frame_spec &frame){ - if (image_inputs[0]->connection) { - return ((Image_node*)(image_inputs[0]->connection))->get_output(frame); - } - else return nullptr; - }; - Video_output* clone(map<string,string> &_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_loader: public Image_node { - public: - Video_loader(){}; - Video_loader(map<string,string> &settings) { - base_settings(settings); - isLoaded=false; - }; - ~Video_loader(){}; - bool load(const string &filename); - Image *output(const Frame_spec &frame); - Video_loader* clone(map<string,string> &_settings) { return new Video_loader(_settings);}; - bool isLoaded; - private: - libav::decoder player; - Image image; - }; - class Video_cycler: public Image_node { - //cycles through video inputs in order - public: - Video_cycler(){}; - Video_cycler(map<string,string> &settings) { - base_settings(settings); - }; - ~Video_cycler(){}; - bool load(const string &filename); - Image *output(const Frame_spec &frame){ - int which_input=0; - if (inputs[0]->connection) { - which_input=((int)((Signal_node*)inputs[0]->connection)->get_output((Time_spec)frame))%image_inputs.size(); - } - if (image_inputs.size()) { - if (image_inputs[which_input]->connection){ - return (((Image_node*)image_inputs[which_input]->connection)->get_output(frame)); - } - } - return nullptr; - } - Video_cycler* clone(map<string,string> &_settings) { return new Video_cycler(_settings);}; - private: - }; - class Signal_colour: public Image_node { - //cycles through video inputs in order - public: - Signal_colour(){}; - Signal_colour(map<string,string> &settings) { - base_settings(settings); - string colours=find_setting(settings,"palette",""); - for (int i=0;i<colours.size()/6;i++){ - palette.push_back(Colour(colours.substr(i*6,6))); - } - for (auto i: palette) { - cerr << "Signal_colour found palette colour: "<<(int)i.r<<" "<<(int)i.g<<" "<<(int)i.b<<endl; - } - prevcol=-1; - }; - ~Signal_colour(){}; - Image *output(const Frame_spec &frame){ - if (palette.size()) { - if (inputs.size()) { - if (inputs[0]->connection){ - int col= ((int)(((Signal_node*)inputs[0]->connection)->get_output(frame)))%palette.size(); - if (col!=prevcol||image.w!=frame.w||image.h!=frame.h){ - image.setup(frame.w,frame.h); - for (int i=0;i<image.w*image.h;i++){ - image.RGBdata[i*3]=palette[col].r; - image.RGBdata[i*3+1]=palette[col].g; - image.RGBdata[i*3+2]=palette[col].b; - } - prevcol=col; - } - return ℑ - } - } - } - return nullptr; - } - Signal_colour* clone(map<string,string> &_settings) { return new Signal_colour(_settings);}; - private: - vector<Rotor::Colour> palette; - Image image; - int prevcol; - }; - class Signal_greyscale: public Image_node { - //Draws signal bars in greyscale - public: - Signal_greyscale(){}; - Signal_greyscale(map<string,string> &settings) { - base_settings(settings); - prevcol=-1; - }; - ~Signal_greyscale(){}; - Image *output(const Frame_spec &frame){ - if (inputs.size()) { - if (inputs[0]->connection){ - float sig= ((((Signal_node*)inputs[0]->connection)->get_output(frame))); - uint8_t col=255-((uint8_t)(sig*255.0f)); - if (col!=prevcol||image.w!=frame.w||image.h!=frame.h){ - image.setup(frame.w,frame.h); - for (int i=0;i<image.w*image.h*3;i++){ - image.RGBdata[i]=col; - } - prevcol=col; - } - return ℑ - } - } - return nullptr; - } - Signal_greyscale* clone(map<string,string> &_settings) { return new Signal_greyscale(_settings);}; - private: - Image image; - uint8_t prevcol; - }; - class Image_arithmetic: public Image_node { - //Draws signal bars in greyscale - public: - Image_arithmetic(){image=nullptr;}; - Image_arithmetic(map<string,string> &settings) { - base_settings(settings); - value=find_setting(settings,"value",0.0f); - string _op=find_setting(settings,"operator","+"); - if (_op=="+") op=ARITHMETIC_plus; - if (_op=="-") op=ARITHMETIC_minus; - if (_op=="*") op=ARITHMETIC_multiply; - if (_op=="/") op=ARITHMETIC_divide; - //if (_op=="%") op=ARITHMETIC_modulo; ??what would this even mean? - image=nullptr; - cerr<<"image_arithmetic: mode "<<op<<", value "<<value<<endl; - } - void link_params() { - for (auto p:parameter_inputs){ - if (p->parameter=="value") { - p->receiver=&value; - } - } - - }; - ~Image_arithmetic(){if (image) delete image;}; - Image *output(const Frame_spec &frame){ - if (image_inputs.size()) { - if (image_inputs[0]->connection){ - if (image) delete image; //from the previous frame- this may not be ideal - //because operator* made a new image it should be deleted - Image *in=(((Image_node*)image_inputs[0]->connection)->get_output(frame)); - switch (op) { - case ARITHMETIC_plus: - image=(*in)+value; - break; - case ARITHMETIC_minus: - image=(*in)-value; - break; - case ARITHMETIC_multiply: - image=(*in)*value; - break; - case ARITHMETIC_divide: - image=(*in)/value; - break; - } - return image; - } - } - return nullptr; - } - Image_arithmetic* clone(map<string,string> &_settings) { return new Image_arithmetic(_settings);}; - private: - Image *image; - float value; - int op; - }; - - class Luma_levels: public Image_node { - //applies LUT To RGB channels equally - public: - Luma_levels(){LUT=nullptr;image=nullptr;}; - Luma_levels(map<string,string> &settings) { - base_settings(settings); - levels_settings(settings); - image=new Image(); - } - void link_params() { - for (auto p:parameter_inputs){ - if (p->parameter=="black_in") p->receiver=&black_in; - if (p->parameter=="white_in") p->receiver=&white_in; - if (p->parameter=="gamma") p->receiver=γ - if (p->parameter=="black_out") p->receiver=&black_out; - if (p->parameter=="white_out") p->receiver=&white_out; - } - }; - ~Luma_levels(){if (LUT) {delete[] LUT;} if (image) delete image; }; - void levels_settings(map<string,string> &settings){ - black_in=find_setting(settings,"black_in",0.0f); - white_in=find_setting(settings,"white_in",1.0f); - gamma=find_setting(settings,"gamma",1.0f); - black_out=find_setting(settings,"black_out",0.0f); - white_out=find_setting(settings,"white_out",1.0f); - LUT=nullptr; - generate_LUT(); - } - void generate_LUT(){ //check this - 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)*256.0f); - } - } - void apply_LUT(const Image& in){ - apply_LUT(in,*image); - } - void apply_LUT(const Image& in,Image &out){ //facility to apply to other images for inherited classes - out.setup(in.w,in.h); - for (int i=0;i<out.w*out.h*3;i++){ - out.RGBdata[i]=LUT[in.RGBdata[i]]; - } - } - Image *output(const Frame_spec &frame){ - if (image_inputs.size()) { - if (image_inputs[0]->connection){ - if (LUT) { - apply_LUT(*(((Image_node*)image_inputs[0]->connection)->get_output(frame))); - return image; - } - } - } - return nullptr; - } - Luma_levels* clone(map<string,string> &_settings) { return new Luma_levels(_settings);}; - protected: - unsigned char *LUT; - Image *image; - float black_in,white_in,gamma,black_out,white_out; - }; - class Echo_trails: public Luma_levels { - //draw trail frames additively that fade off over time - //the hard thing here is how to cache frames, if its done cleverly it could have no impact when - //used linearly - //Image needs to overload operator+ - //need a clever data structure to cache frames - maybe a map of Image pointers - - //we know the frames we want to overlay as offsets ie -25,-20,-15,-10,-5 - //do we keep 25 frames loaded in order to benefit? 25 PAL frames is 60MB so probably so - //OK so: - //make a new set of pointers - //identify if any of the new pointers can inherit old frames - //delete unneeded old frames - //load new frames - //do the calculations - - //new set of pointers? or track frames by absolute frame number? - //with relative pointers and switching frames, could use auto_ptr? - - //this cache mechanism should maybe be inheritable too? - - //it could be hugely beneficial to only do the LUT once? - //although maybe the way to do the fading is to have a LUT for each frame? - - //or is it actually best to use alpha keying after all! - public: - Echo_trails(){image=nullptr;}; - Echo_trails(map<string,string> &settings) { - base_settings(settings); - //duration=find_setting(settings,"duration",1.0f); - number=find_setting(settings,"number",1); - fadeto=find_setting(settings,"fadeto",1.0f); - levels_settings(settings); - image=nullptr; - lastframe=-1; - mode=find_setting(settings,"mode",0.0f); - } - void link_params() { - for (auto p:parameter_inputs){ - if (p->parameter=="black_in") p->receiver=&black_in; - if (p->parameter=="white_in") p->receiver=&white_in; - if (p->parameter=="gamma") p->receiver=γ - if (p->parameter=="black_out") p->receiver=&black_out; - if (p->parameter=="white_out") p->receiver=&white_out; - - //TODO: control an integer - if (p->parameter=="mode") p->receiver=&mode; - } - }; - //~Echo_trails(){if (image) {delete image;} }; - ~Echo_trails(){ - if (image) delete image; - for (auto i:images) {if (image) delete i.second;} - }; - Image *output(const Frame_spec &frame){ - //check if cache is valid - if (images.size()){ - if (frame.w!=image->w||frame.h!=image->h){ //or framerate changed? - //clear cache and start over - images.clear(); - lastframe=-1; - //calculate frame interval - //interval=(int)(((duration/number)*frame.framerate)+0.5); - //total=interval*number; - } - } - int thisframe=frame.frame(); - //iterate cache and throw out any obsolete frames - auto i = std::begin(images); - while (i != std::end(images)) { - // check if the image is in the range we need - if (thisframe-(*i).first>number||thisframe-(*i).first<0) { - delete (*i).second; - i = images.erase(i); - } - else - ++i; - } - //if frame has already been calculated just return it - if (thisframe==lastframe) { - return image; - } - else { - if (image_inputs.size()) { - if (image_inputs[0]->connection){ - if (LUT) { - //need a better strategy here, should be able to get each image once - //copy incoming image **writable - if (image) image->free(); - image=(((Image_node*)image_inputs[0]->connection)->get_output(frame))->clone(); - images[thisframe]=new Image(frame.w,frame.h); - apply_LUT(*(image),*(images[thisframe])); - for (int i=1;i<number;i++){ - //check echo frame isn't at negative time - int absframe=thisframe-i; - if (absframe>-1){ - //check if image is in the cache - if (images.find(absframe)==images.end()){ - images[absframe]=new Image(frame.w,frame.h); - Frame_spec wanted=Frame_spec(absframe,frame.framerate,frame.duration,frame.w,frame.h); - apply_LUT(*(((Image_node*)image_inputs[0]->connection)->get_output(wanted)),*(images[absframe])); - } - //cerr<<"Rotor: about to apply image ("<<images[absframe].w<<"x"<<images[absframe].h<<")"<<endl; - if (fless(1.0f,fadeto)){ - float amount=((((float)number-i)/number)*(1.0f-fadeto))+(1.0f-fadeto); - Image *temp=*images[absframe]*amount; - if (mode<0.5) { - (*image)+=*temp; - } - else { - image->add_wrap(*temp); - } - delete temp; - } - else { - if (mode<0.5) (*image)+=*(images[absframe]); - else (*image)=image->add_wrap(*(images[absframe])); - } - } - } - //for (int i=0;i<frame.w*frame.h*3;i++){ - // image->RGBdata[i]=LUT[in->RGBdata[i]]; - //} - lastframe=thisframe; - return image; - } - } - } - } - return nullptr; - } - Echo_trails* clone(map<string,string> &_settings) { return new Echo_trails(_settings);}; - protected: - float duration,fadeto; - int number; - int interval,total,lastframe; //number of frames between displayed echoes - unordered_map<int,Image*> images; - float mode; //TODO make int, enum string parameter types - }; - #define BLEND_screen 1 - #define BLEND_multiply 2 - #define BLEND_blend 3 - #define BLEND_alpha 4 - #define BLEND_screen_wrap 5 - #define BLEND_multiply_wrap 6 - #define BLEND_xor 7 - class Blend: public Image_node { - public: - Blend(){image=nullptr;}; - Blend(map<string,string> &settings) { - base_settings(settings); - image=nullptr; - amount=find_setting(settings,"amount",1.0f); - string _mode=find_setting(settings,"mode","screen"); - if (_mode=="screen") mode=BLEND_screen; - if (_mode=="multiply") mode=BLEND_multiply; - if (_mode=="blend") mode=BLEND_blend; - if (_mode=="alpha") mode=BLEND_alpha; - if (_mode=="screen_wrap") mode=BLEND_screen_wrap; - if (_mode=="multiply_wrap") mode=BLEND_multiply_wrap; - if (_mode=="xor") mode=BLEND_xor; - }; - void link_params() { - for (auto p:parameter_inputs){ - if (p->parameter=="amount") p->receiver=&amount; - } - }; - ~Blend(){ if (image) delete image;}; - Blend* clone(map<string,string> &_settings) { return new Blend(_settings);}; - Image *output(const Frame_spec &frame){ - if (image_inputs.size()) { - if (image_inputs[0]->connection){ - if (image_inputs.size()>1) { - if (image_inputs[1]->connection) { - //copy incoming image **writable - if (image) delete image; - image=(((Image_node*)image_inputs[0]->connection)->get_output(frame))->clone(); - switch(mode){ - case BLEND_screen: - (*image)+=(*(((Image_node*)image_inputs[1]->connection)->get_output(frame))); - break; - case BLEND_multiply: - (*image)*=(*(((Image_node*)image_inputs[1]->connection)->get_output(frame))); - break; - case BLEND_xor: - (*image)^=(*(((Image_node*)image_inputs[1]->connection)->get_output(frame))); - break; - case BLEND_alpha: - (*image)=(*image).alpha_blend(*(((Image_node*)image_inputs[1]->connection)->get_output(frame))); - break; - case BLEND_screen_wrap: - (*image)=(*image).add_wrap(*(((Image_node*)image_inputs[1]->connection)->get_output(frame))); - break; - case BLEND_multiply_wrap: - (*image)=(*image).divide_wrap(*(((Image_node*)image_inputs[1]->connection)->get_output(frame))); - break; - case BLEND_blend: //has to be last because of initialser of *in? go figure - (*image)*=(1.0f-amount); - Image *in=(*(((Image_node*)image_inputs[1]->connection)->get_output(frame)))*amount; - (*image)+=(*in); - delete in; - break; - - } - return image; - } - } - //if there aren't 2 image inputs connected just return the first - return (((Image_node*)image_inputs[0]->connection)->get_output(frame)); - } - } - return nullptr; - } - private: - Image *image; //is an image generator - int mode; - float amount; //for blend - }; - #define MIRROR_horiz 1 - #define MIRROR_vert 2 - #define MIRROR_horizR 3 - #define MIRROR_vertR 4 - class Mirror: public Image_node { - public: - Mirror(){image=nullptr;}; - Mirror(map<string,string> &settings) { - base_settings(settings); - image=nullptr; - string _mode=find_setting(settings,"mode","horiz"); - if (_mode=="horiz") mode=MIRROR_horiz; - if (_mode=="vert") mode=MIRROR_vert; - if (_mode=="horizR") mode=MIRROR_horizR; - if (_mode=="vertR") mode=MIRROR_vertR; - }; - ~Mirror(){ if (image) delete image;}; - Mirror* clone(map<string,string> &_settings) { return new Mirror(_settings);}; - Image *output(const Frame_spec &frame){ - if (image_inputs.size()) { - if (image_inputs[0]->connection){ - //copy incoming image **writable - if (image) delete image; - image=(((Image_node*)image_inputs[0]->connection)->get_output(frame))->clone(); - switch (mode) { - case MIRROR_horiz: - for (int i=0;i<image->w/2;i++){ - for (int j=0;j<image->h;j++){ - for (int k=0;k<3;k++){ - image->RGBdata[(((j*image->w)+((image->w/2)+i))*3)+k]=image->RGBdata[(((j*image->w)+((image->w/2)-i))*3)+k]; - } - } - } - break; - case MIRROR_vert: - for (int i=0;i<image->w;i++){ - for (int j=0;j<image->h/2;j++){ - for (int k=0;k<3;k++){ - image->RGBdata[((((image->h/2+j)*image->w)+i)*3)+k]=image->RGBdata[((((image->h/2-j)*image->w)+i)*3)+k]; - } - } - } - break; - case MIRROR_horizR: - for (int i=0;i<image->w/2;i++){ - for (int j=0;j<image->h;j++){ - for (int k=0;k<3;k++){ - image->RGBdata[(((j*image->w)+((image->w/2)-i))*3)+k]=image->RGBdata[(((j*image->w)+((image->w/2)+i))*3)+k]; - } - } - } - break; - case MIRROR_vertR: - for (int i=0;i<image->w;i++){ - for (int j=0;j<image->h/2;j++){ - for (int k=0;k<3;k++){ - image->RGBdata[((((image->h/2-j)*image->w)+i)*3)+k]=image->RGBdata[((((image->h/2+j)*image->w)+i)*3)+k]; - } - } - } - break; - } - return image; - } - } - return nullptr; - } - private: - Image *image; //is an image generator - int mode; - }; - class Monochrome: public Image_node { - public: - Monochrome(){}; - Monochrome(map<string,string> &settings) { - base_settings(settings); - }; - ~Monochrome(){ - }; - Monochrome* clone(map<string,string> &_settings) { return new Monochrome(_settings);}; - Image *output(const Frame_spec &frame){ - if (image_inputs.size()) { - if (image_inputs[0]->connection){ - Image *other=(((Image_node*)image_inputs[0]->connection)->get_output(frame)); - image.setup(other->w,other->h); - for (int i=0;i<image.w;i++){ - for (int j=0;j<image.h;j++){ - uint8_t luma=0; - for (int l=0;l<3;l++) luma+=pixels.mono_weights[l][other->RGBdata[(((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: - Image image; - }; - 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(){}; - Transform(map<string,string> &settings) { - base_settings(settings); - tX=find_setting(settings,"transformX",0.0f); - tY=find_setting(settings,"transformY",0.0f); - oX=find_setting(settings,"originX",0.5f); - oY=find_setting(settings,"originX",0.5f); - r=find_setting(settings,"rotation",0.0f); - s=find_setting(settings,"scale",1.0f); - }; - ~Transform(){ - }; - void link_params() { - for (auto p:parameter_inputs){ - if (p->parameter=="scale") p->receiver=&s; - if (p->parameter=="rotation") p->receiver=&r; - if (p->parameter=="transformX") p->receiver=&tX; - if (p->parameter=="transformY") p->receiver=&tY; - if (p->parameter=="originX") p->receiver=&oX; - if (p->parameter=="originY") p->receiver=&oY; - } - }; - Transform* clone(map<string,string> &_settings) { return new Transform(_settings);}; - Image *output(const Frame_spec &frame){ - if (image_inputs.size()) { - if (image_inputs[0]->connection){ - Image *other=(((Image_node*)image_inputs[0]->connection)->get_output(frame)); - if (other) { - image.setup(other->w,other->h); - //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(other->w,other->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=other->w-1; - srcTri[1].y=0; - srcTri[2].x=0; - srcTri[2].y=other->h-1; - for (int i=0;i<3;i++){ - dstTri[i].x=srcTri[i].x+(tX*other->w); - dstTri[i].y=srcTri[i].y+(tY*other->h); - } - trans_mat=getAffineTransform( srcTri, dstTri ); - warpAffine( other->rgb, inter.rgb, trans_mat, inter.rgb.size(), cv::INTER_LINEAR, cv::BORDER_WRAP); - - - // Compute rotation matrix - // - cv::Point centre = cv::Point( oX*other->w, oY*other->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: - Image image; - float tX,tY,oX,oY,r,s; - //todo - quality settings - }; - class Alpha_merge: public Image_node { - public: - Alpha_merge(){image=nullptr;}; - Alpha_merge(map<string,string> &settings) { - base_settings(settings); - }; - ~Alpha_merge(){ if (image) delete image;}; - Alpha_merge* clone(map<string,string> &_settings) { return new Alpha_merge(_settings);}; - Image *output(const Frame_spec &frame){ - if (image_inputs.size()) { - if (image_inputs[0]->connection){ - //copy incoming image **writable - if (image) delete image; - image=(((Image_node*)image_inputs[0]->connection)->get_output(frame))->clone(); - if (image_inputs.size()>1) { - if (image_inputs[1]->connection) { - image->alpha_merge(*((Image_node*)image_inputs[1]->connection)->get_output(frame)); - } - } - //if there aren't 2 image inputs connected just return the first - return image; - } - } - return nullptr; - } - private: - Image *image; //is an image generator - }; - //------------------------------------------------------------------- - 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<string,string> &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; - }; - private: - unordered_map<string,Node*> 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<string,Node*> nodes; - vector<Node*> 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); - int load(Poco::UUID uid); - bool load(string data); - bool loadFile(string &filename); - bool parseXml(); - bool set_resolution(int w,int h); - UUID save(); //save to DB, returns UUID of saved graph - bool loaded; - float duration; - const string toString(); - xmlIO xml; - private: - Node_factory factory; - int outW,outH; - }; - class Audio_thumbnailer: public Base_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<string,string> &_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; - }; - 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) { - audio_thumb=new Audio_thumbnailer(); - state=IDLE; - output_framerate=25.0f; - audio_loaded=false; - - xmlIO xml; - if(xml.loadFile("settings.xml") ){ - graph_dir=xml.getAttribute("Rotor","graph_dir","",0); - media_dir=xml.getAttribute("Rotor","media_dir","",0); - output_dir=xml.getAttribute("Rotor","output_dir","",0); - } - else cerr<<"Rotor: settings.xml not found, using defaults"<<endl; - }; - ~Render_context(){delete audio_thumb;}; - void runTask(); - void add_queue(int item); - Command_response session_command(const std::vector<std::string>& command); - void session_command(const std::vector<std::string>& command,xmlIO& XML,HTTPResponse::HTTPStatus& status); - 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? - 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(); - bool load_video(const string &nodeID,const string &filename);//can be performance or clip - private: - int state; - float progress; //for a locking process: audio analysis or rendering - //thread only does one thing at once - std::deque<int> work_queue; - Poco::Mutex mutex; //lock for access from parent thread - std::string audio_filename; - std::string output_filename; - std::string graph_dir; - std::string media_dir; - std::string output_dir; - - Audio_thumbnailer *audio_thumb; - Graph graph; - Node_factory factory; - float output_framerate; - bool audio_loaded; - - }; -} - -/* -coding style -Types begin with capitals 'New_type' -variables/ instances use lower case with underscore as a seperator -*/ -#endif
\ No newline at end of file |
