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) {