summaryrefslogtreecommitdiff
path: root/NT/src/nodes_audio_analysis.h
diff options
context:
space:
mode:
Diffstat (limited to 'NT/src/nodes_audio_analysis.h')
-rw-r--r--NT/src/nodes_audio_analysis.h329
1 files changed, 329 insertions, 0 deletions
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<double> {
+ 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<double> 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 "<<id<<" of library "<<soname<<" found "<<features.size()<<" features "<<endl;
+ };
+ protected:
+ string soname,id;
+ int outputNo;
+ map <string,float> params;
+ map<double,vampHost::feature> 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<string,string> &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<string,string> &_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<string,string> &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<string,string> &_settings) { return new Audio_analysis2(_settings);};
+ private:
+ vector< pair<string,string>> 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<string,string> &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<string,string> &_settings) { return new Act_segmenter(_settings);};
+ void cleanup(){
+ Vamp_node::cleanup();
+ map<double,vampHost::feature> acts; //temporary storage for new set of features
+ vector<int> 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<int,double> durations;
+ map<int,double> times;
+ int i=0;
+ for (map<double,vampHost::feature>::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<double,vampHost::feature> 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]<i)) {
+ act=i;
+ break;
+ }
+ }
+ }
+ else {
+ f=(*--segments.end()).second;
+ t=(*--segments.end()).first;
+ segments.erase(--segments.end());
+ for (int i=(int)parameters["acts"]->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<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) {
+ 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 "<<features.size()<<" features "<<endl;
+ };
+ private:
+ map<string,vampHost::Analyser> analysers;
+ };
+}
+
+
+
+#endif