Mercurial > hg > qm-vamp-plugins
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