mirror of
https://github.com/Ardour/ardour.git
synced 2025-12-07 15:25:01 +01:00
update/include Queen Mary Vamp plugin set
This commit is contained in:
parent
72060df884
commit
ee2a1b7bea
16 changed files with 6010 additions and 20 deletions
407
libs/vamp-plugins/KeyDetect.cpp
Normal file
407
libs/vamp-plugins/KeyDetect.cpp
Normal file
|
|
@ -0,0 +1,407 @@
|
|||
/* -*- 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.
|
||||
|
||||
This program is free software; you can redistribute it and/or
|
||||
modify it under the terms of the GNU General Public License as
|
||||
published by the Free Software Foundation; either version 2 of the
|
||||
License, or (at your option) any later version. See the file
|
||||
COPYING included with this distribution for more information.
|
||||
*/
|
||||
|
||||
#include "KeyDetect.h"
|
||||
|
||||
using std::string;
|
||||
using std::vector;
|
||||
//using std::cerr;
|
||||
using std::endl;
|
||||
|
||||
#include <cmath>
|
||||
|
||||
|
||||
// Order for circle-of-5ths plotting
|
||||
static int conversion[24] =
|
||||
{ 7, 12, 5, 10, 3, 8, 1, 6, 11, 4, 9, 2,
|
||||
16, 21, 14, 19, 24, 17, 22, 15, 20, 13, 18, 23 };
|
||||
|
||||
|
||||
KeyDetector::KeyDetector(float inputSampleRate) :
|
||||
Plugin(inputSampleRate),
|
||||
m_stepSize(0),
|
||||
m_blockSize(0),
|
||||
m_tuningFrequency(440),
|
||||
m_length(10),
|
||||
m_getKeyMode(0),
|
||||
m_inputFrame(0),
|
||||
m_prevKey(-1)
|
||||
{
|
||||
}
|
||||
|
||||
KeyDetector::~KeyDetector()
|
||||
{
|
||||
delete m_getKeyMode;
|
||||
if ( m_inputFrame ) {
|
||||
delete [] m_inputFrame;
|
||||
}
|
||||
}
|
||||
|
||||
string
|
||||
KeyDetector::getIdentifier() const
|
||||
{
|
||||
return "qm-keydetector";
|
||||
}
|
||||
|
||||
string
|
||||
KeyDetector::getName() const
|
||||
{
|
||||
return "Key Detector";
|
||||
}
|
||||
|
||||
string
|
||||
KeyDetector::getDescription() const
|
||||
{
|
||||
return "Estimate the key of the music";
|
||||
}
|
||||
|
||||
string
|
||||
KeyDetector::getMaker() const
|
||||
{
|
||||
return "Queen Mary, University of London";
|
||||
}
|
||||
|
||||
int
|
||||
KeyDetector::getPluginVersion() const
|
||||
{
|
||||
return 4;
|
||||
}
|
||||
|
||||
string
|
||||
KeyDetector::getCopyright() const
|
||||
{
|
||||
return "Plugin by Katy Noland and Christian Landone. Copyright (c) 2006-2009 QMUL - All Rights Reserved";
|
||||
}
|
||||
|
||||
KeyDetector::ParameterList
|
||||
KeyDetector::getParameterDescriptors() const
|
||||
{
|
||||
ParameterList list;
|
||||
|
||||
ParameterDescriptor desc;
|
||||
desc.identifier = "tuning";
|
||||
desc.name = "Tuning Frequency";
|
||||
desc.description = "Frequency of concert A";
|
||||
desc.unit = "Hz";
|
||||
desc.minValue = 420;
|
||||
desc.maxValue = 460;
|
||||
desc.defaultValue = 440;
|
||||
desc.isQuantized = false;
|
||||
list.push_back(desc);
|
||||
|
||||
desc.identifier = "length";
|
||||
desc.name = "Window Length";
|
||||
desc.unit = "chroma frames";
|
||||
desc.description = "Number of chroma analysis frames per key estimation";
|
||||
desc.minValue = 1;
|
||||
desc.maxValue = 30;
|
||||
desc.defaultValue = 10;
|
||||
desc.isQuantized = true;
|
||||
desc.quantizeStep = 1;
|
||||
list.push_back(desc);
|
||||
|
||||
return list;
|
||||
}
|
||||
|
||||
float
|
||||
KeyDetector::getParameter(std::string param) const
|
||||
{
|
||||
if (param == "tuning") {
|
||||
return m_tuningFrequency;
|
||||
}
|
||||
if (param == "length") {
|
||||
return m_length;
|
||||
}
|
||||
std::cerr << "WARNING: KeyDetector::getParameter: unknown parameter \""
|
||||
<< param << "\"" << std::endl;
|
||||
return 0.0;
|
||||
}
|
||||
|
||||
void
|
||||
KeyDetector::setParameter(std::string param, float value)
|
||||
{
|
||||
if (param == "tuning") {
|
||||
m_tuningFrequency = value;
|
||||
} else if (param == "length") {
|
||||
m_length = int(value + 0.1);
|
||||
} else {
|
||||
std::cerr << "WARNING: KeyDetector::setParameter: unknown parameter \""
|
||||
<< param << "\"" << std::endl;
|
||||
}
|
||||
}
|
||||
|
||||
bool
|
||||
KeyDetector::initialise(size_t channels, size_t stepSize, size_t blockSize)
|
||||
{
|
||||
if (m_getKeyMode) {
|
||||
delete m_getKeyMode;
|
||||
m_getKeyMode = 0;
|
||||
}
|
||||
|
||||
if (channels < getMinChannelCount() ||
|
||||
channels > getMaxChannelCount()) return false;
|
||||
|
||||
m_getKeyMode = new GetKeyMode(int(m_inputSampleRate + 0.1),
|
||||
m_tuningFrequency,
|
||||
m_length, m_length);
|
||||
|
||||
m_stepSize = m_getKeyMode->getHopSize();
|
||||
m_blockSize = m_getKeyMode->getBlockSize();
|
||||
|
||||
if (stepSize != m_stepSize || blockSize != m_blockSize) {
|
||||
std::cerr << "KeyDetector::initialise: ERROR: step/block sizes "
|
||||
<< stepSize << "/" << blockSize << " differ from required "
|
||||
<< m_stepSize << "/" << m_blockSize << std::endl;
|
||||
delete m_getKeyMode;
|
||||
m_getKeyMode = 0;
|
||||
return false;
|
||||
}
|
||||
|
||||
m_inputFrame = new double[m_blockSize];
|
||||
|
||||
m_prevKey = -1;
|
||||
m_first = true;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
void
|
||||
KeyDetector::reset()
|
||||
{
|
||||
if (m_getKeyMode) {
|
||||
delete m_getKeyMode;
|
||||
m_getKeyMode = new GetKeyMode(int(m_inputSampleRate + 0.1),
|
||||
m_tuningFrequency,
|
||||
m_length, m_length);
|
||||
}
|
||||
|
||||
if (m_inputFrame) {
|
||||
for( unsigned int i = 0; i < m_blockSize; i++ ) {
|
||||
m_inputFrame[ i ] = 0.0;
|
||||
}
|
||||
}
|
||||
|
||||
m_prevKey = -1;
|
||||
m_first = true;
|
||||
}
|
||||
|
||||
|
||||
KeyDetector::OutputList
|
||||
KeyDetector::getOutputDescriptors() const
|
||||
{
|
||||
OutputList list;
|
||||
|
||||
float osr = 0.0f;
|
||||
if (m_stepSize == 0) (void)getPreferredStepSize();
|
||||
osr = m_inputSampleRate / m_stepSize;
|
||||
|
||||
OutputDescriptor d;
|
||||
d.identifier = "tonic";
|
||||
d.name = "Tonic Pitch";
|
||||
d.unit = "";
|
||||
d.description = "Tonic of the estimated key (from C = 1 to B = 12)";
|
||||
d.hasFixedBinCount = true;
|
||||
d.binCount = 1;
|
||||
d.hasKnownExtents = true;
|
||||
d.isQuantized = true;
|
||||
d.minValue = 1;
|
||||
d.maxValue = 12;
|
||||
d.quantizeStep = 1;
|
||||
d.sampleRate = osr;
|
||||
d.sampleType = OutputDescriptor::VariableSampleRate;
|
||||
list.push_back(d);
|
||||
|
||||
d.identifier = "mode";
|
||||
d.name = "Key Mode";
|
||||
d.unit = "";
|
||||
d.description = "Major or minor mode of the estimated key (major = 0, minor = 1)";
|
||||
d.hasFixedBinCount = true;
|
||||
d.binCount = 1;
|
||||
d.hasKnownExtents = true;
|
||||
d.isQuantized = true;
|
||||
d.minValue = 0;
|
||||
d.maxValue = 1;
|
||||
d.quantizeStep = 1;
|
||||
d.sampleRate = osr;
|
||||
d.sampleType = OutputDescriptor::VariableSampleRate;
|
||||
list.push_back(d);
|
||||
|
||||
d.identifier = "key";
|
||||
d.name = "Key";
|
||||
d.unit = "";
|
||||
d.description = "Estimated key (from C major = 1 to B major = 12 and C minor = 13 to B minor = 24)";
|
||||
d.hasFixedBinCount = true;
|
||||
d.binCount = 1;
|
||||
d.hasKnownExtents = true;
|
||||
d.isQuantized = true;
|
||||
d.minValue = 1;
|
||||
d.maxValue = 24;
|
||||
d.quantizeStep = 1;
|
||||
d.sampleRate = osr;
|
||||
d.sampleType = OutputDescriptor::VariableSampleRate;
|
||||
list.push_back(d);
|
||||
|
||||
d.identifier = "keystrength";
|
||||
d.name = "Key Strength Plot";
|
||||
d.unit = "";
|
||||
d.description = "Correlation of the chroma vector with stored key profile for each major and minor key";
|
||||
d.hasFixedBinCount = true;
|
||||
d.binCount = 25;
|
||||
d.hasKnownExtents = false;
|
||||
d.isQuantized = false;
|
||||
d.sampleType = OutputDescriptor::OneSamplePerStep;
|
||||
for (int i = 0; i < 24; ++i) {
|
||||
if (i == 12) d.binNames.push_back(" ");
|
||||
int idx = conversion[i];
|
||||
std::string label = getKeyName(idx > 12 ? idx-12 : idx,
|
||||
i >= 12,
|
||||
true);
|
||||
d.binNames.push_back(label);
|
||||
}
|
||||
list.push_back(d);
|
||||
|
||||
return list;
|
||||
}
|
||||
|
||||
KeyDetector::FeatureSet
|
||||
KeyDetector::process(const float *const *inputBuffers,
|
||||
Vamp::RealTime now)
|
||||
{
|
||||
if (m_stepSize == 0) {
|
||||
return FeatureSet();
|
||||
}
|
||||
|
||||
FeatureSet returnFeatures;
|
||||
|
||||
for ( unsigned int i = 0 ; i < m_blockSize; i++ ) {
|
||||
m_inputFrame[i] = (double)inputBuffers[0][i];
|
||||
}
|
||||
|
||||
// int key = (m_getKeyMode->process(m_inputFrame) % 24);
|
||||
int key = m_getKeyMode->process(m_inputFrame);
|
||||
bool minor = m_getKeyMode->isModeMinor(key);
|
||||
int tonic = key;
|
||||
if (tonic > 12) tonic -= 12;
|
||||
|
||||
int prevTonic = m_prevKey;
|
||||
if (prevTonic > 12) prevTonic -= 12;
|
||||
|
||||
if (m_first || (tonic != prevTonic)) {
|
||||
Feature feature;
|
||||
feature.hasTimestamp = true;
|
||||
feature.timestamp = now;
|
||||
// feature.timestamp = now;
|
||||
feature.values.push_back((float)tonic);
|
||||
feature.label = getKeyName(tonic, minor, false);
|
||||
returnFeatures[0].push_back(feature); // tonic
|
||||
}
|
||||
|
||||
if (m_first || (minor != (m_getKeyMode->isModeMinor(m_prevKey)))) {
|
||||
Feature feature;
|
||||
feature.hasTimestamp = true;
|
||||
feature.timestamp = now;
|
||||
feature.values.push_back(minor ? 1.f : 0.f);
|
||||
feature.label = (minor ? "Minor" : "Major");
|
||||
returnFeatures[1].push_back(feature); // mode
|
||||
}
|
||||
|
||||
if (m_first || (key != m_prevKey)) {
|
||||
Feature feature;
|
||||
feature.hasTimestamp = true;
|
||||
feature.timestamp = now;
|
||||
feature.values.push_back((float)key);
|
||||
feature.label = getKeyName(tonic, minor, true);
|
||||
returnFeatures[2].push_back(feature); // key
|
||||
}
|
||||
|
||||
m_prevKey = key;
|
||||
m_first = false;
|
||||
|
||||
Feature ksf;
|
||||
ksf.values.reserve(25);
|
||||
double *keystrengths = m_getKeyMode->getKeyStrengths();
|
||||
for (int i = 0; i < 24; ++i) {
|
||||
if (i == 12) ksf.values.push_back(-1);
|
||||
ksf.values.push_back(keystrengths[conversion[i]-1]);
|
||||
}
|
||||
ksf.hasTimestamp = false;
|
||||
returnFeatures[3].push_back(ksf);
|
||||
|
||||
return returnFeatures;
|
||||
}
|
||||
|
||||
KeyDetector::FeatureSet
|
||||
KeyDetector::getRemainingFeatures()
|
||||
{
|
||||
return FeatureSet();
|
||||
}
|
||||
|
||||
|
||||
size_t
|
||||
KeyDetector::getPreferredStepSize() const
|
||||
{
|
||||
if (!m_stepSize) {
|
||||
GetKeyMode gkm(int(m_inputSampleRate + 0.1),
|
||||
m_tuningFrequency, m_length, m_length);
|
||||
m_stepSize = gkm.getHopSize();
|
||||
m_blockSize = gkm.getBlockSize();
|
||||
}
|
||||
return m_stepSize;
|
||||
}
|
||||
|
||||
size_t
|
||||
KeyDetector::getPreferredBlockSize() const
|
||||
{
|
||||
if (!m_blockSize) {
|
||||
GetKeyMode gkm(int(m_inputSampleRate + 0.1),
|
||||
m_tuningFrequency, m_length, m_length);
|
||||
m_stepSize = gkm.getHopSize();
|
||||
m_blockSize = gkm.getBlockSize();
|
||||
}
|
||||
return m_blockSize;
|
||||
}
|
||||
|
||||
std::string
|
||||
KeyDetector::getKeyName(int index, bool minor, bool includeMajMin) const
|
||||
{
|
||||
// Keys are numbered with 1 => C, 12 => B
|
||||
// This is based on chromagram base set to a C in qm-dsp's GetKeyMode.cpp
|
||||
|
||||
static const char *namesMajor[] = {
|
||||
"C", "Db", "D", "Eb",
|
||||
"E", "F", "F# / Gb", "G",
|
||||
"Ab", "A", "Bb", "B"
|
||||
};
|
||||
|
||||
static const char *namesMinor[] = {
|
||||
"C", "C#", "D", "Eb / D#",
|
||||
"E", "F", "F#", "G",
|
||||
"G#", "A", "Bb", "B"
|
||||
};
|
||||
|
||||
if (index < 1 || index > 12) {
|
||||
return "(unknown)";
|
||||
}
|
||||
|
||||
std::string base;
|
||||
|
||||
if (minor) base = namesMinor[index - 1];
|
||||
else base = namesMajor[index - 1];
|
||||
|
||||
if (!includeMajMin) return base;
|
||||
|
||||
if (minor) return base + " minor";
|
||||
else return base + " major";
|
||||
}
|
||||
|
||||
Loading…
Add table
Add a link
Reference in a new issue