Mercurial > hg > vamp-tempogram
view Tempogram.cpp @ 2:1d0b7dcea27f
Added manual setting of timestamps as wasn't working on both test machines.
author | Carl Bussey <c.bussey@se10.qmul.ac.uk> |
---|---|
date | Wed, 09 Jul 2014 14:32:32 +0100 |
parents | 3fd1a41b089b |
children | 5125d34fda67 |
line wrap: on
line source
// This is a skeleton file for use in creating your own plugin // libraries. Replace MyPlugin and myPlugin throughout with the name // of your first plugin class, and fill in the gaps as appropriate. #include "Tempogram.h" #include "FIRFilter.h" #include "WindowFunction.h" #include <vamp-sdk/FFT.h> #include <cmath> #include <fstream> #include <assert.h> using Vamp::FFT; using namespace std; Tempogram::Tempogram(float inputSampleRate) : Plugin(inputSampleRate), m_blockSize(0), m_stepSize(0), compressionConstant(1000), //make param previousY(NULL), currentY(NULL), tN(1024), //make param thopSize(512), //make param fftInput(NULL), fftOutputReal(NULL), fftOutputImag(NULL), ncLength(0) // Also be sure to set your plugin parameters (presumably stored // in member variables) to their default values here -- the host // will not do that for you { } Tempogram::~Tempogram() { //delete stuff } string Tempogram::getIdentifier() const { return "tempogram"; } string Tempogram::getName() const { return "Tempogram"; } string Tempogram::getDescription() const { // Return something helpful here! return "Cyclic Tempogram as described by Peter Grosche and Meinard Muller"; } string Tempogram::getMaker() const { //Your name here return "Carl Bussey"; } int Tempogram::getPluginVersion() const { // Increment this each time you release a version that behaves // differently from the previous one return 1; } string Tempogram::getCopyright() const { // This function is not ideally named. It does not necessarily // need to say who made the plugin -- getMaker does that -- but it // should indicate the terms under which it is distributed. For // example, "Copyright (year). All Rights Reserved", or "GPL" return ""; } Tempogram::InputDomain Tempogram::getInputDomain() const { return FrequencyDomain; } size_t Tempogram::getPreferredBlockSize() const { return 0; // 0 means "I can handle any block size" } size_t Tempogram::getPreferredStepSize() const { return 0; // 0 means "anything sensible"; in practice this // means the same as the block size for TimeDomain // plugins, or half of it for FrequencyDomain plugins } size_t Tempogram::getMinChannelCount() const { return 1; } size_t Tempogram::getMaxChannelCount() const { return 1; } Tempogram::ParameterList Tempogram::getParameterDescriptors() const { ParameterList list; // If the plugin has no adjustable parameters, return an empty // list here (and there's no need to provide implementations of // getParameter and setParameter in that case either). // Note that it is your responsibility to make sure the parameters // start off having their default values (e.g. in the constructor // above). The host needs to know the default value so it can do // things like provide a "reset to default" function, but it will // not explicitly set your parameters to their defaults for you if // they have not changed in the mean time. ParameterDescriptor C; C.identifier = "C"; C.name = "C"; C.description = "Spectrogram compression constant, C"; C.unit = ""; C.minValue = 2; C.maxValue = 10000; C.defaultValue = 1000; C.isQuantized = false; list.push_back(C); ParameterDescriptor tN; tN.identifier = "tN"; tN.name = "Tempogram FFT length"; tN.description = "Tempogram FFT length."; tN.unit = ""; tN.minValue = 128; tN.maxValue = 4096; tN.defaultValue = 1024; tN.isQuantized = true; tN.quantizeStep = 128; list.push_back(tN); return list; } float Tempogram::getParameter(string identifier) const { if (identifier == "C") { return compressionConstant; // return the ACTUAL current value of your parameter here! } if (identifier == "tN"){ return tN; } return 0; } void Tempogram::setParameter(string identifier, float value) { if (identifier == "C") { compressionConstant = value; // set the actual value of your parameter } if (identifier == "tN") { tN = value; } } Tempogram::ProgramList Tempogram::getPrograms() const { ProgramList list; // If you have no programs, return an empty list (or simply don't // implement this function or getCurrentProgram/selectProgram) return list; } string Tempogram::getCurrentProgram() const { return ""; // no programs } void Tempogram::selectProgram(string name) { } Tempogram::OutputList Tempogram::getOutputDescriptors() const { OutputList list; // See OutputDescriptor documentation for the possibilities here. // Every plugin must have at least one output. OutputDescriptor d; d.identifier = "tempogram"; d.name = "Cyclic Tempogram"; d.description = "Cyclic Tempogram"; d.unit = ""; d.hasFixedBinCount = true; d.binCount = tN; d.hasKnownExtents = false; d.isQuantized = false; d.sampleType = OutputDescriptor::FixedSampleRate; float d_sampleRate = m_inputSampleRate/(m_stepSize * thopSize); d.sampleRate = d_sampleRate > 0.0 && !isnan(d_sampleRate) ? d_sampleRate : 0.0; d.hasDuration = false; list.push_back(d); d.identifier = "nc"; d.name = "Novelty Curve"; d.description = "Novelty Curve"; d.unit = ""; d.hasFixedBinCount = true; d.binCount = 1; d.hasKnownExtents = false; d.isQuantized = false; d.sampleType = OutputDescriptor::FixedSampleRate; d_sampleRate = m_inputSampleRate/m_stepSize; d.sampleRate = d_sampleRate > 0 && !isnan(d_sampleRate) ? d_sampleRate : 0.0; d.hasDuration = false; list.push_back(d); d.identifier = "spect"; d.name = "spect"; d.description = "spect"; d.unit = ""; d.hasFixedBinCount = true; d.binCount = m_blockSize/2; d.hasKnownExtents = false; d.isQuantized = false; d.sampleType = OutputDescriptor::OneSamplePerStep; d.hasDuration = false; list.push_back(d); return list; } bool Tempogram::initialise(size_t channels, size_t stepSize, size_t blockSize) { if (channels < getMinChannelCount() || channels > getMaxChannelCount()) return false; // Real initialisation work goes here! m_blockSize = blockSize; m_stepSize = stepSize; currentY = new float[m_blockSize]; previousY = new float[m_blockSize]; return true; } void Tempogram::reset() { // Clear buffers, reset stored values, etc } Tempogram::FeatureSet Tempogram::process(const float *const *inputBuffers, Vamp::RealTime timestamp) { size_t n = m_blockSize/2 + 1; FeatureSet featureSet; Feature feature; const float *in = inputBuffers[0]; float sum = 0; for (int i = 0; i < n; i++){ float magnitude = sqrt(in[2*i] * in[2*i] + in[2*i + 1] * in[2*i + 1]); feature.values.push_back(magnitude); currentY[i] = log(1+compressionConstant*magnitude); if(currentY[i] >= previousY[i]){ sum += (currentY[i] - previousY[i]); } } noveltyCurve.push_back(sum); float *tmpY = currentY; currentY = previousY; previousY = tmpY; tmpY = NULL; ncTimestamps.push_back(timestamp); featureSet[2].push_back(feature); return featureSet; } void Tempogram::initialiseForGRF(){ hannN = 129; hannWindow = new float[hannN]; hannWindowtN = new float[tN]; fftInput = new double[tN]; fftOutputReal = new double[tN]; fftOutputImag = new double[tN]; ncLength = noveltyCurve.size(); WindowFunction::hanning(hannWindow, hannN, true); } void Tempogram::cleanupForGRF(){ delete []hannWindow; hannWindow = NULL; delete []hannWindowtN; hannWindowtN = NULL; delete []fftInput; fftInput = NULL; delete []fftOutputReal; fftOutputReal = NULL; delete []fftOutputImag; fftOutputImag = NULL; } Tempogram::FeatureSet Tempogram::getRemainingFeatures() { //Make sure this is called at the beginning of the function initialiseForGRF(); FeatureSet featureSet; vector<float> noveltyCurveLocalAverage(ncLength); FIRFilter *filter = new FIRFilter(ncLength, hannN); filter->process(&noveltyCurve[0], hannWindow, &noveltyCurveLocalAverage[0]); delete filter; for(int i = 0; i < ncLength; i++){ noveltyCurve[i] -= noveltyCurveLocalAverage[i]; noveltyCurve[i] = noveltyCurve[i] >= 0 ? noveltyCurve[i] : 0; Feature ncFeature; ncFeature.hasTimestamp = true; ncFeature.timestamp = ncTimestamps[i]; ncFeature.values.push_back(noveltyCurve[i]); featureSet[1].push_back(ncFeature); } WindowFunction::hanning(hannWindowtN, tN); int timestampInc = floor((((float)ncTimestamps[1].nsec - ncTimestamps[0].nsec)/1e9)*(thopSize) + 0.5); int i=0; int index; int frameBeginOffset = floor(tN/2 + 0.5); while(i < ncLength){ Feature feature; for (int n = frameBeginOffset; n < tN; n++){ index = i + n - tN/2; assert (index >= 0); if(index < ncLength){ fftInput[n] = noveltyCurve[i + n] * hannWindowtN[n]; } else if(index >= ncLength){ fftInput[n] = 0.0; //pad the end with zeros } //cout << fftInput[n] << endl; } if (i+tN/2 > ncLength){ feature.timestamp = Vamp::RealTime::fromSeconds(ncTimestamps[i].sec + timestampInc); } else{ feature.timestamp = ncTimestamps[i + tN/2]; } FFT::forward(tN, fftInput, NULL, fftOutputReal, fftOutputImag); //TODO: sample at logarithmic spacing for(int k = 0; k < tN; k++){ float fftOutputPower = (fftOutputReal[k]*fftOutputReal[k] + fftOutputImag[k]*fftOutputImag[k]); //Magnitude or power? feature.values.push_back(fftOutputPower); } i += thopSize; frameBeginOffset = 0; feature.hasTimestamp = true; featureSet[0].push_back(feature); } //Make sure this is called at the end of the function cleanupForGRF(); return featureSet; }