From 89961817f408e921de2c1be6197e2b1ee0f5df98 Mon Sep 17 00:00:00 2001 From: Comment Date: Wed, 29 Jan 2014 23:58:55 +0000 Subject: NT audio framework --- NT/src/nodes_audio_analysis.h | 329 ++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 329 insertions(+) create mode 100644 NT/src/nodes_audio_analysis.h (limited to 'NT/src/nodes_audio_analysis.h') diff --git a/NT/src/nodes_audio_analysis.h b/NT/src/nodes_audio_analysis.h new file mode 100644 index 0000000..4542e19 --- /dev/null +++ b/NT/src/nodes_audio_analysis.h @@ -0,0 +1,329 @@ +#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 Node_type { + public: + 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(){return "";}; + 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(Json::Value& _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); + const double get_output(const Frame_parameters &frame){return 0.0;}; + vector audiodata; + int height,width,samples_per_column; + int out_sample,sample,samples; + int offset; + double 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 double 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 + double uk; + double v1,v2; + v1=v2=0.0; + if (i==features.end()) { + uk=time.duration; + } + else { + uk=i->first; + if (i->second.values.size()) v2=i->second.values[0]; + } + i--; + double lk=i->first; + int ln=i->second.number; + if (i->second.values.size()) v1=i->second.values[0]; + switch (attributes["mode"]->intVal){ + case VAMPHOST_Timeline: + return (((time.time-lk)/(uk-lk))+ln); + case VAMPHOST_Timesteps: + return (double)ln; + case VAMPHOST_Valueline: + return ((time.time-lk)/(uk-lk))+v1; //((((time.time-lk)/(uk-lk))*(v2-v1))+v1); + case VAMPHOST_Values: + return v1; + } + //} + //return (--features.end())->second.values[0]; + } + return 0.0; + } + 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.0); + //title="Audio analysis"; + //description="Analyse audio and output"; + NODEID="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.0); + title="Audio analysis"; + description="Analyse audio and output"; + NODEID="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.0); + create_parameter("acts","number","Number of acts defined","Acts",1.0); + title="Act manager"; + description="Applies a ruleset to manage acts based on segments"; + NODEID="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()<=(uint32_t)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()<(uint32_t)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++; + } + double 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; + double 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"; + NODEID="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.0); + create_parameter("tempo_weight","number","tempo weight","Tempo weighting",1.0); + create_parameter("levels","number","levels","Number of intensity levels",0.0); + }; + 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) { + features.clear(); + 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 -- cgit v1.2.3