Chris@43: /* -*- c-basic-offset: 4 indent-tabs-mode: nil -*- vi:set ts=8 sts=4 sw=4: */ c@0: Chris@43: /* Chris@43: Vamp Tempogram Plugin Chris@43: Carl Bussey, Centre for Digital Music, Queen Mary University of London Chris@43: Copyright 2014 Queen Mary University of London. Chris@43: Chris@43: This program is free software; you can redistribute it and/or Chris@43: modify it under the terms of the GNU General Public License as Chris@43: published by the Free Software Foundation; either version 2 of the Chris@43: License, or (at your option) any later version. See the file Chris@43: COPYING included with this distribution for more information. Chris@43: */ c@0: c@14: #include "TempogramPlugin.h" c@25: c@0: using Vamp::FFT; c@7: using Vamp::RealTime; c@0: using namespace std; c@0: c@14: TempogramPlugin::TempogramPlugin(float inputSampleRate) : c@0: Plugin(inputSampleRate), c@18: m_inputBlockSize(0), //host parameter c@18: m_inputStepSize(0), //host parameter c@29: m_noveltyCurveMinDB(-74), //parameter c@29: m_noveltyCurveMinV(0), //set in initialise() c@18: m_noveltyCurveCompressionConstant(1000), //parameter c@18: m_tempogramLog2WindowLength(10), //parameter c@29: m_tempogramWindowLength(0), //set in initialise() c@18: m_tempogramLog2FftLength(m_tempogramLog2WindowLength), //parameter c@29: m_tempogramFftLength(0), //set in initialise() c@18: m_tempogramLog2HopSize(6), //parameter c@29: m_tempogramHopSize(0), //set in initialise() c@18: m_tempogramMinBPM(30), //parameter c@18: m_tempogramMaxBPM(480), //parameter c@18: m_tempogramMinBin(0), //set in initialise() c@18: m_tempogramMaxBin(0), //set in initialise() c@29: m_tempogramMinLag(0), //set in initialise() c@29: m_tempogramMaxLag(0), //set in initialise() c@18: m_cyclicTempogramMinBPM(30), //reset in initialise() c@18: m_cyclicTempogramNumberOfOctaves(0), //set in initialise() c@18: m_cyclicTempogramOctaveDivider(30) //parameter c@0: c@0: // Also be sure to set your plugin parameters (presumably stored c@0: // in member variables) to their default values here -- the host c@0: // will not do that for you c@0: { c@0: } c@0: c@14: TempogramPlugin::~TempogramPlugin() c@0: { c@0: //delete stuff c@0: } c@0: c@0: string c@14: TempogramPlugin::getIdentifier() const c@0: { c@0: return "tempogram"; c@0: } c@0: c@0: string c@14: TempogramPlugin::getName() const c@0: { c@0: return "Tempogram"; c@0: } c@0: c@0: string c@14: TempogramPlugin::getDescription() const c@0: { c@0: return "Cyclic Tempogram as described by Peter Grosche and Meinard Muller"; c@0: } c@0: c@0: string c@14: TempogramPlugin::getMaker() const c@0: { c@0: return "Carl Bussey"; c@0: } c@0: c@0: int c@14: TempogramPlugin::getPluginVersion() const c@0: { c@0: return 1; c@0: } c@0: c@0: string c@14: TempogramPlugin::getCopyright() const c@0: { Chris@40: return "Copyright 2014 Queen Mary University of London. GPL licence."; c@0: } c@0: c@14: TempogramPlugin::InputDomain c@14: TempogramPlugin::getInputDomain() const c@0: { c@0: return FrequencyDomain; c@0: } c@0: c@0: size_t c@14: TempogramPlugin::getPreferredBlockSize() const c@0: { c@9: return 2048; // 0 means "I can handle any block size" c@0: } c@0: c@0: size_t c@14: TempogramPlugin::getPreferredStepSize() const c@0: { c@9: return 1024; // 0 means "anything sensible"; in practice this c@0: // means the same as the block size for TimeDomain c@0: // plugins, or half of it for FrequencyDomain plugins c@0: } c@0: c@0: size_t c@14: TempogramPlugin::getMinChannelCount() const c@0: { c@0: return 1; c@0: } c@0: c@0: size_t c@14: TempogramPlugin::getMaxChannelCount() const c@0: { c@0: return 1; c@0: } c@0: c@14: TempogramPlugin::ParameterList c@14: TempogramPlugin::getParameterDescriptors() const c@0: { c@0: ParameterList list; c@0: c@0: // If the plugin has no adjustable parameters, return an empty c@0: // list here (and there's no need to provide implementations of c@0: // getParameter and setParameter in that case either). c@0: c@0: // Note that it is your responsibility to make sure the parameters c@0: // start off having their default values (e.g. in the constructor c@0: // above). The host needs to know the default value so it can do c@0: // things like provide a "reset to default" function, but it will c@0: // not explicitly set your parameters to their defaults for you if c@0: // they have not changed in the mean time. c@0: c@14: ParameterDescriptor d1; c@14: d1.identifier = "C"; c@15: d1.name = "Novelty Curve Spectrogram Compression Constant"; c@14: d1.description = "Spectrogram compression constant, C, used when retrieving the novelty curve from the audio."; c@14: d1.unit = ""; c@14: d1.minValue = 2; c@14: d1.maxValue = 10000; c@14: d1.defaultValue = 1000; c@14: d1.isQuantized = false; c@14: list.push_back(d1); c@29: c@29: ParameterDescriptor d2; c@29: d2.identifier = "minDB"; c@29: d2.name = "Novelty Curve Minimum DB"; c@29: d2.description = "Spectrogram minimum DB used when removing unwanted peaks in the Spectrogram when retrieving the novelty curve from the audio."; c@29: d2.unit = ""; c@29: d2.minValue = -100; c@29: d2.maxValue = -50; c@29: d2.defaultValue = -74; c@29: d2.isQuantized = false; c@29: list.push_back(d2); c@9: c@14: ParameterDescriptor d3; c@29: d3.identifier = "log2TN"; c@29: d3.name = "Tempogram Window Length"; c@29: d3.description = "FFT window length when analysing the novelty curve and extracting the tempogram time-frequency function."; c@14: d3.unit = ""; c@29: d3.minValue = 7; c@14: d3.maxValue = 12; c@29: d3.defaultValue = 10; c@14: d3.isQuantized = true; c@14: d3.quantizeStep = 1; c@14: for (int i = d3.minValue; i <= d3.maxValue; i++){ c@14: d3.valueNames.push_back(floatToString(pow((float)2,(float)i))); c@14: } c@14: list.push_back(d3); c@9: c@14: ParameterDescriptor d4; c@29: d4.identifier = "log2HopSize"; c@29: d4.name = "Tempogram Hopsize"; c@29: d4.description = "FFT hopsize when analysing the novelty curve and extracting the tempogram time-frequency function."; c@14: d4.unit = ""; c@14: d4.minValue = 6; c@14: d4.maxValue = 12; c@29: d4.defaultValue = 6; c@14: d4.isQuantized = true; c@14: d4.quantizeStep = 1; c@14: for (int i = d4.minValue; i <= d4.maxValue; i++){ c@14: d4.valueNames.push_back(floatToString(pow((float)2,(float)i))); c@14: } c@14: list.push_back(d4); c@14: c@14: ParameterDescriptor d5; c@29: d5.identifier = "log2FftLength"; c@29: d5.name = "Tempogram FFT Length"; c@29: d5.description = "FFT length when analysing the novelty curve and extracting the tempogram time-frequency function. This parameter determines the amount of zero padding."; c@14: d5.unit = ""; c@29: d5.minValue = 6; c@29: d5.maxValue = 12; Chris@42: d5.defaultValue = 10; c@14: d5.isQuantized = true; c@29: d5.quantizeStep = 1; c@29: for (int i = d5.minValue; i <= d5.maxValue; i++){ Chris@41: d5.valueNames.push_back(floatToString(pow((float)2,(float)i))); c@29: } c@14: list.push_back(d5); c@14: c@14: ParameterDescriptor d6; c@29: d6.identifier = "minBPM"; c@29: d6.name = "(Cyclic) Tempogram Minimum BPM"; c@29: d6.description = "The minimum BPM of the tempogram output bins."; c@14: d6.unit = ""; c@29: d6.minValue = 0; c@14: d6.maxValue = 2000; c@29: d6.defaultValue = 30; c@14: d6.isQuantized = true; c@14: d6.quantizeStep = 5; c@14: list.push_back(d6); c@18: c@18: ParameterDescriptor d7; c@29: d7.identifier = "maxBPM"; c@29: d7.name = "(Cyclic) Tempogram Maximum BPM"; c@29: d7.description = "The maximum BPM of the tempogram output bins."; c@18: d7.unit = ""; c@29: d7.minValue = 30; c@29: d7.maxValue = 2000; c@29: d7.defaultValue = 480; c@18: d7.isQuantized = true; c@29: d7.quantizeStep = 5; c@18: list.push_back(d7); c@29: c@29: ParameterDescriptor d8; c@29: d8.identifier = "octDiv"; c@29: d8.name = "Cyclic Tempogram Octave Divider"; c@29: d8.description = "The number bins within each octave."; c@29: d8.unit = ""; c@29: d8.minValue = 5; c@29: d8.maxValue = 60; c@29: d8.defaultValue = 30; c@29: d8.isQuantized = true; c@29: d8.quantizeStep = 1; c@29: list.push_back(d8); c@0: c@0: return list; c@0: } c@0: c@0: float c@14: TempogramPlugin::getParameter(string identifier) const c@0: { c@0: if (identifier == "C") { c@18: return m_noveltyCurveCompressionConstant; // return the ACTUAL current value of your parameter here! c@0: } c@29: else if (identifier == "minDB"){ c@29: return m_noveltyCurveMinDB; c@29: } c@14: else if (identifier == "log2TN"){ c@18: return m_tempogramLog2WindowLength; c@9: } c@14: else if (identifier == "log2HopSize"){ c@18: return m_tempogramLog2HopSize; c@14: } c@14: else if (identifier == "log2FftLength"){ c@18: return m_tempogramLog2FftLength; c@14: } c@14: else if (identifier == "minBPM") { c@18: return m_tempogramMinBPM; c@9: } c@14: else if (identifier == "maxBPM"){ c@18: return m_tempogramMaxBPM; c@18: } c@18: else if (identifier == "octDiv"){ c@18: return m_cyclicTempogramOctaveDivider; c@0: } c@0: c@0: return 0; c@0: } c@0: c@0: void c@14: TempogramPlugin::setParameter(string identifier, float value) c@0: { c@9: c@0: if (identifier == "C") { c@18: m_noveltyCurveCompressionConstant = value; // set the actual value of your parameter c@0: } c@29: else if (identifier == "minDB"){ c@29: m_noveltyCurveMinDB = value; c@29: } c@14: else if (identifier == "log2TN") { c@18: m_tempogramLog2WindowLength = value; c@0: } c@14: else if (identifier == "log2HopSize"){ c@30: m_tempogramLog2HopSize = value; c@14: } c@18: else if (identifier == "log2FftLength"){ c@30: m_tempogramLog2FftLength = value; c@14: } c@14: else if (identifier == "minBPM") { c@18: m_tempogramMinBPM = value; c@9: } c@14: else if (identifier == "maxBPM"){ c@18: m_tempogramMaxBPM = value; c@18: } c@18: else if (identifier == "octDiv"){ c@18: m_cyclicTempogramOctaveDivider = value; c@9: } c@9: c@9: } c@9: c@14: TempogramPlugin::ProgramList c@14: TempogramPlugin::getPrograms() const c@0: { c@0: ProgramList list; c@0: c@0: // If you have no programs, return an empty list (or simply don't c@0: // implement this function or getCurrentProgram/selectProgram) c@0: c@0: return list; c@0: } c@0: c@0: string c@14: TempogramPlugin::getCurrentProgram() const c@0: { c@0: return ""; // no programs c@0: } c@0: c@0: void c@14: TempogramPlugin::selectProgram(string name) c@0: { c@0: } c@0: c@14: TempogramPlugin::OutputList c@14: TempogramPlugin::getOutputDescriptors() const c@0: { c@0: OutputList list; c@0: c@0: // See OutputDescriptor documentation for the possibilities here. c@0: // Every plugin must have at least one output. c@1: c@7: float d_sampleRate; c@18: float tempogramInputSampleRate = (float)m_inputSampleRate/m_inputStepSize; c@25: OutputDescriptor d1; c@25: d1.identifier = "cyclicTempogram"; c@25: d1.name = "Cyclic Tempogram"; Chris@43: d1.description = "Cyclic tempogram calculated by \"octave folding\" the DFT tempogram"; c@25: d1.unit = ""; c@25: d1.hasFixedBinCount = true; c@25: d1.binCount = m_cyclicTempogramOctaveDivider > 0 && !isnan(m_cyclicTempogramOctaveDivider) ? m_cyclicTempogramOctaveDivider : 0; c@25: d1.hasKnownExtents = false; c@25: d1.isQuantized = false; c@25: d1.sampleType = OutputDescriptor::FixedSampleRate; c@25: d_sampleRate = tempogramInputSampleRate/m_tempogramHopSize; c@25: d1.sampleRate = d_sampleRate > 0.0 && !isnan(d_sampleRate) ? d_sampleRate : 0; c@25: d1.hasDuration = false; c@25: list.push_back(d1); c@25: c@25: OutputDescriptor d2; c@25: d2.identifier = "tempogramDFT"; c@25: d2.name = "Tempogram via DFT"; Chris@43: d2.description = "Tempogram calculated using Discrete Fourier Transform method"; Chris@43: d2.unit = ""; // unit of bin contents, not of "bin label", so not bpm c@25: d2.hasFixedBinCount = true; c@25: d2.binCount = m_tempogramMaxBin - m_tempogramMinBin + 1; c@25: d2.hasKnownExtents = false; c@25: d2.isQuantized = false; c@25: d2.sampleType = OutputDescriptor::FixedSampleRate; c@25: d_sampleRate = tempogramInputSampleRate/m_tempogramHopSize; c@25: d2.sampleRate = d_sampleRate > 0.0 && !isnan(d_sampleRate) ? d_sampleRate : 0.0; c@25: for(int i = m_tempogramMinBin; i <= (int)m_tempogramMaxBin; i++){ c@25: float w = ((float)i/m_tempogramFftLength)*(tempogramInputSampleRate); c@25: d2.binNames.push_back(floatToString(w*60)); c@25: } c@25: d2.hasDuration = false; c@25: list.push_back(d2); c@25: c@21: OutputDescriptor d3; c@25: d3.identifier = "tempogramACT"; c@25: d3.name = "Tempogram via ACT"; Chris@43: d3.description = "Tempogram calculated using autocorrelation method"; Chris@43: d3.unit = ""; // unit of bin contents, not of "bin label", so not bpm c@21: d3.hasFixedBinCount = true; c@28: d3.binCount = m_tempogramMaxLag - m_tempogramMinLag + 1; c@21: d3.hasKnownExtents = false; c@21: d3.isQuantized = false; c@21: d3.sampleType = OutputDescriptor::FixedSampleRate; c@21: d_sampleRate = tempogramInputSampleRate/m_tempogramHopSize; c@25: d3.sampleRate = d_sampleRate > 0.0 && !isnan(d_sampleRate) ? d_sampleRate : 0.0; c@28: for(int lag = m_tempogramMaxLag; lag >= (int)m_tempogramMinLag; lag--){ c@28: d3.binNames.push_back(floatToString(60/(m_inputStepSize*(lag/m_inputSampleRate)))); c@25: } c@21: d3.hasDuration = false; c@21: list.push_back(d3); c@21: c@25: OutputDescriptor d4; c@25: d4.identifier = "nc"; c@25: d4.name = "Novelty Curve"; Chris@43: d4.description = "Novelty curve underlying the tempogram calculations"; c@25: d4.unit = ""; c@25: d4.hasFixedBinCount = true; c@25: d4.binCount = 1; c@25: d4.hasKnownExtents = false; c@25: d4.isQuantized = false; c@25: d4.sampleType = OutputDescriptor::FixedSampleRate; c@9: d_sampleRate = tempogramInputSampleRate; c@25: d4.sampleRate = d_sampleRate > 0 && !isnan(d_sampleRate) ? d_sampleRate : 0; c@25: d4.hasDuration = false; c@25: list.push_back(d4); c@18: c@0: return list; c@0: } c@0: c@20: bool c@20: TempogramPlugin::initialise(size_t channels, size_t stepSize, size_t blockSize) c@20: { c@20: if (channels < getMinChannelCount() || c@20: channels > getMaxChannelCount()) return false; c@20: c@20: // Real initialisation work goes here! c@20: m_inputBlockSize = blockSize; c@20: m_inputStepSize = stepSize; c@20: c@24: //m_spectrogram = Spectrogram(m_inputBlockSize/2 + 1); c@21: if (!handleParameterValues()) return false; c@19: //cout << m_cyclicTempogramOctaveDivider << endl; c@4: c@0: return true; c@0: } c@0: c@0: void c@14: TempogramPlugin::reset() c@0: { c@0: // Clear buffers, reset stored values, etc c@19: m_spectrogram.clear(); c@21: handleParameterValues(); c@0: } c@0: c@14: TempogramPlugin::FeatureSet c@14: TempogramPlugin::process(const float *const *inputBuffers, Vamp::RealTime timestamp) c@0: { c@23: int n = m_inputBlockSize/2 + 1; c@0: const float *in = inputBuffers[0]; c@3: c@9: //calculate magnitude of FrequencyDomain input c@22: vector fftCoefficients; c@23: for (int i = 0; i < n; i++){ c@0: float magnitude = sqrt(in[2*i] * in[2*i] + in[2*i + 1] * in[2*i + 1]); c@29: magnitude = magnitude > m_noveltyCurveMinV ? magnitude : m_noveltyCurveMinV; c@22: fftCoefficients.push_back(magnitude); c@0: } c@22: m_spectrogram.push_back(fftCoefficients); c@24: //m_spectrogram.push_back(fftCoefficients); c@21: c@23: return FeatureSet(); c@0: } c@0: c@14: TempogramPlugin::FeatureSet c@14: TempogramPlugin::getRemainingFeatures() c@11: { c@0: c@18: float * hannWindow = new float[m_tempogramWindowLength]; c@20: for (int i = 0; i < (int)m_tempogramWindowLength; i++){ c@14: hannWindow[i] = 0.0; c@4: } c@11: c@1: FeatureSet featureSet; c@0: c@19: //initialise novelty curve processor c@23: int numberOfBlocks = m_spectrogram.size(); c@20: //cerr << numberOfBlocks << endl; c@22: NoveltyCurveProcessor nc(m_inputSampleRate, m_inputBlockSize, m_noveltyCurveCompressionConstant); c@21: vector noveltyCurve = nc.spectrogramToNoveltyCurve(m_spectrogram); //calculate novelty curvefrom magnitude data c@4: c@9: //push novelty curve data to featureset 1 and set timestamps c@23: for (int i = 0; i < numberOfBlocks; i++){ c@19: Feature noveltyCurveFeature; c@19: noveltyCurveFeature.values.push_back(noveltyCurve[i]); c@19: noveltyCurveFeature.hasTimestamp = false; c@25: featureSet[3].push_back(noveltyCurveFeature); c@21: assert(!isnan(noveltyCurveFeature.values.back())); c@4: } c@4: c@9: //window function for spectrogram c@18: WindowFunction::hanning(hannWindow, m_tempogramWindowLength); c@9: c@9: //initialise spectrogram processor c@18: SpectrogramProcessor spectrogramProcessor(m_tempogramWindowLength, m_tempogramFftLength, m_tempogramHopSize); c@9: //compute spectrogram from novelty curve data (i.e., tempogram) c@25: Tempogram tempogramDFT = spectrogramProcessor.process(&noveltyCurve[0], numberOfBlocks, hannWindow); c@18: delete []hannWindow; c@18: hannWindow = 0; c@0: c@25: int tempogramLength = tempogramDFT.size(); c@7: c@9: //push tempogram data to featureset 0 and set timestamps. c@7: for (int block = 0; block < tempogramLength; block++){ c@25: Feature tempogramDFTFeature; c@28: c@28: assert(tempogramDFT[block].size() == (m_tempogramFftLength/2 + 1)); c@28: for(int k = m_tempogramMinBin; k <= (int)m_tempogramMaxBin; k++){ c@28: tempogramDFTFeature.values.push_back(tempogramDFT[block][k]); c@28: } c@28: tempogramDFTFeature.hasTimestamp = false; c@28: featureSet[1].push_back(tempogramDFTFeature); c@28: } c@28: c@28: AutocorrelationProcessor autocorrelationProcessor(m_tempogramWindowLength, m_tempogramHopSize); c@28: Tempogram tempogramACT = autocorrelationProcessor.process(&noveltyCurve[0], numberOfBlocks); c@28: c@28: for (int block = 0; block < tempogramLength; block++){ c@25: Feature tempogramACTFeature; Chris@44: Chris@44: // cerr << "block = " << block << ", window length = " << m_tempogramWindowLength << ", max lag = " << m_tempogramMaxLag << ", min lag = " << m_tempogramMinLag << endl; c@0: c@28: for(int k = m_tempogramMaxLag; k >= (int)m_tempogramMinLag; k--){ Chris@44: // cerr << "(" << block << "," << k << ") "; c@25: tempogramACTFeature.values.push_back(tempogramACT[block][k]); c@0: } Chris@44: // cerr << endl; c@25: tempogramACTFeature.hasTimestamp = false; c@25: featureSet[2].push_back(tempogramACTFeature); c@0: } c@0: c@18: //Calculate cyclic tempogram c@22: vector< vector > logBins = calculateTempogramNearestNeighbourLogBins(); c@18: c@22: //assert((int)logBins.size() == m_cyclicTempogramOctaveDivider*m_cyclicTempogramNumberOfOctaves); c@18: for (int block = 0; block < tempogramLength; block++){ c@19: Feature cyclicTempogramFeature; c@18: c@23: for (int i = 0; i < m_cyclicTempogramOctaveDivider; i++){ c@18: float sum = 0; c@21: c@23: for (int j = 0; j < m_cyclicTempogramNumberOfOctaves; j++){ Chris@34: Chris@34: if (block >= tempogramDFT.size()) { Chris@34: cerr << "ERROR: at block = " << block << ", i = " << i << ", j = " << j << ": block " << block << " >= tempogramDFT.size() " << tempogramDFT.size() << endl; Chris@34: } else if (j > logBins.size()) { Chris@34: cerr << "ERROR: at block = " << block << ", i = " << i << ", j = " << j << ": j " << j << " >= logBins.size() " << logBins.size() << endl; Chris@34: } else if (i > logBins[j].size()) { Chris@34: cerr << "ERROR: at block = " << block << ", i = " << i << ", j = " << j << ": i " << i << " >= logBins[j].size() " << logBins[j].size() << endl; Chris@34: } else if (logBins[j][i] >= tempogramDFT[block].size()) { Chris@34: cerr << "ERROR: at block = " << block << ", i = " << i << ", j = " << j << ": logBins[j][i] " << logBins[j][i] << " >= tempogramDFT[block].size() " << tempogramDFT[block].size() << endl; Chris@34: } else { Chris@34: sum += tempogramDFT[block][logBins[j][i]]; Chris@34: } c@18: } c@19: cyclicTempogramFeature.values.push_back(sum/m_cyclicTempogramNumberOfOctaves); c@21: assert(!isnan(cyclicTempogramFeature.values.back())); c@18: } c@18: c@19: cyclicTempogramFeature.hasTimestamp = false; c@21: featureSet[0].push_back(cyclicTempogramFeature); c@18: } c@0: c@0: return featureSet; c@0: } c@22: c@22: vector< vector > TempogramPlugin::calculateTempogramNearestNeighbourLogBins() const c@22: { c@22: vector< vector > logBins; c@22: c@22: for (int octave = 0; octave < (int)m_cyclicTempogramNumberOfOctaves; octave++){ c@22: vector octaveBins; c@22: c@22: for (int bin = 0; bin < (int)m_cyclicTempogramOctaveDivider; bin++){ c@22: float bpm = m_cyclicTempogramMinBPM*pow(2.0f, octave+(float)bin/m_cyclicTempogramOctaveDivider); c@22: octaveBins.push_back(bpmToBin(bpm)); c@23: //cout << octaveBins.back() << endl; c@22: } c@22: logBins.push_back(octaveBins); c@22: } c@22: c@22: return logBins; c@22: } c@22: c@22: unsigned int TempogramPlugin::bpmToBin(const float &bpm) const c@22: { c@22: float w = (float)bpm/60; c@22: float sampleRate = m_inputSampleRate/m_inputStepSize; c@22: int bin = floor((float)m_tempogramFftLength*w/sampleRate + 0.5); c@22: c@22: if(bin < 0) bin = 0; c@22: else if(bin > m_tempogramFftLength/2.0f) bin = m_tempogramFftLength; c@22: c@22: return bin; c@22: } c@22: c@22: float TempogramPlugin::binToBPM(const int &bin) const c@22: { c@22: float sampleRate = m_inputSampleRate/m_inputStepSize; c@22: c@22: return (bin*sampleRate/m_tempogramFftLength)*60; c@22: } c@22: c@22: bool TempogramPlugin::handleParameterValues(){ c@22: Chris@42: if (m_tempogramLog2HopSize <= 0) { Chris@42: cerr << "Tempogram log2 hop size " << m_tempogramLog2HopSize Chris@42: << " <= 0, failing initialise" << endl; Chris@42: return false; Chris@42: } Chris@42: if (m_tempogramLog2FftLength <= 0) { Chris@42: cerr << "Tempogram log2 fft length " << m_tempogramLog2FftLength Chris@42: << " <= 0, failing initialise" << endl; Chris@42: return false; Chris@42: } c@22: Chris@42: if (m_tempogramMinBPM < 1) { Chris@42: m_tempogramMinBPM = 1; Chris@42: } c@22: if (m_tempogramMinBPM >= m_tempogramMaxBPM){ c@22: m_tempogramMinBPM = 30; c@22: m_tempogramMaxBPM = 480; c@22: } c@22: c@29: m_noveltyCurveMinV = pow(10,(float)m_noveltyCurveMinDB/20); c@29: c@29: m_tempogramWindowLength = pow(2,m_tempogramLog2WindowLength); c@29: m_tempogramHopSize = pow(2,m_tempogramLog2HopSize); c@29: m_tempogramFftLength = pow(2,m_tempogramLog2FftLength); c@29: c@30: if (m_tempogramFftLength < m_tempogramWindowLength){ c@30: m_tempogramFftLength = m_tempogramWindowLength; c@30: } c@30: c@22: float tempogramInputSampleRate = (float)m_inputSampleRate/m_inputStepSize; c@28: m_tempogramMinBin = (max((int)floor(((m_tempogramMinBPM/60)/tempogramInputSampleRate)*m_tempogramFftLength), 0)); c@28: m_tempogramMaxBin = (min((int)ceil(((m_tempogramMaxBPM/60)/tempogramInputSampleRate)*m_tempogramFftLength), (int)(m_tempogramFftLength/2))); Chris@32: Chris@32: if (m_tempogramMaxBin < m_tempogramMinBin) { Chris@32: cerr << "At audio sample rate " << m_inputSampleRate Chris@32: << ", tempogram sample rate " << tempogramInputSampleRate Chris@32: << " with bpm range " << m_tempogramMinBPM << " -> " Chris@32: << m_tempogramMaxBPM << ", min bin = " << m_tempogramMinBin Chris@32: << " > max bin " << m_tempogramMaxBin Chris@32: << ": can't proceed, failing initialise" << endl; Chris@32: return false; Chris@32: } c@28: c@28: m_tempogramMinLag = max((int)ceil((60/(m_inputStepSize * m_tempogramMaxBPM))*m_inputSampleRate), 0); c@28: m_tempogramMaxLag = min((int)floor((60/(m_inputStepSize * m_tempogramMinBPM))*m_inputSampleRate), (int)m_tempogramWindowLength); Chris@32: Chris@32: if (m_tempogramMaxLag < m_tempogramMinLag) { Chris@32: cerr << "At audio sample rate " << m_inputSampleRate Chris@32: << ", tempogram sample rate " << tempogramInputSampleRate Chris@42: << ", window length " << m_tempogramWindowLength Chris@32: << " with bpm range " << m_tempogramMinBPM << " -> " Chris@42: << m_tempogramMaxBPM << ", min lag = " << m_tempogramMinLag Chris@42: << " > max lag " << m_tempogramMaxLag Chris@32: << ": can't proceed, failing initialise" << endl; Chris@32: return false; Chris@32: } c@22: Chris@42: m_cyclicTempogramMinBPM = m_tempogramMinBPM; Chris@42: float cyclicTempogramMaxBPM = m_tempogramMaxBPM; Chris@42: c@22: m_cyclicTempogramNumberOfOctaves = floor(log2(cyclicTempogramMaxBPM/m_cyclicTempogramMinBPM)); Chris@42: Chris@42: if (m_cyclicTempogramNumberOfOctaves < 1) { Chris@42: cerr << "At audio sample rate " << m_inputSampleRate Chris@42: << ", tempogram sample rate " << tempogramInputSampleRate Chris@42: << " with bpm range " << m_tempogramMinBPM << " -> " Chris@42: << m_tempogramMaxBPM << ", cyclic tempogram min bpm = " Chris@42: << m_cyclicTempogramMinBPM << " and max bpm = " Chris@42: << cyclicTempogramMaxBPM << " giving number of octaves = " Chris@42: << m_cyclicTempogramNumberOfOctaves Chris@42: << ": can't proceed, failing initialise" << endl; Chris@42: return false; Chris@42: } c@22: c@22: return true; c@22: } c@22: c@22: string TempogramPlugin::floatToString(float value) const c@22: { c@22: ostringstream ss; c@22: c@22: if(!(ss << value)) throw runtime_error("TempogramPlugin::floatToString(): invalid conversion from float to string"); c@22: return ss.str(); c@22: }