diff options
| -rw-r--r-- | rotord/rotor.cpp | 26 | ||||
| -rw-r--r-- | rotord/vampHost.cpp | 85 | ||||
| -rw-r--r-- | rotord/vampHost.h | 10 |
3 files changed, 73 insertions, 48 deletions
diff --git a/rotord/rotor.cpp b/rotord/rotor.cpp index 73577f4..2cd894f 100644 --- a/rotord/rotor.cpp +++ b/rotord/rotor.cpp @@ -259,7 +259,7 @@ Command_response Render_context::session_command(const std::vector<std::string>& //great to use c++11 features bool Render_context::load_audio(const string &filename,vector<Base_audio_processor*> processors){ - + av_register_all(); AVFrame* frame = avcodec_alloc_frame(); @@ -363,22 +363,22 @@ bool Render_context::load_audio(const string &filename,vector<Base_audio_process // the audio data, so I won't add any junk here that might confuse you. Typically, if I want to find // documentation on an FFmpeg structure or function, I just type "<name> doxygen" into google (like // "AVFrame doxygen" for AVFrame's docs) - + //av_get_channel_layout_string (char *buf, int buf_size, int nb_channels, uint64_t channel_layout) - - + + //now we can pass the data to the processor(s) for (auto p: processors) { sample_processed=p->process_frame(frame->data[0],frame->nb_samples); } - + mutex.lock(); progress=((double)sample_processed)/samples; mutex.unlock(); } } // You *must* call av_free_packet() after each call to av_read_frame() or else you'll leak memory - av_free_packet(&packet); + av_free_packet(&packet); } // Some codecs will cause frames to be buffered up in the decoding process. If the CODEC_CAP_DELAY flag @@ -399,14 +399,14 @@ bool Render_context::load_audio(const string &filename,vector<Base_audio_process mutex.unlock(); } } - + cerr << "finished processing: "<<sample_processed << " samples of "<<samples<<", "<<((double)sample_processed*100)/samples<<"%"<< std::endl; - + // Clean up! for (auto p: processors) { p->cleanup(); } - + av_free(frame); avcodec_close(codecContext); av_close_input_file(formatContext); @@ -471,7 +471,6 @@ bool Graph::load(string &filename){ } else return false; } - Node_factory::Node_factory(){ //for now, statically load prototype map in constructor add_type("audio_analysis",new Audio_analysis()); @@ -502,14 +501,13 @@ int Audio_thumbnailer::process_frame(uint8_t *_data,int samples_in_frame){ int stride=channels*bytes; int in_sample=0; while (in_sample<samples_in_frame&&column<width) { - //start a new column - + //continue the column while (sample<samples_per_column&&in_sample<samples_in_frame) { //accumulate samples for this column until we run out of samples for (int i=0;i<channels;i++) { unsigned int this_val=0; for (int j=0;j<bytes;j++) { - this_val+=_data[(sample*stride)+(i*bytes)+j]<<(j*8); + this_val+=_data[(in_sample*stride)+(i*bytes)+j]<<(j*8); } //convert from integer data format - i.e s16p - to audio signal in -1..1 range //presume 16 bits for now... @@ -557,7 +555,7 @@ bool Audio_analysis::init(int _channels,int _bits,int _samples, int _rate) { samples=_samples; return analyser.init(soname,id,_channels,_bits,_samples,_rate); - + //attempt to load vamp plugin and prepare to receive frames of data //should the audio analysis contain a vamphost or should it inherit? //maybe neater to contain it in terms of headers etc diff --git a/rotord/vampHost.cpp b/rotord/vampHost.cpp index 2521f46..805481c 100644 --- a/rotord/vampHost.cpp +++ b/rotord/vampHost.cpp @@ -477,7 +477,7 @@ void vampHost::rotorGetFeatures(int frame, int sr, int output,Plugin::FeatureSet } for (unsigned int i = 0; i < features[output].size(); ++i) { - + int displayFrame = frame; @@ -501,15 +501,15 @@ int vampHost::QMAnalyser::process(const string inputFile){ // //int runPlugin(string myname, string soname, string id, string output,int outputNo, string inputFile, ostream& out, bool frames); - + //we want to run a specific plugin, outputting progress to a mutex-protected passed variable //and ultimately passing the data back as a string? //or capture it as an array of floats? //get the progress as a float //how to handle errors? - + //debugger fucking up! program stalls after 1 request in debug!? - + string soname="qm-vamp-plugins"; string id="qm-tempotracker"; string myname=""; @@ -521,18 +521,18 @@ int vampHost::QMAnalyser::process(const string inputFile){ } void vampHost::getTimestamps(int output,Plugin::FeatureSet features, vector<float>& out){ - + /* - vamp-simple-host qm-vamp-plugins:qm-tempotracker 01.wav + vamp-simple-host qm-vamp-plugins:qm-tempotracker 01.wav 0.046439908: 156.61 bpm 0.429569160: 156.61 bpm 0.812698412: 161.50 bpm 1.184217686: 152.00 bpm - - + + vamp-simple-host qm-vamp-plugins:qm-segmenter 01.wav - + 0.000000000: 4 4 23.800000000: 6 6 44.600000000: 5 5 @@ -544,33 +544,33 @@ void vampHost::getTimestamps(int output,Plugin::FeatureSet features, vector<floa 143.800000000: 5 5 153.400000000: 3 3 163.000000000: 8 8 - + seems to be FP seconds then another metric for now we can just take the first part - + features[output][i].timestamp is of type RealTime: represents time values to nanosecond precision int sec; int nsec; 1 sec = 10^9 nanosec - + actually maybe this would be the way to go for rotor- avoiding rounding errors etc for now - ideally will get a float representation - + features[output][i].values is a vector of floats + a description WE DON'T CARE ABOUT ANYTHING <.01 seconds - + static long realTime2Frame(const RealTime &r, unsigned int sampleRate); - + get a vector of floats out, using frames, presuming data has a timestamp - - + + this is crashing with "Aborted (core dumped)" if we check for timestamp - + */ - + cout << "." << features[output].size(); - + //if (!features[output][0].hasTimestamp) { // cerr << output << " channel, getTimestamps: error, featureset doesn't support timestamp" << endl; //}_ @@ -589,7 +589,7 @@ float vampHost::QMAnalyser::get_progress(){ return p; } bool vampHost::Analyser::init(const string &soname,const string &id,const int &_channels,const int &_bits,const int &_samples,const int &_rate,const int &_outputNo,const string &_output){ - + //stuff that only happens once channels =_channels; samples=_samples; @@ -597,18 +597,18 @@ bool vampHost::Analyser::init(const string &soname,const string &id,const int &_ bits=_bits; outputNo=_outputNo; output=_output; - + //http://www.mega-nerd.com/libsndfile/api.html#note1 //libsndfile returns -1..1 for fp data bytes=(bits>>3); stride=channels*bytes; scale=(1.0f/pow(2.0f,bits)); - - + + loader = PluginLoader::getInstance(); key = loader->composePluginKey(soname, id); - Plugin *plugin = loader->loadPlugin(key, _rate, PluginLoader::ADAPT_ALL_SAFE); + plugin = loader->loadPlugin(key, _rate, PluginLoader::ADAPT_ALL_SAFE); if (!plugin) { cerr << ": ERROR: Failed to load plugin \"" << id << "\" from library \"" << soname << "\"" << endl; @@ -616,7 +616,7 @@ bool vampHost::Analyser::init(const string &soname,const string &id,const int &_ } cerr << "Running plugin: \"" << plugin->getIdentifier() << "\"..." << endl; - + blockSize = plugin->getPreferredBlockSize(); stepSize = plugin->getPreferredStepSize(); @@ -629,7 +629,7 @@ bool vampHost::Analyser::init(const string &soname,const string &id,const int &_ } else { stepSize = blockSize; } - } + } else if (stepSize > blockSize) { cerr << "WARNING: stepSize " << stepSize << " > blockSize " << blockSize << ", resetting blockSize to "; if (plugin->getInputDomain() == Plugin::FrequencyDomain) { @@ -642,7 +642,7 @@ bool vampHost::Analyser::init(const string &soname,const string &id,const int &_ overlapSize = blockSize - stepSize; currentStep = 0; finalStepsRemaining = max(1, (blockSize / stepSize) - 1); // at end of file, this many part-silent frames needed after we hit EOF - + plugbuf = new float*[channels]; for (int c = 0; c < channels; ++c) plugbuf[c] = new float[blockSize + 2]; @@ -684,7 +684,7 @@ bool vampHost::Analyser::init(const string &soname,const string &id,const int &_ cerr << "ERROR: Non-existent output \"" << output << "\" requested" << endl; return false; } - } + } else { if (int(outputs.size()) <= outputNo) { cerr << "ERROR: Output " << outputNo << " requested, but plugin has only " << outputs.size() << " output(s)" << endl; @@ -700,7 +700,7 @@ bool vampHost::Analyser::init(const string &soname,const string &id,const int &_ << blockSize << ") failed." << endl; return false; } - + wrapper = dynamic_cast<PluginWrapper *>(plugin); if (wrapper) { // See documentation for @@ -708,16 +708,20 @@ bool vampHost::Analyser::init(const string &soname,const string &id,const int &_ PluginInputDomainAdapter *ida =wrapper->getWrapper<PluginInputDomainAdapter>(); if (ida) adjustment = ida->getTimestampAdjustment(); } - + //everything is prepared to start consuming data in blocks - + in_block=0; blocks_processed=0; + currentStep=0; + featureNo=0; + return true; } void vampHost::Analyser::process_frame(uint8_t *data,int samples_in_frame){ int sample=0; + RealTime rt; //process the whole frame which may be f>1<f blocks //when the frame is finished leave the partial block for the next frame while(sample<samples_in_frame) { @@ -734,13 +738,28 @@ void vampHost::Analyser::process_frame(uint8_t *data,int samples_in_frame){ } if (in_block==blockSize) { //block is ready to be processed - cerr<<"processing block "<<blocks_processed<<endl; + //cerr<<plugin->getIdentifier()<<" processed block "<<blocks_processed<<endl; + + //I /think/ that the vamp plugin keeps processing through the plugbuf until it encounters 0s + rt = RealTime::frame2RealTime(in_block * stepSize, rate); + + Plugin::FeatureSet feat=plugin->process(plugbuf, rt); + + for (unsigned int i = 0; i < feat[outputNo].size(); ++i) { + features[((float)feat[outputNo][i].timestamp.sec)+(((float)feat[outputNo][i].timestamp.nsec)*.000000001)]=featureNo; + featureNo++; + } + + //all good but no features! what gives? + in_block=0; blocks_processed++; } } } void vampHost::Analyser::cleanup(){ + cerr<<plugin->getIdentifier()<<" found "<<features.size()<<" features"<<endl; + //deal with left over data? for (int c = 0; c < channels; ++c) { delete[] plugbuf[c]; } diff --git a/rotord/vampHost.h b/rotord/vampHost.h index 18fe297..f6693a9 100644 --- a/rotord/vampHost.h +++ b/rotord/vampHost.h @@ -59,7 +59,6 @@ namespace vampHost { bool init(const string &soname,const string &id,const int &_channels,const int &_bits,const int &_samples,const int &_rate,const int &_outputNo=0,const string &_output=""); void process_frame(uint8_t *data,int samples_in_frame); void cleanup(); - vector<float> beats; private: PluginLoader *loader; PluginLoader::PluginKey key; @@ -71,6 +70,15 @@ namespace vampHost { int in_block,blocks_processed; string output; float **plugbuf; + + int featureNo; + map<float,int> features; + //what's the best way to store features? + //how will it be used? + //we ask for a signal at a time, return interpolated value representing feature number + fraction + //is there a quick way to search the keys and return the last one below a given value + + }; string getQMBeats(const string soundfile); |
