changeset 9:507f923a93e8

* Add Constant-Q Spectrogram plugin
author Chris Cannam <c.cannam@qmul.ac.uk>
date Mon, 15 May 2006 19:56:21 +0000
parents a8215973f030
children 6ab46726a653
files libmain.cpp plugins/ChromagramPlugin.cpp plugins/ConstantQSpectrogram.cpp plugins/ConstantQSpectrogram.h plugins/TonalChangeDetect.cpp qm-vamp-plugins.pro
diffstat 6 files changed, 431 insertions(+), 11 deletions(-) [+]
line wrap: on
line diff
--- a/libmain.cpp	Mon May 15 15:12:46 2006 +0000
+++ b/libmain.cpp	Mon May 15 19:56:21 2006 +0000
@@ -12,10 +12,12 @@
 
 #include "plugins/BeatDetect.h"
 #include "plugins/ChromagramPlugin.h"
+#include "plugins/ConstantQSpectrogram.h"
 #include "plugins/TonalChangeDetect.h"
 
 static Vamp::PluginAdapter<BeatDetector> beatDetectorAdapter;
 static Vamp::PluginAdapter<ChromagramPlugin> chromagramPluginAdapter;
+static Vamp::PluginAdapter<ConstantQSpectrogram> constantQAdapter;
 static Vamp::PluginAdapter<TonalChangeDetect> tonalChangeDetectorAdapter;
 
 const VampPluginDescriptor *vampGetPluginDescriptor(unsigned int index)
@@ -23,7 +25,8 @@
     switch (index) {
     case  0: return beatDetectorAdapter.getDescriptor();
     case  1: return chromagramPluginAdapter.getDescriptor();
-    case  2: return tonalChangeDetectorAdapter.getDescriptor();
+    case  2: return constantQAdapter.getDescriptor();
+    case  3: return tonalChangeDetectorAdapter.getDescriptor();
     default: return 0;
     }
 }
--- a/plugins/ChromagramPlugin.cpp	Mon May 15 15:12:46 2006 +0000
+++ b/plugins/ChromagramPlugin.cpp	Mon May 15 19:56:21 2006 +0000
@@ -56,7 +56,7 @@
 string
 ChromagramPlugin::getName() const
 {
-    return "chromagram";
+    return "qm-chromagram";
 }
 
 string
@@ -265,7 +265,7 @@
             }
         }
     } else {
-        d.binNames.push_back("C");
+        d.binNames.push_back(names[m_minMIDIPitch % 12]);
     }
 
     d.hasKnownExtents = m_normalized;
@@ -274,13 +274,7 @@
     d.isQuantized = false;
     d.sampleType = OutputDescriptor::OneSamplePerStep;
     list.push_back(d);
-/*
-    d.name = "normalized";
-    d.description = "Normalized Chromagram";
-    d.hasKnownExtents = true;
-    d.maxValue = 1.0;
-    list.push_back(d);
-*/
+
     return list;
 }
 
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/plugins/ConstantQSpectrogram.cpp	Mon May 15 19:56:21 2006 +0000
@@ -0,0 +1,354 @@
+/* -*- c-basic-offset: 4 indent-tabs-mode: nil -*-  vi:set ts=8 sts=4 sw=4: */
+
+/*
+    QM Vamp Plugin Set
+
+    Centre for Digital Music, Queen Mary, University of London.
+    All rights reserved.
+*/
+
+#include "ConstantQSpectrogram.h"
+
+#include <base/Pitch.h>
+#include <dsp/chromagram/ConstantQ.h>
+
+using std::string;
+using std::vector;
+using std::cerr;
+using std::endl;
+
+ConstantQSpectrogram::ConstantQSpectrogram(float inputSampleRate) :
+    Vamp::Plugin(inputSampleRate),
+    m_cq(0),
+    m_step(0),
+    m_block(0),
+    m_bins(1)
+{
+    m_minMIDIPitch = 12;
+    m_maxMIDIPitch = 96;
+    m_tuningFrequency = 440;
+    m_normalized = true;
+    m_bpo = 12;
+
+    setupConfig();
+}
+
+void
+ConstantQSpectrogram::setupConfig()
+{
+    m_config.FS = lrintf(m_inputSampleRate);
+    m_config.min = Pitch::getFrequencyForPitch
+        (m_minMIDIPitch, 0, m_tuningFrequency);
+    m_config.max = Pitch::getFrequencyForPitch
+        (m_maxMIDIPitch, 0, m_tuningFrequency);
+    m_config.BPO = m_bpo;
+    m_config.CQThresh = 0.0054;
+
+    m_step = 0;
+    m_block = 0;
+}
+
+ConstantQSpectrogram::~ConstantQSpectrogram()
+{
+    delete m_cq;
+}
+
+string
+ConstantQSpectrogram::getName() const
+{
+    return "qm-constantq";
+}
+
+string
+ConstantQSpectrogram::getDescription() const
+{
+    return "Constant-Q Spectrogram";
+}
+
+string
+ConstantQSpectrogram::getMaker() const
+{
+    return "Queen Mary, University of London";
+}
+
+int
+ConstantQSpectrogram::getPluginVersion() const
+{
+    return 1;
+}
+
+string
+ConstantQSpectrogram::getCopyright() const
+{
+    return "Copyright (c) 2006 - All Rights Reserved";
+}
+
+ConstantQSpectrogram::ParameterList
+ConstantQSpectrogram::getParameterDescriptors() const
+{
+    ParameterList list;
+
+    ParameterDescriptor desc;
+    desc.name = "minpitch";
+    desc.description = "Minimum Pitch";
+    desc.unit = "MIDI units";
+    desc.minValue = 0;
+    desc.maxValue = 127;
+    desc.defaultValue = 36;
+    desc.isQuantized = true;
+    desc.quantizeStep = 1;
+    list.push_back(desc);
+
+    desc.name = "maxpitch";
+    desc.description = "Maximum Pitch";
+    desc.unit = "MIDI units";
+    desc.minValue = 0;
+    desc.maxValue = 127;
+    desc.defaultValue = 84;
+    desc.isQuantized = true;
+    desc.quantizeStep = 1;
+    list.push_back(desc);
+
+    desc.name = "tuning";
+    desc.description = "Tuning Frequency";
+    desc.unit = "Hz";
+    desc.minValue = 420;
+    desc.maxValue = 460;
+    desc.defaultValue = 440;
+    desc.isQuantized = false;
+    list.push_back(desc);
+    
+    desc.name = "bpo";
+    desc.description = "Bins per Octave";
+    desc.unit = "bins";
+    desc.minValue = 2;
+    desc.maxValue = 36;
+    desc.defaultValue = 12;
+    desc.isQuantized = true;
+    desc.quantizeStep = 1;
+    list.push_back(desc);
+
+    desc.name = "normalized";
+    desc.description = "Normalized";
+    desc.unit = "";
+    desc.minValue = 0;
+    desc.maxValue = 1;
+    desc.defaultValue = 1;
+    desc.isQuantized = true;
+    desc.quantizeStep = 1;
+    list.push_back(desc);
+
+    return list;
+}
+
+float
+ConstantQSpectrogram::getParameter(std::string param) const
+{
+    if (param == "minpitch") {
+        return m_minMIDIPitch;
+    }
+    if (param == "maxpitch") {
+        return m_maxMIDIPitch;
+    }
+    if (param == "tuning") {
+        return m_tuningFrequency;
+    }
+    if (param == "bpo") {
+        return m_bpo;
+    }
+    if (param == "normalized") {
+        return m_normalized;
+    }
+    std::cerr << "WARNING: ConstantQSpectrogram::getParameter: unknown parameter \""
+              << param << "\"" << std::endl;
+    return 0.0;
+}
+
+void
+ConstantQSpectrogram::setParameter(std::string param, float value)
+{
+    if (param == "minpitch") {
+        m_minMIDIPitch = lrintf(value);
+    } else if (param == "maxpitch") {
+        m_maxMIDIPitch = lrintf(value);
+    } else if (param == "tuning") {
+        m_tuningFrequency = value;
+    } else if (param == "bpo") {
+        m_bpo = lrintf(value);
+    } else if (param == "normalized") {
+        m_normalized = (value > 0.0001);
+    } else {
+        std::cerr << "WARNING: ConstantQSpectrogram::setParameter: unknown parameter \""
+                  << param << "\"" << std::endl;
+    }
+
+    setupConfig();
+}
+
+
+bool
+ConstantQSpectrogram::initialise(size_t channels, size_t stepSize, size_t blockSize)
+{
+    if (m_cq) {
+	delete m_cq;
+	m_cq = 0;
+    }
+
+    if (channels < getMinChannelCount() ||
+	channels > getMaxChannelCount()) return false;
+
+    if (stepSize != m_step) return false;
+    if (blockSize != m_block) return false;
+
+    std::cerr << "ConstantQSpectrogram::initialise: step " << stepSize << ", block "
+	      << blockSize << std::endl;
+
+    m_cq = new ConstantQ(m_config);
+    m_bins = (int)ceil(m_bpo * log(m_config.max / m_config.min) / log(2.0));
+    m_cq->sparsekernel();
+
+    return true;
+}
+
+void
+ConstantQSpectrogram::reset()
+{
+    if (m_cq) {
+	delete m_cq;
+	m_cq = new ConstantQ(m_config);
+    }
+}
+
+size_t
+ConstantQSpectrogram::getPreferredStepSize() const
+{
+    if (!m_step) {
+	ConstantQ cq(m_config);
+	m_step = cq.gethop();
+	m_block = cq.getfftlength();
+    }
+
+    return m_step;
+}
+
+size_t
+ConstantQSpectrogram::getPreferredBlockSize() const
+{
+    if (!m_block) {
+	ConstantQ cq(m_config);
+	m_step = cq.gethop();
+	m_block = cq.getfftlength();
+    }
+
+    return m_block;
+}
+
+ConstantQSpectrogram::OutputList
+ConstantQSpectrogram::getOutputDescriptors() const
+{
+    OutputList list;
+
+    OutputDescriptor d;
+    d.name = "constantq";
+    d.unit = "";
+    d.description = "Constant-Q Spectrogram";
+    d.hasFixedBinCount = true;
+    d.binCount = m_bins;
+
+    std::cerr << "Bin count " << d.binCount << std::endl;
+    
+    const char *names[] =
+	{ "C", "C#", "D", "D#", "E", "F", "F#", "G", "G#", "A", "A#", "B" };
+
+    if (m_bpo == 12) {
+        for (int i = 0; i < d.binCount; ++i) {
+            int ipc = m_minMIDIPitch % 12;
+            int index = (i + ipc) % 12;
+            d.binNames.push_back(names[index]);
+        }
+    } else {
+        d.binNames.push_back(names[m_minMIDIPitch % 12]);
+    }
+
+    d.hasKnownExtents = m_normalized;
+    d.minValue = 0.0;
+    d.maxValue = (m_normalized ? 1.0 : 0.0);
+    d.isQuantized = false;
+    d.sampleType = OutputDescriptor::OneSamplePerStep;
+    list.push_back(d);
+
+    return list;
+}
+
+ConstantQSpectrogram::Feature
+ConstantQSpectrogram::normalize(const Feature &feature)
+{
+    float min = 0.0, max = 0.0;
+
+    for (size_t i = 0; i < feature.values.size(); ++i) {
+	if (i == 0 || feature.values[i] < min) min = feature.values[i];
+	if (i == 0 || feature.values[i] > max) max = feature.values[i];
+    }
+	
+    if (max == 0.0 || max == min) return feature;
+
+    Feature normalized;
+    normalized.hasTimestamp = false;
+
+    for (size_t i = 0; i < feature.values.size(); ++i) {
+	normalized.values.push_back((feature.values[i] - min) / (max - min));
+    }
+
+    return normalized;
+}
+
+ConstantQSpectrogram::FeatureSet
+ConstantQSpectrogram::process(float **inputBuffers, Vamp::RealTime /* timestamp */)
+{
+    if (!m_cq) {
+	cerr << "ERROR: ConstantQSpectrogram::process: "
+	     << "Constant-Q has not been initialised"
+	     << endl;
+	return FeatureSet();
+    }
+
+    double *real = new double[m_block];
+    double *imag = new double[m_block];
+    double *cqre = new double[m_bins];
+    double *cqim = new double[m_bins];
+
+    for (size_t i = 0; i < m_block/2; ++i) {
+	real[i] = inputBuffers[0][i*2];
+	real[m_block - i] = real[i];
+        imag[i] = inputBuffers[0][i*2+1];
+        imag[m_block - i] = imag[i];
+    }
+
+    m_cq->process(real, imag, cqre, cqim);
+
+    delete[] real;
+    delete[] imag;
+
+    Feature feature;
+    feature.hasTimestamp = false;
+    for (size_t i = 0; i < m_bins; ++i) {
+	feature.values.push_back(sqrt(cqre[i] * cqre[i] +
+                                      cqim[i] * cqim[i]));
+    }
+    feature.label = "";
+
+    delete[] cqre;
+    delete[] cqim;
+
+    FeatureSet returnFeatures;
+    if (m_normalized) returnFeatures[0].push_back(normalize(feature));
+    else returnFeatures[0].push_back(feature);
+    return returnFeatures;
+}
+
+ConstantQSpectrogram::FeatureSet
+ConstantQSpectrogram::getRemainingFeatures()
+{
+    return FeatureSet();
+}
+
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/plugins/ConstantQSpectrogram.h	Mon May 15 19:56:21 2006 +0000
@@ -0,0 +1,67 @@
+/* -*- c-basic-offset: 4 indent-tabs-mode: nil -*-  vi:set ts=8 sts=4 sw=4: */
+
+/*
+    QM Vamp Plugin Set
+
+    Centre for Digital Music, Queen Mary, University of London.
+    All rights reserved.
+*/
+
+#ifndef _CONSTANT_Q_SPECTROGRAM_PLUGIN_H_
+#define _CONSTANT_Q_SPECTROGRAM_PLUGIN_H_
+
+#include <vamp-sdk/Plugin.h>
+#include <dsp/chromagram/ConstantQ.h>
+
+#include <queue>
+
+class ConstantQSpectrogram : public Vamp::Plugin
+{
+public:
+    ConstantQSpectrogram(float inputSampleRate);
+    virtual ~ConstantQSpectrogram();
+
+    bool initialise(size_t channels, size_t stepSize, size_t blockSize);
+    void reset();
+
+    InputDomain getInputDomain() const { return FrequencyDomain; }
+
+    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:
+    int m_minMIDIPitch;
+    int m_maxMIDIPitch;
+    float m_tuningFrequency;
+    bool m_normalized;
+    int m_bpo;
+    int m_bins;
+
+    void setupConfig();
+
+    CQConfig m_config;
+    ConstantQ *m_cq;
+    mutable size_t m_step;
+    mutable size_t m_block;
+
+    Feature normalize(const Feature &);
+};
+
+
+#endif
--- a/plugins/TonalChangeDetect.cpp	Mon May 15 15:12:46 2006 +0000
+++ b/plugins/TonalChangeDetect.cpp	Mon May 15 19:56:21 2006 +0000
@@ -71,7 +71,7 @@
 
 std::string TonalChangeDetect::getName() const
 {
-    return "TonalChange";
+    return "qm-tonalchange";
 }
 
 std::string TonalChangeDetect::getDescription() const
--- a/qm-vamp-plugins.pro	Mon May 15 15:12:46 2006 +0000
+++ b/qm-vamp-plugins.pro	Mon May 15 19:56:21 2006 +0000
@@ -18,8 +18,10 @@
 # Input
 HEADERS += plugins/BeatDetect.h \
            plugins/ChromagramPlugin.h \
+           plugins/ConstantQSpectrogram.h \
            plugins/TonalChangeDetect.h
 SOURCES += plugins/BeatDetect.cpp \
            plugins/ChromagramPlugin.cpp \
+           plugins/ConstantQSpectrogram.cpp \
            plugins/TonalChangeDetect.cpp \
            ./libmain.cpp