diff options
| -rw-r--r-- | rotord/rotor.cpp | 28 | ||||
| -rwxr-xr-x | rotord/rotor.h | 13 | ||||
| -rw-r--r-- | rotord/vampHost.cpp | 155 | ||||
| -rw-r--r-- | rotord/vampHost.h | 12 |
4 files changed, 190 insertions, 18 deletions
diff --git a/rotord/rotor.cpp b/rotord/rotor.cpp index cd288e3..73577f4 100644 --- a/rotord/rotor.cpp +++ b/rotord/rotor.cpp @@ -30,7 +30,10 @@ void Render_context::runTask() { if (load_audio(audio_filename,processors)) { state=AUDIO_READY; } - else state=IDLE; + else { + //an error occurred: TODO have to clean up allocated data. autoptr? + state=IDLE; + } } sleep(100); } @@ -326,7 +329,10 @@ bool Render_context::load_audio(const string &filename,vector<Base_audio_process std::cout << "The data is in format " <<codecContext->sample_fmt<< " (aka "<< av_get_sample_fmt_name(codecContext->sample_fmt) << ") "<<std::endl; for (auto p: processors) { - p->init(codecContext->channels,16,samples,codecContext->sample_rate); + if(!p->init(codecContext->channels,16,samples,codecContext->sample_rate) ){ + cerr<<"Plugin failed to initialse"<<endl; + return false; + } } AVPacket packet; @@ -397,6 +403,10 @@ bool Render_context::load_audio(const string &filename,vector<Base_audio_process 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); @@ -469,7 +479,7 @@ Node_factory::Node_factory(){ add_type("==",new Is_new_integer()); add_type("signal_output",new Signal_output()); } -void Audio_thumbnailer::init(int _channels,int _bits,int _samples,int _rate) { +bool Audio_thumbnailer::init(int _channels,int _bits,int _samples,int _rate) { //base_audio_processor::init(_channels,_bits,_samples); channels=_channels; bits=_bits; @@ -482,6 +492,7 @@ void Audio_thumbnailer::init(int _channels,int _bits,int _samples,int _rate) { sample=0; samples=0; accum=0.0; + return true; } int Audio_thumbnailer::process_frame(uint8_t *_data,int samples_in_frame){ //begin by processing remaining samples @@ -539,13 +550,13 @@ string Audio_thumbnailer::print(){ } return output; } -void Audio_analysis::init(int _channels,int _bits,int _samples, int _rate) { +bool Audio_analysis::init(int _channels,int _bits,int _samples, int _rate) { //need these to make sense of data channels=_channels; bits=_bits; samples=_samples; - - analyser.init(soname,id,_rate); + + 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? @@ -553,6 +564,9 @@ void Audio_analysis::init(int _channels,int _bits,int _samples, int _rate) { } int Audio_analysis::process_frame(uint8_t *data,int samples_in_frame) { - //the standard vamp process takes a file + analyser.process_frame(data,samples_in_frame); return 1; } +void Audio_analysis::cleanup() { + analyser.cleanup(); +} diff --git a/rotord/rotor.h b/rotord/rotor.h index 11e1170..105ed98 100755 --- a/rotord/rotor.h +++ b/rotord/rotor.h @@ -183,7 +183,7 @@ namespace Rotor { string type; string output_type; string ID; - string check(map<string,string> &settings,string key){ if (settings.find(key)!=settings.end()) return settings[key]; else return "";}; + string check(map<string,string> &settings,string key,string def=""){ if (settings.find(key)!=settings.end()) return settings[key]; else return def;}; void base_settings(map<string,string> &settings) { description=check(settings,"description"); type=check(settings,"type"); @@ -225,7 +225,8 @@ namespace Rotor { class Base_audio_processor: public Signal_node { public: virtual int process_frame(uint8_t *data,int samples)=0; - virtual void init(int _channels,int _bits,int _samples,int _rate)=0; + virtual bool init(int _channels,int _bits,int _samples,int _rate)=0; + virtual void cleanup()=0; int channels,bits,samples,rate; }; //actual nodes------------------------------------------------- @@ -236,9 +237,11 @@ namespace Rotor { base_settings(settings); soname=check(settings,"soname"); id=check(settings,"id"); + outputNo=ofToInt(check(settings,"output","0")); }; Audio_analysis* clone(map<string,string> &_settings) { return new Audio_analysis(_settings);}; - void init(int _channels,int _bits,int _samples,int _rate); + bool init(int _channels,int _bits,int _samples,int _rate); + void cleanup(); int process_frame(uint8_t *data,int samples_in_frame); float get_output(const float &time) { float t=time; @@ -246,6 +249,7 @@ namespace Rotor { } private: string soname,id; + int outputNo; vampHost::Analyser analyser; }; class Signal_divide: public Signal_node { @@ -375,7 +379,8 @@ namespace Rotor { delete[] data; }; Audio_thumbnailer* clone(map<string,string> &_settings) { return new Audio_thumbnailer();}; - void init(int _channels,int _bits,int _samples,int _rate); + bool init(int _channels,int _bits,int _samples,int _rate); + void cleanup(){}; int process_frame(uint8_t *data,int samples_in_frame); string print(); uint8_t *data; diff --git a/rotord/vampHost.cpp b/rotord/vampHost.cpp index b9b0781..e28419c 100644 --- a/rotord/vampHost.cpp +++ b/rotord/vampHost.cpp @@ -588,21 +588,164 @@ float vampHost::QMAnalyser::get_progress(){ mutex.unlock(); return p; } -void vampHost::Analyser::init(const string &soname,const string &id,const int &rate){ +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; + rate=_rate; + 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 *plugin = loader->loadPlugin(key, _rate, PluginLoader::ADAPT_ALL_SAFE); if (!plugin) { cerr << ": ERROR: Failed to load plugin \"" << id << "\" from library \"" << soname << "\"" << endl; - return; + return false; } cerr << "Running plugin: \"" << plugin->getIdentifier() << "\"..." << endl; - delete plugin; + blockSize = plugin->getPreferredBlockSize(); + stepSize = plugin->getPreferredStepSize(); - return; -} + if (blockSize == 0) { + blockSize = 1024; + } + if (stepSize == 0) { + if (plugin->getInputDomain() == Plugin::FrequencyDomain) { + stepSize = blockSize/2; + } else { + stepSize = blockSize; + } + } + else if (stepSize > blockSize) { + cerr << "WARNING: stepSize " << stepSize << " > blockSize " << blockSize << ", resetting blockSize to "; + if (plugin->getInputDomain() == Plugin::FrequencyDomain) { + blockSize = stepSize * 2; + } else { + blockSize = stepSize; + } + cerr << blockSize << endl; + } + 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 + + filebuf = new float[blockSize * channels]; + plugbuf = new float*[channels]; + for (int c = 0; c < channels; ++c) plugbuf[c] = new float[blockSize + 2]; + + cerr << "Using block size = " << blockSize << ", step size = " + << stepSize << endl; + + // The channel queries here are for informational purposes only -- + // a PluginChannelAdapter is being used automatically behind the + // scenes, and it will take case of any channel mismatch + + int minch = plugin->getMinChannelCount(); + int maxch = plugin->getMaxChannelCount(); + cerr << "Plugin accepts " << minch << " -> " << maxch << " channel(s)" << endl; + cerr << "Sound file has " << channels << " (will mix/augment if necessary)" << endl; + + Plugin::OutputList outputs = plugin->getOutputDescriptors(); + Plugin::OutputDescriptor od; + + int returnValue = 1; + int prog = 0; + + RealTime rt; + PluginWrapper *wrapper = 0; + RealTime adjustment = RealTime::zeroTime; + + if (outputs.empty()) { + cerr << "ERROR: Plugin has no outputs!" << endl; + return false; + } + if (outputNo < 0) { + for (size_t oi = 0; oi < outputs.size(); ++oi) { + if (outputs[oi].identifier == output) { + outputNo = oi; + break; + } + } + if (outputNo < 0) { + 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; + return false; + } + } + od = outputs[outputNo]; + cerr << "Output is: \"" << od.identifier << "\"" << endl; + + if (!plugin->initialise(channels, stepSize, blockSize)) { + cerr << "ERROR: Plugin initialise (channels = " << channels + << ", stepSize = " << stepSize << ", blockSize = " + << blockSize << ") failed." << endl; + return false; + } + + wrapper = dynamic_cast<PluginWrapper *>(plugin); + if (wrapper) { + // See documentation for + // PluginInputDomainAdapter::getTimestampAdjustment + 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; + + return true; +} +void vampHost::Analyser::process_frame(uint8_t *data,int samples_in_frame){ + int sample=0; + //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) { + while(sample<samples_in_frame&&in_block<blockSize) { + 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); + } + plugbuf[i][in_block]=((float)((int16_t)this_val))*scale; + } + in_block++; + sample++; + } + if (in_block==blockSize) { + //block is ready to be processed + cerr<<"processing block "<<blocks_processed<<endl; + in_block=0; + blocks_processed++; + } + } +} +void vampHost::Analyser::cleanup(){ + delete[] filebuf; + for (int c = 0; c < channels; ++c) { + delete[] plugbuf[c]; + } + delete[] plugbuf; + delete plugin; +} diff --git a/rotord/vampHost.h b/rotord/vampHost.h index de6ae02..942c98b 100644 --- a/rotord/vampHost.h +++ b/rotord/vampHost.h @@ -55,12 +55,22 @@ namespace vampHost { }; class Analyser{ public: - void init(const string &soname,const string &id,const int &rate); + 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; Plugin *plugin; + int channels,bits,samples,rate; + int bytes,stride; + float scale; + int blockSize,stepSize,overlapSize,finalStepsRemaining,currentStep,outputNo; + int in_block,blocks_processed; + string output; + float *filebuf; + float **plugbuf; }; string getQMBeats(const string soundfile); |
