c@21
|
1 /* -*- c-basic-offset: 4 indent-tabs-mode: nil -*- vi:set ts=8 sts=4 sw=4: */
|
c@21
|
2
|
c@21
|
3 /*
|
c@38
|
4 QM Vamp Plugin Set
|
c@21
|
5
|
c@21
|
6 Centre for Digital Music, Queen Mary, University of London.
|
c@38
|
7 All rights reserved.
|
c@21
|
8 */
|
c@21
|
9
|
c@21
|
10 #include "KeyDetect.h"
|
c@21
|
11
|
c@21
|
12 using std::string;
|
c@21
|
13 using std::vector;
|
c@21
|
14 //using std::cerr;
|
c@21
|
15 using std::endl;
|
c@21
|
16
|
c@21
|
17 #include <cmath>
|
c@21
|
18
|
c@21
|
19
|
c@21
|
20 KeyDetector::KeyDetector(float inputSampleRate) :
|
c@21
|
21 Plugin(inputSampleRate),
|
c@21
|
22 m_stepSize(0),
|
c@21
|
23 m_blockSize(0),
|
c@21
|
24 m_tuningFrequency(440),
|
c@21
|
25 m_length(10),
|
c@21
|
26 m_getKeyMode(0),
|
c@21
|
27 m_inputFrame(0),
|
c@21
|
28 m_prevKey(-1)
|
c@21
|
29 {
|
c@21
|
30 }
|
c@21
|
31
|
c@21
|
32 KeyDetector::~KeyDetector()
|
c@21
|
33 {
|
c@21
|
34 delete m_getKeyMode;
|
c@21
|
35 if ( m_inputFrame ) {
|
c@21
|
36 delete [] m_inputFrame;
|
c@21
|
37 }
|
c@21
|
38 }
|
c@21
|
39
|
c@21
|
40 string
|
c@22
|
41 KeyDetector::getIdentifier() const
|
c@21
|
42 {
|
c@21
|
43 return "qm-keydetector";
|
c@21
|
44 }
|
c@21
|
45
|
c@21
|
46 string
|
c@22
|
47 KeyDetector::getName() const
|
c@22
|
48 {
|
c@22
|
49 return "Key Detector";
|
c@22
|
50 }
|
c@22
|
51
|
c@22
|
52 string
|
c@21
|
53 KeyDetector::getDescription() const
|
c@21
|
54 {
|
c@22
|
55 //!!!
|
c@22
|
56 return "";
|
c@21
|
57 }
|
c@21
|
58
|
c@21
|
59 string
|
c@21
|
60 KeyDetector::getMaker() const
|
c@21
|
61 {
|
c@21
|
62 return "Katy Noland and Christian Landone, Queen Mary, University of London";
|
c@21
|
63 }
|
c@21
|
64
|
c@21
|
65 int
|
c@21
|
66 KeyDetector::getPluginVersion() const
|
c@21
|
67 {
|
c@35
|
68 return 3;
|
c@21
|
69 }
|
c@21
|
70
|
c@21
|
71 string
|
c@21
|
72 KeyDetector::getCopyright() const
|
c@21
|
73 {
|
c@38
|
74 return "Copyright (c) 2006-2008 - All Rights Reserved";
|
c@21
|
75 }
|
c@21
|
76
|
c@21
|
77 KeyDetector::ParameterList
|
c@21
|
78 KeyDetector::getParameterDescriptors() const
|
c@21
|
79 {
|
c@21
|
80 ParameterList list;
|
c@21
|
81
|
c@21
|
82 ParameterDescriptor desc;
|
c@22
|
83 desc.identifier = "tuning";
|
c@22
|
84 desc.name = "Tuning Frequency";
|
c@21
|
85 desc.unit = "Hz";
|
c@21
|
86 desc.minValue = 420;
|
c@21
|
87 desc.maxValue = 460;
|
c@21
|
88 desc.defaultValue = 440;
|
c@21
|
89 desc.isQuantized = false;
|
c@21
|
90 list.push_back(desc);
|
c@21
|
91
|
c@22
|
92 desc.identifier = "length";
|
c@22
|
93 desc.name = "Window Length";
|
c@21
|
94 desc.unit = "chroma frames";
|
c@21
|
95 desc.minValue = 1;
|
c@21
|
96 desc.maxValue = 30;
|
c@21
|
97 desc.defaultValue = 10;
|
c@21
|
98 desc.isQuantized = true;
|
c@21
|
99 desc.quantizeStep = 1;
|
c@21
|
100 list.push_back(desc);
|
c@21
|
101
|
c@21
|
102 return list;
|
c@21
|
103 }
|
c@21
|
104
|
c@21
|
105 float
|
c@21
|
106 KeyDetector::getParameter(std::string param) const
|
c@21
|
107 {
|
c@21
|
108 if (param == "tuning") {
|
c@21
|
109 return m_tuningFrequency;
|
c@21
|
110 }
|
c@21
|
111 if (param == "length") {
|
c@21
|
112 return m_length;
|
c@21
|
113 }
|
c@21
|
114 std::cerr << "WARNING: KeyDetect::getParameter: unknown parameter \""
|
c@21
|
115 << param << "\"" << std::endl;
|
c@21
|
116 return 0.0;
|
c@21
|
117 }
|
c@21
|
118
|
c@21
|
119 void
|
c@21
|
120 KeyDetector::setParameter(std::string param, float value)
|
c@21
|
121 {
|
c@21
|
122 if (param == "tuning") {
|
c@21
|
123 m_tuningFrequency = value;
|
c@21
|
124 } else if (param == "length") {
|
c@21
|
125 m_length = int(value + 0.1);
|
c@21
|
126 } else {
|
c@21
|
127 std::cerr << "WARNING: KeyDetect::setParameter: unknown parameter \""
|
c@21
|
128 << param << "\"" << std::endl;
|
c@21
|
129 }
|
c@21
|
130 }
|
c@21
|
131
|
c@21
|
132 bool
|
c@21
|
133 KeyDetector::initialise(size_t channels, size_t stepSize, size_t blockSize)
|
c@21
|
134 {
|
c@21
|
135 if (m_getKeyMode) {
|
c@21
|
136 delete m_getKeyMode;
|
c@21
|
137 m_getKeyMode = 0;
|
c@21
|
138 }
|
c@21
|
139
|
c@21
|
140 if (channels < getMinChannelCount() ||
|
c@21
|
141 channels > getMaxChannelCount()) return false;
|
c@21
|
142
|
c@21
|
143 m_getKeyMode = new GetKeyMode(int(m_inputSampleRate + 0.1),
|
c@21
|
144 m_tuningFrequency,
|
c@21
|
145 m_length, m_length);
|
c@21
|
146
|
c@21
|
147 m_stepSize = m_getKeyMode->getHopSize();
|
c@21
|
148 m_blockSize = m_getKeyMode->getBlockSize();
|
c@21
|
149
|
c@21
|
150 if (stepSize != m_stepSize || blockSize != m_blockSize) {
|
c@21
|
151 std::cerr << "KeyDetector::initialise: step/block sizes "
|
c@21
|
152 << stepSize << "/" << blockSize << " differ from required "
|
c@21
|
153 << m_stepSize << "/" << m_blockSize << std::endl;
|
c@21
|
154 delete m_getKeyMode;
|
c@21
|
155 m_getKeyMode = 0;
|
c@21
|
156 return false;
|
c@21
|
157 }
|
c@21
|
158
|
c@21
|
159 m_inputFrame = new double[m_blockSize];
|
c@21
|
160
|
c@21
|
161 m_prevKey = -1;
|
c@21
|
162
|
c@21
|
163 return true;
|
c@21
|
164 }
|
c@21
|
165
|
c@21
|
166 void
|
c@21
|
167 KeyDetector::reset()
|
c@21
|
168 {
|
c@21
|
169 if (m_getKeyMode) {
|
c@21
|
170 delete m_getKeyMode;
|
c@21
|
171 m_getKeyMode = new GetKeyMode(int(m_inputSampleRate + 0.1),
|
c@21
|
172 m_tuningFrequency,
|
c@21
|
173 m_length, m_length);
|
c@21
|
174 }
|
c@21
|
175
|
c@21
|
176 if (m_inputFrame) {
|
c@21
|
177 for( unsigned int i = 0; i < m_blockSize; i++ ) {
|
c@21
|
178 m_inputFrame[ i ] = 0.0;
|
c@21
|
179 }
|
c@21
|
180 }
|
c@21
|
181
|
c@21
|
182 m_prevKey = -1;
|
c@21
|
183 }
|
c@21
|
184
|
c@21
|
185
|
c@21
|
186 KeyDetector::OutputList
|
c@21
|
187 KeyDetector::getOutputDescriptors() const
|
c@21
|
188 {
|
c@21
|
189 OutputList list;
|
c@21
|
190
|
c@21
|
191 OutputDescriptor d;
|
c@22
|
192 d.identifier = "tonic";
|
c@22
|
193 d.name = "Tonic Pitch";
|
c@21
|
194 d.unit = "";
|
c@21
|
195 d.hasFixedBinCount = true;
|
c@21
|
196 d.binCount = 1;
|
c@21
|
197 d.hasKnownExtents = true;
|
c@21
|
198 d.isQuantized = true;
|
c@21
|
199 d.minValue = 0;
|
c@21
|
200 d.maxValue = 11;
|
c@21
|
201 d.quantizeStep = 1;
|
c@21
|
202 d.sampleType = OutputDescriptor::OneSamplePerStep;
|
c@21
|
203 list.push_back(d);
|
c@21
|
204
|
c@22
|
205 d.identifier = "mode";
|
c@22
|
206 d.name = "Key Mode";
|
c@21
|
207 d.unit = "";
|
c@21
|
208 d.hasFixedBinCount = true;
|
c@21
|
209 d.binCount = 1;
|
c@21
|
210 d.hasKnownExtents = true;
|
c@21
|
211 d.isQuantized = true;
|
c@21
|
212 d.minValue = 0;
|
c@21
|
213 d.maxValue = 1;
|
c@21
|
214 d.quantizeStep = 1;
|
c@21
|
215 d.binNames.push_back("Major = 0, Minor = 1");
|
c@21
|
216 d.sampleType = OutputDescriptor::OneSamplePerStep;
|
c@21
|
217 list.push_back(d);
|
c@21
|
218
|
c@22
|
219 d.identifier = "key";
|
c@22
|
220 d.name = "Key";
|
c@21
|
221 d.unit = "";
|
c@21
|
222 d.hasFixedBinCount = true;
|
c@21
|
223 d.binCount = 1;
|
c@21
|
224 d.hasKnownExtents = true;
|
c@21
|
225 d.isQuantized = true;
|
c@21
|
226 d.minValue = 0;
|
c@21
|
227 d.maxValue = 23;
|
c@21
|
228 d.quantizeStep = 1;
|
devnull@34
|
229 d.binNames.erase(d.binNames.begin(),d.binNames.end());
|
c@21
|
230 d.sampleType = OutputDescriptor::OneSamplePerStep;
|
c@21
|
231 list.push_back(d);
|
c@21
|
232
|
c@21
|
233 return list;
|
c@21
|
234 }
|
c@21
|
235
|
c@21
|
236 KeyDetector::FeatureSet
|
c@21
|
237 KeyDetector::process(const float *const *inputBuffers,
|
c@21
|
238 Vamp::RealTime now)
|
c@21
|
239 {
|
c@21
|
240 if (m_stepSize == 0) {
|
c@21
|
241 return FeatureSet();
|
c@21
|
242 }
|
c@21
|
243
|
c@21
|
244 FeatureSet returnFeatures;
|
c@21
|
245
|
c@21
|
246 for ( unsigned int i = 0 ; i < m_blockSize; i++ ) {
|
c@21
|
247 m_inputFrame[i] = (double)inputBuffers[0][i];
|
c@21
|
248 }
|
c@21
|
249
|
c@21
|
250 // int key = (m_getKeyMode->process(m_inputFrame) % 24);
|
c@21
|
251 int key = m_getKeyMode->process(m_inputFrame);
|
c@21
|
252 int minor = m_getKeyMode->isModeMinor(key);
|
c@21
|
253 int tonic = key;
|
c@21
|
254 if (tonic > 12) tonic -= 12;
|
c@21
|
255
|
devnull@33
|
256
|
c@21
|
257 int prevTonic = m_prevKey;
|
c@21
|
258 if (prevTonic > 12) prevTonic -= 12;
|
c@21
|
259
|
c@21
|
260 if (tonic != prevTonic) {
|
c@21
|
261 Feature feature;
|
c@21
|
262 feature.hasTimestamp = false;
|
c@21
|
263 // feature.timestamp = now;
|
c@21
|
264 feature.values.push_back((float)tonic);
|
c@21
|
265 feature.label = getKeyName(tonic);
|
c@21
|
266 returnFeatures[0].push_back(feature); // tonic
|
c@21
|
267 }
|
c@21
|
268
|
c@21
|
269 if (minor != (m_getKeyMode->isModeMinor(m_prevKey))) {
|
c@21
|
270 Feature feature;
|
c@21
|
271 feature.hasTimestamp = false;
|
c@21
|
272 feature.values.push_back((float)minor);
|
c@21
|
273 feature.label = (minor ? "Minor" : "Major");
|
c@21
|
274 returnFeatures[1].push_back(feature); // mode
|
c@21
|
275 }
|
c@21
|
276
|
c@21
|
277 if (key != m_prevKey) {
|
c@21
|
278 Feature feature;
|
c@21
|
279 // feature.hasTimestamp = true;
|
c@21
|
280 feature.hasTimestamp = false;
|
c@21
|
281 // feature.timestamp = now;
|
c@21
|
282 feature.values.push_back((float)key);
|
c@21
|
283 feature.label = std::string(getKeyName(tonic));
|
c@21
|
284 if (minor) feature.label += " minor";
|
c@21
|
285 else feature.label += " major";
|
c@21
|
286 returnFeatures[2].push_back(feature); // key
|
c@38
|
287 // cerr << "int key = "<<key<<endl;
|
c@38
|
288 // cerr << "int tonic = "<<tonic<<endl;
|
c@38
|
289 // cerr << "feature label = "<<feature.label<<endl;
|
c@21
|
290 }
|
c@21
|
291
|
c@21
|
292 m_prevKey = key;
|
c@21
|
293
|
c@21
|
294 return returnFeatures;
|
c@21
|
295 }
|
c@21
|
296
|
c@21
|
297 KeyDetector::FeatureSet
|
c@21
|
298 KeyDetector::getRemainingFeatures()
|
c@21
|
299 {
|
c@21
|
300 return FeatureSet();
|
c@21
|
301 }
|
c@21
|
302
|
c@21
|
303
|
c@21
|
304 size_t
|
c@21
|
305 KeyDetector::getPreferredStepSize() const
|
c@21
|
306 {
|
c@21
|
307 if (!m_stepSize) {
|
c@21
|
308 GetKeyMode gkm(int(m_inputSampleRate + 0.1),
|
c@21
|
309 m_tuningFrequency, m_length, m_length);
|
c@21
|
310 m_stepSize = gkm.getHopSize();
|
c@21
|
311 m_blockSize = gkm.getBlockSize();
|
c@21
|
312 }
|
c@21
|
313 return m_stepSize;
|
c@21
|
314 }
|
c@21
|
315
|
c@21
|
316 size_t
|
c@21
|
317 KeyDetector::getPreferredBlockSize() const
|
c@21
|
318 {
|
c@21
|
319 if (!m_blockSize) {
|
c@21
|
320 GetKeyMode gkm(int(m_inputSampleRate + 0.1),
|
c@21
|
321 m_tuningFrequency, m_length, m_length);
|
c@21
|
322 m_stepSize = gkm.getHopSize();
|
c@21
|
323 m_blockSize = gkm.getBlockSize();
|
c@21
|
324 }
|
c@21
|
325 return m_blockSize;
|
c@21
|
326 }
|
c@21
|
327
|
c@21
|
328 const char *
|
c@21
|
329 KeyDetector::getKeyName(int index)
|
c@21
|
330 {
|
devnull@33
|
331 // Keys are numbered with 1 => C, 12 => B
|
devnull@33
|
332 // This is based on chromagram base set to a C in qm-dsp's GetKeyMode.cpp
|
c@21
|
333 static const char *names[] = {
|
c@21
|
334 "C", "C# / Db", "D", "D# / Eb",
|
c@21
|
335 "E", "F", "F# / Gb", "G",
|
c@21
|
336 "G# / Ab", "A", "A# / Bb", "B"
|
c@21
|
337 };
|
c@21
|
338 if (index < 1 || index > 12) {
|
c@21
|
339 return "(unknown)";
|
c@21
|
340 }
|
devnull@33
|
341 return names[index - 1]; //'-1' because our names array starts from 0
|
c@21
|
342 }
|
c@21
|
343
|