1 /* -*- c-basic-offset: 4 indent-tabs-mode: nil -*- vi:set ts=8 sts=4 sw=4: */
6 Centre for Digital Music, Queen Mary, University of London.
8 This program is free software; you can redistribute it and/or
9 modify it under the terms of the GNU General Public License as
10 published by the Free Software Foundation; either version 2 of the
11 License, or (at your option) any later version. See the file
12 COPYING included with this distribution for more information.
15 #include "KeyDetect.h"
25 // Order for circle-of-5ths plotting
26 static int conversion[24] =
27 { 7, 12, 5, 10, 3, 8, 1, 6, 11, 4, 9, 2,
28 16, 21, 14, 19, 24, 17, 22, 15, 20, 13, 18, 23 };
31 KeyDetector::KeyDetector(float inputSampleRate) :
32 Plugin(inputSampleRate),
35 m_tuningFrequency(440),
43 KeyDetector::~KeyDetector()
47 delete [] m_inputFrame;
52 KeyDetector::getIdentifier() const
54 return "qm-keydetector";
58 KeyDetector::getName() const
60 return "Key Detector";
64 KeyDetector::getDescription() const
66 return "Estimate the key of the music";
70 KeyDetector::getMaker() const
72 return "Queen Mary, University of London";
76 KeyDetector::getPluginVersion() const
82 KeyDetector::getCopyright() const
84 return "Plugin by Katy Noland and Christian Landone. Copyright (c) 2006-2009 QMUL - All Rights Reserved";
87 KeyDetector::ParameterList
88 KeyDetector::getParameterDescriptors() const
92 ParameterDescriptor desc;
93 desc.identifier = "tuning";
94 desc.name = "Tuning Frequency";
95 desc.description = "Frequency of concert A";
99 desc.defaultValue = 440;
100 desc.isQuantized = false;
101 list.push_back(desc);
103 desc.identifier = "length";
104 desc.name = "Window Length";
105 desc.unit = "chroma frames";
106 desc.description = "Number of chroma analysis frames per key estimation";
109 desc.defaultValue = 10;
110 desc.isQuantized = true;
111 desc.quantizeStep = 1;
112 list.push_back(desc);
118 KeyDetector::getParameter(std::string param) const
120 if (param == "tuning") {
121 return m_tuningFrequency;
123 if (param == "length") {
126 std::cerr << "WARNING: KeyDetector::getParameter: unknown parameter \""
127 << param << "\"" << std::endl;
132 KeyDetector::setParameter(std::string param, float value)
134 if (param == "tuning") {
135 m_tuningFrequency = value;
136 } else if (param == "length") {
137 m_length = int(value + 0.1);
139 std::cerr << "WARNING: KeyDetector::setParameter: unknown parameter \""
140 << param << "\"" << std::endl;
145 KeyDetector::initialise(size_t channels, size_t stepSize, size_t blockSize)
152 if (channels < getMinChannelCount() ||
153 channels > getMaxChannelCount()) return false;
155 m_getKeyMode = new GetKeyMode(int(m_inputSampleRate + 0.1),
159 m_stepSize = m_getKeyMode->getHopSize();
160 m_blockSize = m_getKeyMode->getBlockSize();
162 if (stepSize != m_stepSize || blockSize != m_blockSize) {
163 std::cerr << "KeyDetector::initialise: ERROR: step/block sizes "
164 << stepSize << "/" << blockSize << " differ from required "
165 << m_stepSize << "/" << m_blockSize << std::endl;
171 m_inputFrame = new double[m_blockSize];
184 m_getKeyMode = new GetKeyMode(int(m_inputSampleRate + 0.1),
190 for( unsigned int i = 0; i < m_blockSize; i++ ) {
191 m_inputFrame[ i ] = 0.0;
200 KeyDetector::OutputList
201 KeyDetector::getOutputDescriptors() const
206 if (m_stepSize == 0) (void)getPreferredStepSize();
207 osr = m_inputSampleRate / m_stepSize;
210 d.identifier = "tonic";
211 d.name = "Tonic Pitch";
213 d.description = "Tonic of the estimated key (from C = 1 to B = 12)";
214 d.hasFixedBinCount = true;
216 d.hasKnownExtents = true;
217 d.isQuantized = true;
222 d.sampleType = OutputDescriptor::VariableSampleRate;
225 d.identifier = "mode";
228 d.description = "Major or minor mode of the estimated key (major = 0, minor = 1)";
229 d.hasFixedBinCount = true;
231 d.hasKnownExtents = true;
232 d.isQuantized = true;
237 d.sampleType = OutputDescriptor::VariableSampleRate;
240 d.identifier = "key";
243 d.description = "Estimated key (from C major = 1 to B major = 12 and C minor = 13 to B minor = 24)";
244 d.hasFixedBinCount = true;
246 d.hasKnownExtents = true;
247 d.isQuantized = true;
252 d.sampleType = OutputDescriptor::VariableSampleRate;
255 d.identifier = "keystrength";
256 d.name = "Key Strength Plot";
258 d.description = "Correlation of the chroma vector with stored key profile for each major and minor key";
259 d.hasFixedBinCount = true;
261 d.hasKnownExtents = false;
262 d.isQuantized = false;
263 d.sampleType = OutputDescriptor::OneSamplePerStep;
264 for (int i = 0; i < 24; ++i) {
265 if (i == 12) d.binNames.push_back(" ");
266 int idx = conversion[i];
267 std::string label = getKeyName(idx > 12 ? idx-12 : idx,
270 d.binNames.push_back(label);
277 KeyDetector::FeatureSet
278 KeyDetector::process(const float *const *inputBuffers,
281 if (m_stepSize == 0) {
285 FeatureSet returnFeatures;
287 for ( unsigned int i = 0 ; i < m_blockSize; i++ ) {
288 m_inputFrame[i] = (double)inputBuffers[0][i];
291 // int key = (m_getKeyMode->process(m_inputFrame) % 24);
292 int key = m_getKeyMode->process(m_inputFrame);
293 bool minor = m_getKeyMode->isModeMinor(key);
295 if (tonic > 12) tonic -= 12;
297 int prevTonic = m_prevKey;
298 if (prevTonic > 12) prevTonic -= 12;
300 if (m_first || (tonic != prevTonic)) {
302 feature.hasTimestamp = true;
303 feature.timestamp = now;
304 // feature.timestamp = now;
305 feature.values.push_back((float)tonic);
306 feature.label = getKeyName(tonic, minor, false);
307 returnFeatures[0].push_back(feature); // tonic
310 if (m_first || (minor != (m_getKeyMode->isModeMinor(m_prevKey)))) {
312 feature.hasTimestamp = true;
313 feature.timestamp = now;
314 feature.values.push_back(minor ? 1.f : 0.f);
315 feature.label = (minor ? "Minor" : "Major");
316 returnFeatures[1].push_back(feature); // mode
319 if (m_first || (key != m_prevKey)) {
321 feature.hasTimestamp = true;
322 feature.timestamp = now;
323 feature.values.push_back((float)key);
324 feature.label = getKeyName(tonic, minor, true);
325 returnFeatures[2].push_back(feature); // key
332 ksf.values.reserve(25);
333 double *keystrengths = m_getKeyMode->getKeyStrengths();
334 for (int i = 0; i < 24; ++i) {
335 if (i == 12) ksf.values.push_back(-1);
336 ksf.values.push_back(keystrengths[conversion[i]-1]);
338 ksf.hasTimestamp = false;
339 returnFeatures[3].push_back(ksf);
341 return returnFeatures;
344 KeyDetector::FeatureSet
345 KeyDetector::getRemainingFeatures()
352 KeyDetector::getPreferredStepSize() const
355 GetKeyMode gkm(int(m_inputSampleRate + 0.1),
356 m_tuningFrequency, m_length, m_length);
357 m_stepSize = gkm.getHopSize();
358 m_blockSize = gkm.getBlockSize();
364 KeyDetector::getPreferredBlockSize() const
367 GetKeyMode gkm(int(m_inputSampleRate + 0.1),
368 m_tuningFrequency, m_length, m_length);
369 m_stepSize = gkm.getHopSize();
370 m_blockSize = gkm.getBlockSize();
376 KeyDetector::getKeyName(int index, bool minor, bool includeMajMin) const
378 // Keys are numbered with 1 => C, 12 => B
379 // This is based on chromagram base set to a C in qm-dsp's GetKeyMode.cpp
381 static const char *namesMajor[] = {
382 "C", "Db", "D", "Eb",
383 "E", "F", "F# / Gb", "G",
387 static const char *namesMinor[] = {
388 "C", "C#", "D", "Eb / D#",
393 if (index < 1 || index > 12) {
399 if (minor) base = namesMinor[index - 1];
400 else base = namesMajor[index - 1];
402 if (!includeMajMin) return base;
404 if (minor) return base + " minor";
405 else return base + " major";