Mercurial > hg > silvet
comparison constant-q-cpp/vamp/CQVamp.cpp @ 366:5d0a2ebb4d17
Bring dependent libraries in to repo
author | Chris Cannam |
---|---|
date | Fri, 24 Jun 2016 14:47:45 +0100 |
parents | |
children |
comparison
equal
deleted
inserted
replaced
365:112766f4c34b | 366:5d0a2ebb4d17 |
---|---|
1 /* -*- c-basic-offset: 4 indent-tabs-mode: nil -*- vi:set ts=8 sts=4 sw=4: */ | |
2 /* | |
3 Constant-Q library | |
4 Copyright (c) 2013-2014 Queen Mary, University of London | |
5 | |
6 Permission is hereby granted, free of charge, to any person | |
7 obtaining a copy of this software and associated documentation | |
8 files (the "Software"), to deal in the Software without | |
9 restriction, including without limitation the rights to use, copy, | |
10 modify, merge, publish, distribute, sublicense, and/or sell copies | |
11 of the Software, and to permit persons to whom the Software is | |
12 furnished to do so, subject to the following conditions: | |
13 | |
14 The above copyright notice and this permission notice shall be | |
15 included in all copies or substantial portions of the Software. | |
16 | |
17 THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, | |
18 EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF | |
19 MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND | |
20 NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY | |
21 CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF | |
22 CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION | |
23 WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. | |
24 | |
25 Except as contained in this notice, the names of the Centre for | |
26 Digital Music; Queen Mary, University of London; and Chris Cannam | |
27 shall not be used in advertising or otherwise to promote the sale, | |
28 use or other dealings in this Software without prior written | |
29 authorization. | |
30 */ | |
31 | |
32 #include "CQVamp.h" | |
33 | |
34 #include "Pitch.h" | |
35 | |
36 #include <algorithm> | |
37 #include <cstdio> | |
38 | |
39 using std::string; | |
40 using std::vector; | |
41 using std::cerr; | |
42 using std::endl; | |
43 | |
44 // The plugin offers either MIDI pitch or frequency range parameters, | |
45 // depending on the midiPitchParameters option given to the | |
46 // constructor. It never offers both. So they can have different | |
47 // defaults; if we're using MIDI pitch, the min and max frequencies | |
48 // will come from those rather than from the m_minFrequency and | |
49 // m_maxFrequency members. | |
50 static const int defaultMinMIDIPitch = 36; | |
51 static const int defaultMaxMIDIPitch = 96; | |
52 static const int defaultBPO = 36; | |
53 static const float defaultMinFrequency = 110; | |
54 static const float defaultMaxFrequency = 14700; | |
55 static const float defaultTuningFrequency = 440.f; | |
56 | |
57 CQVamp::CQVamp(float inputSampleRate, bool midiPitchParameters) : | |
58 Vamp::Plugin(inputSampleRate), | |
59 m_midiPitchParameters(midiPitchParameters), | |
60 m_minMIDIPitch(defaultMinMIDIPitch), | |
61 m_maxMIDIPitch(defaultMaxMIDIPitch), | |
62 m_tuningFrequency(defaultTuningFrequency), | |
63 m_bpo(defaultBPO), | |
64 m_interpolation(CQSpectrogram::InterpolateLinear), | |
65 m_cq(0), | |
66 m_maxFrequency(defaultMaxFrequency), | |
67 m_minFrequency(defaultMinFrequency), | |
68 m_haveStartTime(false), | |
69 m_columnCount(0) | |
70 { | |
71 } | |
72 | |
73 CQVamp::~CQVamp() | |
74 { | |
75 delete m_cq; | |
76 } | |
77 | |
78 string | |
79 CQVamp::getIdentifier() const | |
80 { | |
81 if (m_midiPitchParameters) { | |
82 return "cqvampmidi"; | |
83 } else { | |
84 return "cqvamp"; | |
85 } | |
86 } | |
87 | |
88 string | |
89 CQVamp::getName() const | |
90 { | |
91 if (m_midiPitchParameters) { | |
92 return "CQ Constant-Q Spectrogram (MIDI pitch range)"; | |
93 } else { | |
94 return "CQ Constant-Q Spectrogram (Hz range)"; | |
95 } | |
96 } | |
97 | |
98 string | |
99 CQVamp::getDescription() const | |
100 { | |
101 if (m_midiPitchParameters) { | |
102 return "Extract a spectrogram with constant ratio of centre frequency to resolution from the input audio, specifying the frequency range in MIDI pitch units."; | |
103 } else { | |
104 return "Extract a spectrogram with constant ratio of centre frequency to resolution from the input audio, specifying the frequency range in Hz."; | |
105 } | |
106 } | |
107 | |
108 string | |
109 CQVamp::getMaker() const | |
110 { | |
111 return "Queen Mary, University of London"; | |
112 } | |
113 | |
114 int | |
115 CQVamp::getPluginVersion() const | |
116 { | |
117 return 2; | |
118 } | |
119 | |
120 string | |
121 CQVamp::getCopyright() const | |
122 { | |
123 return "Plugin by Chris Cannam. Method by Christian Schörkhuber and Anssi Klapuri. Copyright (c) 2015 QMUL. BSD/MIT licence."; | |
124 } | |
125 | |
126 CQVamp::ParameterList | |
127 CQVamp::getParameterDescriptors() const | |
128 { | |
129 ParameterList list; | |
130 | |
131 ParameterDescriptor desc; | |
132 | |
133 if (m_midiPitchParameters) { | |
134 | |
135 desc.identifier = "minpitch"; | |
136 desc.name = "Minimum Pitch"; | |
137 desc.unit = "MIDI units"; | |
138 desc.description = "MIDI pitch corresponding to the lowest frequency to be included in the constant-Q transform. (The actual minimum frequency may be lower, as the range always covers an integral number of octaves below the highest frequency.)"; | |
139 desc.minValue = 0; | |
140 desc.maxValue = 127; | |
141 desc.defaultValue = defaultMinMIDIPitch; | |
142 desc.isQuantized = true; | |
143 desc.quantizeStep = 1; | |
144 list.push_back(desc); | |
145 | |
146 desc.identifier = "maxpitch"; | |
147 desc.name = "Maximum Pitch"; | |
148 desc.unit = "MIDI units"; | |
149 desc.description = "MIDI pitch corresponding to the highest frequency to be included in the constant-Q transform"; | |
150 desc.minValue = 0; | |
151 desc.maxValue = 127; | |
152 desc.defaultValue = defaultMaxMIDIPitch; | |
153 desc.isQuantized = true; | |
154 desc.quantizeStep = 1; | |
155 list.push_back(desc); | |
156 | |
157 desc.identifier = "tuning"; | |
158 desc.name = "Tuning Frequency"; | |
159 desc.unit = "Hz"; | |
160 desc.description = "Frequency of concert A"; | |
161 desc.minValue = 360; | |
162 desc.maxValue = 500; | |
163 desc.defaultValue = defaultTuningFrequency; | |
164 desc.isQuantized = false; | |
165 list.push_back(desc); | |
166 | |
167 } else { | |
168 | |
169 desc.identifier = "minfreq"; | |
170 desc.name = "Minimum Frequency"; | |
171 desc.unit = "Hz"; | |
172 desc.description = "Lowest frequency to be included in the constant-Q transform. (The actual minimum frequency may be lower, as the range always covers an integral number of octaves below the highest frequency.)"; | |
173 desc.minValue = 1; | |
174 desc.maxValue = 22050; | |
175 desc.defaultValue = defaultMinFrequency; | |
176 desc.isQuantized = false; | |
177 list.push_back(desc); | |
178 | |
179 desc.identifier = "maxfreq"; | |
180 desc.name = "Maximum Frequency"; | |
181 desc.unit = "Hz"; | |
182 desc.description = "MIDI pitch corresponding to the highest frequency to be included in the constant-Q transform"; | |
183 desc.minValue = 1; | |
184 desc.maxValue = 22050; | |
185 desc.defaultValue = defaultMaxFrequency; | |
186 desc.isQuantized = false; | |
187 list.push_back(desc); | |
188 } | |
189 | |
190 desc.identifier = "bpo"; | |
191 desc.name = "Bins per Octave"; | |
192 desc.unit = "bins"; | |
193 desc.description = "Number of constant-Q transform bins per octave"; | |
194 desc.minValue = 2; | |
195 desc.maxValue = 480; | |
196 desc.defaultValue = defaultBPO; | |
197 desc.isQuantized = true; | |
198 desc.quantizeStep = 1; | |
199 list.push_back(desc); | |
200 | |
201 desc.identifier = "interpolation"; | |
202 desc.name = "Interpolation"; | |
203 desc.unit = ""; | |
204 desc.description = "Interpolation method used to fill empty cells in lower octaves"; | |
205 desc.minValue = 0; | |
206 desc.maxValue = 2; | |
207 desc.defaultValue = 2; | |
208 desc.isQuantized = true; | |
209 desc.quantizeStep = 1; | |
210 desc.valueNames.push_back("None, leave as zero"); | |
211 desc.valueNames.push_back("None, repeat prior value"); | |
212 desc.valueNames.push_back("Linear interpolation"); | |
213 list.push_back(desc); | |
214 | |
215 return list; | |
216 } | |
217 | |
218 float | |
219 CQVamp::getParameter(std::string param) const | |
220 { | |
221 if (param == "minpitch" && m_midiPitchParameters) { | |
222 return m_minMIDIPitch; | |
223 } | |
224 if (param == "maxpitch" && m_midiPitchParameters) { | |
225 return m_maxMIDIPitch; | |
226 } | |
227 if (param == "tuning" && m_midiPitchParameters) { | |
228 return m_tuningFrequency; | |
229 } | |
230 if (param == "bpo") { | |
231 return m_bpo; | |
232 } | |
233 if (param == "interpolation") { | |
234 return (float)m_interpolation; | |
235 } | |
236 if (param == "minfreq" && !m_midiPitchParameters) { | |
237 return m_minFrequency; | |
238 } | |
239 if (param == "maxfreq" && !m_midiPitchParameters) { | |
240 return m_maxFrequency; | |
241 } | |
242 std::cerr << "WARNING: CQVamp::getParameter: unknown parameter \"" | |
243 << param << "\"" << std::endl; | |
244 return 0.0; | |
245 } | |
246 | |
247 void | |
248 CQVamp::setParameter(std::string param, float value) | |
249 { | |
250 if (param == "minpitch" && m_midiPitchParameters) { | |
251 m_minMIDIPitch = int(value + 0.5f); | |
252 } else if (param == "maxpitch" && m_midiPitchParameters) { | |
253 m_maxMIDIPitch = int(value + 0.5f); | |
254 } else if (param == "tuning" && m_midiPitchParameters) { | |
255 m_tuningFrequency = value; | |
256 } else if (param == "bpo") { | |
257 m_bpo = int(value + 0.5f); | |
258 } else if (param == "interpolation") { | |
259 m_interpolation = (CQSpectrogram::Interpolation)int(value + 0.5f); | |
260 } else if (param == "minfreq" && !m_midiPitchParameters) { | |
261 m_minFrequency = value; | |
262 } else if (param == "maxfreq" && !m_midiPitchParameters) { | |
263 m_maxFrequency = value; | |
264 } else { | |
265 std::cerr << "WARNING: CQVamp::setParameter: unknown parameter \"" | |
266 << param << "\"" << std::endl; | |
267 } | |
268 } | |
269 | |
270 bool | |
271 CQVamp::initialise(size_t channels, size_t stepSize, size_t blockSize) | |
272 { | |
273 if (m_cq) { | |
274 delete m_cq; | |
275 m_cq = 0; | |
276 } | |
277 | |
278 if (channels < getMinChannelCount() || | |
279 channels > getMaxChannelCount()) return false; | |
280 | |
281 m_stepSize = stepSize; | |
282 m_blockSize = blockSize; | |
283 | |
284 if (m_midiPitchParameters) { | |
285 m_minFrequency = Pitch::getFrequencyForPitch | |
286 (m_minMIDIPitch, 0, m_tuningFrequency); | |
287 m_maxFrequency = Pitch::getFrequencyForPitch | |
288 (m_maxMIDIPitch, 0, m_tuningFrequency); | |
289 } | |
290 | |
291 reset(); | |
292 | |
293 if (!m_cq || !m_cq->isValid()) { | |
294 cerr << "CQVamp::initialise: Constant-Q parameters not valid! Not initialising" << endl; | |
295 return false; | |
296 } | |
297 | |
298 return true; | |
299 } | |
300 | |
301 void | |
302 CQVamp::reset() | |
303 { | |
304 delete m_cq; | |
305 CQParameters p(m_inputSampleRate, m_minFrequency, m_maxFrequency, m_bpo); | |
306 m_cq = new CQSpectrogram(p, m_interpolation); | |
307 m_haveStartTime = false; | |
308 m_columnCount = 0; | |
309 } | |
310 | |
311 size_t | |
312 CQVamp::getPreferredStepSize() const | |
313 { | |
314 return 0; | |
315 } | |
316 | |
317 size_t | |
318 CQVamp::getPreferredBlockSize() const | |
319 { | |
320 return 0; | |
321 } | |
322 | |
323 std::string | |
324 CQVamp::noteName(int i) const | |
325 { | |
326 static const char *names[] = { | |
327 "C", "C#", "D", "D#", "E", "F", "F#", "G", "G#", "A", "A#", "B" | |
328 }; | |
329 | |
330 const char *n = names[i % 12]; | |
331 int oct = i / 12 - 1; | |
332 char buf[20]; | |
333 sprintf(buf, "%d %s%d", i, n, oct); | |
334 | |
335 return buf; | |
336 } | |
337 | |
338 CQVamp::OutputList | |
339 CQVamp::getOutputDescriptors() const | |
340 { | |
341 OutputList list; | |
342 | |
343 OutputDescriptor d; | |
344 d.identifier = "constantq"; | |
345 d.name = "Constant-Q Spectrogram"; | |
346 d.unit = ""; | |
347 d.description = "Output of constant-Q transform, as a single vector per process block"; | |
348 d.hasFixedBinCount = true; | |
349 d.binCount = (m_cq ? m_cq->getTotalBins() : (9 * 24)); | |
350 | |
351 if (m_cq) { | |
352 char name[20]; | |
353 for (int i = 0; i < (int)d.binCount; ++i) { | |
354 float freq = m_cq->getBinFrequency(d.binCount - i - 1); | |
355 sprintf(name, "%.1f Hz", freq); | |
356 int note = Pitch::getPitchForFrequency(freq, 0, m_tuningFrequency); | |
357 float nearestFreq = | |
358 Pitch::getFrequencyForPitch(note, 0, m_tuningFrequency); | |
359 if (fabs(freq - nearestFreq) < 0.01) { | |
360 d.binNames.push_back(name + std::string(" ") + noteName(note)); | |
361 } else { | |
362 d.binNames.push_back(name); | |
363 } | |
364 } | |
365 } | |
366 | |
367 d.hasKnownExtents = false; | |
368 d.isQuantized = false; | |
369 d.sampleType = OutputDescriptor::FixedSampleRate; | |
370 d.sampleRate = m_inputSampleRate / (m_cq ? m_cq->getColumnHop() : 256); | |
371 list.push_back(d); | |
372 | |
373 return list; | |
374 } | |
375 | |
376 CQVamp::FeatureSet | |
377 CQVamp::process(const float *const *inputBuffers, | |
378 Vamp::RealTime timestamp) | |
379 { | |
380 if (!m_cq) { | |
381 cerr << "ERROR: CQVamp::process: " | |
382 << "Plugin has not been initialised" | |
383 << endl; | |
384 return FeatureSet(); | |
385 } | |
386 | |
387 if (!m_haveStartTime) { | |
388 m_startTime = timestamp; | |
389 m_haveStartTime = true; | |
390 } | |
391 | |
392 vector<double> data; | |
393 for (int i = 0; i < m_blockSize; ++i) data.push_back(inputBuffers[0][i]); | |
394 | |
395 vector<vector<double> > cqout = m_cq->process(data); | |
396 return convertToFeatures(cqout); | |
397 } | |
398 | |
399 CQVamp::FeatureSet | |
400 CQVamp::getRemainingFeatures() | |
401 { | |
402 vector<vector<double> > cqout = m_cq->getRemainingOutput(); | |
403 return convertToFeatures(cqout); | |
404 } | |
405 | |
406 CQVamp::FeatureSet | |
407 CQVamp::convertToFeatures(const vector<vector<double> > &cqout) | |
408 { | |
409 FeatureSet returnFeatures; | |
410 | |
411 int width = cqout.size(); | |
412 int height = m_cq->getTotalBins(); | |
413 | |
414 for (int i = 0; i < width; ++i) { | |
415 | |
416 vector<float> column(height, 0.f); | |
417 int thisHeight = cqout[i].size(); | |
418 for (int j = 0; j < thisHeight; ++j) { | |
419 column[j] = cqout[i][j]; | |
420 } | |
421 | |
422 // put low frequencies at the start | |
423 std::reverse(column.begin(), column.end()); | |
424 | |
425 Feature feature; | |
426 feature.hasTimestamp = true; | |
427 feature.timestamp = m_startTime + Vamp::RealTime::frame2RealTime | |
428 (m_columnCount * m_cq->getColumnHop() - m_cq->getLatency(), | |
429 m_inputSampleRate); | |
430 feature.values = column; | |
431 feature.label = ""; | |
432 | |
433 // cerr << "timestamp = " << feature.timestamp << " (start time = " << m_startTime << ", column count = " << m_columnCount << ", latency = " << m_cq->getLatency() << ", sample rate " << m_inputSampleRate << ")" << endl; | |
434 | |
435 if (feature.timestamp >= m_startTime) { | |
436 returnFeatures[0].push_back(feature); | |
437 } | |
438 | |
439 ++m_columnCount; | |
440 } | |
441 | |
442 return returnFeatures; | |
443 } | |
444 |