Mercurial > hg > vamp-plugin-sdk
comparison examples/FixedTempoEstimator.cpp @ 243:3cf5bd155e5b
* Some build improvements
* Make parameterisable values in tempo estimator into parameters
author | cannam |
---|---|
date | Mon, 10 Nov 2008 22:04:40 +0000 |
parents | 6b30e064cab7 |
children | 8042ab66f707 |
comparison
equal
deleted
inserted
replaced
242:7f3a806ed1df | 243:3cf5bd155e5b |
---|---|
44 using Vamp::RealTime; | 44 using Vamp::RealTime; |
45 | 45 |
46 #include <cmath> | 46 #include <cmath> |
47 | 47 |
48 | 48 |
49 FixedTempoEstimator::FixedTempoEstimator(float inputSampleRate) : | 49 class FixedTempoEstimator::D |
50 Plugin(inputSampleRate), | 50 { |
51 public: | |
52 D(float inputSampleRate); | |
53 ~D(); | |
54 | |
55 size_t getPreferredStepSize() const { return 64; } | |
56 size_t getPreferredBlockSize() const { return 256; } | |
57 | |
58 ParameterList getParameterDescriptors() const; | |
59 float getParameter(string id) const; | |
60 void setParameter(string id, float value); | |
61 | |
62 OutputList getOutputDescriptors() const; | |
63 | |
64 bool initialise(size_t channels, size_t stepSize, size_t blockSize); | |
65 void reset(); | |
66 FeatureSet process(const float *const *, RealTime); | |
67 FeatureSet getRemainingFeatures(); | |
68 | |
69 private: | |
70 void calculate(); | |
71 FeatureSet assembleFeatures(); | |
72 | |
73 float lag2tempo(int); | |
74 int tempo2lag(float); | |
75 | |
76 float m_inputSampleRate; | |
77 size_t m_stepSize; | |
78 size_t m_blockSize; | |
79 | |
80 float m_minbpm; | |
81 float m_maxbpm; | |
82 float m_maxdflen; | |
83 | |
84 float *m_priorMagnitudes; | |
85 | |
86 size_t m_dfsize; | |
87 float *m_df; | |
88 float *m_r; | |
89 float *m_fr; | |
90 float *m_t; | |
91 size_t m_n; | |
92 | |
93 Vamp::RealTime m_start; | |
94 Vamp::RealTime m_lasttime; | |
95 }; | |
96 | |
97 FixedTempoEstimator::D::D(float inputSampleRate) : | |
98 m_inputSampleRate(inputSampleRate), | |
51 m_stepSize(0), | 99 m_stepSize(0), |
52 m_blockSize(0), | 100 m_blockSize(0), |
101 m_minbpm(50), | |
102 m_maxbpm(190), | |
103 m_maxdflen(10), | |
53 m_priorMagnitudes(0), | 104 m_priorMagnitudes(0), |
54 m_df(0), | 105 m_df(0), |
55 m_r(0), | 106 m_r(0), |
56 m_fr(0), | 107 m_fr(0), |
57 m_t(0), | 108 m_t(0), |
58 m_n(0) | 109 m_n(0) |
59 { | 110 { |
60 } | 111 } |
61 | 112 |
62 FixedTempoEstimator::~FixedTempoEstimator() | 113 FixedTempoEstimator::D::~D() |
63 { | 114 { |
64 delete[] m_priorMagnitudes; | 115 delete[] m_priorMagnitudes; |
65 delete[] m_df; | 116 delete[] m_df; |
66 delete[] m_r; | 117 delete[] m_r; |
67 delete[] m_fr; | 118 delete[] m_fr; |
68 delete[] m_t; | 119 delete[] m_t; |
69 } | 120 } |
70 | 121 |
71 string | 122 FixedTempoEstimator::ParameterList |
72 FixedTempoEstimator::getIdentifier() const | 123 FixedTempoEstimator::D::getParameterDescriptors() const |
73 { | 124 { |
74 return "fixedtempo"; | 125 ParameterList list; |
75 } | 126 |
76 | 127 ParameterDescriptor d; |
77 string | 128 d.identifier = "minbpm"; |
78 FixedTempoEstimator::getName() const | 129 d.name = "Minimum estimated tempo"; |
79 { | 130 d.description = "Minimum beat-per-minute value which the tempo estimator is able to return"; |
80 return "Simple Fixed Tempo Estimator"; | 131 d.unit = "bpm"; |
81 } | 132 d.minValue = 10; |
82 | 133 d.maxValue = 360; |
83 string | 134 d.defaultValue = 50; |
84 FixedTempoEstimator::getDescription() const | 135 d.isQuantized = false; |
85 { | 136 list.push_back(d); |
86 return "Study a short section of audio and estimate its tempo, assuming the tempo is constant"; | 137 |
87 } | 138 d.identifier = "maxbpm"; |
88 | 139 d.name = "Maximum estimated tempo"; |
89 string | 140 d.description = "Maximum beat-per-minute value which the tempo estimator is able to return"; |
90 FixedTempoEstimator::getMaker() const | 141 d.defaultValue = 190; |
91 { | 142 list.push_back(d); |
92 return "Vamp SDK Example Plugins"; | 143 |
93 } | 144 d.identifier = "maxdflen"; |
94 | 145 d.name = "Input duration to study"; |
95 int | 146 d.description = "Length of audio input, in seconds, which should be taken into account when estimating tempo. There is no need to supply the plugin with any further input once this time has elapsed since the start of the audio. The tempo estimator may use only the first part of this, up to eight times the slowest beat duration: increasing this value further than that is unlikely to improve results."; |
96 FixedTempoEstimator::getPluginVersion() const | 147 d.unit = "s"; |
97 { | 148 d.minValue = 2; |
98 return 1; | 149 d.maxValue = 40; |
99 } | 150 d.defaultValue = 10; |
100 | 151 list.push_back(d); |
101 string | 152 |
102 FixedTempoEstimator::getCopyright() const | 153 return list; |
103 { | 154 } |
104 return "Code copyright 2008 Queen Mary, University of London. Freely redistributable (BSD license)"; | 155 |
105 } | 156 float |
106 | 157 FixedTempoEstimator::D::getParameter(string id) const |
107 size_t | 158 { |
108 FixedTempoEstimator::getPreferredStepSize() const | 159 if (id == "minbpm") { |
109 { | 160 return m_minbpm; |
110 return 64; | 161 } else if (id == "maxbpm") { |
111 } | 162 return m_maxbpm; |
112 | 163 } else if (id == "maxdflen") { |
113 size_t | 164 return m_maxdflen; |
114 FixedTempoEstimator::getPreferredBlockSize() const | 165 } |
115 { | 166 return 0.f; |
116 return 256; | |
117 } | |
118 | |
119 bool | |
120 FixedTempoEstimator::initialise(size_t channels, size_t stepSize, size_t blockSize) | |
121 { | |
122 if (channels < getMinChannelCount() || | |
123 channels > getMaxChannelCount()) return false; | |
124 | |
125 m_stepSize = stepSize; | |
126 m_blockSize = blockSize; | |
127 | |
128 float dfLengthSecs = 10.f; | |
129 m_dfsize = (dfLengthSecs * m_inputSampleRate) / m_stepSize; | |
130 | |
131 m_priorMagnitudes = new float[m_blockSize/2]; | |
132 m_df = new float[m_dfsize]; | |
133 | |
134 for (size_t i = 0; i < m_blockSize/2; ++i) { | |
135 m_priorMagnitudes[i] = 0.f; | |
136 } | |
137 for (size_t i = 0; i < m_dfsize; ++i) { | |
138 m_df[i] = 0.f; | |
139 } | |
140 | |
141 m_n = 0; | |
142 | |
143 return true; | |
144 } | 167 } |
145 | 168 |
146 void | 169 void |
147 FixedTempoEstimator::reset() | 170 FixedTempoEstimator::D::setParameter(string id, float value) |
148 { | 171 { |
149 cerr << "FixedTempoEstimator: reset called" << endl; | 172 if (id == "minbpm") { |
150 | 173 m_minbpm = value; |
151 if (!m_priorMagnitudes) return; | 174 } else if (id == "maxbpm") { |
152 | 175 m_maxbpm = value; |
153 cerr << "FixedTempoEstimator: resetting" << endl; | 176 } else if (id == "maxdflen") { |
154 | 177 m_maxdflen = value; |
155 for (size_t i = 0; i < m_blockSize/2; ++i) { | 178 } |
156 m_priorMagnitudes[i] = 0.f; | |
157 } | |
158 for (size_t i = 0; i < m_dfsize; ++i) { | |
159 m_df[i] = 0.f; | |
160 } | |
161 | |
162 delete[] m_r; | |
163 m_r = 0; | |
164 | |
165 delete[] m_fr; | |
166 m_fr = 0; | |
167 | |
168 delete[] m_t; | |
169 m_t = 0; | |
170 | |
171 m_n = 0; | |
172 | |
173 m_start = RealTime::zeroTime; | |
174 m_lasttime = RealTime::zeroTime; | |
175 } | |
176 | |
177 FixedTempoEstimator::ParameterList | |
178 FixedTempoEstimator::getParameterDescriptors() const | |
179 { | |
180 ParameterList list; | |
181 return list; | |
182 } | |
183 | |
184 float | |
185 FixedTempoEstimator::getParameter(std::string id) const | |
186 { | |
187 return 0.f; | |
188 } | |
189 | |
190 void | |
191 FixedTempoEstimator::setParameter(std::string id, float value) | |
192 { | |
193 } | 179 } |
194 | 180 |
195 static int TempoOutput = 0; | 181 static int TempoOutput = 0; |
196 static int CandidatesOutput = 1; | 182 static int CandidatesOutput = 1; |
197 static int DFOutput = 2; | 183 static int DFOutput = 2; |
198 static int ACFOutput = 3; | 184 static int ACFOutput = 3; |
199 static int FilteredACFOutput = 4; | 185 static int FilteredACFOutput = 4; |
200 | 186 |
201 FixedTempoEstimator::OutputList | 187 FixedTempoEstimator::OutputList |
202 FixedTempoEstimator::getOutputDescriptors() const | 188 FixedTempoEstimator::D::getOutputDescriptors() const |
203 { | 189 { |
204 OutputList list; | 190 OutputList list; |
205 | 191 |
206 OutputDescriptor d; | 192 OutputDescriptor d; |
207 d.identifier = "tempo"; | 193 d.identifier = "tempo"; |
258 list.push_back(d); | 244 list.push_back(d); |
259 | 245 |
260 return list; | 246 return list; |
261 } | 247 } |
262 | 248 |
249 bool | |
250 FixedTempoEstimator::D::initialise(size_t channels, | |
251 size_t stepSize, size_t blockSize) | |
252 { | |
253 m_stepSize = stepSize; | |
254 m_blockSize = blockSize; | |
255 | |
256 float dfLengthSecs = m_maxdflen; | |
257 m_dfsize = (dfLengthSecs * m_inputSampleRate) / m_stepSize; | |
258 | |
259 m_priorMagnitudes = new float[m_blockSize/2]; | |
260 m_df = new float[m_dfsize]; | |
261 | |
262 for (size_t i = 0; i < m_blockSize/2; ++i) { | |
263 m_priorMagnitudes[i] = 0.f; | |
264 } | |
265 for (size_t i = 0; i < m_dfsize; ++i) { | |
266 m_df[i] = 0.f; | |
267 } | |
268 | |
269 m_n = 0; | |
270 | |
271 return true; | |
272 } | |
273 | |
274 void | |
275 FixedTempoEstimator::D::reset() | |
276 { | |
277 if (!m_priorMagnitudes) return; | |
278 | |
279 for (size_t i = 0; i < m_blockSize/2; ++i) { | |
280 m_priorMagnitudes[i] = 0.f; | |
281 } | |
282 for (size_t i = 0; i < m_dfsize; ++i) { | |
283 m_df[i] = 0.f; | |
284 } | |
285 | |
286 delete[] m_r; | |
287 m_r = 0; | |
288 | |
289 delete[] m_fr; | |
290 m_fr = 0; | |
291 | |
292 delete[] m_t; | |
293 m_t = 0; | |
294 | |
295 m_n = 0; | |
296 | |
297 m_start = RealTime::zeroTime; | |
298 m_lasttime = RealTime::zeroTime; | |
299 } | |
300 | |
263 FixedTempoEstimator::FeatureSet | 301 FixedTempoEstimator::FeatureSet |
264 FixedTempoEstimator::process(const float *const *inputBuffers, RealTime ts) | 302 FixedTempoEstimator::D::process(const float *const *inputBuffers, RealTime ts) |
265 { | 303 { |
266 FeatureSet fs; | 304 FeatureSet fs; |
267 | 305 |
268 if (m_stepSize == 0) { | 306 if (m_stepSize == 0) { |
269 cerr << "ERROR: FixedTempoEstimator::process: " | 307 cerr << "ERROR: FixedTempoEstimator::process: " |
270 << "FixedTempoEstimator has not been initialised" | 308 << "FixedTempoEstimator has not been initialised" |
271 << endl; | 309 << endl; |
272 return fs; | 310 return fs; |
273 } | 311 } |
274 | 312 |
275 // if (m_n < m_dfsize) cerr << "m_n = " << m_n << endl; | |
276 | |
277 if (m_n == 0) m_start = ts; | 313 if (m_n == 0) m_start = ts; |
278 m_lasttime = ts; | 314 m_lasttime = ts; |
279 | 315 |
280 if (m_n == m_dfsize) { | 316 if (m_n == m_dfsize) { |
281 calculate(); | 317 calculate(); |
301 | 337 |
302 m_df[m_n] = value; | 338 m_df[m_n] = value; |
303 | 339 |
304 ++m_n; | 340 ++m_n; |
305 return fs; | 341 return fs; |
306 } | 342 } |
307 | 343 |
308 FixedTempoEstimator::FeatureSet | 344 FixedTempoEstimator::FeatureSet |
309 FixedTempoEstimator::getRemainingFeatures() | 345 FixedTempoEstimator::D::getRemainingFeatures() |
310 { | 346 { |
311 FeatureSet fs; | 347 FeatureSet fs; |
312 if (m_n > m_dfsize) return fs; | 348 if (m_n > m_dfsize) return fs; |
313 calculate(); | 349 calculate(); |
314 fs = assembleFeatures(); | 350 fs = assembleFeatures(); |
315 ++m_n; | 351 ++m_n; |
316 return fs; | 352 return fs; |
317 } | 353 } |
318 | 354 |
319 float | 355 float |
320 FixedTempoEstimator::lag2tempo(int lag) | 356 FixedTempoEstimator::D::lag2tempo(int lag) |
321 { | 357 { |
322 return 60.f / ((lag * m_stepSize) / m_inputSampleRate); | 358 return 60.f / ((lag * m_stepSize) / m_inputSampleRate); |
323 } | 359 } |
324 | 360 |
325 int | 361 int |
326 FixedTempoEstimator::tempo2lag(float tempo) | 362 FixedTempoEstimator::D::tempo2lag(float tempo) |
327 { | 363 { |
328 return ((60.f / tempo) * m_inputSampleRate) / m_stepSize; | 364 return ((60.f / tempo) * m_inputSampleRate) / m_stepSize; |
329 } | 365 } |
330 | 366 |
331 void | 367 void |
332 FixedTempoEstimator::calculate() | 368 FixedTempoEstimator::D::calculate() |
333 { | 369 { |
334 cerr << "FixedTempoEstimator::calculate: m_n = " << m_n << endl; | 370 cerr << "FixedTempoEstimator::calculate: m_n = " << m_n << endl; |
335 | 371 |
336 if (m_r) { | 372 if (m_r) { |
337 cerr << "FixedTempoEstimator::calculate: calculation already happened?" << endl; | 373 cerr << "FixedTempoEstimator::calculate: calculation already happened?" << endl; |
338 return; | 374 return; |
339 } | 375 } |
340 | 376 |
341 if (m_n < m_dfsize / 9) { | 377 if (m_n < m_dfsize / 9 && |
342 cerr << "FixedTempoEstimator::calculate: Not enough data to go on (have " << m_n << ", want at least " << m_dfsize/4 << ")" << endl; | 378 m_n < (1.0 * m_inputSampleRate) / m_stepSize) { // 1 second |
343 return; // not enough data (perhaps we should return the duration of the input as the "estimated" beat length?) | 379 cerr << "FixedTempoEstimator::calculate: Input is too short" << endl; |
380 return; | |
344 } | 381 } |
345 | 382 |
346 int n = m_n; | 383 int n = m_n; |
347 | 384 |
348 m_r = new float[n/2]; | 385 m_r = new float[n/2]; |
417 | 454 |
418 m_fr[i] += m_fr[i] * (weight / 3); | 455 m_fr[i] += m_fr[i] * (weight / 3); |
419 } | 456 } |
420 } | 457 } |
421 | 458 |
422 | |
423 FixedTempoEstimator::FeatureSet | 459 FixedTempoEstimator::FeatureSet |
424 FixedTempoEstimator::assembleFeatures() | 460 FixedTempoEstimator::D::assembleFeatures() |
425 { | 461 { |
426 FeatureSet fs; | 462 FeatureSet fs; |
427 if (!m_r) return fs; // No results | 463 if (!m_r) return fs; // No results |
428 | 464 |
429 Feature feature; | 465 Feature feature; |
453 if (i == n/2-1) feature.label = ""; | 489 if (i == n/2-1) feature.label = ""; |
454 else feature.label = buffer; | 490 else feature.label = buffer; |
455 fs[ACFOutput].push_back(feature); | 491 fs[ACFOutput].push_back(feature); |
456 } | 492 } |
457 | 493 |
458 float t0 = 50.f; // our minimum detected tempo (could be a parameter) | 494 float t0 = m_minbpm; // our minimum detected tempo |
459 float t1 = 190.f; // our maximum detected tempo | 495 float t1 = m_maxbpm; // our maximum detected tempo |
460 | |
461 //!!! need some way for the host (or at least, the user) to know | |
462 //!!! that it should only pass a certain amount of | |
463 //!!! input... e.g. by making the amount configurable | |
464 | 496 |
465 int p0 = tempo2lag(t1); | 497 int p0 = tempo2lag(t1); |
466 int p1 = tempo2lag(t0); | 498 int p1 = tempo2lag(t0); |
467 | 499 |
468 std::map<float, int> candidates; | 500 std::map<float, int> candidates; |
501 | |
502 std::cerr << "minbpm = " << m_minbpm << ", p0 = " << p0 << ", p1 = " << p1 << std::endl; | |
469 | 503 |
470 for (int i = p0; i <= p1 && i < n/2-1; ++i) { | 504 for (int i = p0; i <= p1 && i < n/2-1; ++i) { |
471 | 505 |
472 if (m_fr[i] > m_fr[i-1] && | 506 if (m_fr[i] > m_fr[i-1] && |
473 m_fr[i] > m_fr[i+1]) { | 507 m_fr[i] > m_fr[i+1]) { |
530 | 564 |
531 fs[CandidatesOutput].push_back(feature); | 565 fs[CandidatesOutput].push_back(feature); |
532 | 566 |
533 return fs; | 567 return fs; |
534 } | 568 } |
569 | |
570 | |
571 | |
572 FixedTempoEstimator::FixedTempoEstimator(float inputSampleRate) : | |
573 Plugin(inputSampleRate), | |
574 m_d(new D(inputSampleRate)) | |
575 { | |
576 } | |
577 | |
578 FixedTempoEstimator::~FixedTempoEstimator() | |
579 { | |
580 } | |
581 | |
582 string | |
583 FixedTempoEstimator::getIdentifier() const | |
584 { | |
585 return "fixedtempo"; | |
586 } | |
587 | |
588 string | |
589 FixedTempoEstimator::getName() const | |
590 { | |
591 return "Simple Fixed Tempo Estimator"; | |
592 } | |
593 | |
594 string | |
595 FixedTempoEstimator::getDescription() const | |
596 { | |
597 return "Study a short section of audio and estimate its tempo, assuming the tempo is constant"; | |
598 } | |
599 | |
600 string | |
601 FixedTempoEstimator::getMaker() const | |
602 { | |
603 return "Vamp SDK Example Plugins"; | |
604 } | |
605 | |
606 int | |
607 FixedTempoEstimator::getPluginVersion() const | |
608 { | |
609 return 1; | |
610 } | |
611 | |
612 string | |
613 FixedTempoEstimator::getCopyright() const | |
614 { | |
615 return "Code copyright 2008 Queen Mary, University of London. Freely redistributable (BSD license)"; | |
616 } | |
617 | |
618 size_t | |
619 FixedTempoEstimator::getPreferredStepSize() const | |
620 { | |
621 return m_d->getPreferredStepSize(); | |
622 } | |
623 | |
624 size_t | |
625 FixedTempoEstimator::getPreferredBlockSize() const | |
626 { | |
627 return m_d->getPreferredBlockSize(); | |
628 } | |
629 | |
630 bool | |
631 FixedTempoEstimator::initialise(size_t channels, size_t stepSize, size_t blockSize) | |
632 { | |
633 if (channels < getMinChannelCount() || | |
634 channels > getMaxChannelCount()) return false; | |
635 | |
636 return m_d->initialise(channels, stepSize, blockSize); | |
637 } | |
638 | |
639 void | |
640 FixedTempoEstimator::reset() | |
641 { | |
642 return m_d->reset(); | |
643 } | |
644 | |
645 FixedTempoEstimator::ParameterList | |
646 FixedTempoEstimator::getParameterDescriptors() const | |
647 { | |
648 return m_d->getParameterDescriptors(); | |
649 } | |
650 | |
651 float | |
652 FixedTempoEstimator::getParameter(std::string id) const | |
653 { | |
654 return m_d->getParameter(id); | |
655 } | |
656 | |
657 void | |
658 FixedTempoEstimator::setParameter(std::string id, float value) | |
659 { | |
660 m_d->setParameter(id, value); | |
661 } | |
662 | |
663 FixedTempoEstimator::OutputList | |
664 FixedTempoEstimator::getOutputDescriptors() const | |
665 { | |
666 return m_d->getOutputDescriptors(); | |
667 } | |
668 | |
669 FixedTempoEstimator::FeatureSet | |
670 FixedTempoEstimator::process(const float *const *inputBuffers, RealTime ts) | |
671 { | |
672 return m_d->process(inputBuffers, ts); | |
673 } | |
674 | |
675 FixedTempoEstimator::FeatureSet | |
676 FixedTempoEstimator::getRemainingFeatures() | |
677 { | |
678 return m_d->getRemainingFeatures(); | |
679 } |