summaryrefslogtreecommitdiff
path: root/rotord/rotor.h
diff options
context:
space:
mode:
Diffstat (limited to 'rotord/rotor.h')
-rwxr-xr-xrotord/rotor.h1391
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 &parameter,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=&divide_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 &image;
- }
- }
- }
- 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 &image;
- }
- }
- 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=&gamma;
- 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=&gamma;
- 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 &image;
- }
- }
- 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 &image;
- }
- }
- }
- 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