changeset 0:3f318eae66a2

* Queen Mary plugin collection for the Vamp plugin API
author Chris Cannam <c.cannam@qmul.ac.uk>
date Wed, 05 Apr 2006 17:34:40 +0000
parents
children 5b324278b726
files Makefile plugins/BeatDetect.cpp plugins/BeatDetect.h plugins/ChromagramPlugin.cpp plugins/ChromagramPlugin.h plugins/TonalChangeDetect.cpp plugins/TonalChangeDetect.h qm-vamp-plugins.pro
diffstat 8 files changed, 1461 insertions(+), 0 deletions(-) [+]
line wrap: on
line diff
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/Makefile	Wed Apr 05 17:34:40 2006 +0000
@@ -0,0 +1,161 @@
+#############################################################################
+# Makefile for building: libqm-vamp-plugins.so.1.0.0
+# Generated by qmake (1.07a) (Qt 3.3.4) on: Wed Apr  5 17:23:42 2006
+# Project:  qm-vamp-plugins.pro
+# Template: lib
+# Command: $(QMAKE) -o Makefile qm-vamp-plugins.pro
+#############################################################################
+
+####### Compiler, tools and options
+
+CC       = gcc
+CXX      = g++
+LEX      = flex
+YACC     = yacc
+CFLAGS   = -pipe -Wall -W -O2 -fPIC  -DQT_NO_DEBUG -DQT_SHARED -DQT_THREAD_SUPPORT
+CXXFLAGS = -pipe -Wall -W -O2 -fPIC  -DQT_NO_DEBUG -DQT_SHARED -DQT_THREAD_SUPPORT
+LEXFLAGS = 
+YACCFLAGS= -d
+INCPATH  = -I/usr/share/qt3/mkspecs/default -I. -I../../sonic-visualiser/plugin/vamp-plugin-sdk -I../../qm-dsp/trunk -I. -Iplugins -I/usr/include/qt3 -Itmp_moc/
+LINK     = g++
+LFLAGS   = -shared -Wl,-soname,libqm-vamp-plugins.so.1
+LIBS     = $(SUBLIBS) -L/usr/share/qt3/lib -L/usr/X11R6/lib -lqt-mt -lXext -lX11 -lm
+AR       = ar cqs
+RANLIB   = 
+MOC      = /usr/share/qt3/bin/moc
+UIC      = /usr/share/qt3/bin/uic
+QMAKE    = qmake
+TAR      = tar -cf
+GZIP     = gzip -9f
+COPY     = cp -f
+COPY_FILE= $(COPY)
+COPY_DIR = $(COPY) -r
+INSTALL_FILE= $(COPY_FILE)
+INSTALL_DIR = $(COPY_DIR)
+DEL_FILE = rm -f
+SYMLINK  = ln -sf
+DEL_DIR  = rmdir
+MOVE     = mv -f
+CHK_DIR_EXISTS= test -d
+MKDIR    = mkdir -p
+
+####### Output directory
+
+OBJECTS_DIR = tmp_obj/
+
+####### Files
+
+HEADERS = plugins/BeatDetect.h \
+		plugins/ChromagramPlugin.h \
+		plugins/TonalChangeDetect.h
+SOURCES = plugins/BeatDetect.cpp \
+		plugins/ChromagramPlugin.cpp \
+		plugins/TonalChangeDetect.cpp
+OBJECTS = tmp_obj/BeatDetect.o \
+		tmp_obj/ChromagramPlugin.o \
+		tmp_obj/TonalChangeDetect.o
+FORMS = 
+UICDECLS = 
+UICIMPLS = 
+SRCMOC   = 
+OBJMOC = 
+DIST	   = qm-vamp-plugins.pro
+QMAKE_TARGET = qm-vamp-plugins
+DESTDIR  = 
+TARGET   = libqm-vamp-plugins.so.1.0.0
+TARGETA	= libqm-vamp-plugins.a
+TARGETD	= libqm-vamp-plugins.so.1.0.0
+TARGET0	= libqm-vamp-plugins.so
+TARGET1	= libqm-vamp-plugins.so.1
+TARGET2	= libqm-vamp-plugins.so.1.0
+
+first: all
+####### Implicit rules
+
+.SUFFIXES: .c .o .cpp .cc .cxx .C
+
+.cpp.o:
+	$(CXX) -c $(CXXFLAGS) $(INCPATH) -o $@ $<
+
+.cc.o:
+	$(CXX) -c $(CXXFLAGS) $(INCPATH) -o $@ $<
+
+.cxx.o:
+	$(CXX) -c $(CXXFLAGS) $(INCPATH) -o $@ $<
+
+.C.o:
+	$(CXX) -c $(CXXFLAGS) $(INCPATH) -o $@ $<
+
+.c.o:
+	$(CC) -c $(CFLAGS) $(INCPATH) -o $@ $<
+
+####### Build rules
+
+all: Makefile  $(TARGET)
+
+$(TARGET):  $(UICDECLS) $(OBJECTS) $(OBJMOC) $(SUBLIBS) $(OBJCOMP)  
+	-$(DEL_FILE) $(TARGET) $(TARGET0) $(TARGET1) $(TARGET2)
+	$(LINK) $(LFLAGS) -o $(TARGET) $(OBJECTS) $(OBJMOC) $(LIBS) $(OBJCOMP)
+	-ln -s $(TARGET) $(TARGET0)
+	-ln -s $(TARGET) $(TARGET1)
+	-ln -s $(TARGET) $(TARGET2)
+
+
+
+staticlib: $(TARGETA)
+
+$(TARGETA):  $(UICDECLS) $(OBJECTS) $(OBJMOC) $(OBJCOMP)
+	-$(DEL_FILE) $(TARGETA) 
+	$(AR) $(TARGETA) $(OBJECTS) $(OBJMOC)
+
+mocables: $(SRCMOC)
+uicables: $(UICDECLS) $(UICIMPLS)
+
+$(MOC): 
+	( cd $(QTDIR)/src/moc && $(MAKE) )
+
+Makefile: qm-vamp-plugins.pro  /usr/share/qt3/mkspecs/default/qmake.conf /usr/share/qt3/lib/libqt-mt.prl
+	$(QMAKE) -o Makefile qm-vamp-plugins.pro
+qmake: 
+	@$(QMAKE) -o Makefile qm-vamp-plugins.pro
+
+dist: 
+	@mkdir -p tmp_obj/qm-vamp-plugins && $(COPY_FILE) --parents $(SOURCES) $(HEADERS) $(FORMS) $(DIST) tmp_obj/qm-vamp-plugins/ && ( cd `dirname tmp_obj/qm-vamp-plugins` && $(TAR) qm-vamp-plugins.tar qm-vamp-plugins && $(GZIP) qm-vamp-plugins.tar ) && $(MOVE) `dirname tmp_obj/qm-vamp-plugins`/qm-vamp-plugins.tar.gz . && $(DEL_FILE) -r tmp_obj/qm-vamp-plugins
+
+mocclean:
+
+uiclean:
+
+yaccclean:
+lexclean:
+clean:
+	-$(DEL_FILE) $(OBJECTS)
+	-$(DEL_FILE) *~ core *.core
+
+
+####### Sub-libraries
+
+distclean: clean
+	-$(DEL_FILE) $(TARGET) $(TARGET)
+	-$(DEL_FILE) $(TARGET0) $(TARGET1) $(TARGET2) $(TARGETA)
+
+
+FORCE:
+
+####### Compile
+
+tmp_obj/BeatDetect.o: plugins/BeatDetect.cpp plugins/BeatDetect.h
+	$(CXX) -c $(CXXFLAGS) $(INCPATH) -o tmp_obj/BeatDetect.o plugins/BeatDetect.cpp
+
+tmp_obj/ChromagramPlugin.o: plugins/ChromagramPlugin.cpp plugins/ChromagramPlugin.h
+	$(CXX) -c $(CXXFLAGS) $(INCPATH) -o tmp_obj/ChromagramPlugin.o plugins/ChromagramPlugin.cpp
+
+tmp_obj/TonalChangeDetect.o: plugins/TonalChangeDetect.cpp plugins/TonalChangeDetect.h
+	$(CXX) -c $(CXXFLAGS) $(INCPATH) -o tmp_obj/TonalChangeDetect.o plugins/TonalChangeDetect.cpp
+
+####### Install
+
+install:  
+
+uninstall:  
+
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/plugins/BeatDetect.cpp	Wed Apr 05 17:34:40 2006 +0000
@@ -0,0 +1,296 @@
+/* -*- 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 "BeatDetect.h"
+
+#include "dsp/onsets/DetectionFunction.h"
+#include "dsp/tempotracking/TempoTrack.h"
+
+using std::string;
+using std::vector;
+using std::cerr;
+using std::endl;
+
+float BeatDetector::m_stepSecs = 0.01161;
+
+class BeatDetectorData
+{
+public:
+    BeatDetectorData(const DFConfig &config) : dfConfig(config) {
+	df = new DetectionFunction(config);
+    }
+    ~BeatDetectorData() {
+	delete df;
+    }
+    void reset() {
+	delete df;
+	df = new DetectionFunction(dfConfig);
+	dfOutput.clear();
+    }
+
+    DFConfig dfConfig;
+    DetectionFunction *df;
+    vector<double> dfOutput;
+};
+    
+
+BeatDetector::BeatDetector(float inputSampleRate) :
+    Vamp::Plugin(inputSampleRate),
+    m_d(0),
+    m_dfType(DF_COMPLEXSD)
+{
+}
+
+BeatDetector::~BeatDetector()
+{
+    delete m_d;
+}
+
+string
+BeatDetector::getName() const
+{
+    return "beats";
+}
+
+string
+BeatDetector::getDescription() const
+{
+    return "Beat Detector";
+}
+
+string
+BeatDetector::getMaker() const
+{
+    return "QMUL";
+}
+
+int
+BeatDetector::getPluginVersion() const
+{
+    return 1;
+}
+
+string
+BeatDetector::getCopyright() const
+{
+    return "Copyright (c) 2006 - All Rights Reserved";
+}
+
+BeatDetector::ParameterList
+BeatDetector::getParameterDescriptors() const
+{
+    ParameterList list;
+
+    ParameterDescriptor desc;
+    desc.name = "dftype";
+    desc.description = "Onset Detection Function Type";
+    desc.minValue = 0;
+    desc.maxValue = 3;
+    desc.defaultValue = 3;
+    desc.isQuantized = true;
+    desc.quantizeStep = 1;
+    desc.valueNames.push_back("High-Frequency Content");
+    desc.valueNames.push_back("Spectral Difference");
+    desc.valueNames.push_back("Phase Deviation");
+    desc.valueNames.push_back("Complex Domain");
+    list.push_back(desc);
+
+    return list;
+}
+
+float
+BeatDetector::getParameter(std::string name) const
+{
+    if (name == "dftype") {
+        switch (m_dfType) {
+        case DF_HFC: return 0;
+        case DF_SPECDIFF: return 1;
+        case DF_PHASEDEV: return 2;
+        default: case DF_COMPLEXSD: return 3;
+        }
+    }
+    return 0.0;
+}
+
+void
+BeatDetector::setParameter(std::string name, float value)
+{
+    if (name == "dftype") {
+        switch (lrintf(value)) {
+        case 0: m_dfType = DF_HFC; break;
+        case 1: m_dfType = DF_SPECDIFF; break;
+        case 2: m_dfType = DF_PHASEDEV; break;
+        default: case 3: m_dfType = DF_COMPLEXSD; break;
+        }
+    }
+}
+
+bool
+BeatDetector::initialise(size_t channels, size_t stepSize, size_t blockSize)
+{
+    if (m_d) {
+	delete m_d;
+	m_d = 0;
+    }
+
+    if (channels < getMinChannelCount() ||
+	channels > getMaxChannelCount()) return false;
+
+    DFConfig dfConfig;
+    dfConfig.DFType = m_dfType;
+    dfConfig.stepSecs = float(stepSize) / m_inputSampleRate;
+    dfConfig.stepSize = stepSize;
+    dfConfig.frameLength = blockSize;
+    
+    m_d = new BeatDetectorData(dfConfig);
+    return true;
+}
+
+void
+BeatDetector::reset()
+{
+    if (m_d) m_d->reset();
+}
+
+size_t
+BeatDetector::getPreferredStepSize() const
+{
+    return size_t(m_inputSampleRate * m_stepSecs + 0.0001);
+}
+
+size_t
+BeatDetector::getPreferredBlockSize() const
+{
+    return getPreferredStepSize() * 2;
+}
+
+BeatDetector::OutputList
+BeatDetector::getOutputDescriptors() const
+{
+    OutputList list;
+
+    OutputDescriptor beat;
+    beat.name = "beats";
+    beat.unit = "";
+    beat.description = "Detected Beats";
+    beat.hasFixedBinCount = true;
+    beat.binCount = 0;
+    beat.sampleType = OutputDescriptor::VariableSampleRate;
+    beat.sampleRate = 1.0 / m_stepSecs;
+
+    OutputDescriptor df;
+    df.name = "detection_fn";
+    df.unit = "";
+    df.description = "Beat Detection Function";
+    df.hasFixedBinCount = true;
+    df.binCount = 1;
+    df.hasKnownExtents = false;
+    df.isQuantized = false;
+    df.sampleType = OutputDescriptor::OneSamplePerStep;
+
+    list.push_back(beat);
+    list.push_back(df);
+
+    return list;
+}
+
+BeatDetector::FeatureSet
+BeatDetector::process(float **inputBuffers, Vamp::RealTime /* timestamp */)
+{
+    if (!m_d) {
+	cerr << "ERROR: BeatDetector::process: "
+	     << "BeatDetector has not been initialised"
+	     << endl;
+	return FeatureSet();
+    }
+
+    // convert float* to double*
+    double *tempBuffer = new double[m_d->dfConfig.frameLength];
+    for (size_t i = 0; i < m_d->dfConfig.frameLength; ++i) {
+	tempBuffer[i] = inputBuffers[0][i];
+    }
+
+    double output = m_d->df->process(tempBuffer);
+    delete[] tempBuffer;
+
+    m_d->dfOutput.push_back(output);
+
+    FeatureSet returnFeatures;
+
+    Feature feature;
+    feature.hasTimestamp = false;
+    feature.values.push_back(output);
+
+    returnFeatures[1].push_back(feature); // detection function is output 1
+    return returnFeatures;
+}
+
+BeatDetector::FeatureSet
+BeatDetector::getRemainingFeatures()
+{
+    if (!m_d) {
+	cerr << "ERROR: BeatDetector::getRemainingFeatures: "
+	     << "BeatDetector has not been initialised"
+	     << endl;
+	return FeatureSet();
+    }
+
+    double aCoeffs[] = { 1.0000, -0.5949, 0.2348 };
+    double bCoeffs[] = { 0.1600,  0.3200, 0.1600 };
+
+    TTParams ttParams;
+    ttParams.winLength = 512;
+    ttParams.lagLength = 128;
+    ttParams.LPOrd = 2;
+    ttParams.LPACoeffs = aCoeffs;
+    ttParams.LPBCoeffs = bCoeffs;
+    ttParams.alpha = 9;
+    ttParams.WinT.post = 8;
+    ttParams.WinT.pre = 7;
+
+    TempoTrack tempoTracker(ttParams);
+    vector<int> beats = tempoTracker.process(m_d->dfOutput);
+
+    FeatureSet returnFeatures;
+
+    for (size_t i = 0; i < beats.size(); ++i) {
+
+	size_t frame = beats[i] * m_d->dfConfig.stepSize;
+
+	Feature feature;
+	feature.hasTimestamp = true;
+	feature.timestamp = Vamp::RealTime::frame2RealTime
+	    (frame, lrintf(m_inputSampleRate));
+
+	float bpm = 0.0;
+	int frameIncrement = 0;
+
+	if (i < beats.size() - 1) {
+
+	    frameIncrement = (beats[i+1] - beats[i]) * m_d->dfConfig.stepSize;
+
+	    // one beat is frameIncrement frames, so there are
+	    // samplerate/frameIncrement bps, so
+	    // 60*samplerate/frameIncrement bpm
+
+	    if (frameIncrement > 0) {
+		bpm = (60.0 * m_inputSampleRate) / frameIncrement;
+		bpm = int(bpm * 100.0 + 0.5) / 100.0;
+                static char label[100];
+                sprintf(label, "%f bpm", bpm);
+                feature.label = label;
+	    }
+	}
+
+	returnFeatures[0].push_back(feature); // beats are output 0
+    }
+
+    return returnFeatures;
+}
+
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/plugins/BeatDetect.h	Wed Apr 05 17:34:40 2006 +0000
@@ -0,0 +1,56 @@
+/* -*- 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.
+*/
+
+//!!! This guard inadequate, must be unique to plugin if plugin is to
+//be compilable in with app as well as being, well, a plugin
+#ifndef _BEAT_DETECT_PLUGIN_H_
+#define _BEAT_DETECT_PLUGIN_H_
+
+#include "vamp-sdk/Plugin.h"
+
+class BeatDetectorData;
+
+class BeatDetector : public Vamp::Plugin
+{
+public:
+    BeatDetector(float inputSampleRate);
+    virtual ~BeatDetector();
+
+    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:
+    BeatDetectorData *m_d;
+    int m_dfType;
+    static float m_stepSecs;
+};
+
+
+#endif
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/plugins/ChromagramPlugin.cpp	Wed Apr 05 17:34:40 2006 +0000
@@ -0,0 +1,376 @@
+/* -*- 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 "ChromagramPlugin.h"
+
+#include "base/Pitch.h"
+#include "dsp/chromagram/Chromagram.h"
+
+using std::string;
+using std::vector;
+using std::cerr;
+using std::endl;
+
+ChromagramPlugin::ChromagramPlugin(float inputSampleRate) :
+    Vamp::Plugin(inputSampleRate),
+    m_chromagram(0),
+    m_step(0),
+    m_block(0),
+    m_stepDelay(0)
+{
+    m_minMIDIPitch = 12;
+    m_maxMIDIPitch = 96;
+    m_tuningFrequency = 440;
+    m_normalized = true;
+    m_bpo = 12;
+
+    setupConfig();
+}
+
+void
+ChromagramPlugin::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_config.isNormalised = m_normalized;
+
+    m_step = 0;
+    m_block = 0;
+}
+
+ChromagramPlugin::~ChromagramPlugin()
+{
+    delete m_chromagram;
+}
+
+string
+ChromagramPlugin::getName() const
+{
+    return "chromagram";
+}
+
+string
+ChromagramPlugin::getDescription() const
+{
+    return "Chromagram";
+}
+
+string
+ChromagramPlugin::getMaker() const
+{
+    return "QMUL";
+}
+
+int
+ChromagramPlugin::getPluginVersion() const
+{
+    return 2;
+}
+
+string
+ChromagramPlugin::getCopyright() const
+{
+    return "Copyright (c) 2006 - All Rights Reserved";
+}
+
+ChromagramPlugin::ParameterList
+ChromagramPlugin::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 = 12;
+    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 = 96;
+    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
+ChromagramPlugin::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: ChromagramPlugin::getParameter: unknown parameter \""
+              << param << "\"" << std::endl;
+    return 0.0;
+}
+
+void
+ChromagramPlugin::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: ChromagramPlugin::setParameter: unknown parameter \""
+                  << param << "\"" << std::endl;
+    }
+
+    setupConfig();
+}
+
+
+bool
+ChromagramPlugin::initialise(size_t channels, size_t stepSize, size_t blockSize)
+{
+    if (m_chromagram) {
+	delete m_chromagram;
+	m_chromagram = 0;
+    }
+
+    if (channels < getMinChannelCount() ||
+	channels > getMaxChannelCount()) return false;
+
+    if (stepSize != m_step) return false;
+    if (blockSize != m_block) return false;
+
+//    m_stepDelay = (blockSize - stepSize) / 2;
+//    m_stepDelay = m_stepDelay / stepSize;
+    m_stepDelay = (blockSize - stepSize) / stepSize; //!!! why? seems about right to look at, but...
+
+    std::cerr << "ChromagramPlugin::initialise: step " << stepSize << ", block "
+	      << blockSize << ", delay " << m_stepDelay << std::endl;
+
+    m_chromagram = new Chromagram(m_config);
+    return true;
+}
+
+void
+ChromagramPlugin::reset()
+{
+    if (m_chromagram) {
+	delete m_chromagram;
+	m_chromagram = new Chromagram(m_config);
+    }
+    while (!m_pending.empty()) m_pending.pop();
+}
+
+size_t
+ChromagramPlugin::getPreferredStepSize() const
+{
+    if (!m_step) {
+	Chromagram chroma(m_config);
+	m_step = chroma.getHopSize();
+	m_block = chroma.getFrameSize();
+    }
+
+    return m_step;
+}
+
+size_t
+ChromagramPlugin::getPreferredBlockSize() const
+{
+    if (!m_block) {
+	Chromagram chroma(m_config);
+	m_step = chroma.getHopSize();
+	m_block = chroma.getFrameSize();
+    }
+
+    return m_block;
+}
+
+ChromagramPlugin::OutputList
+ChromagramPlugin::getOutputDescriptors() const
+{
+    OutputList list;
+
+    OutputDescriptor d;
+//    d.name = "unnormalized";
+    d.name = "chromagram";
+    d.unit = "";
+    d.description = "Chromagram";
+    d.hasFixedBinCount = true;
+    d.binCount = m_config.BPO;
+    
+    const char *names[] =
+	{ "C", "C#", "D", "D#", "E", "F", "F#", "G", "G#", "A", "A#", "B" };
+
+    if (d.binCount % 12 == 0) {
+        for (int i = 0; i < 12; ++i) {
+            int ipc = m_minMIDIPitch % 12;
+            int index = (i + ipc) % 12;
+            d.binNames.push_back(names[index]);
+            for (int j = 0; j < d.binCount / 12 - 1; ++j) {
+                d.binNames.push_back("");
+            }
+        }
+    } else {
+        d.binNames.push_back("C");
+    }
+
+    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);
+/*
+    d.name = "normalized";
+    d.description = "Normalized Chromagram";
+    d.hasKnownExtents = true;
+    d.maxValue = 1.0;
+    list.push_back(d);
+*/
+    return list;
+}
+
+ChromagramPlugin::Feature
+ChromagramPlugin::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;
+}
+
+ChromagramPlugin::FeatureSet
+ChromagramPlugin::process(float **inputBuffers, Vamp::RealTime /* timestamp */)
+{
+    if (!m_chromagram) {
+	cerr << "ERROR: ChromagramPlugin::process: "
+	     << "Chromagram has not been initialised"
+	     << endl;
+	return FeatureSet();
+    }
+
+    // convert float* to double*
+    double *tempBuffer = new double[m_block];
+    for (size_t i = 0; i < m_block; ++i) {
+	tempBuffer[i] = inputBuffers[0][i];
+    }
+
+    double *output = m_chromagram->process(tempBuffer);
+    delete[] tempBuffer;
+    
+    Feature feature;
+    feature.hasTimestamp = false;
+    for (size_t i = 0; i < m_config.BPO; ++i) {
+	feature.values.push_back(output[i]);
+    }
+    feature.label = "";
+
+    FeatureSet returnFeatures;
+
+    if (m_stepDelay == 0) {
+	returnFeatures[0].push_back(feature);
+//	returnFeatures[1].push_back(normalize(feature));
+	return returnFeatures;
+    }
+	
+    if (m_pending.size() == m_stepDelay) {
+	returnFeatures[0].push_back(m_pending.front());
+//	returnFeatures[1].push_back(normalize(m_pending.front()));
+	m_pending.pop();
+    } else {
+	returnFeatures[0].push_back(Feature());
+//	returnFeatures[1].push_back(Feature());
+    }
+
+    m_pending.push(feature);
+    return returnFeatures;
+}
+
+ChromagramPlugin::FeatureSet
+ChromagramPlugin::getRemainingFeatures()
+{
+    FeatureSet returnFeatures;
+
+    while (!m_pending.empty()) {
+	returnFeatures[0].push_back(m_pending.front());
+//	returnFeatures[1].push_back(normalize(m_pending.front()));
+	m_pending.pop();
+    }
+	
+    return returnFeatures;
+}
+
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/plugins/ChromagramPlugin.h	Wed Apr 05 17:34:40 2006 +0000
@@ -0,0 +1,70 @@
+/* -*- 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.
+*/
+
+//!!! This guard inadequate, must be unique to plugin if plugin is to
+//be compilable in with app as well as being, well, a plugin
+#ifndef _CHROMAGRAM_PLUGIN_H_
+#define _CHROMAGRAM_PLUGIN_H_
+
+#include "vamp-sdk/Plugin.h"
+#include "dsp/chromagram/Chromagram.h"
+
+#include <queue>
+
+class ChromagramPlugin : public Vamp::Plugin
+{
+public:
+    ChromagramPlugin(float inputSampleRate);
+    virtual ~ChromagramPlugin();
+
+    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:
+    int m_minMIDIPitch;
+    int m_maxMIDIPitch;
+    float m_tuningFrequency;
+    bool m_normalized;
+    int m_bpo;
+
+    void setupConfig();
+
+    ChromaConfig m_config;
+    Chromagram *m_chromagram;
+    mutable size_t m_step;
+    mutable size_t m_block;
+    size_t m_stepDelay;
+    std::queue<Feature> m_pending;
+
+    Feature normalize(const Feature &);
+};
+
+
+#endif
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/plugins/TonalChangeDetect.cpp	Wed Apr 05 17:34:40 2006 +0000
@@ -0,0 +1,408 @@
+/* -*- 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 "TonalChangeDetect.h"
+
+#include "base/Pitch.h"
+#include "dsp/chromagram/Chromagram.h"
+#include "dsp/tonal/ChangeDetectionFunction.h"
+
+TonalChangeDetect::TonalChangeDetect(float fInputSampleRate)	
+    : Vamp::Plugin(fInputSampleRate),
+      m_chromagram(0),
+      m_step(0),
+      m_block(0),
+      m_stepDelay(0)
+{
+    m_minMIDIPitch = 32;
+    m_maxMIDIPitch = 108;
+    m_tuningFrequency = 440;	
+    m_iSmoothingWidth = 5;
+
+    setupConfig();
+}
+
+TonalChangeDetect::~TonalChangeDetect()
+{
+}
+
+bool TonalChangeDetect::initialise(size_t channels, size_t stepSize, size_t blockSize)
+{
+    if (m_chromagram) {
+        delete m_chromagram;
+        m_chromagram = 0;
+    }
+	
+    if (channels < getMinChannelCount() ||
+        channels > getMaxChannelCount()) {
+        std::cerr << "TonalChangeDetect::initialise: Given channel count " << channels << " outside acceptable range (" << getMinChannelCount() << " to " << getMaxChannelCount() << ")" << std::endl;
+        return false;
+    }
+	
+    if (stepSize != m_step) {
+        std::cerr << "TonalChangeDetect::initialise: Given step size " << stepSize << " differs from only acceptable value " << m_step << std::endl;
+        return false;
+    }
+    if (blockSize != m_block) {
+        std::cerr << "TonalChangeDetect::initialise: Given step size " << stepSize << " differs from only acceptable value " << m_step << std::endl;
+        return false;
+    }
+	
+    //    m_stepDelay = (blockSize - stepSize) / 2;
+    //    m_stepDelay = m_stepDelay / stepSize;
+    m_stepDelay = (blockSize - stepSize) / stepSize; //!!! why? seems about right to look at, but...
+	
+    std::cerr << "TonalChangeDetect::initialise: step " << stepSize << ", block "
+              << blockSize << ", delay " << m_stepDelay << std::endl;
+	
+    m_chromagram = new Chromagram(m_config);
+	
+    m_vaCurrentVector.resize(12, 0.0);
+	
+    return true;
+	
+}
+
+std::string TonalChangeDetect::getName() const
+{
+    return "TonalChange";
+}
+
+std::string TonalChangeDetect::getDescription() const
+{
+    return "TonalChange";
+}
+
+std::string TonalChangeDetect::getMaker() const
+{
+    return "Martin Gasser";
+}
+
+int TonalChangeDetect::getPluginVersion() const
+{
+    return 1;
+}
+
+std::string TonalChangeDetect::getCopyright() const
+{
+    return "Copyright (c) 2006 - All Rights Reserved";
+}
+
+TonalChangeDetect::ParameterList TonalChangeDetect::getParameterDescriptors() const
+{
+    ParameterList list;
+
+    ParameterDescriptor desc;
+    desc.name = "smoothingwidth";
+    desc.description = "Gaussian smoothing";
+    desc.unit = "frames";
+    desc.minValue = 0;
+    desc.maxValue = 20;
+    desc.defaultValue = 5;
+    desc.isQuantized = true;
+    desc.quantizeStep = 1;
+    list.push_back(desc);
+
+    desc;
+    desc.name = "minpitch";
+    desc.description = "Chromagram minimum pitch";
+    desc.unit = "MIDI units";
+    desc.minValue = 0;
+    desc.maxValue = 127;
+    desc.defaultValue = 32;
+    desc.isQuantized = true;
+    desc.quantizeStep = 1;
+    list.push_back(desc);
+
+    desc.name = "maxpitch";
+    desc.description = "Chromagram maximum pitch";
+    desc.unit = "MIDI units";
+    desc.minValue = 0;
+    desc.maxValue = 127;
+    desc.defaultValue = 108;
+    desc.isQuantized = true;
+    desc.quantizeStep = 1;
+    list.push_back(desc);
+
+    desc.name = "tuning";
+    desc.description = "Chromagram tuning frequency";
+    desc.unit = "Hz";
+    desc.minValue = 420;
+    desc.maxValue = 460;
+    desc.defaultValue = 440;
+    desc.isQuantized = false;
+    list.push_back(desc);
+
+    return list;
+}
+
+float
+TonalChangeDetect::getParameter(std::string param) const
+{
+    if (param == "smoothingwidth") {
+        return m_iSmoothingWidth;
+    }
+    if (param == "minpitch") {
+        return m_minMIDIPitch;
+    }
+    if (param == "maxpitch") {
+        return m_maxMIDIPitch;
+    }
+    if (param == "tuning") {
+        return m_tuningFrequency;
+    }
+
+    std::cerr << "WARNING: ChromagramPlugin::getParameter: unknown parameter \""
+              << param << "\"" << std::endl;
+    return 0.0;
+}
+
+void
+TonalChangeDetect::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 == "smoothingwidth") {
+        m_iSmoothingWidth = int(value);
+    } else {
+        std::cerr << "WARNING: ChromagramPlugin::setParameter: unknown parameter \""
+                  << param << "\"" << std::endl;
+    }
+
+    setupConfig();
+}
+
+
+void TonalChangeDetect::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 = 12;
+    m_config.CQThresh = 0.0054;
+    m_config.isNormalised = false;
+
+    m_step = 0;
+    m_block = 0;
+	
+	
+}
+
+void
+TonalChangeDetect::reset()
+{
+    if (m_chromagram) {
+        delete m_chromagram;
+        m_chromagram = new Chromagram(m_config);
+    }
+    while (!m_pending.empty()) m_pending.pop();
+	
+    m_vaCurrentVector.resize(12, 0.0);
+}
+
+size_t
+TonalChangeDetect::getPreferredStepSize() const
+{
+    if (!m_step) {
+	Chromagram chroma(m_config);
+	m_step = chroma.getHopSize();
+	m_block = chroma.getFrameSize();
+    }
+
+    return m_step;
+}
+
+size_t
+TonalChangeDetect::getPreferredBlockSize() const
+{
+    if (!m_step) {
+	Chromagram chroma(m_config);
+	m_step = chroma.getHopSize();
+	m_block = chroma.getFrameSize();
+    }
+
+    return m_block;
+}
+
+TonalChangeDetect::OutputList TonalChangeDetect::getOutputDescriptors() const
+{
+    OutputList list;
+	 
+    OutputDescriptor hc;
+    hc.name = "tcstransform";
+    hc.unit = "";
+    hc.description = "Transform to 6D Tonal Content Space";
+    hc.hasFixedBinCount = true;
+    hc.binCount = 6;
+    hc.hasKnownExtents = true;
+    hc.minValue = -1.0;
+    hc.maxValue = 1.0;
+    hc.isQuantized = false;
+    hc.sampleType = OutputDescriptor::OneSamplePerStep;
+
+    OutputDescriptor d;
+    d.name = "tcfunction";
+    d.unit = "";
+    d.minValue = 0;
+    d.minValue = 2;
+    d.description = "Tonal Change Detection Function";
+    d.hasFixedBinCount = true;
+    d.binCount = 1;
+    d.hasKnownExtents = true;
+    d.isQuantized = false;
+    d.sampleType = OutputDescriptor::VariableSampleRate;
+    double dStepSecs = double(m_step) / m_inputSampleRate;
+    d.sampleRate = 1.0f / dStepSecs;
+	
+    OutputDescriptor changes;
+    changes.name = "changepositions";
+    changes.unit = "";
+    changes.description = "Tonal Change Positions";
+    changes.hasFixedBinCount = true;
+    changes.binCount = 0;
+    changes.sampleType = OutputDescriptor::VariableSampleRate;
+    changes.sampleRate = 1.0 / dStepSecs;
+	
+    list.push_back(hc);
+    list.push_back(d);
+    list.push_back(changes);
+
+    return list;
+}
+
+TonalChangeDetect::FeatureSet TonalChangeDetect::process(float **inputBuffers, Vamp::RealTime timestamp)
+{
+    if (!m_chromagram) {
+	cerr << "ERROR: TonalChangeDetect::process: "
+	     << "Chromagram has not been initialised"
+	     << endl;
+	return FeatureSet();
+    }
+
+    // convert float* to double*
+    double *tempBuffer = new double[m_block];
+    for (size_t i = 0; i < m_block; ++i) {
+        tempBuffer[i] = inputBuffers[0][i];
+    }
+
+    double *output = m_chromagram->process(tempBuffer);
+    delete[] tempBuffer;
+
+    for (size_t i = 0; i < 12; i++)
+    {
+        m_vaCurrentVector[i] = output[i];
+    }
+	
+	
+    FeatureSet returnFeatures;
+
+    if (m_stepDelay == 0) {
+        m_vaCurrentVector.normalizeL1();
+        TCSVector tcsVector = m_TonalEstimator.transform2TCS(m_vaCurrentVector);
+        m_TCSGram.addTCSVector(tcsVector);
+	
+        Feature feature;
+        feature.hasTimestamp = false;
+        for (int i = 0; i < 6; i++)
+        { feature.values.push_back(static_cast<float>(tcsVector[i])); }
+        feature.label = "";
+        returnFeatures[0].push_back(feature);
+
+        return returnFeatures;
+    }
+	
+    if (m_pending.size() == m_stepDelay) {
+	
+        ChromaVector v = m_pending.front();
+        v.normalizeL1();
+        TCSVector tcsVector = m_TonalEstimator.transform2TCS(v);
+        m_TCSGram.addTCSVector(tcsVector);
+	
+        Feature feature;
+        feature.hasTimestamp = false;
+        for (int i = 0; i < 6; i++)
+        { feature.values.push_back(static_cast<float>(tcsVector[i])); }
+        feature.label = "";
+        returnFeatures[0].push_back(feature);
+        m_pending.pop();
+		
+    } else {
+        returnFeatures[0].push_back(Feature());
+        m_TCSGram.addTCSVector(TCSVector());
+    }
+
+    m_pending.push(m_vaCurrentVector);
+	
+
+    return returnFeatures;
+}
+
+TonalChangeDetect::FeatureSet TonalChangeDetect::getRemainingFeatures()
+{
+    FeatureSet returnFeatures;
+
+    while (!m_pending.empty()) {
+        ChromaVector v = m_pending.front();
+        v.normalizeL1();
+        TCSVector tcsVector = m_TonalEstimator.transform2TCS(v);
+        m_TCSGram.addTCSVector(tcsVector);
+	
+        Feature feature;
+        feature.hasTimestamp = false;
+        for (int i = 0; i < 6; i++)
+        { feature.values.push_back(static_cast<float>(tcsVector[i])); }
+        feature.label = "";
+        returnFeatures[0].push_back(feature);
+        m_pending.pop();
+    }
+	
+    ChangeDFConfig dfc;
+    dfc.smoothingWidth = double(m_iSmoothingWidth);
+    ChangeDetectionFunction df(dfc);
+    ChangeDistance d = df.process(m_TCSGram);
+	
+	
+	
+    for (int i = 0; i < d.size(); i++)
+    {
+        double dCurrent = d[i];
+        double dPrevious = d[i > 0 ? i - 1 : i];
+        double dNext = d[i < d.size()-1 ? i + 1 : i];
+		
+        Feature feature;
+        feature.label = "";
+        feature.hasTimestamp = true;
+        feature.timestamp = Vamp::RealTime::frame2RealTime(i*m_step, m_inputSampleRate);
+        feature.values.push_back(dCurrent);
+        returnFeatures[1].push_back(feature);
+
+
+        if (dCurrent > dPrevious && dCurrent > dNext)
+        {
+            Feature featurePeak;
+            featurePeak.label = "";
+            featurePeak.hasTimestamp = true;
+            featurePeak.timestamp = Vamp::RealTime::frame2RealTime(i*m_step, m_inputSampleRate);
+            returnFeatures[2].push_back(feature);
+        }
+
+    }
+
+	
+    return returnFeatures;
+	
+}
+
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/plugins/TonalChangeDetect.h	Wed Apr 05 17:34:40 2006 +0000
@@ -0,0 +1,77 @@
+/* -*- 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 _TONALCHANGEDETECT_
+#define _TONALCHANGEDETECT_
+
+#include "dsp/chromagram/Chromagram.h"
+#include "dsp/tonal/TonalEstimator.h"
+#include "dsp/tonal/TCSgram.h"
+
+#include "vamp-sdk/Plugin.h"
+
+#include <queue>
+#include <vector>
+#include <valarray>
+
+class TonalChangeDetect : public Vamp::Plugin
+{
+public:
+	TonalChangeDetect(float fInputSampleRate);
+	virtual ~TonalChangeDetect();
+
+    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();
+	
+private:
+	void setupConfig();
+
+    ChromaConfig m_config;
+    Chromagram *m_chromagram;
+	TonalEstimator m_TonalEstimator;
+    mutable size_t m_step;
+    mutable size_t m_block;
+    size_t m_stepDelay;
+    std::queue<ChromaVector> m_pending;
+	ChromaVector m_vaCurrentVector;
+	TCSGram m_TCSGram;
+	
+private:
+	int m_iSmoothingWidth;  // smoothing window size
+	
+    int m_minMIDIPitch;     // chromagram parameters
+    int m_maxMIDIPitch;
+    float m_tuningFrequency;
+	
+};
+
+
+#endif // _TONALCHANGEDETECT_
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/qm-vamp-plugins.pro	Wed Apr 05 17:34:40 2006 +0000
@@ -0,0 +1,17 @@
+TEMPLATE = lib
+CONFIG += release warn_on dll
+OBJECTS_DIR = tmp_obj
+MOC_DIR = tmp_moc
+
+INCLUDEPATH += ../../sonic-visualiser/plugin/vamp-plugin-sdk ../../qm-dsp/trunk
+
+DEPENDPATH += plugins
+INCLUDEPATH += . plugins
+
+# Input
+HEADERS += plugins/BeatDetect.h \
+           plugins/ChromagramPlugin.h \
+           plugins/TonalChangeDetect.h
+SOURCES += plugins/BeatDetect.cpp \
+           plugins/ChromagramPlugin.cpp \
+           plugins/TonalChangeDetect.cpp