Mercurial > hg > qm-vamp-plugins
comparison plugins/KeyDetect.cpp @ 21:d695fc2baa91
* Turning key-mode plugin into a generic key detection plugin, and attempting to
debug it
author | Chris Cannam <c.cannam@qmul.ac.uk> |
---|---|
date | Mon, 22 Jan 2007 17:32:40 +0000 |
parents | |
children | 6d014fb538db |
comparison
equal
deleted
inserted
replaced
20:1f1881046b0c | 21:d695fc2baa91 |
---|---|
1 /* -*- c-basic-offset: 4 indent-tabs-mode: nil -*- vi:set ts=8 sts=4 sw=4: */ | |
2 | |
3 /* | |
4 Vamp | |
5 | |
6 An API for audio analysis and feature extraction plugins. | |
7 | |
8 Centre for Digital Music, Queen Mary, University of London. | |
9 Copyright 2006-2007 QMUL. | |
10 | |
11 Permission is hereby granted, free of charge, to any person | |
12 obtaining a copy of this software and associated documentation | |
13 files (the "Software"), to deal in the Software without | |
14 restriction, including without limitation the rights to use, copy, | |
15 modify, merge, publish, distribute, sublicense, and/or sell copies | |
16 of the Software, and to permit persons to whom the Software is | |
17 furnished to do so, subject to the following conditions: | |
18 | |
19 The above copyright notice and this permission notice shall be | |
20 included in all copies or substantial portions of the Software. | |
21 | |
22 THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, | |
23 EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF | |
24 MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND | |
25 NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR | |
26 ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF | |
27 CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION | |
28 WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. | |
29 | |
30 Except as contained in this notice, the names of the Centre for | |
31 Digital Music; Queen Mary, University of London; and Chris Cannam | |
32 shall not be used in advertising or otherwise to promote the sale, | |
33 use or other dealings in this Software without prior written | |
34 authorization. | |
35 */ | |
36 | |
37 #include "KeyDetect.h" | |
38 | |
39 using std::string; | |
40 using std::vector; | |
41 //using std::cerr; | |
42 using std::endl; | |
43 | |
44 #include <cmath> | |
45 | |
46 | |
47 KeyDetector::KeyDetector(float inputSampleRate) : | |
48 Plugin(inputSampleRate), | |
49 m_stepSize(0), | |
50 m_blockSize(0), | |
51 m_tuningFrequency(440), | |
52 m_length(10), | |
53 m_getKeyMode(0), | |
54 m_inputFrame(0), | |
55 m_prevKey(-1) | |
56 { | |
57 } | |
58 | |
59 KeyDetector::~KeyDetector() | |
60 { | |
61 delete m_getKeyMode; | |
62 if ( m_inputFrame ) { | |
63 delete [] m_inputFrame; | |
64 } | |
65 } | |
66 | |
67 string | |
68 KeyDetector::getName() const | |
69 { | |
70 return "qm-keydetector"; | |
71 } | |
72 | |
73 string | |
74 KeyDetector::getDescription() const | |
75 { | |
76 return "Key Detector"; | |
77 } | |
78 | |
79 string | |
80 KeyDetector::getMaker() const | |
81 { | |
82 return "Katy Noland and Christian Landone, Queen Mary, University of London"; | |
83 } | |
84 | |
85 int | |
86 KeyDetector::getPluginVersion() const | |
87 { | |
88 return 2; | |
89 } | |
90 | |
91 string | |
92 KeyDetector::getCopyright() const | |
93 { | |
94 return "Copyright (c) 2006-2007 - All Rights Reserved"; | |
95 } | |
96 | |
97 KeyDetector::ParameterList | |
98 KeyDetector::getParameterDescriptors() const | |
99 { | |
100 ParameterList list; | |
101 | |
102 ParameterDescriptor desc; | |
103 desc.name = "tuning"; | |
104 desc.description = "Tuning Frequency"; | |
105 desc.unit = "Hz"; | |
106 desc.minValue = 420; | |
107 desc.maxValue = 460; | |
108 desc.defaultValue = 440; | |
109 desc.isQuantized = false; | |
110 list.push_back(desc); | |
111 | |
112 desc.name = "length"; | |
113 desc.description = "Window Length"; | |
114 desc.unit = "chroma frames"; | |
115 desc.minValue = 1; | |
116 desc.maxValue = 30; | |
117 desc.defaultValue = 10; | |
118 desc.isQuantized = true; | |
119 desc.quantizeStep = 1; | |
120 list.push_back(desc); | |
121 | |
122 return list; | |
123 } | |
124 | |
125 float | |
126 KeyDetector::getParameter(std::string param) const | |
127 { | |
128 if (param == "tuning") { | |
129 return m_tuningFrequency; | |
130 } | |
131 if (param == "length") { | |
132 return m_length; | |
133 } | |
134 std::cerr << "WARNING: KeyDetect::getParameter: unknown parameter \"" | |
135 << param << "\"" << std::endl; | |
136 return 0.0; | |
137 } | |
138 | |
139 void | |
140 KeyDetector::setParameter(std::string param, float value) | |
141 { | |
142 if (param == "tuning") { | |
143 m_tuningFrequency = value; | |
144 } else if (param == "length") { | |
145 m_length = int(value + 0.1); | |
146 } else { | |
147 std::cerr << "WARNING: KeyDetect::setParameter: unknown parameter \"" | |
148 << param << "\"" << std::endl; | |
149 } | |
150 } | |
151 | |
152 bool | |
153 KeyDetector::initialise(size_t channels, size_t stepSize, size_t blockSize) | |
154 { | |
155 if (m_getKeyMode) { | |
156 delete m_getKeyMode; | |
157 m_getKeyMode = 0; | |
158 } | |
159 | |
160 if (channels < getMinChannelCount() || | |
161 channels > getMaxChannelCount()) return false; | |
162 | |
163 m_getKeyMode = new GetKeyMode(int(m_inputSampleRate + 0.1), | |
164 m_tuningFrequency, | |
165 m_length, m_length); | |
166 | |
167 m_stepSize = m_getKeyMode->getHopSize(); | |
168 m_blockSize = m_getKeyMode->getBlockSize(); | |
169 | |
170 if (stepSize != m_stepSize || blockSize != m_blockSize) { | |
171 std::cerr << "KeyDetector::initialise: step/block sizes " | |
172 << stepSize << "/" << blockSize << " differ from required " | |
173 << m_stepSize << "/" << m_blockSize << std::endl; | |
174 delete m_getKeyMode; | |
175 m_getKeyMode = 0; | |
176 return false; | |
177 } | |
178 | |
179 m_inputFrame = new double[m_blockSize]; | |
180 | |
181 m_prevKey = -1; | |
182 | |
183 return true; | |
184 } | |
185 | |
186 void | |
187 KeyDetector::reset() | |
188 { | |
189 if (m_getKeyMode) { | |
190 delete m_getKeyMode; | |
191 m_getKeyMode = new GetKeyMode(int(m_inputSampleRate + 0.1), | |
192 m_tuningFrequency, | |
193 m_length, m_length); | |
194 } | |
195 | |
196 if (m_inputFrame) { | |
197 for( unsigned int i = 0; i < m_blockSize; i++ ) { | |
198 m_inputFrame[ i ] = 0.0; | |
199 } | |
200 } | |
201 | |
202 m_prevKey = -1; | |
203 } | |
204 | |
205 | |
206 KeyDetector::OutputList | |
207 KeyDetector::getOutputDescriptors() const | |
208 { | |
209 OutputList list; | |
210 | |
211 OutputDescriptor d; | |
212 d.name = "tonic"; | |
213 d.unit = ""; | |
214 d.description = "Tonic Pitch"; | |
215 d.hasFixedBinCount = true; | |
216 d.binCount = 1; | |
217 d.hasKnownExtents = true; | |
218 d.isQuantized = true; | |
219 d.minValue = 0; | |
220 d.maxValue = 11; | |
221 d.quantizeStep = 1; | |
222 d.sampleType = OutputDescriptor::OneSamplePerStep; | |
223 list.push_back(d); | |
224 | |
225 d.name = "mode"; | |
226 d.unit = ""; | |
227 d.description = "Key Mode"; | |
228 d.hasFixedBinCount = true; | |
229 d.binCount = 1; | |
230 d.hasKnownExtents = true; | |
231 d.isQuantized = true; | |
232 d.minValue = 0; | |
233 d.maxValue = 1; | |
234 d.quantizeStep = 1; | |
235 d.binNames.push_back("Major = 0, Minor = 1"); | |
236 d.sampleType = OutputDescriptor::OneSamplePerStep; | |
237 list.push_back(d); | |
238 | |
239 d.name = "key"; | |
240 d.unit = ""; | |
241 d.description = "Key"; | |
242 d.hasFixedBinCount = true; | |
243 d.binCount = 1; | |
244 d.hasKnownExtents = true; | |
245 d.isQuantized = true; | |
246 d.minValue = 0; | |
247 d.maxValue = 23; | |
248 d.quantizeStep = 1; | |
249 d.sampleType = OutputDescriptor::OneSamplePerStep; | |
250 list.push_back(d); | |
251 | |
252 return list; | |
253 } | |
254 | |
255 KeyDetector::FeatureSet | |
256 KeyDetector::process(const float *const *inputBuffers, | |
257 Vamp::RealTime now) | |
258 { | |
259 if (m_stepSize == 0) { | |
260 return FeatureSet(); | |
261 } | |
262 | |
263 FeatureSet returnFeatures; | |
264 | |
265 for ( unsigned int i = 0 ; i < m_blockSize; i++ ) { | |
266 m_inputFrame[i] = (double)inputBuffers[0][i]; | |
267 } | |
268 | |
269 // int key = (m_getKeyMode->process(m_inputFrame) % 24); | |
270 int key = m_getKeyMode->process(m_inputFrame); | |
271 int minor = m_getKeyMode->isModeMinor(key); | |
272 int tonic = key; | |
273 if (tonic > 12) tonic -= 12; | |
274 | |
275 int prevTonic = m_prevKey; | |
276 if (prevTonic > 12) prevTonic -= 12; | |
277 | |
278 if (tonic != prevTonic) { | |
279 Feature feature; | |
280 feature.hasTimestamp = false; | |
281 // feature.timestamp = now; | |
282 feature.values.push_back((float)tonic); | |
283 feature.label = getKeyName(tonic); | |
284 returnFeatures[0].push_back(feature); // tonic | |
285 } | |
286 | |
287 if (minor != (m_getKeyMode->isModeMinor(m_prevKey))) { | |
288 Feature feature; | |
289 feature.hasTimestamp = false; | |
290 feature.values.push_back((float)minor); | |
291 feature.label = (minor ? "Minor" : "Major"); | |
292 returnFeatures[1].push_back(feature); // mode | |
293 } | |
294 | |
295 if (key != m_prevKey) { | |
296 Feature feature; | |
297 // feature.hasTimestamp = true; | |
298 feature.hasTimestamp = false; | |
299 // feature.timestamp = now; | |
300 feature.values.push_back((float)key); | |
301 feature.label = std::string(getKeyName(tonic)); | |
302 if (minor) feature.label += " minor"; | |
303 else feature.label += " major"; | |
304 returnFeatures[2].push_back(feature); // key | |
305 } | |
306 | |
307 m_prevKey = key; | |
308 | |
309 return returnFeatures; | |
310 } | |
311 | |
312 KeyDetector::FeatureSet | |
313 KeyDetector::getRemainingFeatures() | |
314 { | |
315 return FeatureSet(); | |
316 } | |
317 | |
318 | |
319 size_t | |
320 KeyDetector::getPreferredStepSize() const | |
321 { | |
322 if (!m_stepSize) { | |
323 GetKeyMode gkm(int(m_inputSampleRate + 0.1), | |
324 m_tuningFrequency, m_length, m_length); | |
325 m_stepSize = gkm.getHopSize(); | |
326 m_blockSize = gkm.getBlockSize(); | |
327 } | |
328 return m_stepSize; | |
329 } | |
330 | |
331 size_t | |
332 KeyDetector::getPreferredBlockSize() const | |
333 { | |
334 if (!m_blockSize) { | |
335 GetKeyMode gkm(int(m_inputSampleRate + 0.1), | |
336 m_tuningFrequency, m_length, m_length); | |
337 m_stepSize = gkm.getHopSize(); | |
338 m_blockSize = gkm.getBlockSize(); | |
339 } | |
340 return m_blockSize; | |
341 } | |
342 | |
343 const char * | |
344 KeyDetector::getKeyName(int index) | |
345 { | |
346 static const char *names[] = { | |
347 "C", "C# / Db", "D", "D# / Eb", | |
348 "E", "F", "F# / Gb", "G", | |
349 "G# / Ab", "A", "A# / Bb", "B" | |
350 }; | |
351 if (index < 1 || index > 12) { | |
352 return "(unknown)"; | |
353 } | |
354 return names[index - 1]; | |
355 } | |
356 |