Mercurial > hg > constant-q-cpp
comparison vamp/CQChromaVamp.cpp @ 170:b96b0addbca7
Pull out chroma class into library
author | Chris Cannam <c.cannam@qmul.ac.uk> |
---|---|
date | Wed, 04 Feb 2015 15:09:06 +0000 |
parents | 8a1d240ac542 |
children | a12642e36167 |
comparison
equal
deleted
inserted
replaced
169:8a1d240ac542 | 170:b96b0addbca7 |
---|---|
29 authorization. | 29 authorization. |
30 */ | 30 */ |
31 | 31 |
32 #include "CQChromaVamp.h" | 32 #include "CQChromaVamp.h" |
33 | 33 |
34 #include "cq/CQSpectrogram.h" | 34 #include "cq/Chromagram.h" |
35 | |
36 #include "Pitch.h" | |
37 | 35 |
38 #include <algorithm> | 36 #include <algorithm> |
39 #include <cstdio> | |
40 | 37 |
41 using std::string; | 38 using std::string; |
42 using std::vector; | 39 using std::vector; |
43 using std::cerr; | 40 using std::cerr; |
44 using std::endl; | 41 using std::endl; |
52 Vamp::Plugin(inputSampleRate), | 49 Vamp::Plugin(inputSampleRate), |
53 m_lowestOctave(defaultLowestOctave), | 50 m_lowestOctave(defaultLowestOctave), |
54 m_octaveCount(defaultOctaveCount), | 51 m_octaveCount(defaultOctaveCount), |
55 m_tuningFrequency(defaultTuningFrequency), | 52 m_tuningFrequency(defaultTuningFrequency), |
56 m_bpo(defaultBPO), | 53 m_bpo(defaultBPO), |
57 m_cq(0), | 54 m_chroma(0), |
58 m_maxFrequency(0), | |
59 m_minFrequency(0), | |
60 m_haveStartTime(false), | 55 m_haveStartTime(false), |
61 m_columnCount(0) | 56 m_columnCount(0) |
62 { | 57 { |
63 } | 58 } |
64 | 59 |
65 CQChromaVamp::~CQChromaVamp() | 60 CQChromaVamp::~CQChromaVamp() |
66 { | 61 { |
67 delete m_cq; | 62 delete m_chroma; |
68 } | 63 } |
69 | 64 |
70 string | 65 string |
71 CQChromaVamp::getIdentifier() const | 66 CQChromaVamp::getIdentifier() const |
72 { | 67 { |
194 } | 189 } |
195 | 190 |
196 bool | 191 bool |
197 CQChromaVamp::initialise(size_t channels, size_t stepSize, size_t blockSize) | 192 CQChromaVamp::initialise(size_t channels, size_t stepSize, size_t blockSize) |
198 { | 193 { |
199 if (m_cq) { | 194 if (m_chroma) { |
200 delete m_cq; | 195 delete m_chroma; |
201 m_cq = 0; | 196 m_chroma = 0; |
202 } | 197 } |
203 | 198 |
204 if (channels < getMinChannelCount() || | 199 if (channels < getMinChannelCount() || |
205 channels > getMaxChannelCount()) return false; | 200 channels > getMaxChannelCount()) return false; |
206 | 201 |
207 m_stepSize = stepSize; | 202 m_stepSize = stepSize; |
208 m_blockSize = blockSize; | 203 m_blockSize = blockSize; |
209 | 204 |
210 int highestOctave = m_lowestOctave + m_octaveCount - 1; | |
211 | |
212 int midiPitchLimit = (1 + highestOctave) * 12 + 12; // C just beyond top | |
213 double midiPitchLimitFreq = | |
214 Pitch::getFrequencyForPitch(midiPitchLimit, 0, m_tuningFrequency); | |
215 | |
216 // Max frequency is frequency of the MIDI pitch just beyond the | |
217 // top octave range (midiPitchLimit) minus one bin, then minus | |
218 // floor(bins per semitone / 2) | |
219 int bps = m_bpo / 12; | |
220 m_maxFrequency = midiPitchLimitFreq / pow(2, (1.0 + floor(bps/2)) / m_bpo); | |
221 | |
222 // Min frequency is frequency of midiPitchLimit lowered by the | |
223 // appropriate number of octaves. | |
224 m_minFrequency = midiPitchLimitFreq / pow(2, m_octaveCount + 1); | |
225 | |
226 // cerr << "lowest octave: " << m_lowestOctave << ", highest octave: " | |
227 // << highestOctave << ", limit midi pitch: " << midiPitchLimit | |
228 // << ", min freq " << m_minFrequency << ", max freq " << m_maxFrequency | |
229 // << endl; | |
230 | |
231 reset(); | 205 reset(); |
232 | 206 |
233 if (!m_cq || !m_cq->isValid()) { | 207 if (!m_chroma || !m_chroma->isValid()) { |
234 cerr << "CQVamp::initialise: Constant-Q parameters not valid! Not initialising" << endl; | 208 cerr << "CQVamp::initialise: Constant-Q parameters not valid! Not initialising" << endl; |
235 return false; | 209 return false; |
236 } | 210 } |
237 | 211 |
238 return true; | 212 return true; |
239 } | 213 } |
240 | 214 |
241 void | 215 void |
242 CQChromaVamp::reset() | 216 CQChromaVamp::reset() |
243 { | 217 { |
244 delete m_cq; | 218 delete m_chroma; |
245 CQParameters p(m_inputSampleRate, m_minFrequency, m_maxFrequency, m_bpo); | 219 Chromagram::Parameters p(m_inputSampleRate); |
246 m_cq = new CQSpectrogram(p, CQSpectrogram::InterpolateLinear); | 220 p.lowestOctave = m_lowestOctave; |
221 p.octaves = m_octaveCount; | |
222 p.bpo = m_bpo; | |
223 p.tuningFrequency = m_tuningFrequency; | |
224 | |
225 m_chroma = new Chromagram(p); | |
247 | 226 |
248 m_haveStartTime = false; | 227 m_haveStartTime = false; |
249 m_startTime = Vamp::RealTime::zeroTime; | 228 m_startTime = Vamp::RealTime::zeroTime; |
250 m_columnCount = 0; | 229 m_columnCount = 0; |
251 } | 230 } |
263 } | 242 } |
264 | 243 |
265 CQChromaVamp::OutputList | 244 CQChromaVamp::OutputList |
266 CQChromaVamp::getOutputDescriptors() const | 245 CQChromaVamp::getOutputDescriptors() const |
267 { | 246 { |
268 static const char *names[] = { | |
269 "C", "C#", "D", "D#", "E", "F", "F#", "G", "G#", "A", "A#", "B" | |
270 }; | |
271 | |
272 OutputList list; | 247 OutputList list; |
273 | 248 |
274 OutputDescriptor d; | 249 OutputDescriptor d; |
275 d.identifier = "chromagram"; | 250 d.identifier = "chromagram"; |
276 d.name = "Chromagram"; | 251 d.name = "Chromagram"; |
277 d.unit = ""; | 252 d.unit = ""; |
278 d.description = "Chromagram obtained from output of constant-Q transform, folding over each process block into a single-octave vector"; | 253 d.description = "Chromagram obtained from output of constant-Q transform, folding over each process block into a single-octave vector"; |
279 d.hasFixedBinCount = true; | 254 d.hasFixedBinCount = true; |
280 d.binCount = m_bpo; | 255 d.binCount = m_bpo; |
281 | 256 |
282 if (m_cq) { | 257 if (m_chroma) { |
283 char name[20]; | |
284 for (int i = 0; i < (int)d.binCount; ++i) { | 258 for (int i = 0; i < (int)d.binCount; ++i) { |
285 float freq = m_cq->getBinFrequency(d.binCount - i - 1); | 259 d.binNames.push_back(m_chroma->getBinName(i)); |
286 int note = Pitch::getPitchForFrequency(freq, 0, m_tuningFrequency); | |
287 float nearestFreq = | |
288 Pitch::getFrequencyForPitch(note, 0, m_tuningFrequency); | |
289 sprintf(name, "%d", i); | |
290 if (fabs(freq - nearestFreq) < 0.01) { | |
291 d.binNames.push_back(name + std::string(" ") + names[note % 12]); | |
292 } else { | |
293 d.binNames.push_back(name); | |
294 } | |
295 } | 260 } |
296 } | 261 } |
297 | 262 |
298 d.hasKnownExtents = false; | 263 d.hasKnownExtents = false; |
299 d.isQuantized = false; | 264 d.isQuantized = false; |
300 d.sampleType = OutputDescriptor::FixedSampleRate; | 265 d.sampleType = OutputDescriptor::FixedSampleRate; |
301 d.sampleRate = m_inputSampleRate / (m_cq ? m_cq->getColumnHop() : 256); | 266 d.sampleRate = m_inputSampleRate / (m_chroma ? m_chroma->getColumnHop() : 256); |
302 list.push_back(d); | 267 list.push_back(d); |
303 | 268 |
304 return list; | 269 return list; |
305 } | 270 } |
306 | 271 |
307 CQChromaVamp::FeatureSet | 272 CQChromaVamp::FeatureSet |
308 CQChromaVamp::process(const float *const *inputBuffers, | 273 CQChromaVamp::process(const float *const *inputBuffers, |
309 Vamp::RealTime timestamp) | 274 Vamp::RealTime timestamp) |
310 { | 275 { |
311 if (!m_cq) { | 276 if (!m_chroma) { |
312 cerr << "ERROR: CQChromaVamp::process: " | 277 cerr << "ERROR: CQChromaVamp::process: " |
313 << "Plugin has not been initialised" | 278 << "Plugin has not been initialised" |
314 << endl; | 279 << endl; |
315 return FeatureSet(); | 280 return FeatureSet(); |
316 } | 281 } |
321 } | 286 } |
322 | 287 |
323 vector<double> data; | 288 vector<double> data; |
324 for (int i = 0; i < m_blockSize; ++i) data.push_back(inputBuffers[0][i]); | 289 for (int i = 0; i < m_blockSize; ++i) data.push_back(inputBuffers[0][i]); |
325 | 290 |
326 vector<vector<double> > cqout = m_cq->process(data); | 291 vector<vector<double> > chromaout = m_chroma->process(data); |
327 return convertToFeatures(cqout); | 292 return convertToFeatures(chromaout); |
328 } | 293 } |
329 | 294 |
330 CQChromaVamp::FeatureSet | 295 CQChromaVamp::FeatureSet |
331 CQChromaVamp::getRemainingFeatures() | 296 CQChromaVamp::getRemainingFeatures() |
332 { | 297 { |
333 vector<vector<double> > cqout = m_cq->getRemainingOutput(); | 298 vector<vector<double> > chromaout = m_chroma->getRemainingOutput(); |
334 return convertToFeatures(cqout); | 299 return convertToFeatures(chromaout); |
335 } | 300 } |
336 | 301 |
337 CQChromaVamp::FeatureSet | 302 CQChromaVamp::FeatureSet |
338 CQChromaVamp::convertToFeatures(const vector<vector<double> > &cqout) | 303 CQChromaVamp::convertToFeatures(const vector<vector<double> > &chromaout) |
339 { | 304 { |
340 FeatureSet returnFeatures; | 305 FeatureSet returnFeatures; |
341 | 306 |
342 int width = cqout.size(); | 307 int width = chromaout.size(); |
343 | 308 |
344 for (int i = 0; i < width; ++i) { | 309 for (int i = 0; i < width; ++i) { |
345 | 310 |
346 vector<float> column(m_bpo, 0.f); | 311 vector<float> column(chromaout[i].begin(), chromaout[i].end()); |
347 | |
348 // fold and invert to put low frequencies at the start | |
349 | |
350 int thisHeight = cqout[i].size(); | |
351 | |
352 for (int j = 0; j < thisHeight; ++j) { | |
353 column[m_bpo - (j % m_bpo) - 1] += cqout[i][j]; | |
354 } | |
355 | 312 |
356 Feature feature; | 313 Feature feature; |
357 feature.hasTimestamp = true; | 314 feature.hasTimestamp = true; |
358 feature.timestamp = m_startTime + Vamp::RealTime::frame2RealTime | 315 feature.timestamp = m_startTime + Vamp::RealTime::frame2RealTime |
359 (m_columnCount * m_cq->getColumnHop() - m_cq->getLatency(), | 316 (m_columnCount * m_chroma->getColumnHop() - m_chroma->getLatency(), |
360 m_inputSampleRate); | 317 m_inputSampleRate); |
361 feature.values = column; | 318 feature.values = column; |
362 feature.label = ""; | 319 feature.label = ""; |
363 | 320 |
364 if (feature.timestamp >= m_startTime) { | 321 if (feature.timestamp >= m_startTime) { |