* Add tempo tracker plugin from piem
authorChris Cannam <cannam@all-day-breakfast.com>
Mon, 22 May 2006 11:43:23 +0000 (11:43 +0000)
committerChris Cannam <cannam@all-day-breakfast.com>
Mon, 22 May 2006 11:43:23 +0000 (11:43 +0000)
* Add various MIDI note range options to note tracker

libmain.cpp
plugins/Notes.cpp
plugins/Notes.h
plugins/Tempo.cpp [new file with mode: 0644]
plugins/Tempo.h [new file with mode: 0644]

index a349b9d..b9159cd 100644 (file)
 #include "plugins/Onset.h"
 #include "plugins/Pitch.h"
 #include "plugins/Notes.h"
+#include "plugins/Tempo.h"
 
 static Vamp::PluginAdapter<Onset> onsetAdapter;
 static Vamp::PluginAdapter<Pitch> pitchAdapter;
 static Vamp::PluginAdapter<Notes> notesAdapter;
+static Vamp::PluginAdapter<Tempo> tempoAdapter;
 
 const VampPluginDescriptor *vampGetPluginDescriptor(unsigned int index)
 {
@@ -31,6 +33,7 @@ const VampPluginDescriptor *vampGetPluginDescriptor(unsigned int index)
     case  0: return onsetAdapter.getDescriptor();
     case  1: return pitchAdapter.getDescriptor();
     case  2: return notesAdapter.getDescriptor();
+    case  3: return tempoAdapter.getDescriptor();
     default: return 0;
     }
 }
index 39ad0f2..105518a 100644 (file)
@@ -36,7 +36,12 @@ Notes::Notes(float inputSampleRate) :
     m_pitchmode(aubio_pitchm_freq),
     m_threshold(0.3),
     m_silence(-90),
-    m_median(6)
+    m_median(6),
+    m_minpitch(32),
+    m_maxpitch(95),
+    m_wrapRange(false),
+    m_avoidLeaps(false),
+    m_prevPitch(-1)
 {
 }
 
@@ -116,6 +121,7 @@ Notes::initialise(size_t channels, size_t stepSize, size_t blockSize)
                                        lrintf(m_inputSampleRate));
     m_currentOnset = Vamp::RealTime::zeroTime;
     m_haveCurrent = false;
+    m_prevPitch = -1;
 
     return true;
 }
@@ -175,6 +181,48 @@ Notes::getParameterDescriptors() const
     list.push_back(desc);
 
     desc = ParameterDescriptor();
+    desc.name = "minpitch";
+    desc.description = "Minimum Pitch";
+    desc.minValue = 0;
+    desc.maxValue = 127;
+    desc.defaultValue = 32;
+    desc.unit = "MIDI units";
+    desc.isQuantized = true;
+    desc.quantizeStep = 1;
+    list.push_back(desc);
+
+    desc = ParameterDescriptor();
+    desc.name = "maxpitch";
+    desc.description = "Maximum Pitch";
+    desc.minValue = 0;
+    desc.maxValue = 127;
+    desc.defaultValue = 95;
+    desc.unit = "MIDI units";
+    desc.isQuantized = true;
+    desc.quantizeStep = 1;
+    list.push_back(desc);
+
+    desc = ParameterDescriptor();
+    desc.name = "wraprange";
+    desc.description = "Fold Higher or Lower Notes into Range";
+    desc.minValue = 0;
+    desc.maxValue = 1;
+    desc.defaultValue = 0;
+    desc.isQuantized = true;
+    desc.quantizeStep = 1;
+    list.push_back(desc);
+
+    desc = ParameterDescriptor();
+    desc.name = "avoidleaps";
+    desc.description = "Avoid Multi-Octave Jumps";
+    desc.minValue = 0;
+    desc.maxValue = 1;
+    desc.defaultValue = 0;
+    desc.isQuantized = true;
+    desc.quantizeStep = 1;
+    list.push_back(desc);
+
+    desc = ParameterDescriptor();
     desc.name = "peakpickthreshold";
     desc.description = "Peak Picker Threshold";
     desc.minValue = 0;
@@ -207,6 +255,14 @@ Notes::getParameter(std::string param) const
         return m_threshold;
     } else if (param == "silencethreshold") {
         return m_silence;
+    } else if (param == "minpitch") {
+        return m_minpitch;
+    } else if (param == "maxpitch") {
+        return m_maxpitch;
+    } else if (param == "wraprange") {
+        return m_wrapRange ? 1.0 : 0.0;
+    } else if (param == "avoidleaps") {
+        return m_avoidLeaps ? 1.0 : 0.0;
     } else {
         return 0.0;
     }
@@ -237,6 +293,14 @@ Notes::setParameter(std::string param, float value)
         m_threshold = value;
     } else if (param == "silencethreshold") {
         m_silence = value;
+    } else if (param == "minpitch") {
+        m_minpitch = lrintf(value);
+    } else if (param == "maxpitch") {
+        m_maxpitch = lrintf(value);
+    } else if (param == "wraprange") {
+        m_wrapRange = (value > 0.5);
+    } else if (param == "avoidleaps") {
+        m_avoidLeaps = (value > 0.5);
     }
 }
 
@@ -324,12 +388,41 @@ Notes::pushNote(FeatureSet &fs, const Vamp::RealTime &offTime)
     float median = toSort[toSort.size()/2];
     if (median < 45.0) return;
 
+    float freq = median;
+    int midiPitch = (int)FLOOR(aubio_freqtomidi(freq) + 0.5);
+    
+    if (m_avoidLeaps) {
+        if (m_prevPitch >= 0) {
+            while (midiPitch < m_prevPitch - 12) {
+                midiPitch += 12;
+                freq *= 2;
+            }
+            while (midiPitch > m_prevPitch + 12) {
+                midiPitch -= 12;
+                freq /= 2;
+            }
+        }
+    }
+
+    while (midiPitch < m_minpitch) {
+        if (!m_wrapRange) return;
+        midiPitch += 12;
+        freq *= 2;
+    }
+
+    while (midiPitch > m_maxpitch) {
+        if (!m_wrapRange) return;
+        midiPitch -= 12;
+        freq /= 2;
+    }
+
+    m_prevPitch = midiPitch;
+
     Feature feature;
     feature.hasTimestamp = true;
     if (m_currentOnset < m_delay) m_currentOnset = m_delay;
     feature.timestamp = m_currentOnset - m_delay;
-    feature.values.push_back(median);
-//    feature.values.push_back(FLOOR(aubio_freqtomidi(median) + 0.5));
+    feature.values.push_back(freq);
     feature.values.push_back
         (Vamp::RealTime::realTime2Frame(offTime, lrintf(m_inputSampleRate)) -
          Vamp::RealTime::realTime2Frame(m_currentOnset, lrintf(m_inputSampleRate)));
index cb28230..be2e162 100644 (file)
@@ -69,6 +69,10 @@ protected:
     size_t m_stepSize;
     size_t m_blockSize;
     size_t m_channelCount;
+    int m_minpitch;
+    int m_maxpitch;
+    bool m_wrapRange;
+    bool m_avoidLeaps;
     std::deque<float> m_notebuf;
     size_t m_count;
     Vamp::RealTime m_delay;
@@ -76,6 +80,7 @@ protected:
     Vamp::RealTime m_lastTimeStamp;
     float m_currentLevel;
     bool m_haveCurrent;
+    int m_prevPitch;
 
     void pushNote(FeatureSet &, const Vamp::RealTime &);
 };
diff --git a/plugins/Tempo.cpp b/plugins/Tempo.cpp
new file mode 100644 (file)
index 0000000..5fc3cc6
--- /dev/null
@@ -0,0 +1,290 @@
+/* -*- c-basic-offset: 4 indent-tabs-mode: nil -*-  vi:set ts=8 sts=4 sw=4: */
+
+/*
+    Vamp feature extraction plugins using Paul Brossier's Aubio library.
+
+    Centre for Digital Music, Queen Mary, University of London.
+    This file copyright 2006 Chris Cannam.
+    
+    This program is free software; you can redistribute it and/or
+    modify it under the terms of the GNU General Public License as
+    published by the Free Software Foundation; either version 2 of the
+    License, or (at your option) any later version.  See the file
+    COPYING included with this distribution for more information.
+
+*/
+
+#include <math.h>
+#include "Tempo.h"
+
+using std::string;
+using std::vector;
+using std::cerr;
+using std::endl;
+
+Tempo::Tempo(float inputSampleRate) :
+    Plugin(inputSampleRate),
+    m_ibuf(0),
+    m_fftgrain(0),
+    m_onset(0),
+    m_pv(0),
+    m_peakpick(0),
+    m_onsetdet(0),
+    m_onsettype(aubio_onset_specdiff),
+    m_beattracking(0),
+    m_dfframe(0),
+    m_btout(0),
+    m_btcounter(0),
+    m_threshold(0.3),
+    m_silence(-90),
+    m_channelCount(1)
+{
+}
+
+Tempo::~Tempo()
+{
+    if (m_onsetdet) aubio_onsetdetection_free(m_onsetdet);
+    if (m_ibuf) del_fvec(m_ibuf);
+    if (m_onset) del_fvec(m_onset);
+    if (m_fftgrain) del_cvec(m_fftgrain);
+    if (m_pv) del_aubio_pvoc(m_pv);
+    if (m_peakpick) del_aubio_peakpicker(m_peakpick);
+    if (m_beattracking) del_aubio_beattracking(m_beattracking);
+    if (m_dfframe) del_fvec(m_dfframe);
+    if (m_btout) del_fvec(m_btout);
+}
+
+string
+Tempo::getName() const
+{
+    return "aubiotempo";
+}
+
+string
+Tempo::getDescription() const
+{
+    return "Aubio Tempo Detector";
+}
+
+string
+Tempo::getMaker() const
+{
+    return "Paul Brossier (plugin by Chris Cannam)";
+}
+
+int
+Tempo::getPluginVersion() const
+{
+    return 1;
+}
+
+string
+Tempo::getCopyright() const
+{
+    return "GPL";
+}
+
+bool
+Tempo::initialise(size_t channels, size_t stepSize, size_t blockSize)
+{
+    m_channelCount = channels;
+    m_stepSize = stepSize;
+    m_blockSize = blockSize;
+
+    m_ibuf = new_fvec(stepSize, channels);
+    m_onset = new_fvec(1, channels);
+    m_fftgrain = new_cvec(blockSize, channels);
+    m_pv = new_aubio_pvoc(blockSize, stepSize, channels);
+    m_peakpick = new_aubio_peakpicker(m_threshold);
+
+    m_onsetdet = new_aubio_onsetdetection(m_onsettype, blockSize, channels);
+    
+    m_delay = Vamp::RealTime::frame2RealTime(3 * stepSize,
+                                             lrintf(m_inputSampleRate));
+
+    m_lastBeat = Vamp::RealTime::zeroTime - m_delay - m_delay;
+
+    m_winlen = 512*512/stepSize;
+    m_dfframe = new_fvec(m_winlen,channels);
+    m_btstep = m_winlen/4;
+    m_btout = new_fvec(m_btstep,channels);
+    m_beattracking = new_aubio_beattracking(m_winlen,channels);
+
+    return true;
+}
+
+void
+Tempo::reset()
+{
+}
+
+size_t
+Tempo::getPreferredStepSize() const
+{
+    return 512;
+}
+
+size_t
+Tempo::getPreferredBlockSize() const
+{
+    return 2 * getPreferredStepSize();
+}
+
+Tempo::ParameterList
+Tempo::getParameterDescriptors() const
+{
+    ParameterList list;
+    
+    ParameterDescriptor desc;
+    desc.name = "onsettype";
+    desc.description = "Onset Detection Function Type";
+    desc.minValue = 0;
+    desc.maxValue = 6;
+    desc.defaultValue = (int)aubio_onset_complex;
+    desc.isQuantized = true;
+    desc.quantizeStep = 1;
+    desc.valueNames.push_back("Energy Based");
+    desc.valueNames.push_back("Spectral Difference");
+    desc.valueNames.push_back("High-Frequency Content");
+    desc.valueNames.push_back("Complex Domain");
+    desc.valueNames.push_back("Phase Deviation");
+    desc.valueNames.push_back("Kullback-Liebler");
+    desc.valueNames.push_back("Modified Kullback-Liebler");
+    list.push_back(desc);
+
+    desc = ParameterDescriptor();
+    desc.name = "peakpickthreshold";
+    desc.description = "Peak Picker Threshold";
+    desc.minValue = 0;
+    desc.maxValue = 1;
+    desc.defaultValue = 0.3;
+    desc.isQuantized = false;
+    list.push_back(desc);
+
+    desc = ParameterDescriptor();
+    desc.name = "silencethreshold";
+    desc.description = "Silence Threshold";
+    desc.minValue = -120;
+    desc.maxValue = 0;
+    desc.defaultValue = -90;
+    desc.unit = "dB";
+    desc.isQuantized = false;
+    list.push_back(desc);
+
+    return list;
+}
+
+float
+Tempo::getParameter(std::string param) const
+{
+    if (param == "onsettype") {
+        return m_onsettype;
+    } else if (param == "peakpickthreshold") {
+        return m_threshold;
+    } else if (param == "silencethreshold") {
+        return m_silence;
+    } else {
+        return 0.0;
+    }
+}
+
+void
+Tempo::setParameter(std::string param, float value)
+{
+    if (param == "onsettype") {
+        switch (lrintf(value)) {
+        case 0: m_onsettype = aubio_onset_energy; break;
+        case 1: m_onsettype = aubio_onset_specdiff; break;
+        case 2: m_onsettype = aubio_onset_hfc; break;
+        case 3: m_onsettype = aubio_onset_complex; break;
+        case 4: m_onsettype = aubio_onset_phase; break;
+        case 5: m_onsettype = aubio_onset_kl; break;
+        case 6: m_onsettype = aubio_onset_mkl; break;
+        }
+    } else if (param == "peakpickthreshold") {
+        m_threshold = value;
+    } else if (param == "silencethreshold") {
+        m_silence = value;
+    }
+}
+
+Tempo::OutputList
+Tempo::getOutputDescriptors() const
+{
+    OutputList list;
+
+    OutputDescriptor d;
+    d.name = "beats";
+    d.unit = "";
+    d.description = "Beats";
+    d.hasFixedBinCount = true;
+    d.binCount = 0;
+    d.sampleType = OutputDescriptor::VariableSampleRate;
+    d.sampleRate = 0;
+    list.push_back(d);
+
+    return list;
+}
+
+Tempo::FeatureSet
+Tempo::process(float **inputBuffers, Vamp::RealTime timestamp)
+{
+    for (size_t i = 0; i < m_stepSize; ++i) {
+        for (size_t j = 0; j < m_channelCount; ++j) {
+            fvec_write_sample(m_ibuf, inputBuffers[j][i], j, i);
+        }
+    }
+
+    aubio_pvoc_do(m_pv, m_ibuf, m_fftgrain);
+    aubio_onsetdetection(m_onsetdet, m_fftgrain, m_onset);
+
+    if ( m_btcounter == m_btstep - 1 ) {
+        aubio_beattracking_do(m_beattracking,m_dfframe,m_btout);
+        /* rotate dfframe */
+        for (size_t i = 0 ; i < m_winlen - m_btstep; i++ ) 
+                m_dfframe->data[0][i] = m_dfframe->data[0][i+m_btstep];
+        for (size_t i = m_winlen - m_btstep ; i < m_winlen; i++ ) 
+                m_dfframe->data[0][i] = 0.;
+                
+        m_btcounter = -1;
+    }
+    m_btcounter++;
+    bool isonset = aubio_peakpick_pimrt_wt( m_onset, m_peakpick, 
+        &(m_dfframe->data[0][m_winlen - m_btstep + m_btcounter]));
+    bool istactus = 0;
+
+
+    /* check if any of the predicted beat correspond to the current time */
+    for (size_t i = 1; i < m_btout->data[0][0]; i++ ) { 
+            if (m_btcounter == m_btout->data[0][i]) {
+                    if (aubio_silence_detection(m_ibuf, m_silence)) {
+                            isonset  = false;
+                            istactus = false;
+                    } else {
+                            istactus = true;
+                    }
+            }
+    }
+
+    FeatureSet returnFeatures;
+
+    if (istactus == true) {
+        if (timestamp - m_lastBeat >= m_delay) {
+            Feature onsettime;
+            onsettime.hasTimestamp = true;
+            if (timestamp < m_delay) timestamp = m_delay;
+            onsettime.timestamp = timestamp - m_delay;
+            returnFeatures[0].push_back(onsettime);
+            m_lastBeat = timestamp;
+        }
+    }
+
+    return returnFeatures;
+}
+
+Tempo::FeatureSet
+Tempo::getRemainingFeatures()
+{
+    return FeatureSet();
+}
+
diff --git a/plugins/Tempo.h b/plugins/Tempo.h
new file mode 100644 (file)
index 0000000..ce746e8
--- /dev/null
@@ -0,0 +1,77 @@
+/* -*- c-basic-offset: 4 indent-tabs-mode: nil -*-  vi:set ts=8 sts=4 sw=4: */
+
+/*
+    Vamp feature extraction plugins using Paul Brossier's Aubio library.
+
+    Centre for Digital Music, Queen Mary, University of London.
+    This file copyright 2006 Chris Cannam.
+    
+    This program is free software; you can redistribute it and/or
+    modify it under the terms of the GNU General Public License as
+    published by the Free Software Foundation; either version 2 of the
+    License, or (at your option) any later version.  See the file
+    COPYING included with this distribution for more information.
+
+*/
+
+#ifndef _TEMPO_PLUGIN_H_
+#define _TEMPO_PLUGIN_H_
+
+#include <vamp-sdk/Plugin.h>
+#include <aubio/aubio.h>
+
+class Tempo : public Vamp::Plugin
+{
+public:
+    Tempo(float inputSampleRate);
+    virtual ~Tempo();
+
+    bool initialise(size_t channels, size_t stepSize, size_t blockSize);
+    void reset();
+
+    InputDomain getInputDomain() const { return TimeDomain; }
+
+    std::string getName() const;
+    std::string getDescription() const;
+    std::string getMaker() const;
+    int getPluginVersion() const;
+    std::string getCopyright() const;
+
+    ParameterList getParameterDescriptors() const;
+    float getParameter(std::string) const;
+    void setParameter(std::string, float);
+
+    size_t getPreferredStepSize() const;
+    size_t getPreferredBlockSize() const;
+
+    OutputList getOutputDescriptors() const;
+
+    FeatureSet process(float **inputBuffers, Vamp::RealTime timestamp);
+
+    FeatureSet getRemainingFeatures();
+
+protected:
+    fvec_t *m_ibuf;
+    cvec_t *m_fftgrain;
+    fvec_t *m_onset;
+    aubio_pvoc_t *m_pv;
+    aubio_pickpeak_t *m_peakpick;
+    aubio_onsetdetection_t *m_onsetdet;
+    aubio_onsetdetection_type m_onsettype;
+    aubio_beattracking_t *m_beattracking;
+    fvec_t *m_dfframe;
+    fvec_t *m_btout;
+    uint_t m_winlen;
+    sint_t m_btstep;
+    sint_t m_btcounter;
+    float m_threshold;
+    float m_silence;
+    size_t m_stepSize;
+    size_t m_blockSize;
+    size_t m_channelCount;
+    Vamp::RealTime m_delay;
+    Vamp::RealTime m_lastBeat;
+};
+
+
+#endif