c@89
|
1 /* -*- c-basic-offset: 4 indent-tabs-mode: nil -*- vi:set ts=8 sts=4 sw=4: */
|
c@89
|
2
|
c@89
|
3 /*
|
c@89
|
4 QM Vamp Plugin Set
|
c@89
|
5
|
c@89
|
6 Centre for Digital Music, Queen Mary, University of London.
|
c@135
|
7
|
c@135
|
8 This program is free software; you can redistribute it and/or
|
c@135
|
9 modify it under the terms of the GNU General Public License as
|
c@135
|
10 published by the Free Software Foundation; either version 2 of the
|
c@135
|
11 License, or (at your option) any later version. See the file
|
c@135
|
12 COPYING included with this distribution for more information.
|
c@89
|
13 */
|
c@89
|
14
|
c@89
|
15 #include "BarBeatTrack.h"
|
c@89
|
16
|
c@89
|
17 #include <dsp/onsets/DetectionFunction.h>
|
c@89
|
18 #include <dsp/onsets/PeakPicking.h>
|
c@89
|
19 #include <dsp/tempotracking/TempoTrackV2.h>
|
c@89
|
20 #include <dsp/tempotracking/DownBeat.h>
|
c@89
|
21 #include <maths/MathUtilities.h>
|
c@89
|
22
|
c@89
|
23 using std::string;
|
c@89
|
24 using std::vector;
|
c@89
|
25 using std::cerr;
|
c@89
|
26 using std::endl;
|
c@89
|
27
|
c@130
|
28 #ifndef __GNUC__
|
c@130
|
29 #include <alloca.h>
|
c@130
|
30 #endif
|
c@130
|
31
|
c@89
|
32 float BarBeatTracker::m_stepSecs = 0.01161; // 512 samples at 44100
|
c@89
|
33
|
c@89
|
34 class BarBeatTrackerData
|
c@89
|
35 {
|
c@89
|
36 public:
|
c@89
|
37 BarBeatTrackerData(float rate, const DFConfig &config) : dfConfig(config) {
|
luis@144
|
38 df = new DetectionFunction(config);
|
c@89
|
39 // decimation factor aims at resampling to c. 3KHz; must be power of 2
|
c@89
|
40 int factor = MathUtilities::nextPowerOfTwo(rate / 3000);
|
c@95
|
41 // std::cerr << "BarBeatTrackerData: factor = " << factor << std::endl;
|
c@89
|
42 downBeat = new DownBeat(rate, factor, config.stepSize);
|
c@89
|
43 }
|
c@89
|
44 ~BarBeatTrackerData() {
|
luis@144
|
45 delete df;
|
c@89
|
46 delete downBeat;
|
c@89
|
47 }
|
c@89
|
48 void reset() {
|
luis@144
|
49 delete df;
|
luis@144
|
50 df = new DetectionFunction(dfConfig);
|
luis@144
|
51 dfOutput.clear();
|
c@89
|
52 downBeat->resetAudioBuffer();
|
c@89
|
53 origin = Vamp::RealTime::zeroTime;
|
c@89
|
54 }
|
c@89
|
55
|
c@89
|
56 DFConfig dfConfig;
|
c@89
|
57 DetectionFunction *df;
|
c@89
|
58 DownBeat *downBeat;
|
c@89
|
59 vector<double> dfOutput;
|
c@89
|
60 Vamp::RealTime origin;
|
c@89
|
61 };
|
luis@144
|
62
|
c@89
|
63
|
c@89
|
64 BarBeatTracker::BarBeatTracker(float inputSampleRate) :
|
c@89
|
65 Vamp::Plugin(inputSampleRate),
|
c@89
|
66 m_d(0),
|
luis@144
|
67 m_bpb(4),
|
luis@144
|
68 m_alpha(0.9), // changes are as per the BeatTrack.cpp
|
luis@144
|
69 m_tightness(4.), // changes are as per the BeatTrack.cpp
|
luis@144
|
70 m_inputtempo(120.), // changes are as per the BeatTrack.cpp
|
luis@144
|
71 m_constraintempo(false) // changes are as per the BeatTrack.cpp
|
c@89
|
72 {
|
c@89
|
73 }
|
c@89
|
74
|
c@89
|
75 BarBeatTracker::~BarBeatTracker()
|
c@89
|
76 {
|
c@89
|
77 delete m_d;
|
c@89
|
78 }
|
c@89
|
79
|
c@89
|
80 string
|
c@89
|
81 BarBeatTracker::getIdentifier() const
|
c@89
|
82 {
|
c@89
|
83 return "qm-barbeattracker";
|
c@89
|
84 }
|
c@89
|
85
|
c@89
|
86 string
|
c@89
|
87 BarBeatTracker::getName() const
|
c@89
|
88 {
|
c@89
|
89 return "Bar and Beat Tracker";
|
c@89
|
90 }
|
c@89
|
91
|
c@89
|
92 string
|
c@89
|
93 BarBeatTracker::getDescription() const
|
c@89
|
94 {
|
c@89
|
95 return "Estimate bar and beat locations";
|
c@89
|
96 }
|
c@89
|
97
|
c@89
|
98 string
|
c@89
|
99 BarBeatTracker::getMaker() const
|
c@89
|
100 {
|
c@89
|
101 return "Queen Mary, University of London";
|
c@89
|
102 }
|
c@89
|
103
|
c@89
|
104 int
|
c@89
|
105 BarBeatTracker::getPluginVersion() const
|
c@89
|
106 {
|
c@145
|
107 return 3;
|
c@89
|
108 }
|
c@89
|
109
|
c@89
|
110 string
|
c@89
|
111 BarBeatTracker::getCopyright() const
|
c@89
|
112 {
|
c@149
|
113 return "Plugin by Matthew Davies, Christian Landone and Chris Cannam. Copyright (c) 2006-2013 QMUL - All Rights Reserved";
|
c@89
|
114 }
|
c@89
|
115
|
c@89
|
116 BarBeatTracker::ParameterList
|
c@89
|
117 BarBeatTracker::getParameterDescriptors() const
|
c@89
|
118 {
|
c@89
|
119 ParameterList list;
|
c@89
|
120
|
c@89
|
121 ParameterDescriptor desc;
|
c@89
|
122
|
c@89
|
123 desc.identifier = "bpb";
|
c@89
|
124 desc.name = "Beats per Bar";
|
c@89
|
125 desc.description = "The number of beats in each bar";
|
c@89
|
126 desc.minValue = 2;
|
c@89
|
127 desc.maxValue = 16;
|
c@89
|
128 desc.defaultValue = 4;
|
c@89
|
129 desc.isQuantized = true;
|
c@89
|
130 desc.quantizeStep = 1;
|
c@89
|
131 list.push_back(desc);
|
c@89
|
132
|
luis@144
|
133 // changes are as per the BeatTrack.cpp
|
luis@144
|
134 //Alpha Parameter of Beat Tracker
|
luis@144
|
135 desc.identifier = "alpha";
|
luis@144
|
136 desc.name = "Alpha";
|
luis@144
|
137 desc.description = "Inertia - Flexibility Trade Off";
|
luis@144
|
138 desc.minValue = 0.1;
|
luis@144
|
139 desc.maxValue = 0.99;
|
luis@144
|
140 desc.defaultValue = 0.90;
|
luis@144
|
141 desc.unit = "";
|
luis@144
|
142 desc.isQuantized = false;
|
luis@144
|
143 list.push_back(desc);
|
luis@144
|
144
|
c@148
|
145 // We aren't exposing tightness as a parameter, it's fixed at 4
|
luis@144
|
146
|
luis@144
|
147 // changes are as per the BeatTrack.cpp
|
luis@144
|
148 //User input tempo
|
luis@144
|
149 desc.identifier = "inputtempo";
|
c@148
|
150 desc.name = "Tempo Hint";
|
c@151
|
151 desc.description = "User-defined tempo on which to centre the tempo preference function";
|
luis@144
|
152 desc.minValue = 50;
|
luis@144
|
153 desc.maxValue = 250;
|
luis@144
|
154 desc.defaultValue = 120;
|
luis@144
|
155 desc.unit = "BPM";
|
luis@144
|
156 desc.isQuantized = true;
|
luis@144
|
157 list.push_back(desc);
|
luis@144
|
158
|
luis@144
|
159 // changes are as per the BeatTrack.cpp
|
luis@144
|
160 desc.identifier = "constraintempo";
|
luis@144
|
161 desc.name = "Constrain Tempo";
|
c@148
|
162 desc.description = "Constrain more tightly around the tempo hint, using a Gaussian weighting instead of Rayleigh";
|
luis@144
|
163 desc.minValue = 0;
|
luis@144
|
164 desc.maxValue = 1;
|
luis@144
|
165 desc.defaultValue = 0;
|
luis@144
|
166 desc.isQuantized = true;
|
luis@144
|
167 desc.quantizeStep = 1;
|
luis@144
|
168 desc.unit = "";
|
luis@144
|
169 desc.valueNames.clear();
|
luis@144
|
170 list.push_back(desc);
|
luis@144
|
171
|
luis@144
|
172
|
c@89
|
173 return list;
|
c@89
|
174 }
|
c@89
|
175
|
c@89
|
176 float
|
c@89
|
177 BarBeatTracker::getParameter(std::string name) const
|
c@89
|
178 {
|
luis@144
|
179 if (name == "bpb") {
|
luis@144
|
180 return m_bpb;
|
luis@144
|
181 } else if (name == "alpha") {
|
luis@144
|
182 return m_alpha;
|
luis@144
|
183 } else if (name == "inputtempo") {
|
luis@144
|
184 return m_inputtempo;
|
luis@144
|
185 } else if (name == "constraintempo") {
|
luis@144
|
186 return m_constraintempo ? 1.0 : 0.0;
|
luis@144
|
187 }
|
c@89
|
188 return 0.0;
|
c@89
|
189 }
|
c@89
|
190
|
c@89
|
191 void
|
c@89
|
192 BarBeatTracker::setParameter(std::string name, float value)
|
c@89
|
193 {
|
luis@144
|
194 if (name == "bpb") {
|
luis@144
|
195 m_bpb = lrintf(value);
|
luis@144
|
196 } else if (name == "alpha") {
|
luis@144
|
197 m_alpha = value;
|
luis@144
|
198 } else if (name == "inputtempo") {
|
luis@144
|
199 m_inputtempo = value;
|
luis@144
|
200 } else if (name == "constraintempo") {
|
luis@144
|
201 m_constraintempo = (value > 0.5);
|
luis@144
|
202 }
|
c@89
|
203 }
|
c@89
|
204
|
c@89
|
205 bool
|
c@89
|
206 BarBeatTracker::initialise(size_t channels, size_t stepSize, size_t blockSize)
|
c@89
|
207 {
|
c@89
|
208 if (m_d) {
|
luis@144
|
209 delete m_d;
|
luis@144
|
210 m_d = 0;
|
c@89
|
211 }
|
c@89
|
212
|
c@89
|
213 if (channels < getMinChannelCount() ||
|
luis@144
|
214 channels > getMaxChannelCount()) {
|
c@89
|
215 std::cerr << "BarBeatTracker::initialise: Unsupported channel count: "
|
c@89
|
216 << channels << std::endl;
|
c@89
|
217 return false;
|
c@89
|
218 }
|
c@89
|
219
|
c@89
|
220 if (stepSize != getPreferredStepSize()) {
|
c@89
|
221 std::cerr << "ERROR: BarBeatTracker::initialise: Unsupported step size for this sample rate: "
|
c@89
|
222 << stepSize << " (wanted " << (getPreferredStepSize()) << ")" << std::endl;
|
c@89
|
223 return false;
|
c@89
|
224 }
|
c@89
|
225
|
c@89
|
226 if (blockSize != getPreferredBlockSize()) {
|
c@89
|
227 std::cerr << "WARNING: BarBeatTracker::initialise: Sub-optimal block size for this sample rate: "
|
c@89
|
228 << blockSize << " (wanted " << getPreferredBlockSize() << ")" << std::endl;
|
c@89
|
229 // return false;
|
c@89
|
230 }
|
c@89
|
231
|
c@89
|
232 DFConfig dfConfig;
|
c@89
|
233 dfConfig.DFType = DF_COMPLEXSD;
|
c@89
|
234 dfConfig.stepSize = stepSize;
|
c@89
|
235 dfConfig.frameLength = blockSize;
|
c@89
|
236 dfConfig.dbRise = 3;
|
c@89
|
237 dfConfig.adaptiveWhitening = false;
|
c@89
|
238 dfConfig.whiteningRelaxCoeff = -1;
|
c@89
|
239 dfConfig.whiteningFloor = -1;
|
luis@144
|
240
|
c@89
|
241 m_d = new BarBeatTrackerData(m_inputSampleRate, dfConfig);
|
c@89
|
242 m_d->downBeat->setBeatsPerBar(m_bpb);
|
c@89
|
243 return true;
|
c@89
|
244 }
|
c@89
|
245
|
c@89
|
246 void
|
c@89
|
247 BarBeatTracker::reset()
|
c@89
|
248 {
|
c@89
|
249 if (m_d) m_d->reset();
|
c@89
|
250 }
|
c@89
|
251
|
c@89
|
252 size_t
|
c@89
|
253 BarBeatTracker::getPreferredStepSize() const
|
c@89
|
254 {
|
c@89
|
255 size_t step = size_t(m_inputSampleRate * m_stepSecs + 0.0001);
|
c@95
|
256 if (step < 1) step = 1;
|
c@89
|
257 // std::cerr << "BarBeatTracker::getPreferredStepSize: input sample rate is " << m_inputSampleRate << ", step size is " << step << std::endl;
|
c@89
|
258 return step;
|
c@89
|
259 }
|
c@89
|
260
|
c@89
|
261 size_t
|
c@89
|
262 BarBeatTracker::getPreferredBlockSize() const
|
c@89
|
263 {
|
c@89
|
264 size_t theoretical = getPreferredStepSize() * 2;
|
c@89
|
265
|
c@89
|
266 // I think this is not necessarily going to be a power of two, and
|
c@89
|
267 // the host might have a problem with that, but I'm not sure we
|
c@89
|
268 // can do much about it here
|
c@89
|
269 return theoretical;
|
c@89
|
270 }
|
c@89
|
271
|
c@89
|
272 BarBeatTracker::OutputList
|
c@89
|
273 BarBeatTracker::getOutputDescriptors() const
|
c@89
|
274 {
|
c@89
|
275 OutputList list;
|
c@89
|
276
|
c@89
|
277 OutputDescriptor beat;
|
c@89
|
278 beat.identifier = "beats";
|
c@89
|
279 beat.name = "Beats";
|
c@89
|
280 beat.description = "Beat locations labelled with metrical position";
|
c@89
|
281 beat.unit = "";
|
c@89
|
282 beat.hasFixedBinCount = true;
|
c@89
|
283 beat.binCount = 0;
|
c@89
|
284 beat.sampleType = OutputDescriptor::VariableSampleRate;
|
c@89
|
285 beat.sampleRate = 1.0 / m_stepSecs;
|
c@89
|
286
|
c@89
|
287 OutputDescriptor bars;
|
c@89
|
288 bars.identifier = "bars";
|
c@89
|
289 bars.name = "Bars";
|
c@89
|
290 bars.description = "Bar locations";
|
c@89
|
291 bars.unit = "";
|
c@89
|
292 bars.hasFixedBinCount = true;
|
c@89
|
293 bars.binCount = 0;
|
c@89
|
294 bars.sampleType = OutputDescriptor::VariableSampleRate;
|
c@89
|
295 bars.sampleRate = 1.0 / m_stepSecs;
|
c@89
|
296
|
c@89
|
297 OutputDescriptor beatcounts;
|
c@89
|
298 beatcounts.identifier = "beatcounts";
|
c@89
|
299 beatcounts.name = "Beat Count";
|
c@89
|
300 beatcounts.description = "Beat counter function";
|
c@89
|
301 beatcounts.unit = "";
|
c@89
|
302 beatcounts.hasFixedBinCount = true;
|
c@89
|
303 beatcounts.binCount = 1;
|
c@89
|
304 beatcounts.sampleType = OutputDescriptor::VariableSampleRate;
|
c@89
|
305 beatcounts.sampleRate = 1.0 / m_stepSecs;
|
c@89
|
306
|
c@90
|
307 OutputDescriptor beatsd;
|
c@90
|
308 beatsd.identifier = "beatsd";
|
c@90
|
309 beatsd.name = "Beat Spectral Difference";
|
c@90
|
310 beatsd.description = "Beat spectral difference function used for bar-line detection";
|
c@90
|
311 beatsd.unit = "";
|
c@90
|
312 beatsd.hasFixedBinCount = true;
|
c@90
|
313 beatsd.binCount = 1;
|
c@90
|
314 beatsd.sampleType = OutputDescriptor::VariableSampleRate;
|
c@90
|
315 beatsd.sampleRate = 1.0 / m_stepSecs;
|
c@90
|
316
|
c@89
|
317 list.push_back(beat);
|
c@89
|
318 list.push_back(bars);
|
c@89
|
319 list.push_back(beatcounts);
|
c@90
|
320 list.push_back(beatsd);
|
c@89
|
321
|
c@89
|
322 return list;
|
c@89
|
323 }
|
c@89
|
324
|
c@89
|
325 BarBeatTracker::FeatureSet
|
c@89
|
326 BarBeatTracker::process(const float *const *inputBuffers,
|
c@89
|
327 Vamp::RealTime timestamp)
|
c@89
|
328 {
|
c@89
|
329 if (!m_d) {
|
luis@144
|
330 cerr << "ERROR: BarBeatTracker::process: "
|
luis@144
|
331 << "BarBeatTracker has not been initialised"
|
luis@144
|
332 << endl;
|
luis@144
|
333 return FeatureSet();
|
c@89
|
334 }
|
c@89
|
335
|
c@89
|
336 // We use time domain input, because DownBeat requires it -- so we
|
c@89
|
337 // use the time-domain version of DetectionFunction::process which
|
c@89
|
338 // does its own FFT. It requires doubles as input, so we need to
|
c@89
|
339 // make a temporary copy
|
c@89
|
340
|
c@89
|
341 // We only support a single input channel
|
c@89
|
342
|
c@89
|
343 const int fl = m_d->dfConfig.frameLength;
|
c@130
|
344 #ifndef __GNUC__
|
c@130
|
345 double *dfinput = (double *)alloca(fl * sizeof(double));
|
c@130
|
346 #else
|
c@89
|
347 double dfinput[fl];
|
c@130
|
348 #endif
|
c@89
|
349 for (int i = 0; i < fl; ++i) dfinput[i] = inputBuffers[0][i];
|
c@89
|
350
|
c@89
|
351 double output = m_d->df->process(dfinput);
|
c@89
|
352
|
c@89
|
353 if (m_d->dfOutput.empty()) m_d->origin = timestamp;
|
c@89
|
354
|
c@93
|
355 // std::cerr << "df[" << m_d->dfOutput.size() << "] is " << output << std::endl;
|
c@89
|
356 m_d->dfOutput.push_back(output);
|
c@89
|
357
|
c@89
|
358 // Downsample and store the incoming audio block.
|
c@89
|
359 // We have an overlap on the incoming audio stream (step size is
|
c@89
|
360 // half block size) -- this function is configured to take only a
|
c@89
|
361 // step size's worth, so effectively ignoring the overlap. Note
|
c@89
|
362 // however that this means we omit the last blocksize - stepsize
|
c@89
|
363 // samples completely for the purposes of barline detection
|
c@89
|
364 // (hopefully not a problem)
|
c@89
|
365 m_d->downBeat->pushAudioBlock(inputBuffers[0]);
|
c@89
|
366
|
c@89
|
367 return FeatureSet();
|
c@89
|
368 }
|
c@89
|
369
|
c@89
|
370 BarBeatTracker::FeatureSet
|
c@89
|
371 BarBeatTracker::getRemainingFeatures()
|
c@89
|
372 {
|
c@89
|
373 if (!m_d) {
|
luis@144
|
374 cerr << "ERROR: BarBeatTracker::getRemainingFeatures: "
|
luis@144
|
375 << "BarBeatTracker has not been initialised"
|
luis@144
|
376 << endl;
|
luis@144
|
377 return FeatureSet();
|
c@89
|
378 }
|
c@89
|
379
|
c@89
|
380 return barBeatTrack();
|
c@89
|
381 }
|
c@89
|
382
|
c@89
|
383 BarBeatTracker::FeatureSet
|
c@89
|
384 BarBeatTracker::barBeatTrack()
|
c@89
|
385 {
|
c@89
|
386 vector<double> df;
|
c@89
|
387 vector<double> beatPeriod;
|
c@89
|
388 vector<double> tempi;
|
c@89
|
389
|
c@89
|
390 for (size_t i = 2; i < m_d->dfOutput.size(); ++i) { // discard first two elts
|
c@89
|
391 df.push_back(m_d->dfOutput[i]);
|
c@89
|
392 beatPeriod.push_back(0.0);
|
c@89
|
393 }
|
c@89
|
394 if (df.empty()) return FeatureSet();
|
c@89
|
395
|
c@89
|
396 TempoTrackV2 tt(m_inputSampleRate, m_d->dfConfig.stepSize);
|
luis@144
|
397
|
luis@144
|
398 // changes are as per the BeatTrack.cpp - allow m_inputtempo and m_constraintempo to be set be the user
|
luis@144
|
399 tt.calculateBeatPeriod(df, beatPeriod, tempi, m_inputtempo, m_constraintempo);
|
c@89
|
400
|
c@89
|
401 vector<double> beats;
|
luis@144
|
402 // changes are as per the BeatTrack.cpp - allow m_alpha and m_tightness to be set be the user
|
luis@144
|
403 tt.calculateBeats(df, beatPeriod, beats, m_alpha, m_tightness);
|
luis@144
|
404
|
luis@144
|
405 // tt.calculateBeatPeriod(df, beatPeriod, tempi, 0., 0); // use default parameters
|
luis@144
|
406
|
luis@144
|
407 // vector<double> beats;
|
luis@144
|
408 // tt.calculateBeats(df, beatPeriod, beats, 0.9, 4.); // use default parameters until i fix this plugin too
|
c@89
|
409
|
c@89
|
410 vector<int> downbeats;
|
c@89
|
411 size_t downLength = 0;
|
c@89
|
412 const float *downsampled = m_d->downBeat->getBufferedAudio(downLength);
|
c@89
|
413 m_d->downBeat->findDownBeats(downsampled, downLength, beats, downbeats);
|
c@89
|
414
|
c@90
|
415 vector<double> beatsd;
|
c@90
|
416 m_d->downBeat->getBeatSD(beatsd);
|
c@90
|
417
|
c@89
|
418 // std::cerr << "BarBeatTracker: found downbeats at: ";
|
c@89
|
419 // for (int i = 0; i < downbeats.size(); ++i) std::cerr << downbeats[i] << " " << std::endl;
|
luis@144
|
420
|
c@89
|
421 FeatureSet returnFeatures;
|
c@89
|
422
|
c@89
|
423 char label[20];
|
c@89
|
424
|
c@89
|
425 int dbi = 0;
|
c@89
|
426 int beat = 0;
|
c@89
|
427 int bar = 0;
|
c@89
|
428
|
c@124
|
429 if (!downbeats.empty()) {
|
c@124
|
430 // get the right number for the first beat; this will be
|
c@124
|
431 // incremented before use (at top of the following loop)
|
c@124
|
432 int firstDown = downbeats[0];
|
c@124
|
433 beat = m_bpb - firstDown - 1;
|
c@124
|
434 if (beat == m_bpb) beat = 0;
|
c@124
|
435 }
|
c@124
|
436
|
c@89
|
437 for (size_t i = 0; i < beats.size(); ++i) {
|
c@89
|
438
|
luis@144
|
439 size_t frame = beats[i] * m_d->dfConfig.stepSize;
|
c@89
|
440
|
c@89
|
441 if (dbi < downbeats.size() && i == downbeats[dbi]) {
|
c@89
|
442 beat = 0;
|
c@89
|
443 ++bar;
|
c@89
|
444 ++dbi;
|
c@89
|
445 } else {
|
c@89
|
446 ++beat;
|
c@89
|
447 }
|
c@89
|
448
|
c@89
|
449 // outputs are:
|
c@89
|
450 //
|
c@89
|
451 // 0 -> beats
|
c@89
|
452 // 1 -> bars
|
c@89
|
453 // 2 -> beat counter function
|
luis@144
|
454
|
luis@144
|
455 Feature feature;
|
luis@144
|
456 feature.hasTimestamp = true;
|
luis@144
|
457 feature.timestamp = m_d->origin + Vamp::RealTime::frame2RealTime
|
luis@144
|
458 (frame, lrintf(m_inputSampleRate));
|
c@89
|
459
|
c@89
|
460 sprintf(label, "%d", beat + 1);
|
c@89
|
461 feature.label = label;
|
luis@144
|
462 returnFeatures[0].push_back(feature); // labelled beats
|
c@89
|
463
|
c@89
|
464 feature.values.push_back(beat + 1);
|
c@89
|
465 returnFeatures[2].push_back(feature); // beat function
|
c@89
|
466
|
c@90
|
467 if (i > 0 && i <= beatsd.size()) {
|
c@90
|
468 feature.values.clear();
|
c@90
|
469 feature.values.push_back(beatsd[i-1]);
|
c@90
|
470 feature.label = "";
|
c@90
|
471 returnFeatures[3].push_back(feature); // beat spectral difference
|
c@90
|
472 }
|
c@90
|
473
|
c@89
|
474 if (beat == 0) {
|
c@89
|
475 feature.values.clear();
|
c@89
|
476 sprintf(label, "%d", bar);
|
c@89
|
477 feature.label = label;
|
c@89
|
478 returnFeatures[1].push_back(feature); // bars
|
c@89
|
479 }
|
c@89
|
480 }
|
c@89
|
481
|
c@89
|
482 return returnFeatures;
|
c@89
|
483 }
|
c@89
|
484
|