summaryrefslogtreecommitdiff
path: root/rotord
diff options
context:
space:
mode:
Diffstat (limited to 'rotord')
-rw-r--r--rotord/src/graph.cpp9
-rw-r--r--rotord/src/graph.h63
-rw-r--r--rotord/src/libavwrapper.cpp22
-rw-r--r--rotord/src/nodes_audio_analysis.cpp84
-rw-r--r--rotord/src/nodes_audio_analysis.h83
-rw-r--r--rotord/src/nodes_drawing.h7
-rw-r--r--rotord/src/rendercontext.cpp3
-rw-r--r--rotord/src/rotor.cpp315
-rw-r--r--rotord/src/rotor.h52
-rw-r--r--rotord/src/rotord.cpp14
-rw-r--r--rotord/src/rotord.h6
-rw-r--r--rotord/src/vampHost.cpp38
-rw-r--r--rotord/src/vampHost.h2
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> &params);
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>