diff options
| author | Comment <tim@gray.(none)> | 2013-11-02 16:13:41 +0000 |
|---|---|---|
| committer | Comment <tim@gray.(none)> | 2013-11-02 16:13:41 +0000 |
| commit | 510a5218d5e981db3dde9ed31ddc6f732ed6a22a (patch) | |
| tree | b9d09c4dbdefed1ec693e0492841cde426ef6643 /rotord/src | |
| parent | 8864f5da9649c176e472a7fb8913dd8114deec54 (diff) | |
| parent | eaccb1437465c6aa49e1d5511876852543e3b0fa (diff) | |
merge branch
Diffstat (limited to 'rotord/src')
| -rw-r--r-- | rotord/src/graph.cpp | 9 | ||||
| -rw-r--r-- | rotord/src/graph.h | 63 | ||||
| -rw-r--r-- | rotord/src/libavwrapper.cpp | 22 | ||||
| -rw-r--r-- | rotord/src/nodes_audio_analysis.cpp | 84 | ||||
| -rw-r--r-- | rotord/src/nodes_audio_analysis.h | 83 | ||||
| -rw-r--r-- | rotord/src/nodes_drawing.h | 7 | ||||
| -rw-r--r-- | rotord/src/rendercontext.cpp | 3 | ||||
| -rw-r--r-- | rotord/src/rotor.cpp | 315 | ||||
| -rw-r--r-- | rotord/src/rotor.h | 52 | ||||
| -rw-r--r-- | rotord/src/rotord.cpp | 14 | ||||
| -rw-r--r-- | rotord/src/rotord.h | 6 | ||||
| -rw-r--r-- | rotord/src/vampHost.cpp | 38 | ||||
| -rw-r--r-- | rotord/src/vampHost.h | 2 |
13 files changed, 447 insertions, 251 deletions
diff --git a/rotord/src/graph.cpp b/rotord/src/graph.cpp index c33c59d..3f1d02b 100644 --- a/rotord/src/graph.cpp +++ b/rotord/src/graph.cpp @@ -566,9 +566,12 @@ bool Graph::load_audio(const string &filename,vector<Audio_processor*> processor Logger& logger = Logger::get("Rotor"); logger.information("Analysing "+filename); - audio_loaded=false; + //audio_loaded=false; libav::audio_decoder loader; - loader.open(filename); + if (!loader.open(filename)) { + logger.error("ERROR: Could not open audio: "+filename); + return false; + } duration=loader.get_duration(); @@ -612,7 +615,7 @@ bool Graph::load_audio(const string &filename,vector<Audio_processor*> processor } logger.information("Finished audio analysis"); - audio_loaded=true; + //audio_loaded=true; return true; } bool Graph::load_video(const string &nodeID,const string &filename){ diff --git a/rotord/src/graph.h b/rotord/src/graph.h index 6f2ee79..ddf2471 100644 --- a/rotord/src/graph.h +++ b/rotord/src/graph.h @@ -97,6 +97,34 @@ namespace Rotor { class Thumbnailer{ public: bool make(const string &inputfilename,int w,int h,const string &outputfilename) { + //first try svg + RsvgHandle * rsvg=rsvg_handle_new_from_file(inputfilename.c_str(),nullptr); + if (rsvg) { + RsvgDimensionData dims; + rsvg_handle_get_dimensions(rsvg,&dims); + cerr<<"Rotor: SVG loaded "<<inputfilename<<" , "<<dims.width<<"x"<<dims.height<<endl; + cv::Mat cp=cv::Mat::zeros(h,w,CV_8UC4); + cv::Mat co=cv::Mat(h,w,CV_8UC3); + cairo_surface_t * cs = cairo_image_surface_create_for_data (cp.data, + CAIRO_FORMAT_RGB24, + w, + h, + w*4); + cairo_t * cr = cairo_create (cs); + cairo_translate(cr, w/2, h/2); + cairo_scale(cr, ((float)w)/640.0f,((float)w)/640.0f); + cairo_translate(cr, -w/2, -h/2); + cairo_translate(cr, w/2-(dims.width/2), h/2-(dims.height/2)); + rsvg_handle_render_cairo(rsvg,cr); + cv::cvtColor(cp,co,CV_RGBA2BGR,3); + cv::imwrite(outputfilename.c_str(),co); + cairo_destroy(cr); + cairo_surface_destroy(cs); + rsvg_handle_close(rsvg,nullptr); + //g_object_unref(rsvg); + return true; + } + //if not svg feed to FFMS2 if (player.open(inputfilename)){ if (player.fetch_frame(w,h,player.get_number_frames()/4)) { Image i; @@ -105,9 +133,42 @@ namespace Rotor { cvtColor(i.rgb,cp,CV_RGB2BGR); cv::imwrite(outputfilename.c_str(),cp); return true; - } } + //fall back to audio + libav::audio_decoder loader; + if (loader.open(inputfilename)) { + Audio_thumbnailer at; + at.width=w; + at.height=h; + int rate = loader.get_sample_rate(); + int samples = loader.get_number_samples(); + int channels= loader.get_number_channels(); + int bits = loader.get_bit_depth(); + at.init(channels,bits,samples,rate); + bool finished=false; + uint16_t *audio=new uint16_t[1024*loader.get_number_channels()]; + uint64_t sample=0; + while (loader.get_samples(audio,sample,1024)) { + at.process_frame((uint8_t*)audio,1024); + sample+=1024; + } + cv::Mat co=cv::Mat(h,w,CV_8UC3); + uchar op; + for (int i=0;i<h;i++){ + uchar* r=co.ptr(i); //pointer to row + for (int j=0;j<w;j++){ + //audio rms in 0..1 range + if (at.audiodata[j]>abs(((float)i-h/2)/(h/2))) op=0xFF; + else op=0x00; + r[j*3]=op/4; + r[j*3+1]=op; + r[j*3+2]=op/4; + } + } + cv::imwrite(outputfilename.c_str(),co); + return true; + } return false; } private: diff --git a/rotord/src/libavwrapper.cpp b/rotord/src/libavwrapper.cpp index 26523fc..2ee9985 100644 --- a/rotord/src/libavwrapper.cpp +++ b/rotord/src/libavwrapper.cpp @@ -33,15 +33,25 @@ bool libav::video_decoder::open(const std::string& filename){ Mutex::ScopedLock lock(mutex); loaded=false; + Poco::File f=Poco::File(filename); + if (!f.exists()) { + cerr<<"ERROR: "<<filename<<" does not exist"<<endl; + return false; + } + //first check if an index object exists Poco::StringTokenizer tokens(filename,"."); string idxfile=""; - for (int i=0;i<tokens.count()-1;i++){ - idxfile+=tokens[i]; - idxfile+="."; + if (tokens.count()>1){ + for (int i=0;i<tokens.count()-1;i++){ + idxfile+=tokens[i]; + idxfile+="."; + } + idxfile+="idx"; } - idxfile+="idx"; - Poco::File f=Poco::File(idxfile); + else idxfile=filename+".idx"; + + f=Poco::File(idxfile); bool makeindex=true; FFMS_Index *index; if (f.exists()) { @@ -54,7 +64,7 @@ bool libav::video_decoder::open(const std::string& filename){ } } if (makeindex) { - cerr<<"FFMS2: created index "<<idxfile<<endl; + cerr<<"FFMS2: creating index "<<idxfile<<endl; index = FFMS_MakeIndex(filename.c_str(), 0, 0, NULL, NULL, FFMS_IEH_IGNORE, NULL, NULL, &err); FFMS_WriteIndex(idxfile.c_str(),index,&err); } diff --git a/rotord/src/nodes_audio_analysis.cpp b/rotord/src/nodes_audio_analysis.cpp index 220f14b..a2a9c63 100644 --- a/rotord/src/nodes_audio_analysis.cpp +++ b/rotord/src/nodes_audio_analysis.cpp @@ -83,11 +83,11 @@ namespace Rotor{ } void Vamp_node::cleanup() { analyser.cleanup(); - //print_features(); + features=analyser.features; } string Vamp_node::get_features(){ string data; - for (auto i: analyser.features) { + for (auto i: features) { data=data+" ["+toString(i.second.number)+":"+toString(i.first); if (i.second.values.size()) { data+=" ("; @@ -105,4 +105,84 @@ namespace Rotor{ } return data; } + bool sortsegments(std::pair<int,float> i,std::pair<int,float> j){ + return (i.second<j.second); + } + void Intensity_segmenter::cleanup(){ + //algorithm idea: + //get average tempo and intensity for each segment and store them + //scale by the range to get a value from 0.0 to 1.0 + //add tempo and intensity according to a weighting + //score the results (ie 1st place, 2nd place) to end up with a set of integer numbers + + //for (auto a:analysers) a.second.cleanup(); //WHY NOT WORK - its as if the call is const + analysers["segmenter"].cleanup(); + analysers["tempo"].cleanup(); + analysers["intensity"].cleanup(); + cerr<<analysers["segmenter"].features.size()<<" segments"<<endl; + cerr<<analysers["tempo"].features.size()<<" tempo features"<<endl; + cerr<<analysers["intensity"].features.size()<<" intensity features"<<endl; + int i=0; + float min_tempo=9999999.0f; + float min_intensity=9999999.0f; + float max_tempo=0.0f; + float max_intensity=0.0f; + vector<float> tempos; + vector<float> intensities; + vector<float> times; + auto g=++analysers["segmenter"].features.begin(); + for (auto f=analysers["segmenter"].features.begin();g!=analysers["segmenter"].features.end();f++,g++,i++){ + cerr<<"segment "<<i<<": "<<f->first<<" to "<<g->first<<endl; + times.push_back(f->first); + //integrate tempo and intensity algorithmically + float tempo=0; + if (analysers["tempo"].features.size()) { + float pt=f->first; + float pv=analysers["tempo"].get_value(f->first); + for (auto u=analysers["tempo"].features.upper_bound(f->first);u!=analysers["tempo"].features.upper_bound(g->first);u++){ + tempo +=(u->first-pt)*(u->second.values[0]+pv)*0.5f; //area of the slice + pt=u->first; + pv=u->second.values[0]; + } + tempo +=(g->first-pt)*(analysers["tempo"].get_value(g->first)+pv)*0.5f; //area of the last slice + tempo /=g->first-f->first; //average value; + } + if (tempo>max_tempo) max_tempo=tempo; + if (tempo<min_tempo) min_tempo=tempo; + tempos.push_back(tempo); + cerr<<"segment "<<i<<" average tempo: "<<tempo<<endl; + + float intensity=0; + if (analysers["intensity"].features.size()) { + float pt=f->first; + float pv=analysers["intensity"].get_value(f->first); + for (auto u=analysers["intensity"].features.upper_bound(f->first);u!=analysers["intensity"].features.upper_bound(g->first);u++){ + intensity +=(u->first-pt)*(u->second.values[0]+pv)*0.5f; //area of the slice + pt=u->first; + pv=u->second.values[0]; + } + intensity +=(g->first-pt)*(analysers["intensity"].get_value(g->first)+pv)*0.5f; //area of the last slice + intensity /=g->first-f->first; //average value; + } + if (intensity>max_intensity) max_intensity=intensity; + if (intensity<min_intensity) min_intensity=intensity; + intensities.push_back(intensity); + cerr<<"segment "<<i<<" average intensity: "<<intensity<<endl; + } + //make relative scale 0.0-1.0 and save weighted totals + vector< pair<int,float>> totals; + for (i=0;i<tempos.size();i++){ + tempos[i]=(tempos[i]-min_tempo)/(max_tempo-min_tempo); + intensities[i]=(intensities[i]-min_intensity)/(max_intensity-min_intensity); + totals.push_back(make_pair(i,(tempos[i]*parameters["tempo_weight"]->value)+(intensities[i]*parameters["intensity_weight"]->value))); + } + //sort and convert to features + std::sort(totals.begin(),totals.end(),sortsegments); + for (int i=0;i<totals.size();i++){ + vampHost::feature f; + f.values.push_back((float)i); + features[times[totals[i].first]]=f; + } + return true; + } }
\ No newline at end of file diff --git a/rotord/src/nodes_audio_analysis.h b/rotord/src/nodes_audio_analysis.h index db33b1f..a3fe5b8 100644 --- a/rotord/src/nodes_audio_analysis.h +++ b/rotord/src/nodes_audio_analysis.h @@ -9,6 +9,18 @@ namespace Rotor { #define VAMPHOST_Timesteps 2 #define VAMPHOST_Valueline 3 #define VAMPHOST_Values 4 + class Audio_processor: public Signal_node { + public: + virtual Audio_processor(){}; + virtual ~Audio_processor(){}; + virtual int process_frame(uint8_t *data,int samples)=0; + virtual bool init(int _channels,int _bits,int _samples,int _rate)=0; + virtual void cleanup()=0; + virtual void print_summary(){}; + virtual string get_features(){}; + int channels,bits,samples,rate; + }; + //actual nodes------------------------------------------------- class Audio_thumbnailer: public Audio_processor { public: Audio_thumbnailer(){ @@ -43,9 +55,9 @@ namespace Rotor { void cleanup(); int process_frame(uint8_t *data,int samples_in_frame); const float output(const Time_spec &time) { - if (analyser.features.size()) { - auto i=analyser.features.upper_bound(time.time); //the first element in the container whose key is considered to go after k - if (i!=analyser.features.end()){ + if (features.size()) { + auto i=features.upper_bound(time.time); //the first element in the container whose key is considered to go after k + if (i!=features.end()){ float uk=i->first; float v1,v2; v1=v2=0.0f; @@ -72,15 +84,15 @@ namespace Rotor { } string get_features(); void print_summary(){ - cerr<<"vamp plugin "<<id<<" of library "<<soname<<" found "<<analyser.features.size()<<" features "<<endl; + cerr<<"vamp plugin "<<id<<" of library "<<soname<<" found "<<features.size()<<" features "<<endl; }; protected: string soname,id; - int outputNo; - vampHost::Analyser analyser; + int outputNo; map <string,float> params; - private: - //?? + map<float,vampHost::feature> features; + private: + vampHost::Analyser analyser; }; class Audio_analysis: public Vamp_node { //vamp node that allows the user to choose a plugin @@ -170,20 +182,20 @@ namespace Rotor { vector<int> act_count; for (int i=0;i<(int)parameters["acts"]->value;i++) act_count.push_back(0); - if (analyser.features.size()<=(int)parameters["acts"]->value+1){ + if (features.size()<=(int)parameters["acts"]->value+1){ //iteratively split segments and refresh durations //this could work well on the original data //pick the longest and split it in two //refresh durations - this can be a function //keep going //finally copy out - while (analyser.features.size()<(int)parameters["acts"]->value+1){ + while (features.size()<(int)parameters["acts"]->value+1){ map<int,float> durations; map<int,float> times; int i=0; - for (map<float,vampHost::feature>::iterator f=analyser.features.begin();f!=analyser.features.end();++f){ + for (map<float,vampHost::feature>::iterator f=features.begin();f!=features.end();++f){ auto g=f; - if (++g!=analyser.features.end()){ + if (++g!=features.end()){ durations[i]=g->first-f->first; times[i]=f->first; } @@ -197,10 +209,10 @@ namespace Rotor { n=d.first; } } - analyser.features[times[n]+(durations[n]/2)]=analyser.features[times[n]]; + features[times[n]+(durations[n]/2)]=features[times[n]]; } int j=0; - for (auto f: analyser.features){ + for (auto f: features){ f.second.number=j; acts[f.first]=f.second; j++; @@ -216,7 +228,7 @@ namespace Rotor { //start with a set of the segments numbers //(the aim: to access the segment numbers by the time that they start) - map<float,vampHost::feature> segments=analyser.features; + map<float,vampHost::feature> segments=features; segments.erase(--segments.end()); //assign the acts from the beginning and end in towards the middle @@ -260,8 +272,8 @@ namespace Rotor { } } //add last item back on - acts[(*--analyser.features.end()).first]=analyser.features[(*--analyser.features.end()).first]; - analyser.features=acts; + acts[(*--features.end()).first]=features[(*--features.end()).first]; + features=acts; } private: @@ -271,41 +283,40 @@ namespace Rotor { public: Intensity_segmenter(){ title="Intensity segmenter"; - description="Combines segmentation, tempo and intensity"; - UID="74e05d10-3ffa-11e3-b5f9-7335309da36a"; + description="Combines the output of segmentation, tempo, and intensity analysis plugins"; + UID="6ce236b6-4080-11e3-90b7-74d02b29f6a6"; analysers["segmenter"]=vampHost::Analyser(); analysers["tempo"]=vampHost::Analyser(); analysers["intensity"]=vampHost::Analyser(); + create_parameter("intensity_weight","number","intensity weight","Intensity weighting",1.0f); + create_parameter("tempo_weight","number","tempo weight","Tempo weighting",1.0f); }; Intensity_segmenter(map<string,string> &settings):Intensity_segmenter() { base_settings(settings); }; ~Intensity_segmenter(){}; Intensity_segmenter* clone(map<string,string> &_settings) { return new Intensity_segmenter(_settings);}; - bool init(int _channels,int _bits,int _samples, int _rate) { - //params needed? - analysers["segmenter"].init("qm-vamp-plugins","qm-segmenter",_channels,_bits,_samples,_rate,0,params); - analysers["tempo"].init("qm-vamp-plugins","qm-tempotracker",_channels,_bits,_samples,_rate,2,params); - analysers["tempo"].init("bbc-vamp-plugins","bbc-intensity",_channels,_bits,_samples,_rate,0,params); - return true; + bool init(int _channels,int _bits,int _samples,int _rate) { + return analysers["segmenter"].init("qm-vamp-plugins","qm-segmenter",_channels,_bits,_samples,_rate,0,params)\ + &&analysers["tempo"].init("qm-vamp-plugins","qm-tempotracker",_channels,_bits,_samples,_rate,2,params)\ + &&analysers["intensity"].init("bbc-vamp-plugins","bbc-intensity",_channels,_bits,_samples,_rate,0,params); } int process_frame(uint8_t *data,int samples_in_frame) { - for (auto a:analysers) a.second.process_frame(data,samples_in_frame); + //for (auto a:analysers) a.second.process_frame(data,samples_in_frame); //WHY NOT WORK + analysers["segmenter"].process_frame(data,samples_in_frame); + analysers["tempo"].process_frame(data,samples_in_frame); + analysers["intensity"].process_frame(data,samples_in_frame); return 1; } - void cleanup() { - for (auto a:analysers) a.second.cleanup(); - - for (auto f:analysers["segmenter"].features){ - - } - //add last item back on - //acts[(*--analyser.features.end()).first]=analyser.features[(*--analyser.features.end()).first]; - //analyser.features=acts; - } + void cleanup(); //all the fun happens here + void print_summary(){ + cerr<<"Intensity segmenter found "<<features.size()<<" features "<<endl; + }; private: map<string,vampHost::Analyser> analysers; }; } + + #endif diff --git a/rotord/src/nodes_drawing.h b/rotord/src/nodes_drawing.h index b26841e..f46c2b0 100644 --- a/rotord/src/nodes_drawing.h +++ b/rotord/src/nodes_drawing.h @@ -3,7 +3,6 @@ #include "rotor.h" #include <cairo.h> -#include <rsvg.h> namespace Rotor { class Draw_node: public Image_node { @@ -217,6 +216,7 @@ namespace Rotor { }; class Svg: public Draw_node { public: + //rsvg should be cleanup? Svg(){ title="SVG"; description="Draws svg files"; @@ -238,7 +238,10 @@ namespace Rotor { } else cerr<<"Rotor: SVG failed to load "<<attributes["filename"]->value<<endl; }; - ~Svg(){}; + ~Svg(){ + rsvg_handle_close(rsvg,nullptr); + //g_object_unref(rsvg); + }; Svg* clone(map<string,string> &_settings) { return new Svg(_settings);}; void vector_output(cairo_t * cr,const Frame_spec &frame){ //to make it resolution independent diff --git a/rotord/src/rendercontext.cpp b/rotord/src/rendercontext.cpp index bba8d31..779a1b7 100644 --- a/rotord/src/rendercontext.cpp +++ b/rotord/src/rendercontext.cpp @@ -24,6 +24,7 @@ void Render_context::runTask() { } } if (graph.load_audio(graph.audio_filename,processors)) { + graph.audio_loaded=false; state=IDLE; } else { @@ -428,7 +429,7 @@ void Render_context::session_command(const Session_command& command,xmlIO& XML,H } else { status=HTTPResponse::HTTP_BAD_REQUEST; - logger.error("ERROR: Could not print features for node "+features_node); + logger.error("ERROR: Could not print features for node /"+features_node+"/"); } } else { diff --git a/rotord/src/rotor.cpp b/rotord/src/rotor.cpp index 88284c9..840298e 100644 --- a/rotord/src/rotor.cpp +++ b/rotord/src/rotor.cpp @@ -19,61 +19,62 @@ Node_factory::Node_factory(){ //this can be hard coded also // - category["signals"]=vector<Node*>(); - add_type("time",new Time(),category["signals"]); - add_type("track_time",new Track_time(),category["signals"]); - add_type("at_track_time",new At_track_time(),category["signals"]); + category["Signals"]=vector<Node*>(); + add_type("time",new Time(),category["Signals"]); + add_type("track_time",new Track_time(),category["Signals"]); + add_type("at_track_time",new At_track_time(),category["Signals"]); // add_type("signal_output",new Signal_output()); add_type("testcard",new Testcard()); // - category["channels"]=vector<Node*>(); - add_type("invert",new Invert(),category["channels"]); - add_type("monochrome",new Monochrome(),category["channels"]); - add_type("blend",new Blend(),category["channels"]); - add_type("image_arithmetic",new Image_arithmetic(),category["channels"]); - add_type("alpha_merge",new Alpha_merge(),category["channels"]); - add_type("difference_matte",new Difference_matte(),category["channels"]); - add_type("rgb_levels",new RGB_levels(),category["channels"]); - add_type("luma_levels",new Luma_levels(),category["channels"]); + category["Channels"]=vector<Node*>(); + add_type("invert",new Invert(),category["Channels"]); + add_type("monochrome",new Monochrome(),category["Channels"]); + add_type("blend",new Blend(),category["Channels"]); + add_type("image_arithmetic",new Image_arithmetic(),category["Channels"]); + add_type("alpha_merge",new Alpha_merge(),category["Channels"]); + add_type("difference_matte",new Difference_matte(),category["Channels"]); + add_type("rgb_levels",new RGB_levels(),category["Channels"]); + add_type("luma_levels",new Luma_levels(),category["Channels"]); - category["source"]=vector<Node*>(); - add_type("signal_colour",new Signal_colour(),category["source"]); - add_type("signal_greyscale",new Signal_greyscale(),category["source"]); - add_type("shape",new Shape(),category["source"]); - add_type("text",new Text(),category["source"]); - add_type("waves",new Waves(),category["source"]); - add_type("still_image",new Still_image(),category["source"]); - add_type("video_loader",new Video_loader(),category["source"]); - add_type("svg",new Svg(),category["source"]); + category["Source"]=vector<Node*>(); + add_type("signal_colour",new Signal_colour(),category["Source"]); + add_type("signal_greyscale",new Signal_greyscale(),category["Source"]); + add_type("shape",new Shape(),category["Source"]); + add_type("text",new Text(),category["Source"]); + add_type("waves",new Waves(),category["Source"]); + add_type("still_image",new Still_image(),category["Source"]); + add_type("video_loader",new Video_loader(),category["Source"]); + add_type("video_bank",new Video_bank(),category["Source"]); + add_type("svg",new Svg(),category["Source"]); - category["distort"]=vector<Node*>(); - add_type("mirror",new Mirror(),category["distort"]); - add_type("transform",new Transform(),category["distort"]); + category["Distort"]=vector<Node*>(); + add_type("mirror",new Mirror(),category["Distort"]); + add_type("transform",new Transform(),category["Distort"]); - category["editing"]=vector<Node*>(); - add_type("video_cycler",new Video_cycler(),category["editing"]); - add_type("video_output",new Video_output(),category["editing"]); - add_type("act_segmenter",new Act_segmenter(),category["editing"]); + category["Editing"]=vector<Node*>(); + add_type("video_cycler",new Video_cycler(),category["Editing"]); + add_type("video_output",new Video_output(),category["Editing"]); + add_type("act_segmenter",new Act_segmenter(),category["Editing"]); - category["audio"]=vector<Node*>(); - add_type("audio_analysis",new Audio_analysis(),category["audio"]); - add_type("audio_analysis2",new Audio_analysis2(),category["audio"]); - add_type("intensity_segmenter",new Intensity_segmenter(),category["audio"]); + category["Audio"]=vector<Node*>(); + add_type("audio_analysis",new Audio_analysis(),category["Audio"]); + add_type("audio_analysis2",new Audio_analysis2(),category["Audio"]); + add_type("intensity_segmenter",new Intensity_segmenter(),category["Audio"]); - category["maths"]=vector<Node*>(); - add_type("comparison",new Comparison(),category["maths"]); //TODO: alias to symbols - add_type("arithmetic",new Arithmetic(),category["maths"]); //TODO: alias to symbols - add_type("bang",new Is_new_integer(),category["maths"]); - add_type("on_off",new On_off(),category["maths"]); - add_type("random",new Random(),category["maths"]); - add_type("noise",new Noise(),category["maths"]); + category["Maths"]=vector<Node*>(); + add_type("comparison",new Comparison(),category["Maths"]); //TODO: alias to symbols + add_type("arithmetic",new Arithmetic(),category["Maths"]); //TODO: alias to symbols + add_type("bang",new Is_new_integer(),category["Maths"]); + add_type("on_off",new On_off(),category["Maths"]); + add_type("random",new Random(),category["Maths"]); + add_type("noise",new Noise(),category["Maths"]); - category["fx"]=vector<Node*>(); - add_type("blur",new Blur(),category["fx"]); - add_type("vhs",new VHS(),category["fx"]); - add_type("echo_trails",new Echo_trails(),category["fx"]); - add_type("video_feedback",new Video_feedback(),category["fx"]); + category["FX"]=vector<Node*>(); + add_type("blur",new Blur(),category["FX"]); + add_type("vhs",new VHS(),category["FX"]); + add_type("echo_trails",new Echo_trails(),category["FX"]); + add_type("video_feedback",new Video_feedback(),category["FX"]); } bool Signal_input::connect(Node* source) { @@ -112,7 +113,89 @@ bool Node_factory::list_node(const string &t,xmlIO XML){ } } XML.addValue("error","Node /"+t+"/ not found"); + return false; }; +bool Node_factory::list_node(const string &t,Json::Value &JSON){ + for (auto& type: type_map) { + if (type.first==t) { + JSON["node"]=list_node(type.second); + return true; + } + } + JSON["error"]="Node /"+t+"/ not found"; + return false; +}; +Json::Value Node_factory::list_node(Rotor::Node* _node){ + Json::Value node; + node["type"]=_node->type; + node["title"]=_node->title; + node["inputs"]=_node->duplicate_inputs?"expandable":"fixed"; + if (dynamic_cast<Signal_node*> (_node)!=nullptr) node["output"]="signal"; + if (dynamic_cast<Image_node*> (_node)!=nullptr) node["output"]="image"; + node["description"]=_node->description; + node["UID"]=_node->UID; + if (_node->inputs.size()){ + node["signal_inputs"]=Json::arrayValue; + for (auto& input: _node->inputs) { + Json::Value signal_input; + signal_input["title"]=input->title; + signal_input["description"]=input->description; + node["signal_inputs"].append(signal_input); + } + } + if (dynamic_cast<Image_node*> (_node)!=nullptr) { + if ((dynamic_cast<Image_node*>(_node))->image_inputs.size()){ + node["image_inputs"]=Json::arrayValue; + for (auto& input: (dynamic_cast<Image_node*>(_node))->image_inputs) { + Json::Value image_input; + image_input["title"]=input->title; + image_input["description"]=input->description; + node["image_inputs"].append(image_input); + } + } + } + if (_node->parameters.size()){ + node["parameters"]=Json::arrayValue; + for (auto& param: _node->parameters) { + Json::Value parameter; + parameter["name"]=param.first; + parameter["type"]=param.second->type; + parameter["title"]=param.second->title; + parameter["description"]=param.second->description; + parameter["value"]=param.second->value; + parameter["min"]=param.second->min; + parameter["max"]=param.second->max; + parameter["step"]=param.second->step; + node["parameters"].append(parameter); + } + } + if (_node->attributes.size()){ + node["attributes"]=Json::arrayValue; + for (auto& attr: _node->attributes) { + Json::Value attribute; + attribute["name"]=attr.first; + attribute["title"]=attr.second->title; + attribute["description"]=attr.second->description; + if (attr.second->vals.size()){ //document attribute enumeration + attribute["value"]=attr.second->value; + attribute["type"]="enum"; + attribute["options"]=Json::arrayValue; + for (auto val: attr.second->vals){ + attribute["options"].append(val); + } + } + else { + attribute["type"]=attr.second->type; + if (attr.second->type=="array"){ + attribute["value"]=Json::arrayValue; + } + else attribute["value"]=attr.second->value; + } + node["attributes"].append(attribute); + } + } + return node; +} void Node_factory::list_node(Rotor::Node* type,xmlIO XML,int i=0){ XML.addTag("node"); XML.addAttribute("node","type",type->type,i); @@ -162,8 +245,8 @@ void Node_factory::list_node(Rotor::Node* type,xmlIO XML,int i=0){ XML.addAttribute("attribute","name",attribute.first,j); XML.addAttribute("attribute","title",attribute.second->title,j); XML.addAttribute("attribute","description",attribute.second->description,j); - XML.addAttribute("attribute","value",attribute.second->value,j); if (attribute.second->vals.size()){ //document attribute enumeration + XML.addAttribute("attribute","value",attribute.second->value,j); XML.addAttribute("attribute","type","enum",j); XML.pushTag("attribute",j); int k=0; @@ -174,7 +257,15 @@ void Node_factory::list_node(Rotor::Node* type,xmlIO XML,int i=0){ } XML.popTag(); } - else XML.addAttribute("attribute","type","string",j); + else { + XML.addAttribute("attribute","type",attribute.second->type,j); + if (attribute.second->type=="array"){ + XML.pushTag("attribute",j); + XML.addTag("value"); + XML.popTag(); + } + else XML.addAttribute("attribute","value",attribute.second->value,j); + } j++; } XML.popTag(); @@ -210,69 +301,9 @@ void Node_factory::list_categories(Json::Value &JSON){ category["name"]=_category.first; category["nodes"]=Json::arrayValue; for (auto& _node: _category.second){ - Json::Value node; - node["type"]=_node->type; - node["title"]=_node->title; - node["inputs"]=_node->duplicate_inputs?"expandable":"fixed"; - if (dynamic_cast<Signal_node*> (_node)!=nullptr) node["output"]="signal"; - if (dynamic_cast<Image_node*> (_node)!=nullptr) node["output"]="image"; - node["description"]=_node->description; - node["UID"]=_node->UID; - if (_node->inputs.size()){ - node["signal_inputs"]=Json::arrayValue; - for (auto& input: _node->inputs) { - Json::Value signal_input; - signal_input["title"]=input->title; - signal_input["description"]=input->description; - node["signal_inputs"].append(signal_input); - } - } - if (dynamic_cast<Image_node*> (_node)!=nullptr) { - if ((dynamic_cast<Image_node*>(_node))->image_inputs.size()){ - node["image_inputs"]=Json::arrayValue; - for (auto& input: (dynamic_cast<Image_node*>(_node))->image_inputs) { - Json::Value image_input; - image_input["title"]=input->title; - image_input["description"]=input->description; - node["image_inputs"].append(image_input); - } - } - } - if (_node->parameters.size()){ - node["parameters"]=Json::arrayValue; - for (auto& param: _node->parameters) { - Json::Value parameter; - parameter["name"]=param.first; - parameter["type"]=param.second->type; - parameter["title"]=param.second->title; - parameter["description"]=param.second->description; - parameter["value"]=param.second->value; - parameter["min"]=param.second->min; - parameter["max"]=param.second->max; - parameter["step"]=param.second->step; - node["parameters"].append(parameter); - } - } - if (_node->attributes.size()){ - node["attributes"]=Json::arrayValue; - for (auto& attr: _node->attributes) { - Json::Value attribute; - attribute["name"]=attr.first; - attribute["title"]=attr.second->title; - attribute["description"]=attr.second->description; - attribute["value"]=attr.second->value; - if (attr.second->vals.size()){ //document attribute enumeration - attribute["type"]="enum"; - attribute["options"]=Json::arrayValue; - for (auto val: attr.second->vals){ - attribute["options"].append(val); - } - } - else attribute["type"]="string"; - node["attributes"].append(attribute); - } - } - category["nodes"].append(node); + //cerr<<"Adding "<<_category.first<<":"<<_node->type<<endl; + + category["nodes"].append(list_node(_node)); } JSON["category"].append(category); } @@ -281,69 +312,7 @@ void Node_factory::list_nodes(Json::Value &JSON){ JSON["nodeslist"]=Json::arrayValue; for (auto& type: type_map) { if (type.second->description!="") { //blank description = internal/ testing node - Json::Value node; - node["type"]=type.first; - node["title"]=type.second->title; - node["inputs"]=type.second->duplicate_inputs?"expandable":"fixed"; - if (dynamic_cast<Signal_node*> (type.second)!=nullptr) node["output"]="signal"; - if (dynamic_cast<Image_node*> (type.second)!=nullptr) node["output"]="image"; - node["description"]=type.second->description; - node["UID"]=type.second->UID; - if (type.second->inputs.size()){ - node["signal_inputs"]=Json::arrayValue; - for (auto& input: type.second->inputs) { - Json::Value signal_input; - signal_input["title"]=input->title; - signal_input["description"]=input->description; - node["signal_inputs"].append(signal_input); - } - } - if (dynamic_cast<Image_node*> (type.second)!=nullptr) { - if ((dynamic_cast<Image_node*>(type.second))->image_inputs.size()){ - node["image_inputs"]=Json::arrayValue; - for (auto& input: (dynamic_cast<Image_node*>(type.second))->image_inputs) { - Json::Value image_input; - image_input["title"]=input->title; - image_input["description"]=input->description; - node["image_inputs"].append(image_input); - } - } - } - if (type.second->parameters.size()){ - node["parameters"]=Json::arrayValue; - for (auto& param: type.second->parameters) { - Json::Value parameter; - parameter["name"]=param.first; - parameter["type"]=param.second->type; - parameter["title"]=param.second->title; - parameter["description"]=param.second->description; - parameter["value"]=param.second->value; - parameter["min"]=param.second->min; - parameter["max"]=param.second->max; - parameter["step"]=param.second->step; - node["parameters"].append(parameter); - } - } - if (type.second->attributes.size()){ - node["attributes"]=Json::arrayValue; - for (auto& attr: type.second->attributes) { - Json::Value attribute; - attribute["name"]=attr.first; - attribute["title"]=attr.second->title; - attribute["description"]=attr.second->description; - attribute["value"]=attr.second->value; - if (attr.second->vals.size()){ //document attribute enumeration - attribute["type"]="enum"; - attribute["options"]=Json::arrayValue; - for (auto val: attr.second->vals){ - attribute["options"].append(val); - } - } - else attribute["type"]="string"; - node["attributes"].append(attribute); - } - } - JSON["nodeslist"].append(node); + JSON["nodeslist"].append(list_node(type.second)); } } }
\ No newline at end of file diff --git a/rotord/src/rotor.h b/rotord/src/rotor.h index d9ba66d..94ec643 100644 --- a/rotord/src/rotor.h +++ b/rotord/src/rotor.h @@ -75,6 +75,7 @@ Requirements #include <iostream> #include <json/json.h> +#include <rsvg.h> #include "Poco/Net/HTTPResponse.h" #include "Poco/Logger.h" @@ -205,7 +206,7 @@ namespace Rotor { class Attribute{ //description of a static attribute which can be an enumerated string array public: virtual ~Attribute(){}; - Attribute(const string &_desc,const string &_title,const string &_value,std::vector<std::string> _vals={}): description(_desc),title(_title),value(_value),intVal(0){ + Attribute(const string &_desc,const string &_title,const string &_value,std::vector<std::string> _vals={},std::string _type="string"): description(_desc),title(_title),value(_value),intVal(0),type(_type){ vals=_vals; init(_value); }; @@ -217,7 +218,7 @@ namespace Rotor { } else intVal=0; } - string value,description,title; + string value,description,title,type; std::vector<std::string> vals; int intVal; }; @@ -239,15 +240,15 @@ namespace Rotor { void create_parameter(const string &_name,const string &_type,const string &_desc,const string &_title,float _value=1.0f,float _min=0.0f,float _max=0.0f,float _step=0.0f,Node* _connect=nullptr) { parameters[_name]=new Parameter(_type,_desc,_title,_value,_min,_max,_step,_connect); }; - void create_attribute(const string &_attr,const string &_desc,const string &_title,const string &_value,std::vector<std::string> _vals={}) { - attributes[_attr]=new Attribute(_desc,_title,_value,_vals); + void create_attribute(const string &_attr,const string &_desc,const string &_title,const string &_value,std::vector<std::string> _vals={},std::string _type="string") { + attributes[_attr]=new Attribute(_desc,_title,_value,_vals,_type); }; - void create_attribute(string *alias,const string &_attr,const string &_desc,const string &_title,const string &_value,std::vector<std::string> _vals={}) { - create_attribute(_attr,_desc,_title,_value,_vals); + void create_attribute(string *alias,const string &_attr,const string &_desc,const string &_title,const string &_value,std::vector<std::string> _vals={},std::string _type="string") { + create_attribute(_attr,_desc,_title,_value,_vals,_type); alias=&(attributes[_attr]->value); }; - void create_attribute(int *alias,const string &_attr,const string &_desc,const string &_title,const string &_value,std::vector<std::string> _vals={}) { - create_attribute(_attr,_desc,_title,_value,_vals); + void create_attribute(int *alias,const string &_attr,const string &_desc,const string &_title,const string &_value,std::vector<std::string> _vals={},std::string _type="string") { + create_attribute(_attr,_desc,_title,_value,_vals,_type); alias=&(attributes[_attr]->intVal); }; virtual void init_attribute(const string &attr){}; @@ -342,17 +343,6 @@ namespace Rotor { protected: unsigned char *lut; }; - class Audio_processor: public Signal_node { - public: - virtual Audio_processor(){}; - virtual ~Audio_processor(){}; - virtual int process_frame(uint8_t *data,int samples)=0; - virtual bool init(int _channels,int _bits,int _samples,int _rate)=0; - virtual void cleanup()=0; - virtual void print_summary(){}; - virtual string get_features(){}; - int channels,bits,samples,rate; - }; //actual nodes------------------------------------------------- #define CYCLER_cut 1 #define CYCLER_mix 2 @@ -578,6 +568,24 @@ namespace Rotor { Image in1,in2,in2t,temp; //for blend frames; string *filename; }; + class Video_bank: public Image_node { + public: + Video_bank(){ + create_attribute("filenames","names of video files to load","File names","",{},"array"); + UID="73616e66-4306-11e3-981e-74d02b29f6a6"; + title="Video bank"; + description="Loads a banks of video files"; + }; + Video_bank(map<string,string> &settings): Video_bank() { + base_settings(settings); + }; + Image *output(const Frame_spec &frame){ + return nullptr; + } + Video_bank* clone(map<string,string> &_settings) { return new Video_bank(_settings);}; + ~Video_bank(){}; + private: + }; class Video_output: public Image_node { //Video_output 'presents' the output movie. Aspect ratio, bars, fadein/fadeout would happen here public: @@ -706,14 +714,16 @@ namespace Rotor { return NULL; }; bool list_node(const string &t,xmlIO XML); + bool list_node(const string &t,Json::Value &JSON); void list_node(Rotor::Node* type,xmlIO XML,int i=0); + Json::Value list_node(Rotor::Node* type); void list_nodes(xmlIO XML); void list_nodes(Json::Value &JSON); void list_categories(xmlIO XML); void list_categories(Json::Value &JSON); private: - unordered_map<string,Node*> type_map; - unordered_map<string,vector<Rotor::Node*> > category; + map<string,Node*> type_map; + map<string,vector<Rotor::Node*> > category; }; } diff --git a/rotord/src/rotord.cpp b/rotord/src/rotord.cpp index de853e4..c4c562e 100644 --- a/rotord/src/rotord.cpp +++ b/rotord/src/rotord.cpp @@ -212,8 +212,20 @@ HTTPRequestHandler* RotorRequestHandlerFactory::createRequestHandler(const HTTPS } } else if (command[0]=="listnode") { - XML.pushTag("rotor"); if (request.getMethod()=="GET") { + if (command.size()>1){ + if (command[1]=="json") { + Json::Value root; + Json::StyledWriter writer; + + Node_factory factory; + factory.list_node(body,root); + string content = writer.write(root); + status=HTTPResponse::HTTP_OK; + return new RenderContextHandler(content, status); + } + } + XML.pushTag("rotor"); Node_factory factory; if (factory.list_node(body,XML)) status=HTTPResponse::HTTP_OK; } diff --git a/rotord/src/rotord.h b/rotord/src/rotord.h index 0eb1eb3..dd6a25c 100644 --- a/rotord/src/rotord.h +++ b/rotord/src/rotord.h @@ -86,12 +86,15 @@ class RenderContextHandler: public HTTPRequestHandler class RotorRequestHandlerFactory: public HTTPRequestHandlerFactory { public: - RotorRequestHandlerFactory(){ + RotorRequestHandlerFactory(): manager(pool){ xmlIO xml; if(xml.loadFile("settings.xml") ){ media_dir=xml.getAttribute("Rotor","media_dir","",0); thumbnail_dir=xml.getAttribute("Rotor","thumbnail_dir","",0); } + pool.addCapacity(300); + + //manager=Poco::TaskManager(pool); } HTTPRequestHandler* createRequestHandler(const HTTPServerRequest& request); private: @@ -100,6 +103,7 @@ class RotorRequestHandlerFactory: public HTTPRequestHandlerFactory std::unordered_map<std::string,std::string> renders; Poco::UUIDGenerator idGen; Poco::TaskManager manager; + Poco::ThreadPool pool; std::string media_dir; std::string thumbnail_dir; }; diff --git a/rotord/src/vampHost.cpp b/rotord/src/vampHost.cpp index c1340f2..2ddc62f 100644 --- a/rotord/src/vampHost.cpp +++ b/rotord/src/vampHost.cpp @@ -275,6 +275,10 @@ bool vampHost::Analyser::init(const string &soname,const string &id,const int &_ return false; } + cerr << "Vamphost Plugin initialised: (channels = " << channels + << ", stepSize = " << stepSize << ", blockSize = " + << blockSize << ")" << endl; + wrapper = dynamic_cast<PluginWrapper *>(plugin); if (wrapper) { // See documentation for @@ -294,6 +298,7 @@ bool vampHost::Analyser::init(const string &soname,const string &id,const int &_ return true; } void vampHost::Analyser::process_frame(uint8_t *data,int samples_in_frame){ + int sample=0; uint16_t *_data=(uint16_t*)data; @@ -317,17 +322,25 @@ void vampHost::Analyser::process_frame(uint8_t *data,int samples_in_frame){ //I /think/ that the vamp plugin keeps processing through the plugbuf until it encounters 0s rt = RealTime::frame2RealTime(currentStep * stepSize, rate); //48000); //setting different rate doesn't affect it + Plugin::FeatureSet feat=plugin->process(plugbuf, rt); + float t; + for (unsigned int i = 0; i < feat[outputNo].size(); ++i) { feature f; f.number=featureNo; f.values=feat[outputNo][i].values; - features[((float)feat[outputNo][i].timestamp.sec)+(((float)feat[outputNo][i].timestamp.nsec)*.000000001)]=f; + //fix for plugins that don't set timestamp properly + t=((float)feat[outputNo][i].timestamp.sec)+(((float)feat[outputNo][i].timestamp.nsec)*.000000001); + if (t<.01) t=((rt.sec)+(rt.nsec)*.000000001); + features[t]=f; featureNo++; } + //if (feat[outputNo].size()>0) cerr<<"vamphost: "<<t<<":"<<features.size()<<" features"<<endl; + //shunt it down for (int i=0;i<blockSize-stepSize;i++){ for (int j=0;j<channels;j++){ @@ -335,13 +348,14 @@ void vampHost::Analyser::process_frame(uint8_t *data,int samples_in_frame){ } } + //if (feat[outputNo].size()>0) cerr<<plugin->getIdentifier()<<" step:"<<currentStep<<" sample:"<<currentStep * stepSize<<" features:"<<features.size()<<endl; + in_block-=stepSize; - currentStep++; + currentStep++; //WTF this number does not increase when called the 2nd way } } } void vampHost::Analyser::cleanup(){ - //process final block while(in_block<blockSize) { for (int i=0;i<channels;i++) { @@ -386,3 +400,21 @@ void vampHost::Analyser::cleanup(){ delete[] plugbuf; delete plugin; } +float vampHost::Analyser::get_value(const float &time) { + if (features.size()) { + auto i=features.upper_bound(time); //the first element in the container whose key is considered to go after k + if (i!=features.end()){ + float uk=i->first; + float v1,v2; + v1=v2=0.0f; + if (i->second.values.size()) v2=i->second.values[0]; + i--; + float lk=i->first; + int ln=i->second.number; + if (i->second.values.size()) v1=i->second.values[0]; + + return ((((time-lk)/(uk-lk))*(v2-v1))+v1); + } + } + return 0.0f; +} diff --git a/rotord/src/vampHost.h b/rotord/src/vampHost.h index 56de901..856a2cc 100644 --- a/rotord/src/vampHost.h +++ b/rotord/src/vampHost.h @@ -64,7 +64,7 @@ namespace vampHost { bool init(const string &soname,const string &id,const int &_channels,const int &_bits,const int &_samples,const int &_rate,int outputNo,const map<string,float> ¶ms); void process_frame(uint8_t *data,int samples_in_frame); void cleanup(); - + float get_value(const float &time); //map<double,int> features; map<float,feature> features; //map<time,featureNo> |
