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 }