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@204
|
57 m_t(0),
|
cannam@200
|
58 m_n(0)
|
cannam@198
|
59 {
|
cannam@198
|
60 }
|
cannam@198
|
61
|
cannam@198
|
62 FixedTempoEstimator::~FixedTempoEstimator()
|
cannam@198
|
63 {
|
cannam@198
|
64 delete[] m_priorMagnitudes;
|
cannam@198
|
65 delete[] m_df;
|
cannam@200
|
66 delete[] m_r;
|
cannam@200
|
67 delete[] m_fr;
|
cannam@204
|
68 delete[] m_t;
|
cannam@198
|
69 }
|
cannam@198
|
70
|
cannam@198
|
71 string
|
cannam@198
|
72 FixedTempoEstimator::getIdentifier() const
|
cannam@198
|
73 {
|
cannam@198
|
74 return "fixedtempo";
|
cannam@198
|
75 }
|
cannam@198
|
76
|
cannam@198
|
77 string
|
cannam@198
|
78 FixedTempoEstimator::getName() const
|
cannam@198
|
79 {
|
cannam@198
|
80 return "Simple Fixed Tempo Estimator";
|
cannam@198
|
81 }
|
cannam@198
|
82
|
cannam@198
|
83 string
|
cannam@198
|
84 FixedTempoEstimator::getDescription() const
|
cannam@198
|
85 {
|
cannam@198
|
86 return "Study a short section of audio and estimate its tempo, assuming the tempo is constant";
|
cannam@198
|
87 }
|
cannam@198
|
88
|
cannam@198
|
89 string
|
cannam@198
|
90 FixedTempoEstimator::getMaker() const
|
cannam@198
|
91 {
|
cannam@198
|
92 return "Vamp SDK Example Plugins";
|
cannam@198
|
93 }
|
cannam@198
|
94
|
cannam@198
|
95 int
|
cannam@198
|
96 FixedTempoEstimator::getPluginVersion() const
|
cannam@198
|
97 {
|
cannam@198
|
98 return 1;
|
cannam@198
|
99 }
|
cannam@198
|
100
|
cannam@198
|
101 string
|
cannam@198
|
102 FixedTempoEstimator::getCopyright() const
|
cannam@198
|
103 {
|
cannam@198
|
104 return "Code copyright 2008 Queen Mary, University of London. Freely redistributable (BSD license)";
|
cannam@198
|
105 }
|
cannam@198
|
106
|
cannam@198
|
107 size_t
|
cannam@198
|
108 FixedTempoEstimator::getPreferredStepSize() const
|
cannam@198
|
109 {
|
cannam@198
|
110 return 0;
|
cannam@198
|
111 }
|
cannam@198
|
112
|
cannam@198
|
113 size_t
|
cannam@198
|
114 FixedTempoEstimator::getPreferredBlockSize() const
|
cannam@198
|
115 {
|
cannam@202
|
116 return 128;
|
cannam@198
|
117 }
|
cannam@198
|
118
|
cannam@198
|
119 bool
|
cannam@198
|
120 FixedTempoEstimator::initialise(size_t channels, size_t stepSize, size_t blockSize)
|
cannam@198
|
121 {
|
cannam@198
|
122 if (channels < getMinChannelCount() ||
|
cannam@198
|
123 channels > getMaxChannelCount()) return false;
|
cannam@198
|
124
|
cannam@198
|
125 m_stepSize = stepSize;
|
cannam@198
|
126 m_blockSize = blockSize;
|
cannam@198
|
127
|
cannam@198
|
128 float dfLengthSecs = 8.f;
|
cannam@198
|
129 m_dfsize = (dfLengthSecs * m_inputSampleRate) / m_stepSize;
|
cannam@198
|
130
|
cannam@198
|
131 m_priorMagnitudes = new float[m_blockSize/2];
|
cannam@198
|
132 m_df = new float[m_dfsize];
|
cannam@198
|
133
|
cannam@198
|
134 for (size_t i = 0; i < m_blockSize/2; ++i) {
|
cannam@198
|
135 m_priorMagnitudes[i] = 0.f;
|
cannam@198
|
136 }
|
cannam@198
|
137 for (size_t i = 0; i < m_dfsize; ++i) {
|
cannam@198
|
138 m_df[i] = 0.f;
|
cannam@198
|
139 }
|
cannam@198
|
140
|
cannam@198
|
141 m_n = 0;
|
cannam@198
|
142
|
cannam@198
|
143 return true;
|
cannam@198
|
144 }
|
cannam@198
|
145
|
cannam@198
|
146 void
|
cannam@198
|
147 FixedTempoEstimator::reset()
|
cannam@198
|
148 {
|
cannam@198
|
149 std::cerr << "FixedTempoEstimator: reset called" << std::endl;
|
cannam@198
|
150
|
cannam@198
|
151 if (!m_priorMagnitudes) return;
|
cannam@198
|
152
|
cannam@198
|
153 std::cerr << "FixedTempoEstimator: resetting" << std::endl;
|
cannam@198
|
154
|
cannam@198
|
155 for (size_t i = 0; i < m_blockSize/2; ++i) {
|
cannam@198
|
156 m_priorMagnitudes[i] = 0.f;
|
cannam@198
|
157 }
|
cannam@198
|
158 for (size_t i = 0; i < m_dfsize; ++i) {
|
cannam@198
|
159 m_df[i] = 0.f;
|
cannam@198
|
160 }
|
cannam@198
|
161
|
cannam@200
|
162 delete[] m_r;
|
cannam@200
|
163 m_r = 0;
|
cannam@200
|
164
|
cannam@200
|
165 delete[] m_fr;
|
cannam@200
|
166 m_fr = 0;
|
cannam@200
|
167
|
cannam@204
|
168 delete[] m_t;
|
cannam@204
|
169 m_t = 0;
|
cannam@204
|
170
|
cannam@198
|
171 m_n = 0;
|
cannam@198
|
172
|
cannam@198
|
173 m_start = RealTime::zeroTime;
|
cannam@198
|
174 m_lasttime = RealTime::zeroTime;
|
cannam@198
|
175 }
|
cannam@198
|
176
|
cannam@198
|
177 FixedTempoEstimator::ParameterList
|
cannam@198
|
178 FixedTempoEstimator::getParameterDescriptors() const
|
cannam@198
|
179 {
|
cannam@198
|
180 ParameterList list;
|
cannam@198
|
181 return list;
|
cannam@198
|
182 }
|
cannam@198
|
183
|
cannam@198
|
184 float
|
cannam@198
|
185 FixedTempoEstimator::getParameter(std::string id) const
|
cannam@198
|
186 {
|
cannam@198
|
187 return 0.f;
|
cannam@198
|
188 }
|
cannam@198
|
189
|
cannam@198
|
190 void
|
cannam@198
|
191 FixedTempoEstimator::setParameter(std::string id, float value)
|
cannam@198
|
192 {
|
cannam@198
|
193 }
|
cannam@198
|
194
|
cannam@200
|
195 static int TempoOutput = 0;
|
cannam@200
|
196 static int CandidatesOutput = 1;
|
cannam@200
|
197 static int DFOutput = 2;
|
cannam@200
|
198 static int ACFOutput = 3;
|
cannam@200
|
199 static int FilteredACFOutput = 4;
|
cannam@200
|
200
|
cannam@198
|
201 FixedTempoEstimator::OutputList
|
cannam@198
|
202 FixedTempoEstimator::getOutputDescriptors() const
|
cannam@198
|
203 {
|
cannam@198
|
204 OutputList list;
|
cannam@198
|
205
|
cannam@198
|
206 OutputDescriptor d;
|
cannam@198
|
207 d.identifier = "tempo";
|
cannam@198
|
208 d.name = "Tempo";
|
cannam@198
|
209 d.description = "Estimated tempo";
|
cannam@198
|
210 d.unit = "bpm";
|
cannam@198
|
211 d.hasFixedBinCount = true;
|
cannam@198
|
212 d.binCount = 1;
|
cannam@198
|
213 d.hasKnownExtents = false;
|
cannam@198
|
214 d.isQuantized = false;
|
cannam@198
|
215 d.sampleType = OutputDescriptor::VariableSampleRate;
|
cannam@198
|
216 d.sampleRate = m_inputSampleRate;
|
cannam@198
|
217 d.hasDuration = true; // our returned tempo spans a certain range
|
cannam@198
|
218 list.push_back(d);
|
cannam@198
|
219
|
cannam@200
|
220 d.identifier = "candidates";
|
cannam@200
|
221 d.name = "Tempo candidates";
|
cannam@200
|
222 d.description = "Possible tempo estimates, one per bin with the most likely in the first bin";
|
cannam@200
|
223 d.unit = "bpm";
|
cannam@200
|
224 d.hasFixedBinCount = false;
|
cannam@200
|
225 list.push_back(d);
|
cannam@200
|
226
|
cannam@198
|
227 d.identifier = "detectionfunction";
|
cannam@198
|
228 d.name = "Detection Function";
|
cannam@198
|
229 d.description = "Onset detection function";
|
cannam@198
|
230 d.unit = "";
|
cannam@198
|
231 d.hasFixedBinCount = 1;
|
cannam@198
|
232 d.binCount = 1;
|
cannam@198
|
233 d.hasKnownExtents = true;
|
cannam@198
|
234 d.minValue = 0.0;
|
cannam@198
|
235 d.maxValue = 1.0;
|
cannam@198
|
236 d.isQuantized = false;
|
cannam@198
|
237 d.quantizeStep = 0.0;
|
cannam@198
|
238 d.sampleType = OutputDescriptor::FixedSampleRate;
|
cannam@198
|
239 if (m_stepSize) {
|
cannam@198
|
240 d.sampleRate = m_inputSampleRate / m_stepSize;
|
cannam@198
|
241 } else {
|
cannam@198
|
242 d.sampleRate = m_inputSampleRate / (getPreferredBlockSize()/2);
|
cannam@198
|
243 }
|
cannam@198
|
244 d.hasDuration = false;
|
cannam@198
|
245 list.push_back(d);
|
cannam@198
|
246
|
cannam@198
|
247 d.identifier = "acf";
|
cannam@198
|
248 d.name = "Autocorrelation Function";
|
cannam@198
|
249 d.description = "Autocorrelation of onset detection function";
|
cannam@198
|
250 d.hasKnownExtents = false;
|
cannam@201
|
251 d.unit = "r";
|
cannam@198
|
252 list.push_back(d);
|
cannam@198
|
253
|
cannam@198
|
254 d.identifier = "filtered_acf";
|
cannam@198
|
255 d.name = "Filtered Autocorrelation";
|
cannam@198
|
256 d.description = "Filtered autocorrelation of onset detection function";
|
cannam@201
|
257 d.unit = "r";
|
cannam@198
|
258 list.push_back(d);
|
cannam@198
|
259
|
cannam@198
|
260 return list;
|
cannam@198
|
261 }
|
cannam@198
|
262
|
cannam@198
|
263 FixedTempoEstimator::FeatureSet
|
cannam@198
|
264 FixedTempoEstimator::process(const float *const *inputBuffers, RealTime ts)
|
cannam@198
|
265 {
|
cannam@198
|
266 FeatureSet fs;
|
cannam@198
|
267
|
cannam@198
|
268 if (m_stepSize == 0) {
|
cannam@198
|
269 cerr << "ERROR: FixedTempoEstimator::process: "
|
cannam@198
|
270 << "FixedTempoEstimator has not been initialised"
|
cannam@198
|
271 << endl;
|
cannam@198
|
272 return fs;
|
cannam@198
|
273 }
|
cannam@198
|
274
|
cannam@200
|
275 // if (m_n < m_dfsize) std::cerr << "m_n = " << m_n << std::endl;
|
cannam@198
|
276
|
cannam@198
|
277 if (m_n == 0) m_start = ts;
|
cannam@198
|
278 m_lasttime = ts;
|
cannam@198
|
279
|
cannam@198
|
280 if (m_n == m_dfsize) {
|
cannam@200
|
281 calculate();
|
cannam@200
|
282 fs = assembleFeatures();
|
cannam@198
|
283 ++m_n;
|
cannam@198
|
284 return fs;
|
cannam@198
|
285 }
|
cannam@198
|
286
|
cannam@198
|
287 if (m_n > m_dfsize) return FeatureSet();
|
cannam@198
|
288
|
cannam@198
|
289 int count = 0;
|
cannam@198
|
290
|
cannam@198
|
291 for (size_t i = 1; i < m_blockSize/2; ++i) {
|
cannam@198
|
292
|
cannam@198
|
293 float real = inputBuffers[0][i*2];
|
cannam@198
|
294 float imag = inputBuffers[0][i*2 + 1];
|
cannam@198
|
295
|
cannam@198
|
296 float sqrmag = real * real + imag * imag;
|
cannam@198
|
297
|
cannam@198
|
298 if (m_priorMagnitudes[i] > 0.f) {
|
cannam@198
|
299 float diff = 10.f * log10f(sqrmag / m_priorMagnitudes[i]);
|
cannam@198
|
300 if (diff >= 3.f) ++count;
|
cannam@198
|
301 }
|
cannam@198
|
302
|
cannam@198
|
303 m_priorMagnitudes[i] = sqrmag;
|
cannam@198
|
304 }
|
cannam@198
|
305
|
cannam@198
|
306 m_df[m_n] = float(count) / float(m_blockSize/2);
|
cannam@198
|
307 ++m_n;
|
cannam@198
|
308 return fs;
|
cannam@198
|
309 }
|
cannam@198
|
310
|
cannam@198
|
311 FixedTempoEstimator::FeatureSet
|
cannam@198
|
312 FixedTempoEstimator::getRemainingFeatures()
|
cannam@198
|
313 {
|
cannam@198
|
314 FeatureSet fs;
|
cannam@198
|
315 if (m_n > m_dfsize) return fs;
|
cannam@200
|
316 calculate();
|
cannam@200
|
317 fs = assembleFeatures();
|
cannam@198
|
318 ++m_n;
|
cannam@198
|
319 return fs;
|
cannam@198
|
320 }
|
cannam@198
|
321
|
cannam@198
|
322 float
|
cannam@199
|
323 FixedTempoEstimator::lag2tempo(int lag)
|
cannam@199
|
324 {
|
cannam@198
|
325 return 60.f / ((lag * m_stepSize) / m_inputSampleRate);
|
cannam@198
|
326 }
|
cannam@198
|
327
|
cannam@200
|
328 void
|
cannam@200
|
329 FixedTempoEstimator::calculate()
|
cannam@200
|
330 {
|
cannam@200
|
331 std::cerr << "FixedTempoEstimator::calculate: m_n = " << m_n << std::endl;
|
cannam@200
|
332
|
cannam@200
|
333 if (m_r) {
|
cannam@200
|
334 std::cerr << "FixedTempoEstimator::calculate: calculation already happened?" << std::endl;
|
cannam@200
|
335 return;
|
cannam@200
|
336 }
|
cannam@200
|
337
|
cannam@200
|
338 if (m_n < m_dfsize / 6) {
|
cannam@200
|
339 std::cerr << "FixedTempoEstimator::calculate: Not enough data to go on (have " << m_n << ", want at least " << m_dfsize/4 << ")" << std::endl;
|
cannam@200
|
340 return; // not enough data (perhaps we should return the duration of the input as the "estimated" beat length?)
|
cannam@200
|
341 }
|
cannam@200
|
342
|
cannam@200
|
343 int n = m_n;
|
cannam@200
|
344
|
cannam@200
|
345 m_r = new float[n/2];
|
cannam@200
|
346 m_fr = new float[n/2];
|
cannam@204
|
347 m_t = new float[n/2];
|
cannam@200
|
348
|
cannam@200
|
349 for (int i = 0; i < n/2; ++i) {
|
cannam@200
|
350 m_r[i] = 0.f;
|
cannam@200
|
351 m_fr[i] = 0.f;
|
cannam@204
|
352 m_t[i] = 0.f;
|
cannam@200
|
353 }
|
cannam@200
|
354
|
cannam@200
|
355 for (int i = 0; i < n/2; ++i) {
|
cannam@200
|
356
|
cannam@200
|
357 for (int j = i; j < n-1; ++j) {
|
cannam@200
|
358 m_r[i] += m_df[j] * m_df[j - i];
|
cannam@200
|
359 }
|
cannam@200
|
360
|
cannam@200
|
361 m_r[i] /= n - i - 1;
|
cannam@200
|
362 }
|
cannam@200
|
363
|
cannam@200
|
364 for (int i = 1; i < n/2; ++i) {
|
cannam@200
|
365
|
cannam@204
|
366 float weight = 1.f - fabsf(128.f - lag2tempo(i)) * 0.005;
|
cannam@204
|
367 if (weight < 0.f) weight = 0.f;
|
cannam@204
|
368 weight = weight * weight;
|
cannam@204
|
369 std::cerr << "i = " << i << ": tempo = " << lag2tempo(i) << ", weight = " << weight << std::endl;
|
cannam@204
|
370
|
cannam@204
|
371 // m_fr[i] = m_r[i];
|
cannam@204
|
372 m_fr[i] = 0;
|
cannam@204
|
373
|
cannam@204
|
374 m_fr[i] = m_r[i] * (1 + weight/20.f);
|
cannam@204
|
375 }
|
cannam@204
|
376
|
cannam@204
|
377 float related[4] = { 1.5, 0.66666667, 0.5 };
|
cannam@204
|
378
|
cannam@204
|
379 for (int i = 1; i < n/2 - 1; ++i) {
|
cannam@204
|
380
|
cannam@204
|
381 if (!(m_fr[i] > m_fr[i-1] &&
|
cannam@204
|
382 m_fr[i] >= m_fr[i+1])) {
|
cannam@204
|
383 continue;
|
cannam@204
|
384 }
|
cannam@204
|
385
|
cannam@204
|
386 m_t[i] = lag2tempo(i);
|
cannam@200
|
387
|
cannam@200
|
388 int div = 1;
|
cannam@200
|
389
|
cannam@204
|
390 for (int j = 0; j < sizeof(related)/sizeof(related[0]); ++j) {
|
cannam@204
|
391
|
cannam@204
|
392 int k0 = i / related[j];
|
cannam@204
|
393
|
cannam@204
|
394 if (k0 > 1 && k0 < n/2 - 2) {
|
cannam@204
|
395
|
cannam@204
|
396 for (int k = k0 - 1; k <= k0 + 1; ++k) {
|
cannam@204
|
397
|
cannam@204
|
398 if (m_r[k] > m_r[k-1] &&
|
cannam@204
|
399 m_r[k] >= m_r[k+1]) {
|
cannam@204
|
400
|
cannam@204
|
401 std::cerr << "peak at " << i << " (val " << m_r[i] << ", tempo " << lag2tempo(i) << ") has sympathetic peak at " << k << " (val " << m_r[k] << " for relative tempo " << lag2tempo(k) / related[j] << ")" << std::endl;
|
cannam@204
|
402
|
cannam@204
|
403 m_t[i] = m_t[i] + lag2tempo(k) / related[j];
|
cannam@204
|
404 ++div;
|
cannam@204
|
405 }
|
cannam@204
|
406 }
|
cannam@204
|
407 }
|
cannam@204
|
408 }
|
cannam@204
|
409
|
cannam@204
|
410 m_t[i] /= div;
|
cannam@204
|
411
|
cannam@204
|
412 if (div > 1) {
|
cannam@204
|
413 std::cerr << "adjusting tempo from " << lag2tempo(i) << " to "
|
cannam@204
|
414 << m_t[i] << std::endl;
|
cannam@204
|
415 }
|
cannam@204
|
416 }
|
cannam@204
|
417 /*
|
cannam@204
|
418 for (int i = 1; i < n/2; ++i) {
|
cannam@204
|
419
|
cannam@204
|
420 // int div = 1;
|
cannam@204
|
421 int j = i * 2;
|
cannam@200
|
422
|
cannam@200
|
423 while (j < n/2) {
|
cannam@204
|
424 m_fr[i] += m_fr[j] * 0.1;
|
cannam@200
|
425 j *= 2;
|
cannam@204
|
426 // ++div;
|
cannam@200
|
427 }
|
cannam@204
|
428
|
cannam@204
|
429 // m_fr[i] /= div;
|
cannam@204
|
430 }
|
cannam@204
|
431
|
cannam@202
|
432 // 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
|
433
|
cannam@200
|
434
|
cannam@204
|
435 // }
|
cannam@204
|
436 */
|
cannam@200
|
437 std::cerr << "FixedTempoEstimator::calculate done" << std::endl;
|
cannam@200
|
438 }
|
cannam@200
|
439
|
cannam@200
|
440
|
cannam@198
|
441 FixedTempoEstimator::FeatureSet
|
cannam@200
|
442 FixedTempoEstimator::assembleFeatures()
|
cannam@198
|
443 {
|
cannam@198
|
444 FeatureSet fs;
|
cannam@200
|
445 if (!m_r) return fs; // No results
|
cannam@200
|
446
|
cannam@198
|
447 Feature feature;
|
cannam@198
|
448 feature.hasTimestamp = true;
|
cannam@198
|
449 feature.hasDuration = false;
|
cannam@198
|
450 feature.label = "";
|
cannam@198
|
451 feature.values.clear();
|
cannam@198
|
452 feature.values.push_back(0.f);
|
cannam@198
|
453
|
cannam@200
|
454 char buffer[40];
|
cannam@198
|
455
|
cannam@198
|
456 int n = m_n;
|
cannam@198
|
457
|
cannam@198
|
458 for (int i = 0; i < n; ++i) {
|
cannam@198
|
459 feature.timestamp = RealTime::frame2RealTime(i * m_stepSize,
|
cannam@198
|
460 m_inputSampleRate);
|
cannam@200
|
461 feature.values[0] = m_df[i];
|
cannam@198
|
462 feature.label = "";
|
cannam@200
|
463 fs[DFOutput].push_back(feature);
|
cannam@198
|
464 }
|
cannam@198
|
465
|
cannam@199
|
466 for (int i = 1; i < n/2; ++i) {
|
cannam@198
|
467 feature.timestamp = RealTime::frame2RealTime(i * m_stepSize,
|
cannam@198
|
468 m_inputSampleRate);
|
cannam@200
|
469 feature.values[0] = m_r[i];
|
cannam@199
|
470 sprintf(buffer, "%.1f bpm", lag2tempo(i));
|
cannam@200
|
471 if (i == n/2-1) feature.label = "";
|
cannam@200
|
472 else feature.label = buffer;
|
cannam@200
|
473 fs[ACFOutput].push_back(feature);
|
cannam@198
|
474 }
|
cannam@198
|
475
|
cannam@198
|
476 float t0 = 60.f;
|
cannam@198
|
477 float t1 = 180.f;
|
cannam@198
|
478
|
cannam@198
|
479 int p0 = ((60.f / t1) * m_inputSampleRate) / m_stepSize;
|
cannam@198
|
480 int p1 = ((60.f / t0) * m_inputSampleRate) / m_stepSize;
|
cannam@198
|
481
|
cannam@200
|
482 // std::cerr << "p0 = " << p0 << ", p1 = " << p1 << std::endl;
|
cannam@198
|
483
|
cannam@198
|
484 int pc = p1 - p0 + 1;
|
cannam@200
|
485 // std::cerr << "pc = " << pc << std::endl;
|
cannam@198
|
486
|
cannam@200
|
487 // int maxpi = 0;
|
cannam@200
|
488 // float maxp = 0.f;
|
cannam@198
|
489
|
cannam@200
|
490 std::map<float, int> candidates;
|
cannam@198
|
491
|
cannam@200
|
492 for (int i = p0; i <= p1 && i < n/2-1; ++i) {
|
cannam@198
|
493
|
cannam@200
|
494 // Only candidates here are those that were peaks in the
|
cannam@200
|
495 // original acf
|
cannam@200
|
496 // if (r[i] > r[i-1] && r[i] > r[i+1]) {
|
cannam@200
|
497 // candidates[filtered] = i;
|
cannam@200
|
498 // }
|
cannam@198
|
499
|
cannam@200
|
500 candidates[m_fr[i]] = i;
|
cannam@198
|
501
|
cannam@198
|
502 feature.timestamp = RealTime::frame2RealTime(i * m_stepSize,
|
cannam@198
|
503 m_inputSampleRate);
|
cannam@200
|
504 feature.values[0] = m_fr[i];
|
cannam@199
|
505 sprintf(buffer, "%.1f bpm", lag2tempo(i));
|
cannam@200
|
506 if (i == p1 || i == n/2-2) feature.label = "";
|
cannam@200
|
507 else feature.label = buffer;
|
cannam@200
|
508 fs[FilteredACFOutput].push_back(feature);
|
cannam@198
|
509 }
|
cannam@198
|
510
|
cannam@200
|
511 // std::cerr << "maxpi = " << maxpi << " for tempo " << lag2tempo(maxpi) << " (value = " << maxp << ")" << std::endl;
|
cannam@198
|
512
|
cannam@200
|
513 if (candidates.empty()) {
|
cannam@200
|
514 std::cerr << "No tempo candidates!" << std::endl;
|
cannam@200
|
515 return fs;
|
cannam@200
|
516 }
|
cannam@198
|
517
|
cannam@198
|
518 feature.hasTimestamp = true;
|
cannam@198
|
519 feature.timestamp = m_start;
|
cannam@198
|
520
|
cannam@198
|
521 feature.hasDuration = true;
|
cannam@198
|
522 feature.duration = m_lasttime - m_start;
|
cannam@198
|
523
|
cannam@200
|
524 std::map<float, int>::const_iterator ci = candidates.end();
|
cannam@200
|
525 --ci;
|
cannam@200
|
526 int maxpi = ci->second;
|
cannam@198
|
527
|
cannam@204
|
528 if (m_t[maxpi] > 0) {
|
cannam@204
|
529 feature.values[0] = m_t[maxpi];
|
cannam@204
|
530 } else {
|
cannam@204
|
531 // shouldn't happen -- it would imply that this high value was not a peak!
|
cannam@204
|
532 feature.values[0] = lag2tempo(maxpi);
|
cannam@204
|
533 std::cerr << "WARNING: No stored tempo for index " << maxpi << std::endl;
|
cannam@204
|
534 }
|
cannam@204
|
535
|
cannam@204
|
536 sprintf(buffer, "%.1f bpm", feature.values[0]);
|
cannam@199
|
537 feature.label = buffer;
|
cannam@199
|
538
|
cannam@200
|
539 fs[TempoOutput].push_back(feature);
|
cannam@198
|
540
|
cannam@200
|
541 feature.values.clear();
|
cannam@200
|
542 feature.label = "";
|
cannam@200
|
543
|
cannam@200
|
544 while (feature.values.size() < 8) {
|
cannam@204
|
545 feature.values.push_back(lag2tempo(ci->second)); //!!!??? use m_t?
|
cannam@200
|
546 if (ci == candidates.begin()) break;
|
cannam@200
|
547 --ci;
|
cannam@200
|
548 }
|
cannam@200
|
549
|
cannam@200
|
550 fs[CandidatesOutput].push_back(feature);
|
cannam@200
|
551
|
cannam@198
|
552 return fs;
|
cannam@198
|
553 }
|