cannam@198
|
1 /* -*- c-basic-offset: 4 indent-tabs-mode: nil -*- vi:set ts=8 sts=4 sw=4: */
|
cannam@198
|
2
|
cannam@198
|
3 /*
|
cannam@198
|
4 Vamp
|
cannam@198
|
5
|
cannam@198
|
6 An API for audio analysis and feature extraction plugins.
|
cannam@198
|
7
|
cannam@198
|
8 Centre for Digital Music, Queen Mary, University of London.
|
cannam@198
|
9 Copyright 2006-2008 Chris Cannam and QMUL.
|
cannam@198
|
10
|
cannam@198
|
11 Permission is hereby granted, free of charge, to any person
|
cannam@198
|
12 obtaining a copy of this software and associated documentation
|
cannam@198
|
13 files (the "Software"), to deal in the Software without
|
cannam@198
|
14 restriction, including without limitation the rights to use, copy,
|
cannam@198
|
15 modify, merge, publish, distribute, sublicense, and/or sell copies
|
cannam@198
|
16 of the Software, and to permit persons to whom the Software is
|
cannam@198
|
17 furnished to do so, subject to the following conditions:
|
cannam@198
|
18
|
cannam@198
|
19 The above copyright notice and this permission notice shall be
|
cannam@198
|
20 included in all copies or substantial portions of the Software.
|
cannam@198
|
21
|
cannam@198
|
22 THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
cannam@198
|
23 EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
cannam@198
|
24 MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
cannam@198
|
25 NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR
|
cannam@198
|
26 ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF
|
cannam@198
|
27 CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
cannam@198
|
28 WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
cannam@198
|
29
|
cannam@198
|
30 Except as contained in this notice, the names of the Centre for
|
cannam@198
|
31 Digital Music; Queen Mary, University of London; and Chris Cannam
|
cannam@198
|
32 shall not be used in advertising or otherwise to promote the sale,
|
cannam@198
|
33 use or other dealings in this Software without prior written
|
cannam@198
|
34 authorization.
|
cannam@198
|
35 */
|
cannam@198
|
36
|
cannam@198
|
37 #include "FixedTempoEstimator.h"
|
cannam@198
|
38
|
cannam@198
|
39 using std::string;
|
cannam@198
|
40 using std::vector;
|
cannam@198
|
41 using std::cerr;
|
cannam@198
|
42 using std::endl;
|
cannam@198
|
43
|
cannam@198
|
44 using Vamp::RealTime;
|
cannam@198
|
45
|
cannam@198
|
46 #include <cmath>
|
cannam@198
|
47
|
cannam@198
|
48
|
cannam@198
|
49 FixedTempoEstimator::FixedTempoEstimator(float inputSampleRate) :
|
cannam@198
|
50 Plugin(inputSampleRate),
|
cannam@198
|
51 m_stepSize(0),
|
cannam@198
|
52 m_blockSize(0),
|
cannam@198
|
53 m_priorMagnitudes(0),
|
cannam@200
|
54 m_df(0),
|
cannam@200
|
55 m_r(0),
|
cannam@200
|
56 m_fr(0),
|
cannam@200
|
57 m_n(0)
|
cannam@198
|
58 {
|
cannam@198
|
59 }
|
cannam@198
|
60
|
cannam@198
|
61 FixedTempoEstimator::~FixedTempoEstimator()
|
cannam@198
|
62 {
|
cannam@198
|
63 delete[] m_priorMagnitudes;
|
cannam@198
|
64 delete[] m_df;
|
cannam@200
|
65 delete[] m_r;
|
cannam@200
|
66 delete[] m_fr;
|
cannam@198
|
67 }
|
cannam@198
|
68
|
cannam@198
|
69 string
|
cannam@198
|
70 FixedTempoEstimator::getIdentifier() const
|
cannam@198
|
71 {
|
cannam@198
|
72 return "fixedtempo";
|
cannam@198
|
73 }
|
cannam@198
|
74
|
cannam@198
|
75 string
|
cannam@198
|
76 FixedTempoEstimator::getName() const
|
cannam@198
|
77 {
|
cannam@198
|
78 return "Simple Fixed Tempo Estimator";
|
cannam@198
|
79 }
|
cannam@198
|
80
|
cannam@198
|
81 string
|
cannam@198
|
82 FixedTempoEstimator::getDescription() const
|
cannam@198
|
83 {
|
cannam@198
|
84 return "Study a short section of audio and estimate its tempo, assuming the tempo is constant";
|
cannam@198
|
85 }
|
cannam@198
|
86
|
cannam@198
|
87 string
|
cannam@198
|
88 FixedTempoEstimator::getMaker() const
|
cannam@198
|
89 {
|
cannam@198
|
90 return "Vamp SDK Example Plugins";
|
cannam@198
|
91 }
|
cannam@198
|
92
|
cannam@198
|
93 int
|
cannam@198
|
94 FixedTempoEstimator::getPluginVersion() const
|
cannam@198
|
95 {
|
cannam@198
|
96 return 1;
|
cannam@198
|
97 }
|
cannam@198
|
98
|
cannam@198
|
99 string
|
cannam@198
|
100 FixedTempoEstimator::getCopyright() const
|
cannam@198
|
101 {
|
cannam@198
|
102 return "Code copyright 2008 Queen Mary, University of London. Freely redistributable (BSD license)";
|
cannam@198
|
103 }
|
cannam@198
|
104
|
cannam@198
|
105 size_t
|
cannam@198
|
106 FixedTempoEstimator::getPreferredStepSize() const
|
cannam@198
|
107 {
|
cannam@198
|
108 return 0;
|
cannam@198
|
109 }
|
cannam@198
|
110
|
cannam@198
|
111 size_t
|
cannam@198
|
112 FixedTempoEstimator::getPreferredBlockSize() const
|
cannam@198
|
113 {
|
cannam@200
|
114 return 64;
|
cannam@198
|
115 }
|
cannam@198
|
116
|
cannam@198
|
117 bool
|
cannam@198
|
118 FixedTempoEstimator::initialise(size_t channels, size_t stepSize, size_t blockSize)
|
cannam@198
|
119 {
|
cannam@198
|
120 if (channels < getMinChannelCount() ||
|
cannam@198
|
121 channels > getMaxChannelCount()) return false;
|
cannam@198
|
122
|
cannam@198
|
123 m_stepSize = stepSize;
|
cannam@198
|
124 m_blockSize = blockSize;
|
cannam@198
|
125
|
cannam@198
|
126 float dfLengthSecs = 8.f;
|
cannam@198
|
127 m_dfsize = (dfLengthSecs * m_inputSampleRate) / m_stepSize;
|
cannam@198
|
128
|
cannam@198
|
129 m_priorMagnitudes = new float[m_blockSize/2];
|
cannam@198
|
130 m_df = new float[m_dfsize];
|
cannam@198
|
131
|
cannam@198
|
132 for (size_t i = 0; i < m_blockSize/2; ++i) {
|
cannam@198
|
133 m_priorMagnitudes[i] = 0.f;
|
cannam@198
|
134 }
|
cannam@198
|
135 for (size_t i = 0; i < m_dfsize; ++i) {
|
cannam@198
|
136 m_df[i] = 0.f;
|
cannam@198
|
137 }
|
cannam@198
|
138
|
cannam@198
|
139 m_n = 0;
|
cannam@198
|
140
|
cannam@198
|
141 return true;
|
cannam@198
|
142 }
|
cannam@198
|
143
|
cannam@198
|
144 void
|
cannam@198
|
145 FixedTempoEstimator::reset()
|
cannam@198
|
146 {
|
cannam@198
|
147 std::cerr << "FixedTempoEstimator: reset called" << std::endl;
|
cannam@198
|
148
|
cannam@198
|
149 if (!m_priorMagnitudes) return;
|
cannam@198
|
150
|
cannam@198
|
151 std::cerr << "FixedTempoEstimator: resetting" << std::endl;
|
cannam@198
|
152
|
cannam@198
|
153 for (size_t i = 0; i < m_blockSize/2; ++i) {
|
cannam@198
|
154 m_priorMagnitudes[i] = 0.f;
|
cannam@198
|
155 }
|
cannam@198
|
156 for (size_t i = 0; i < m_dfsize; ++i) {
|
cannam@198
|
157 m_df[i] = 0.f;
|
cannam@198
|
158 }
|
cannam@198
|
159
|
cannam@200
|
160 delete[] m_r;
|
cannam@200
|
161 m_r = 0;
|
cannam@200
|
162
|
cannam@200
|
163 delete[] m_fr;
|
cannam@200
|
164 m_fr = 0;
|
cannam@200
|
165
|
cannam@198
|
166 m_n = 0;
|
cannam@198
|
167
|
cannam@198
|
168 m_start = RealTime::zeroTime;
|
cannam@198
|
169 m_lasttime = RealTime::zeroTime;
|
cannam@198
|
170 }
|
cannam@198
|
171
|
cannam@198
|
172 FixedTempoEstimator::ParameterList
|
cannam@198
|
173 FixedTempoEstimator::getParameterDescriptors() const
|
cannam@198
|
174 {
|
cannam@198
|
175 ParameterList list;
|
cannam@198
|
176 return list;
|
cannam@198
|
177 }
|
cannam@198
|
178
|
cannam@198
|
179 float
|
cannam@198
|
180 FixedTempoEstimator::getParameter(std::string id) const
|
cannam@198
|
181 {
|
cannam@198
|
182 return 0.f;
|
cannam@198
|
183 }
|
cannam@198
|
184
|
cannam@198
|
185 void
|
cannam@198
|
186 FixedTempoEstimator::setParameter(std::string id, float value)
|
cannam@198
|
187 {
|
cannam@198
|
188 }
|
cannam@198
|
189
|
cannam@200
|
190 static int TempoOutput = 0;
|
cannam@200
|
191 static int CandidatesOutput = 1;
|
cannam@200
|
192 static int DFOutput = 2;
|
cannam@200
|
193 static int ACFOutput = 3;
|
cannam@200
|
194 static int FilteredACFOutput = 4;
|
cannam@200
|
195
|
cannam@198
|
196 FixedTempoEstimator::OutputList
|
cannam@198
|
197 FixedTempoEstimator::getOutputDescriptors() const
|
cannam@198
|
198 {
|
cannam@198
|
199 OutputList list;
|
cannam@198
|
200
|
cannam@198
|
201 OutputDescriptor d;
|
cannam@198
|
202 d.identifier = "tempo";
|
cannam@198
|
203 d.name = "Tempo";
|
cannam@198
|
204 d.description = "Estimated tempo";
|
cannam@198
|
205 d.unit = "bpm";
|
cannam@198
|
206 d.hasFixedBinCount = true;
|
cannam@198
|
207 d.binCount = 1;
|
cannam@198
|
208 d.hasKnownExtents = false;
|
cannam@198
|
209 d.isQuantized = false;
|
cannam@198
|
210 d.sampleType = OutputDescriptor::VariableSampleRate;
|
cannam@198
|
211 d.sampleRate = m_inputSampleRate;
|
cannam@198
|
212 d.hasDuration = true; // our returned tempo spans a certain range
|
cannam@198
|
213 list.push_back(d);
|
cannam@198
|
214
|
cannam@200
|
215 d.identifier = "candidates";
|
cannam@200
|
216 d.name = "Tempo candidates";
|
cannam@200
|
217 d.description = "Possible tempo estimates, one per bin with the most likely in the first bin";
|
cannam@200
|
218 d.unit = "bpm";
|
cannam@200
|
219 d.hasFixedBinCount = false;
|
cannam@200
|
220 list.push_back(d);
|
cannam@200
|
221
|
cannam@198
|
222 d.identifier = "detectionfunction";
|
cannam@198
|
223 d.name = "Detection Function";
|
cannam@198
|
224 d.description = "Onset detection function";
|
cannam@198
|
225 d.unit = "";
|
cannam@198
|
226 d.hasFixedBinCount = 1;
|
cannam@198
|
227 d.binCount = 1;
|
cannam@198
|
228 d.hasKnownExtents = true;
|
cannam@198
|
229 d.minValue = 0.0;
|
cannam@198
|
230 d.maxValue = 1.0;
|
cannam@198
|
231 d.isQuantized = false;
|
cannam@198
|
232 d.quantizeStep = 0.0;
|
cannam@198
|
233 d.sampleType = OutputDescriptor::FixedSampleRate;
|
cannam@198
|
234 if (m_stepSize) {
|
cannam@198
|
235 d.sampleRate = m_inputSampleRate / m_stepSize;
|
cannam@198
|
236 } else {
|
cannam@198
|
237 d.sampleRate = m_inputSampleRate / (getPreferredBlockSize()/2);
|
cannam@198
|
238 }
|
cannam@198
|
239 d.hasDuration = false;
|
cannam@198
|
240 list.push_back(d);
|
cannam@198
|
241
|
cannam@198
|
242 d.identifier = "acf";
|
cannam@198
|
243 d.name = "Autocorrelation Function";
|
cannam@198
|
244 d.description = "Autocorrelation of onset detection function";
|
cannam@198
|
245 d.hasKnownExtents = false;
|
cannam@201
|
246 d.unit = "r";
|
cannam@198
|
247 list.push_back(d);
|
cannam@198
|
248
|
cannam@198
|
249 d.identifier = "filtered_acf";
|
cannam@198
|
250 d.name = "Filtered Autocorrelation";
|
cannam@198
|
251 d.description = "Filtered autocorrelation of onset detection function";
|
cannam@201
|
252 d.unit = "r";
|
cannam@198
|
253 list.push_back(d);
|
cannam@198
|
254
|
cannam@198
|
255 return list;
|
cannam@198
|
256 }
|
cannam@198
|
257
|
cannam@198
|
258 FixedTempoEstimator::FeatureSet
|
cannam@198
|
259 FixedTempoEstimator::process(const float *const *inputBuffers, RealTime ts)
|
cannam@198
|
260 {
|
cannam@198
|
261 FeatureSet fs;
|
cannam@198
|
262
|
cannam@198
|
263 if (m_stepSize == 0) {
|
cannam@198
|
264 cerr << "ERROR: FixedTempoEstimator::process: "
|
cannam@198
|
265 << "FixedTempoEstimator has not been initialised"
|
cannam@198
|
266 << endl;
|
cannam@198
|
267 return fs;
|
cannam@198
|
268 }
|
cannam@198
|
269
|
cannam@200
|
270 // if (m_n < m_dfsize) std::cerr << "m_n = " << m_n << std::endl;
|
cannam@198
|
271
|
cannam@198
|
272 if (m_n == 0) m_start = ts;
|
cannam@198
|
273 m_lasttime = ts;
|
cannam@198
|
274
|
cannam@198
|
275 if (m_n == m_dfsize) {
|
cannam@200
|
276 calculate();
|
cannam@200
|
277 fs = assembleFeatures();
|
cannam@198
|
278 ++m_n;
|
cannam@198
|
279 return fs;
|
cannam@198
|
280 }
|
cannam@198
|
281
|
cannam@198
|
282 if (m_n > m_dfsize) return FeatureSet();
|
cannam@198
|
283
|
cannam@198
|
284 int count = 0;
|
cannam@198
|
285
|
cannam@198
|
286 for (size_t i = 1; i < m_blockSize/2; ++i) {
|
cannam@198
|
287
|
cannam@198
|
288 float real = inputBuffers[0][i*2];
|
cannam@198
|
289 float imag = inputBuffers[0][i*2 + 1];
|
cannam@198
|
290
|
cannam@198
|
291 float sqrmag = real * real + imag * imag;
|
cannam@198
|
292
|
cannam@198
|
293 if (m_priorMagnitudes[i] > 0.f) {
|
cannam@198
|
294 float diff = 10.f * log10f(sqrmag / m_priorMagnitudes[i]);
|
cannam@198
|
295 if (diff >= 3.f) ++count;
|
cannam@198
|
296 }
|
cannam@198
|
297
|
cannam@198
|
298 m_priorMagnitudes[i] = sqrmag;
|
cannam@198
|
299 }
|
cannam@198
|
300
|
cannam@198
|
301 m_df[m_n] = float(count) / float(m_blockSize/2);
|
cannam@198
|
302 ++m_n;
|
cannam@198
|
303 return fs;
|
cannam@198
|
304 }
|
cannam@198
|
305
|
cannam@198
|
306 FixedTempoEstimator::FeatureSet
|
cannam@198
|
307 FixedTempoEstimator::getRemainingFeatures()
|
cannam@198
|
308 {
|
cannam@198
|
309 FeatureSet fs;
|
cannam@198
|
310 if (m_n > m_dfsize) return fs;
|
cannam@200
|
311 calculate();
|
cannam@200
|
312 fs = assembleFeatures();
|
cannam@198
|
313 ++m_n;
|
cannam@198
|
314 return fs;
|
cannam@198
|
315 }
|
cannam@198
|
316
|
cannam@198
|
317 float
|
cannam@199
|
318 FixedTempoEstimator::lag2tempo(int lag)
|
cannam@199
|
319 {
|
cannam@198
|
320 return 60.f / ((lag * m_stepSize) / m_inputSampleRate);
|
cannam@198
|
321 }
|
cannam@198
|
322
|
cannam@200
|
323 void
|
cannam@200
|
324 FixedTempoEstimator::calculate()
|
cannam@200
|
325 {
|
cannam@200
|
326 std::cerr << "FixedTempoEstimator::calculate: m_n = " << m_n << std::endl;
|
cannam@200
|
327
|
cannam@200
|
328 if (m_r) {
|
cannam@200
|
329 std::cerr << "FixedTempoEstimator::calculate: calculation already happened?" << std::endl;
|
cannam@200
|
330 return;
|
cannam@200
|
331 }
|
cannam@200
|
332
|
cannam@200
|
333 if (m_n < m_dfsize / 6) {
|
cannam@200
|
334 std::cerr << "FixedTempoEstimator::calculate: Not enough data to go on (have " << m_n << ", want at least " << m_dfsize/4 << ")" << std::endl;
|
cannam@200
|
335 return; // not enough data (perhaps we should return the duration of the input as the "estimated" beat length?)
|
cannam@200
|
336 }
|
cannam@200
|
337
|
cannam@200
|
338 int n = m_n;
|
cannam@200
|
339
|
cannam@200
|
340 m_r = new float[n/2];
|
cannam@200
|
341 m_fr = new float[n/2];
|
cannam@200
|
342
|
cannam@200
|
343 for (int i = 0; i < n/2; ++i) {
|
cannam@200
|
344 m_r[i] = 0.f;
|
cannam@200
|
345 m_fr[i] = 0.f;
|
cannam@200
|
346 }
|
cannam@200
|
347
|
cannam@200
|
348 for (int i = 0; i < n/2; ++i) {
|
cannam@200
|
349
|
cannam@200
|
350 for (int j = i; j < n-1; ++j) {
|
cannam@200
|
351 m_r[i] += m_df[j] * m_df[j - i];
|
cannam@200
|
352 }
|
cannam@200
|
353
|
cannam@200
|
354 m_r[i] /= n - i - 1;
|
cannam@200
|
355 }
|
cannam@200
|
356
|
cannam@200
|
357 for (int i = 1; i < n/2; ++i) {
|
cannam@200
|
358
|
cannam@200
|
359 m_fr[i] = m_r[i];
|
cannam@200
|
360
|
cannam@200
|
361 int div = 1;
|
cannam@200
|
362
|
cannam@200
|
363 int j = i;
|
cannam@200
|
364
|
cannam@200
|
365 while (j < n/2) {
|
cannam@200
|
366 m_fr[i] += m_r[j];
|
cannam@200
|
367 j *= 2;
|
cannam@200
|
368 ++div;
|
cannam@200
|
369 }
|
cannam@200
|
370 /*
|
cannam@200
|
371 for (int j = 1; j <= (n/2 - 1)/i; ++j) {
|
cannam@200
|
372 m_fr[i] += m_r[i * j];
|
cannam@200
|
373 ++div;
|
cannam@200
|
374 }
|
cannam@200
|
375 */
|
cannam@200
|
376 std::cerr << "i = " << i << ", (n/2 - 1)/i = " << (n/2 - 1)/i << ", sum = " << m_fr[i] << ", div = " << div << ", val = " << m_fr[i] / div << ", t = " << lag2tempo(i) << std::endl;
|
cannam@200
|
377
|
cannam@200
|
378
|
cannam@200
|
379 // m_fr[i] /= 1 + (n/2 - 1)/i;
|
cannam@200
|
380 m_fr[i] /= div;
|
cannam@200
|
381 }
|
cannam@200
|
382
|
cannam@200
|
383 std::cerr << "FixedTempoEstimator::calculate done" << std::endl;
|
cannam@200
|
384 }
|
cannam@200
|
385
|
cannam@200
|
386
|
cannam@198
|
387 FixedTempoEstimator::FeatureSet
|
cannam@200
|
388 FixedTempoEstimator::assembleFeatures()
|
cannam@198
|
389 {
|
cannam@198
|
390 FeatureSet fs;
|
cannam@200
|
391 if (!m_r) return fs; // No results
|
cannam@200
|
392
|
cannam@198
|
393 Feature feature;
|
cannam@198
|
394 feature.hasTimestamp = true;
|
cannam@198
|
395 feature.hasDuration = false;
|
cannam@198
|
396 feature.label = "";
|
cannam@198
|
397 feature.values.clear();
|
cannam@198
|
398 feature.values.push_back(0.f);
|
cannam@198
|
399
|
cannam@200
|
400 char buffer[40];
|
cannam@198
|
401
|
cannam@198
|
402 int n = m_n;
|
cannam@198
|
403
|
cannam@198
|
404 for (int i = 0; i < n; ++i) {
|
cannam@198
|
405 feature.timestamp = RealTime::frame2RealTime(i * m_stepSize,
|
cannam@198
|
406 m_inputSampleRate);
|
cannam@200
|
407 feature.values[0] = m_df[i];
|
cannam@198
|
408 feature.label = "";
|
cannam@200
|
409 fs[DFOutput].push_back(feature);
|
cannam@198
|
410 }
|
cannam@198
|
411
|
cannam@199
|
412 for (int i = 1; i < n/2; ++i) {
|
cannam@198
|
413 feature.timestamp = RealTime::frame2RealTime(i * m_stepSize,
|
cannam@198
|
414 m_inputSampleRate);
|
cannam@200
|
415 feature.values[0] = m_r[i];
|
cannam@199
|
416 sprintf(buffer, "%.1f bpm", lag2tempo(i));
|
cannam@200
|
417 if (i == n/2-1) feature.label = "";
|
cannam@200
|
418 else feature.label = buffer;
|
cannam@200
|
419 fs[ACFOutput].push_back(feature);
|
cannam@198
|
420 }
|
cannam@198
|
421
|
cannam@198
|
422 float t0 = 60.f;
|
cannam@198
|
423 float t1 = 180.f;
|
cannam@198
|
424
|
cannam@198
|
425 int p0 = ((60.f / t1) * m_inputSampleRate) / m_stepSize;
|
cannam@198
|
426 int p1 = ((60.f / t0) * m_inputSampleRate) / m_stepSize;
|
cannam@198
|
427
|
cannam@200
|
428 // std::cerr << "p0 = " << p0 << ", p1 = " << p1 << std::endl;
|
cannam@198
|
429
|
cannam@198
|
430 int pc = p1 - p0 + 1;
|
cannam@200
|
431 // std::cerr << "pc = " << pc << std::endl;
|
cannam@198
|
432
|
cannam@200
|
433 // int maxpi = 0;
|
cannam@200
|
434 // float maxp = 0.f;
|
cannam@198
|
435
|
cannam@200
|
436 std::map<float, int> candidates;
|
cannam@198
|
437
|
cannam@200
|
438 for (int i = p0; i <= p1 && i < n/2-1; ++i) {
|
cannam@198
|
439
|
cannam@200
|
440 // Only candidates here are those that were peaks in the
|
cannam@200
|
441 // original acf
|
cannam@200
|
442 // if (r[i] > r[i-1] && r[i] > r[i+1]) {
|
cannam@200
|
443 // candidates[filtered] = i;
|
cannam@200
|
444 // }
|
cannam@198
|
445
|
cannam@200
|
446 candidates[m_fr[i]] = i;
|
cannam@198
|
447
|
cannam@198
|
448 feature.timestamp = RealTime::frame2RealTime(i * m_stepSize,
|
cannam@198
|
449 m_inputSampleRate);
|
cannam@200
|
450 feature.values[0] = m_fr[i];
|
cannam@199
|
451 sprintf(buffer, "%.1f bpm", lag2tempo(i));
|
cannam@200
|
452 if (i == p1 || i == n/2-2) feature.label = "";
|
cannam@200
|
453 else feature.label = buffer;
|
cannam@200
|
454 fs[FilteredACFOutput].push_back(feature);
|
cannam@198
|
455 }
|
cannam@198
|
456
|
cannam@200
|
457 // std::cerr << "maxpi = " << maxpi << " for tempo " << lag2tempo(maxpi) << " (value = " << maxp << ")" << std::endl;
|
cannam@198
|
458
|
cannam@200
|
459 if (candidates.empty()) {
|
cannam@200
|
460 std::cerr << "No tempo candidates!" << std::endl;
|
cannam@200
|
461 return fs;
|
cannam@200
|
462 }
|
cannam@198
|
463
|
cannam@198
|
464 feature.hasTimestamp = true;
|
cannam@198
|
465 feature.timestamp = m_start;
|
cannam@198
|
466
|
cannam@198
|
467 feature.hasDuration = true;
|
cannam@198
|
468 feature.duration = m_lasttime - m_start;
|
cannam@198
|
469
|
cannam@200
|
470 std::map<float, int>::const_iterator ci = candidates.end();
|
cannam@200
|
471 --ci;
|
cannam@200
|
472 int maxpi = ci->second;
|
cannam@200
|
473
|
cannam@200
|
474 feature.values[0] = lag2tempo(maxpi);
|
cannam@198
|
475
|
cannam@200
|
476 sprintf(buffer, "%.1f bpm", lag2tempo(maxpi));
|
cannam@199
|
477 feature.label = buffer;
|
cannam@199
|
478
|
cannam@200
|
479 fs[TempoOutput].push_back(feature);
|
cannam@198
|
480
|
cannam@200
|
481 feature.values.clear();
|
cannam@200
|
482 feature.label = "";
|
cannam@200
|
483
|
cannam@200
|
484 while (feature.values.size() < 8) {
|
cannam@200
|
485 feature.values.push_back(lag2tempo(ci->second));
|
cannam@200
|
486 if (ci == candidates.begin()) break;
|
cannam@200
|
487 --ci;
|
cannam@200
|
488 }
|
cannam@200
|
489
|
cannam@200
|
490 fs[CandidatesOutput].push_back(feature);
|
cannam@200
|
491
|
cannam@198
|
492 return fs;
|
cannam@198
|
493 }
|