# HG changeset patch # User Carl Bussey # Date 1408031006 -3600 # Node ID 89bc9e5199d7f990ae08767099fae8a3bb3bcdf1 # Parent 1e4c02ca8b81bd4022efd0dceda8429ff2e47864 * Added Cyclic Tempogram output - very buggy right now! diff -r 1e4c02ca8b81 -r 89bc9e5199d7 TempogramPlugin.cpp --- a/TempogramPlugin.cpp Thu Aug 14 11:36:09 2014 +0100 +++ b/TempogramPlugin.cpp Thu Aug 14 16:43:26 2014 +0100 @@ -14,20 +14,23 @@ TempogramPlugin::TempogramPlugin(float inputSampleRate) : Plugin(inputSampleRate), - m_blockSize(0), - m_stepSize(0), - m_compressionConstant(1000), //parameter - m_minDB(0), - m_log2WindowLength(10), //parameter - m_windowLength(pow((float)2,m_log2WindowLength)), - m_log2FftLength(m_log2WindowLength), - m_fftLength(m_windowLength), - m_log2HopSize(6), //parameter - m_hopSize(pow((float)2,m_log2HopSize)), - m_minBPM(30), - m_maxBPM(480), - m_minBin(0), //set in initialise() - m_maxBin(0) //set in initialise() + m_inputBlockSize(0), //host parameter + m_inputStepSize(0), //host parameter + m_noveltyCurveMinDB(0), //set in initialise() + m_noveltyCurveCompressionConstant(1000), //parameter + m_tempogramLog2WindowLength(10), //parameter + m_tempogramWindowLength(pow((float)2,m_tempogramLog2WindowLength)), + m_tempogramLog2FftLength(m_tempogramLog2WindowLength), //parameter + m_tempogramFftLength(m_tempogramWindowLength), + m_tempogramLog2HopSize(6), //parameter + m_tempogramHopSize(pow((float)2,m_tempogramLog2HopSize)), + m_tempogramMinBPM(30), //parameter + m_tempogramMaxBPM(480), //parameter + m_tempogramMinBin(0), //set in initialise() + m_tempogramMaxBin(0), //set in initialise() + m_cyclicTempogramMinBPM(30), //reset in initialise() + m_cyclicTempogramNumberOfOctaves(0), //set in initialise() + m_cyclicTempogramOctaveDivider(30) //parameter // Also be sure to set your plugin parameters (presumably stored // in member variables) to their default values here -- the host @@ -191,7 +194,7 @@ ParameterDescriptor d5; d5.identifier = "minBPM"; - d5.name = "Tempogram Minimum BPM"; + d5.name = "(Cyclic) Tempogram Minimum BPM"; d5.description = "The minimum BPM of the tempogram output bins."; d5.unit = ""; d5.minValue = 0; @@ -203,8 +206,8 @@ ParameterDescriptor d6; d6.identifier = "maxBPM"; - d6.name = "Tempogram Maximum BPM"; - d6.description = "The minimum BPM of the tempogram output bins."; + d6.name = "(Cyclic) Tempogram Maximum BPM"; + d6.description = "The maximum BPM of the tempogram output bins."; d6.unit = ""; d6.minValue = 30; d6.maxValue = 2000; @@ -212,6 +215,18 @@ d6.isQuantized = true; d6.quantizeStep = 5; list.push_back(d6); + + ParameterDescriptor d7; + d7.identifier = "octDiv"; + d7.name = "Cyclic Tempogram Octave Divider"; + d7.description = "The number bins within each octave."; + d7.unit = ""; + d7.minValue = 5; + d7.maxValue = 60; + d7.defaultValue = 30; + d7.isQuantized = true; + d7.quantizeStep = 1; + list.push_back(d7); return list; } @@ -220,22 +235,25 @@ TempogramPlugin::getParameter(string identifier) const { if (identifier == "C") { - return m_compressionConstant; // return the ACTUAL current value of your parameter here! + return m_noveltyCurveCompressionConstant; // return the ACTUAL current value of your parameter here! } else if (identifier == "log2TN"){ - return m_log2WindowLength; + return m_tempogramLog2WindowLength; } else if (identifier == "log2HopSize"){ - return m_log2HopSize; + return m_tempogramLog2HopSize; } else if (identifier == "log2FftLength"){ - return m_log2FftLength; + return m_tempogramLog2FftLength; } else if (identifier == "minBPM") { - return m_minBPM; + return m_tempogramMinBPM; } else if (identifier == "maxBPM"){ - return m_maxBPM; + return m_tempogramMaxBPM; + } + else if (identifier == "octDiv"){ + return m_cyclicTempogramOctaveDivider; } return 0; @@ -246,25 +264,28 @@ { if (identifier == "C") { - m_compressionConstant = value; // set the actual value of your parameter + m_noveltyCurveCompressionConstant = value; // set the actual value of your parameter } else if (identifier == "log2TN") { - m_windowLength = pow(2,value); - m_log2WindowLength = value; + m_tempogramWindowLength = pow(2,value); + m_tempogramLog2WindowLength = value; } else if (identifier == "log2HopSize"){ - m_hopSize = pow(2,value); - m_log2HopSize = value; + m_tempogramHopSize = pow(2,value); + m_tempogramLog2HopSize = value; } - else if (identifier == "log2HopFftLength"){ - m_fftLength = pow(2,value); - m_log2FftLength = value; + else if (identifier == "log2FftLength"){ + m_tempogramFftLength = pow(2,value); + m_tempogramLog2FftLength = value; } else if (identifier == "minBPM") { - m_minBPM = value; + m_tempogramMinBPM = value; } else if (identifier == "maxBPM"){ - m_maxBPM = value; + m_tempogramMaxBPM = value; + } + else if (identifier == "octDiv"){ + m_cyclicTempogramOctaveDivider = value; } } @@ -311,41 +332,57 @@ // See OutputDescriptor documentation for the possibilities here. // Every plugin must have at least one output. - OutputDescriptor d; + OutputDescriptor d1; float d_sampleRate; - float tempogramInputSampleRate = (float)m_inputSampleRate/m_stepSize; + float tempogramInputSampleRate = (float)m_inputSampleRate/m_inputStepSize; - d.identifier = "tempogram"; - d.name = "Tempogram"; - d.description = "Tempogram"; - d.unit = "BPM"; - d.hasFixedBinCount = true; - d.binCount = m_maxBin - m_minBin + 1; - d.hasKnownExtents = false; - d.isQuantized = false; - d.sampleType = OutputDescriptor::FixedSampleRate; - d_sampleRate = tempogramInputSampleRate/m_hopSize; - d.sampleRate = d_sampleRate > 0.0 && !isnan(d_sampleRate) ? d_sampleRate : 0.0; - for(int i = m_minBin; i <= (int)m_maxBin; i++){ - float w = ((float)i/m_fftLength)*(tempogramInputSampleRate); - d.binNames.push_back(floatToString(w*60)); + d1.identifier = "tempogram"; + d1.name = "Tempogram"; + d1.description = "Tempogram"; + d1.unit = "BPM"; + d1.hasFixedBinCount = true; + d1.binCount = m_tempogramMaxBin - m_tempogramMinBin + 1; + d1.hasKnownExtents = false; + d1.isQuantized = false; + d1.sampleType = OutputDescriptor::FixedSampleRate; + d_sampleRate = tempogramInputSampleRate/m_tempogramHopSize; + d1.sampleRate = d_sampleRate > 0.0 && !isnan(d_sampleRate) ? d_sampleRate : 0.0; + for(int i = m_tempogramMinBin; i <= (int)m_tempogramMaxBin; i++){ + float w = ((float)i/m_tempogramFftLength)*(tempogramInputSampleRate); + d1.binNames.push_back(floatToString(w*60)); } - d.hasDuration = false; - list.push_back(d); + d1.hasDuration = false; + list.push_back(d1); - 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; + OutputDescriptor d2; + d2.identifier = "nc"; + d2.name = "Novelty Curve"; + d2.description = "Novelty Curve"; + d2.unit = ""; + d2.hasFixedBinCount = true; + d2.binCount = 1; + d2.hasKnownExtents = false; + d2.isQuantized = false; + d2.sampleType = OutputDescriptor::FixedSampleRate; d_sampleRate = tempogramInputSampleRate; - d.sampleRate = d_sampleRate > 0 && !isnan(d_sampleRate) ? d_sampleRate : 0.0; - d.hasDuration = false; - list.push_back(d); + d2.sampleRate = d_sampleRate > 0 && !isnan(d_sampleRate) ? d_sampleRate : 0; + d2.hasDuration = false; + list.push_back(d2); + + OutputDescriptor d3; + d3.identifier = "cyclicTempogram"; + d3.name = "Cyclic Tempogram"; + d3.description = "Cyclic Tempogram"; + d3.unit = ""; + d3.hasFixedBinCount = true; + d3.binCount = m_cyclicTempogramOctaveDivider > 0 && !isnan(m_cyclicTempogramOctaveDivider) ? m_cyclicTempogramOctaveDivider : 0; + d3.hasKnownExtents = false; + d3.isQuantized = false; + d3.sampleType = OutputDescriptor::FixedSampleRate; + d_sampleRate = tempogramInputSampleRate/m_tempogramHopSize; + d3.sampleRate = d_sampleRate > 0.0 && !isnan(d_sampleRate) ? d_sampleRate : 0; + d3.hasDuration = false; + list.push_back(d3); return list; } @@ -357,22 +394,31 @@ channels > getMaxChannelCount()) return false; // Real initialisation work goes here! - m_blockSize = blockSize; - m_stepSize = stepSize; - m_minDB = pow(10,(float)-74/20); + m_inputBlockSize = blockSize; + m_inputStepSize = stepSize; + m_noveltyCurveMinDB = pow(10,(float)-74/20); - if (m_fftLength < m_windowLength){ - m_fftLength = m_windowLength; + if (m_tempogramFftLength < m_tempogramWindowLength){ + m_tempogramFftLength = m_tempogramWindowLength; } - if (m_minBPM > m_maxBPM){ - m_minBPM = 30; - m_maxBPM = 480; + if (m_tempogramMinBPM > m_tempogramMaxBPM){ + m_tempogramMinBPM = 30; + m_tempogramMaxBPM = 480; } - float tempogramInputSampleRate = (float)m_inputSampleRate/m_stepSize; - m_minBin = (unsigned int)(max(floor(((m_minBPM/60)/tempogramInputSampleRate)*m_fftLength), (float)0.0)); - m_maxBin = (unsigned int)(min(ceil(((m_maxBPM/60)/tempogramInputSampleRate)*m_fftLength), (float)m_fftLength/2)); - m_spectrogram = SpectrogramTransposed(m_blockSize/2 + 1); + float tempogramInputSampleRate = (float)m_inputSampleRate/m_inputStepSize; + m_tempogramMinBin = (unsigned int)(max(floor(((m_tempogramMinBPM/60)/tempogramInputSampleRate)*m_tempogramFftLength), (float)0.0)); + m_tempogramMaxBin = (unsigned int)(min(ceil(((m_tempogramMaxBPM/60)/tempogramInputSampleRate)*m_tempogramFftLength), (float)m_tempogramFftLength/2)); + + if (m_tempogramMinBPM > m_cyclicTempogramMinBPM) m_cyclicTempogramMinBPM = m_tempogramMinBPM; + float cyclicTempogramMaxBPM = 480; + if (m_tempogramMaxBPM < cyclicTempogramMaxBPM) cyclicTempogramMaxBPM = m_tempogramMaxBPM; + + m_spectrogram = SpectrogramTransposed(m_inputBlockSize/2 + 1); + + m_cyclicTempogramNumberOfOctaves = floor(log2(cyclicTempogramMaxBPM/m_cyclicTempogramMinBPM)); + int numberOfBinsInFirstOctave = bpmToBin(m_cyclicTempogramMinBPM); + if (m_cyclicTempogramOctaveDivider < numberOfBinsInFirstOctave) m_cyclicTempogramOctaveDivider = numberOfBinsInFirstOctave; return true; } @@ -385,14 +431,13 @@ TempogramPlugin::reset() { // Clear buffers, reset stored values, etc - ncTimestamps.clear(); - m_spectrogram = SpectrogramTransposed(m_blockSize/2 + 1); + m_spectrogram = SpectrogramTransposed(m_inputBlockSize/2 + 1); } TempogramPlugin::FeatureSet TempogramPlugin::process(const float *const *inputBuffers, Vamp::RealTime timestamp) { - size_t n = m_blockSize/2 + 1; + size_t n = m_inputBlockSize/2 + 1; FeatureSet featureSet; Feature feature; @@ -402,21 +447,45 @@ //calculate magnitude of FrequencyDomain input for (unsigned int i = 0; i < n; i++){ float magnitude = sqrt(in[2*i] * in[2*i] + in[2*i + 1] * in[2*i + 1]); - magnitude = magnitude > m_minDB ? magnitude : m_minDB; + magnitude = magnitude > m_noveltyCurveMinDB ? magnitude : m_noveltyCurveMinDB; m_spectrogram[i].push_back(magnitude); } - - ncTimestamps.push_back(timestamp); //save timestamp return featureSet; } +vector TempogramPlugin::calculateTempogramNearestNeighbourLogBins() const +{ + vector logBins; + + for (int i = 0; i < (int)ceil(m_cyclicTempogramNumberOfOctaves*m_cyclicTempogramOctaveDivider); i++){ + float bpm = m_cyclicTempogramMinBPM*pow(2.0f, (float)i/m_cyclicTempogramOctaveDivider); + int bin = bpmToBin(bpm); + + logBins.push_back(bin); + } + + return logBins; +} + +int TempogramPlugin::bpmToBin(const float &bpm) const +{ + float w = (float)bpm/60; + float sampleRate = m_inputSampleRate/m_inputStepSize; + int bin = floor((float)m_tempogramFftLength*w/sampleRate + 0.5); + + if(bin < 0) bin = 0; + else if(bin > m_tempogramFftLength/2) bin = m_tempogramFftLength; + + return bin; +} + TempogramPlugin::FeatureSet TempogramPlugin::getRemainingFeatures() { - float * hannWindow = new float[m_windowLength]; - for (unsigned int i = 0; i < m_windowLength; i++){ + float * hannWindow = new float[m_tempogramWindowLength]; + for (unsigned int i = 0; i < m_tempogramWindowLength; i++){ hannWindow[i] = 0.0; } @@ -424,7 +493,7 @@ //initialise m_noveltyCurve processor size_t numberOfBlocks = m_spectrogram[0].size(); - NoveltyCurveProcessor nc(m_inputSampleRate, m_blockSize, numberOfBlocks, m_compressionConstant); + NoveltyCurveProcessor nc(m_inputSampleRate, m_inputBlockSize, numberOfBlocks, m_noveltyCurveCompressionConstant); m_noveltyCurve = nc.spectrogramToNoveltyCurve(m_spectrogram); //calculate novelty curve from magnitude data //push novelty curve data to featureset 1 and set timestamps @@ -437,36 +506,48 @@ } //window function for spectrogram - WindowFunction::hanning(hannWindow, m_windowLength); + WindowFunction::hanning(hannWindow, m_tempogramWindowLength); //initialise spectrogram processor - SpectrogramProcessor spectrogramProcessor(m_windowLength, m_fftLength, m_hopSize); + SpectrogramProcessor spectrogramProcessor(m_tempogramWindowLength, m_tempogramFftLength, m_tempogramHopSize); //compute spectrogram from novelty curve data (i.e., tempogram) - Spectrogram tempogram = spectrogramProcessor.process(&m_noveltyCurve[0], numberOfBlocks, hannWindow); + Tempogram tempogram = spectrogramProcessor.process(&m_noveltyCurve[0], numberOfBlocks, hannWindow); + delete []hannWindow; + hannWindow = 0; - int timePointer = m_hopSize-m_windowLength/2; int tempogramLength = tempogram.size(); //push tempogram data to featureset 0 and set timestamps. for (int block = 0; block < tempogramLength; block++){ Feature feature; - //int timeMS = floor(1000*(m_stepSize*timePointer)/m_inputSampleRate + 0.5); - - assert(tempogram[block].size() == (m_fftLength/2 + 1)); - for(int k = m_minBin; k < (int)m_maxBin; k++){ + assert(tempogram[block].size() == (m_tempogramFftLength/2 + 1)); + for(int k = m_tempogramMinBin; k < (int)m_tempogramMaxBin; k++){ feature.values.push_back(tempogram[block][k]); - //cout << tempogram[k][block] << endl; } feature.hasTimestamp = false; - //feature.timestamp = RealTime::fromMilliseconds(timeMS); featureSet[0].push_back(feature); - - timePointer += m_hopSize; } - delete []hannWindow; - hannWindow = 0; + //Calculate cyclic tempogram + vector logBins = calculateTempogramNearestNeighbourLogBins(); + Tempogram cyclicTempogram; + + for (int block = 0; block < tempogramLength; block++){ + Feature feature; + + for (int i = 0; i < m_cyclicTempogramOctaveDivider; i++){ + float sum = 0; + for (int j = 0; j < m_cyclicTempogramNumberOfOctaves; j++){ + sum += tempogram[block][logBins[i+j*m_cyclicTempogramOctaveDivider]]; + } + feature.values.push_back(sum/m_cyclicTempogramNumberOfOctaves); + } + + feature.hasTimestamp = false; + + featureSet[2].push_back(feature); + } return featureSet; } diff -r 1e4c02ca8b81 -r 89bc9e5199d7 TempogramPlugin.h --- a/TempogramPlugin.h Thu Aug 14 11:36:09 2014 +0100 +++ b/TempogramPlugin.h Thu Aug 14 16:43:26 2014 +0100 @@ -34,6 +34,8 @@ using std::string; using std::vector; +typedef Spectrogram Tempogram; + class TempogramPlugin : public Vamp::Plugin { public: @@ -73,31 +75,38 @@ protected: // plugin-specific data and methods go here - size_t m_blockSize; - size_t m_stepSize; - float m_compressionConstant; + size_t m_inputBlockSize; + size_t m_inputStepSize; SpectrogramTransposed m_spectrogram; //spectrogram data vector m_noveltyCurve; //novelty curve data - float m_minDB; + float m_noveltyCurveMinDB; void cleanup(); //used to release anything allocated in initialise() string floatToString(float value) const; + vector calculateTempogramNearestNeighbourLogBins() const; void updateBPMParameters(); + int bpmToBin(const float &bpm) const; - //FFT params for noveltyCurve -> tempogra - float m_log2WindowLength; - size_t m_windowLength; - float m_log2FftLength; - size_t m_fftLength; - float m_log2HopSize; - size_t m_hopSize; + //Novelty Curve specific parameters + float m_noveltyCurveCompressionConstant; - float m_minBPM; // tempogram output bin range min - float m_maxBPM; // tempogram output bin range max - unsigned int m_minBin; - unsigned int m_maxBin; + //Tempogram specific parameters + float m_tempogramLog2WindowLength; + size_t m_tempogramWindowLength; + float m_tempogramLog2FftLength; + size_t m_tempogramFftLength; + float m_tempogramLog2HopSize; + size_t m_tempogramHopSize; - vector ncTimestamps; + float m_tempogramMinBPM; // tempogram output bin range min + float m_tempogramMaxBPM; // tempogram output bin range max + unsigned int m_tempogramMinBin; + unsigned int m_tempogramMaxBin; + + //Cyclic tempogram parameters + float m_cyclicTempogramMinBPM; + int m_cyclicTempogramNumberOfOctaves; + int m_cyclicTempogramOctaveDivider; };