summaryrefslogtreecommitdiff
path: root/rotord/src/rotor.h
diff options
context:
space:
mode:
Diffstat (limited to 'rotord/src/rotor.h')
-rwxr-xr-xrotord/src/rotor.h1391
1 files changed, 1391 insertions, 0 deletions
diff --git a/rotord/src/rotor.h b/rotord/src/rotor.h
new file mode 100755
index 0000000..f922fcf
--- /dev/null
+++ b/rotord/src/rotor.h
@@ -0,0 +1,1391 @@
+#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