#ifndef ROTOR_NODES_AUDIO_ANALYSIS #define ROTOR_NODES_AUDIO_ANALYSIS #include "rotor.h" #include "vampHost.h" namespace Rotor { #define VAMPHOST_Timeline 1 #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(){ height=128; width=512; //fit //trying to reduce valgrind errors out_sample=0; //sample in whole track sample=0; samples=0; accum=0.0; }; ~Audio_thumbnailer(){}; Audio_thumbnailer* clone(map &_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); void print_vector(xmlIO XML); vector audiodata; int height,width,samples_per_column; int out_sample,sample,samples; int offset; float scale,accum; }; class Vamp_node: public Audio_processor { //base class for vamp plugin hosts public: Vamp_node(){ create_attribute("mode","Data output mode","Mode","timeline",{"timeline","timesteps","valueline","values"}); }; bool init(int _channels,int _bits,int _samples,int _rate); void cleanup(); int process_frame(uint8_t *data,int samples_in_frame); const float output(const Time_spec &time) { 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; 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]; int m=attributes["mode"]->intVal; // switch (attributes["mode"]->intVal){ case VAMPHOST_Timeline: return (((time.time-lk)/(uk-lk))+ln); case VAMPHOST_Timesteps: return (float)ln; case VAMPHOST_Valueline: return ((((time.time-lk)/(uk-lk))*(v2-v1))+v1); case VAMPHOST_Values: return v1; } } } return 0.0f; } string get_features(); void print_summary(){ cerr<<"vamp plugin "< params; map features; private: vampHost::Analyser analyser; }; class Audio_analysis: public Vamp_node { //vamp node that allows the user to choose a plugin public: Audio_analysis(){ //create_attribute("soname","Plugin library to use","Plugin library","vamp-example-plugins"); //create_attribute("id","ID of Plugin to use","Plugin ID","percussiononsets"); //create_attribute("analyser","Analyser Plugin to use","Analyser plugin","barbeattracker",{"barbeattracker","segmenter"}); create_parameter("outputNo","number","Plugin output to use","Output number",0.0f); //title="Audio analysis"; //description="Analyse audio and output"; UID="b769f54e-2d0b-11e3-87dd-f73fc7b1c636"; }; Audio_analysis(map &settings):Audio_analysis() { base_settings(settings); soname=find_setting(settings,"soname"); id=find_setting(settings,"id"); outputNo=find_setting(settings,"outputNo",0); }; ~Audio_analysis(){}; Audio_analysis* clone(map &_settings) { return new Audio_analysis(_settings);}; private: }; class Audio_analysis2: public Vamp_node { //reworked the plugin loader public: Audio_analysis2(){ //create_attribute("soname","Plugin library to use","Plugin library","vamp-example-plugins",{"horiz","vert","horizR","vertR"}); //create_attribute("id","ID of Plugin to use","Plugin ID","percussiononsets",{"horiz","vert","horizR","vertR"}); create_attribute("analyser","Analyser Plugin to use","Analyser plugin","barbeattracker",{"adaptivespectrum","barbeattracker","chromagram","dwt","mfcc","onsetdetector","segmenter","similarity","tempotracker","transcription"}); create_parameter("outputNo","number","Plugin output to use","Output number",0.0f); title="Audio analysis"; description="Analyse audio and output"; UID="b769f54e-2d0b-11e3-87dd-f73fc7b1c636"; plugins.push_back(make_pair("qm-adaptivespectrogram","qm-vamp-plugins")); plugins.push_back(make_pair("qm-barbeattracker","qm-vamp-plugins")); plugins.push_back(make_pair("qm-chromagram","qm-vamp-plugins")); plugins.push_back(make_pair("qm-dwt","qm-vamp-plugins")); plugins.push_back(make_pair("qm-mfcc","qm-vamp-plugins")); plugins.push_back(make_pair("qm-onsetdetector","qm-vamp-plugins")); plugins.push_back(make_pair("qm-segmenter","qm-vamp-plugins")); plugins.push_back(make_pair("qm-similarity","qm-vamp-plugins")); plugins.push_back(make_pair("qm-tempotracker","qm-vamp-plugins")); plugins.push_back(make_pair("qm-transcription","qm-vamp-plugins")); }; Audio_analysis2(map &settings):Audio_analysis2() { base_settings(settings); soname=plugins[attributes["analyser"]->intVal-1].second; id=plugins[attributes["analyser"]->intVal-1].first; outputNo=find_setting(settings,"outputNo",0); }; ~Audio_analysis2(){}; void init_attribute(const string &attr){ if (attr=="analyser") { soname=plugins[attributes["analyser"]->intVal-1].second; id=plugins[attributes["analyser"]->intVal-1].first; } if (attr=="outputNo") { outputNo=parameters["outputNo"]->value; } }; Audio_analysis2* clone(map &_settings) { return new Audio_analysis2(_settings);}; private: vector< pair> plugins; }; class Act_segmenter: public Vamp_node { //vamp node that applies a ruleset to manage a set of acts via a cycler public: Act_segmenter(){ create_parameter("outputNo","number","Plugin output to use","Output number",0.0f); create_parameter("acts","number","Number of acts defined","Acts",1.0f); title="Act manager"; description="Applies a ruleset to manage acts based on segments"; UID="c55359a2-2d0b-11e3-8a3d-53fa9c2b8859"; }; Act_segmenter(map &settings):Act_segmenter() { base_settings(settings); soname="qm-vamp-plugins"; id="qm-segmenter"; outputNo=find_setting(settings,"outputNo",0); }; ~Act_segmenter(){}; Act_segmenter* clone(map &_settings) { return new Act_segmenter(_settings);}; void cleanup(){ Vamp_node::cleanup(); map acts; //temporary storage for new set of features vector act_count; for (int i=0;i<(int)parameters["acts"]->value;i++) act_count.push_back(0); 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 (features.size()<(int)parameters["acts"]->value+1){ map durations; map times; int i=0; for (map::iterator f=features.begin();f!=features.end();++f){ auto g=f; if (++g!=features.end()){ durations[i]=g->first-f->first; times[i]=f->first; } i++; } float f=0; int n=-1; for (auto d: durations){ if (d.second>f){ f=d.second; n=d.first; } } features[times[n]+(durations[n]/2)]=features[times[n]]; } int j=0; for (auto f: features){ f.second.number=j; acts[f.first]=f.second; j++; } } else { //there are extra segments to assign to acts //assign act numbers to segments //work through the segments //have a list of segments that are to be filled //start at the ends and work inwards //have a list of how many times each segment has been used //start with a set of the segments numbers //(the aim: to access the segment numbers by the time that they start) map segments=features; segments.erase(--segments.end()); //assign the acts from the beginning and end in towards the middle //when all slots have bveen used it picks the middle item and uses surrrounding items again //there is a limit of item use based on how near it is to the edge bool take_start=true; while (segments.size()){ int act=-1; vampHost::feature f; float t; if (take_start){ f=(*segments.begin()).second; t=(*segments.begin()).first; segments.erase(segments.begin()); for (int i=0;i<(int)parameters["acts"]->value-1;i++) { if (act_count[i]==0||(act_count[i+1]>act_count[i]&&act_count[i]value-1;i>0;i--) { if (act_count[i]==0||(act_count[i-1]>act_count[i]&&act_count[i]<(int)parameters["acts"]->value-i)) { act=i; break; } } } if (act==-1){ //all slots are filled equally act=(int)parameters["acts"]->value/2; } act_count[act]++; f.number=act; acts[t]=f; take_start=!take_start; } } //add last item back on acts[(*--features.end()).first]=features[(*--features.end()).first]; features=acts; } private: }; class Intensity_segmenter: public Vamp_node { //vamp node that applies a ruleset to manage a set of acts via a cycler public: Intensity_segmenter(){ title="Intensity segmenter"; 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 &settings):Intensity_segmenter() { base_settings(settings); }; ~Intensity_segmenter(){}; Intensity_segmenter* clone(map &_settings) { return new Intensity_segmenter(_settings);}; 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); //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(); //all the fun happens here void print_summary(){ cerr<<"Intensity segmenter found "< analysers; }; } #endif