diff plugins/ConstantQSpectrogram.cpp @ 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
children 74ce5b481132
line wrap: on
line diff
--- /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();
+}
+