c@0
|
1
|
c@0
|
2 // This is a skeleton file for use in creating your own plugin
|
c@0
|
3 // libraries. Replace MyPlugin and myPlugin throughout with the name
|
c@0
|
4 // of your first plugin class, and fill in the gaps as appropriate.
|
c@0
|
5
|
c@0
|
6
|
c@0
|
7 #include "Tempogram.h"
|
c@9
|
8 #include <sstream>
|
c@9
|
9 #include <stdexcept>
|
c@4
|
10
|
c@0
|
11 using Vamp::FFT;
|
c@7
|
12 using Vamp::RealTime;
|
c@0
|
13 using namespace std;
|
c@0
|
14
|
c@0
|
15 Tempogram::Tempogram(float inputSampleRate) :
|
c@0
|
16 Plugin(inputSampleRate),
|
c@0
|
17 m_blockSize(0),
|
c@1
|
18 m_stepSize(0),
|
c@13
|
19 m_compressionConstant(1000), //parameter
|
c@13
|
20 m_minDB(0),
|
c@13
|
21 m_log2WindowLength(10),
|
c@13
|
22 m_windowLength(pow((float)2,(float)m_log2WindowLength)), //parameter
|
c@13
|
23 m_fftLength(4096), //parameter
|
c@13
|
24 m_hopSize(64), //parameter
|
c@13
|
25 m_minBPM(30),
|
c@13
|
26 m_maxBPM(480),
|
c@13
|
27 m_minBin(0), //set in initialise()
|
c@13
|
28 m_maxBin(0), //set in initialise()
|
c@13
|
29 m_numberOfBlocks(0) //incremented in process()
|
c@0
|
30
|
c@0
|
31 // Also be sure to set your plugin parameters (presumably stored
|
c@0
|
32 // in member variables) to their default values here -- the host
|
c@0
|
33 // will not do that for you
|
c@0
|
34 {
|
c@0
|
35 }
|
c@0
|
36
|
c@0
|
37 Tempogram::~Tempogram()
|
c@0
|
38 {
|
c@0
|
39 //delete stuff
|
c@7
|
40 cleanup();
|
c@0
|
41 }
|
c@0
|
42
|
c@0
|
43 string
|
c@0
|
44 Tempogram::getIdentifier() const
|
c@0
|
45 {
|
c@0
|
46 return "tempogram";
|
c@0
|
47 }
|
c@0
|
48
|
c@0
|
49 string
|
c@0
|
50 Tempogram::getName() const
|
c@0
|
51 {
|
c@0
|
52 return "Tempogram";
|
c@0
|
53 }
|
c@0
|
54
|
c@0
|
55 string
|
c@0
|
56 Tempogram::getDescription() const
|
c@0
|
57 {
|
c@0
|
58 // Return something helpful here!
|
c@0
|
59 return "Cyclic Tempogram as described by Peter Grosche and Meinard Muller";
|
c@0
|
60 }
|
c@0
|
61
|
c@0
|
62 string
|
c@0
|
63 Tempogram::getMaker() const
|
c@0
|
64 {
|
c@0
|
65 //Your name here
|
c@0
|
66 return "Carl Bussey";
|
c@0
|
67 }
|
c@0
|
68
|
c@0
|
69 int
|
c@0
|
70 Tempogram::getPluginVersion() const
|
c@0
|
71 {
|
c@0
|
72 // Increment this each time you release a version that behaves
|
c@0
|
73 // differently from the previous one
|
c@0
|
74 return 1;
|
c@0
|
75 }
|
c@0
|
76
|
c@0
|
77 string
|
c@0
|
78 Tempogram::getCopyright() const
|
c@0
|
79 {
|
c@0
|
80 // This function is not ideally named. It does not necessarily
|
c@0
|
81 // need to say who made the plugin -- getMaker does that -- but it
|
c@0
|
82 // should indicate the terms under which it is distributed. For
|
c@0
|
83 // example, "Copyright (year). All Rights Reserved", or "GPL"
|
c@0
|
84 return "";
|
c@0
|
85 }
|
c@0
|
86
|
c@0
|
87 Tempogram::InputDomain
|
c@0
|
88 Tempogram::getInputDomain() const
|
c@0
|
89 {
|
c@0
|
90 return FrequencyDomain;
|
c@0
|
91 }
|
c@0
|
92
|
c@0
|
93 size_t
|
c@0
|
94 Tempogram::getPreferredBlockSize() const
|
c@0
|
95 {
|
c@9
|
96 return 2048; // 0 means "I can handle any block size"
|
c@0
|
97 }
|
c@0
|
98
|
c@0
|
99 size_t
|
c@0
|
100 Tempogram::getPreferredStepSize() const
|
c@0
|
101 {
|
c@9
|
102 return 1024; // 0 means "anything sensible"; in practice this
|
c@0
|
103 // means the same as the block size for TimeDomain
|
c@0
|
104 // plugins, or half of it for FrequencyDomain plugins
|
c@0
|
105 }
|
c@0
|
106
|
c@0
|
107 size_t
|
c@0
|
108 Tempogram::getMinChannelCount() const
|
c@0
|
109 {
|
c@0
|
110 return 1;
|
c@0
|
111 }
|
c@0
|
112
|
c@0
|
113 size_t
|
c@0
|
114 Tempogram::getMaxChannelCount() const
|
c@0
|
115 {
|
c@0
|
116 return 1;
|
c@0
|
117 }
|
c@0
|
118
|
c@0
|
119 Tempogram::ParameterList
|
c@0
|
120 Tempogram::getParameterDescriptors() const
|
c@0
|
121 {
|
c@0
|
122 ParameterList list;
|
c@0
|
123
|
c@0
|
124 // If the plugin has no adjustable parameters, return an empty
|
c@0
|
125 // list here (and there's no need to provide implementations of
|
c@0
|
126 // getParameter and setParameter in that case either).
|
c@0
|
127
|
c@0
|
128 // Note that it is your responsibility to make sure the parameters
|
c@0
|
129 // start off having their default values (e.g. in the constructor
|
c@0
|
130 // above). The host needs to know the default value so it can do
|
c@0
|
131 // things like provide a "reset to default" function, but it will
|
c@0
|
132 // not explicitly set your parameters to their defaults for you if
|
c@0
|
133 // they have not changed in the mean time.
|
c@0
|
134
|
c@9
|
135 ParameterDescriptor d;
|
c@9
|
136 d.identifier = "C";
|
c@9
|
137 d.name = "C";
|
c@9
|
138 d.description = "Spectrogram compression constant, C, used when retrieving the novelty curve from the audio.";
|
c@9
|
139 d.unit = "";
|
c@9
|
140 d.minValue = 2;
|
c@9
|
141 d.maxValue = 10000;
|
c@9
|
142 d.defaultValue = 1000;
|
c@9
|
143 d.isQuantized = false;
|
c@9
|
144 list.push_back(d);
|
c@9
|
145
|
c@13
|
146 d.identifier = "log2TN";
|
c@9
|
147 d.name = "Tempogram Window Length";
|
c@9
|
148 d.description = "FFT window length when analysing the novelty curve and extracting the tempogram time-frequency function.";
|
c@9
|
149 d.unit = "";
|
c@13
|
150 d.minValue = 7;
|
c@13
|
151 d.maxValue = 12;
|
c@13
|
152 d.defaultValue = 10;
|
c@9
|
153 d.isQuantized = true;
|
c@13
|
154 d.quantizeStep = 1;
|
c@13
|
155 for (int i = d.minValue; i <= d.maxValue; i++){
|
c@13
|
156 d.valueNames.push_back(floatToString(pow((float)2,(float)i)));
|
c@13
|
157 }
|
c@9
|
158 list.push_back(d);
|
c@0
|
159
|
c@9
|
160 d.identifier = "minBPM";
|
c@9
|
161 d.name = "Minimum BPM";
|
c@9
|
162 d.description = "The minimum BPM of the tempogram output bins.";
|
c@9
|
163 d.unit = "";
|
c@9
|
164 d.minValue = 0;
|
c@9
|
165 d.maxValue = 2000;
|
c@9
|
166 d.defaultValue = 30;
|
c@9
|
167 d.isQuantized = true;
|
c@9
|
168 d.quantizeStep = 5;
|
c@9
|
169 list.push_back(d);
|
c@9
|
170
|
c@9
|
171 d.identifier = "maxBPM";
|
c@9
|
172 d.name = "Maximum BPM";
|
c@9
|
173 d.description = "The minimum BPM of the tempogram output bins.";
|
c@9
|
174 d.unit = "";
|
c@9
|
175 d.minValue = 30;
|
c@9
|
176 d.maxValue = 2000;
|
c@9
|
177 d.defaultValue = 480;
|
c@9
|
178 d.isQuantized = true;
|
c@9
|
179 d.quantizeStep = 5;
|
c@9
|
180 list.push_back(d);
|
c@0
|
181
|
c@0
|
182 return list;
|
c@0
|
183 }
|
c@0
|
184
|
c@0
|
185 float
|
c@0
|
186 Tempogram::getParameter(string identifier) const
|
c@0
|
187 {
|
c@0
|
188 if (identifier == "C") {
|
c@13
|
189 return m_compressionConstant; // return the ACTUAL current value of your parameter here!
|
c@0
|
190 }
|
c@13
|
191 if (identifier == "log2TN"){
|
c@13
|
192 return m_log2WindowLength;
|
c@9
|
193 }
|
c@9
|
194 if (identifier == "minBPM") {
|
c@13
|
195 return m_minBPM;
|
c@9
|
196 }
|
c@9
|
197 if (identifier == "maxBPM"){
|
c@13
|
198 return m_maxBPM;
|
c@0
|
199 }
|
c@0
|
200
|
c@0
|
201 return 0;
|
c@0
|
202 }
|
c@0
|
203
|
c@0
|
204 void
|
c@0
|
205 Tempogram::setParameter(string identifier, float value)
|
c@0
|
206 {
|
c@9
|
207
|
c@0
|
208 if (identifier == "C") {
|
c@13
|
209 m_compressionConstant = value; // set the actual value of your parameter
|
c@0
|
210 }
|
c@13
|
211 if (identifier == "log2TN") {
|
c@13
|
212 m_windowLength = pow(2,value);
|
c@13
|
213 m_log2WindowLength = value;
|
c@0
|
214 }
|
c@9
|
215 if (identifier == "minBPM") {
|
c@13
|
216 m_minBPM = value;
|
c@9
|
217 }
|
c@9
|
218 if (identifier == "maxBPM"){
|
c@13
|
219 m_maxBPM = value;
|
c@9
|
220 }
|
c@9
|
221
|
c@9
|
222 }
|
c@9
|
223
|
c@9
|
224 void Tempogram::updateBPMParameters(){
|
c@9
|
225
|
c@0
|
226 }
|
c@0
|
227
|
c@0
|
228 Tempogram::ProgramList
|
c@0
|
229 Tempogram::getPrograms() const
|
c@0
|
230 {
|
c@0
|
231 ProgramList list;
|
c@0
|
232
|
c@0
|
233 // If you have no programs, return an empty list (or simply don't
|
c@0
|
234 // implement this function or getCurrentProgram/selectProgram)
|
c@0
|
235
|
c@0
|
236 return list;
|
c@0
|
237 }
|
c@0
|
238
|
c@0
|
239 string
|
c@0
|
240 Tempogram::getCurrentProgram() const
|
c@0
|
241 {
|
c@0
|
242 return ""; // no programs
|
c@0
|
243 }
|
c@0
|
244
|
c@0
|
245 void
|
c@0
|
246 Tempogram::selectProgram(string name)
|
c@0
|
247 {
|
c@0
|
248 }
|
c@0
|
249
|
c@9
|
250 string Tempogram::floatToString(float value) const
|
c@9
|
251 {
|
c@9
|
252 ostringstream ss;
|
c@9
|
253
|
c@9
|
254 if(!(ss << value)) throw runtime_error("Tempogram::floatToString(): invalid conversion from float to string");
|
c@9
|
255 return ss.str();
|
c@9
|
256 }
|
c@9
|
257
|
c@0
|
258 Tempogram::OutputList
|
c@0
|
259 Tempogram::getOutputDescriptors() const
|
c@0
|
260 {
|
c@0
|
261 OutputList list;
|
c@0
|
262
|
c@0
|
263 // See OutputDescriptor documentation for the possibilities here.
|
c@0
|
264 // Every plugin must have at least one output.
|
c@1
|
265
|
c@0
|
266 OutputDescriptor d;
|
c@7
|
267 float d_sampleRate;
|
c@9
|
268 float tempogramInputSampleRate = (float)m_inputSampleRate/m_stepSize;
|
c@7
|
269
|
c@1
|
270 d.identifier = "tempogram";
|
c@7
|
271 d.name = "Tempogram";
|
c@7
|
272 d.description = "Tempogram";
|
c@9
|
273 d.unit = "BPM";
|
c@1
|
274 d.hasFixedBinCount = true;
|
c@13
|
275 d.binCount = m_maxBin - m_minBin + 1;
|
c@0
|
276 d.hasKnownExtents = false;
|
c@0
|
277 d.isQuantized = false;
|
c@1
|
278 d.sampleType = OutputDescriptor::FixedSampleRate;
|
c@13
|
279 d_sampleRate = tempogramInputSampleRate/m_hopSize;
|
c@1
|
280 d.sampleRate = d_sampleRate > 0.0 && !isnan(d_sampleRate) ? d_sampleRate : 0.0;
|
c@13
|
281 for(int i = m_minBin; i <= (int)m_maxBin; i++){
|
c@13
|
282 float w = ((float)i/m_fftLength)*(tempogramInputSampleRate);
|
c@9
|
283 d.binNames.push_back(floatToString(w*60));
|
c@9
|
284 }
|
c@0
|
285 d.hasDuration = false;
|
c@0
|
286 list.push_back(d);
|
c@7
|
287
|
c@1
|
288 d.identifier = "nc";
|
c@1
|
289 d.name = "Novelty Curve";
|
c@1
|
290 d.description = "Novelty Curve";
|
c@1
|
291 d.unit = "";
|
c@1
|
292 d.hasFixedBinCount = true;
|
c@1
|
293 d.binCount = 1;
|
c@1
|
294 d.hasKnownExtents = false;
|
c@1
|
295 d.isQuantized = false;
|
c@1
|
296 d.sampleType = OutputDescriptor::FixedSampleRate;
|
c@9
|
297 d_sampleRate = tempogramInputSampleRate;
|
c@1
|
298 d.sampleRate = d_sampleRate > 0 && !isnan(d_sampleRate) ? d_sampleRate : 0.0;
|
c@1
|
299 d.hasDuration = false;
|
c@1
|
300 list.push_back(d);
|
c@1
|
301
|
c@0
|
302 return list;
|
c@0
|
303 }
|
c@0
|
304
|
c@0
|
305 bool
|
c@0
|
306 Tempogram::initialise(size_t channels, size_t stepSize, size_t blockSize)
|
c@0
|
307 {
|
c@0
|
308 if (channels < getMinChannelCount() ||
|
c@0
|
309 channels > getMaxChannelCount()) return false;
|
c@9
|
310
|
c@0
|
311 // Real initialisation work goes here!
|
c@0
|
312 m_blockSize = blockSize;
|
c@1
|
313 m_stepSize = stepSize;
|
c@13
|
314 m_minDB = pow(10,(float)-74/20);
|
c@0
|
315
|
c@13
|
316 if (m_minBPM > m_maxBPM){
|
c@13
|
317 m_minBPM = 30;
|
c@13
|
318 m_maxBPM = 480;
|
c@9
|
319 }
|
c@9
|
320 float tempogramInputSampleRate = (float)m_inputSampleRate/m_stepSize;
|
c@13
|
321 m_minBin = (unsigned int)(max(floor(((m_minBPM/60)/tempogramInputSampleRate)*m_fftLength), (float)0.0));
|
c@13
|
322 m_maxBin = (unsigned int)(min(ceil(((m_maxBPM/60)/tempogramInputSampleRate)*m_fftLength), (float)m_fftLength/2));
|
c@9
|
323
|
c@13
|
324 m_spectrogram = vector< vector<float> >(m_blockSize/2 + 1);
|
c@4
|
325
|
c@0
|
326 return true;
|
c@0
|
327 }
|
c@0
|
328
|
c@7
|
329 void Tempogram::cleanup(){
|
c@7
|
330
|
c@7
|
331 }
|
c@7
|
332
|
c@0
|
333 void
|
c@0
|
334 Tempogram::reset()
|
c@0
|
335 {
|
c@0
|
336 // Clear buffers, reset stored values, etc
|
c@7
|
337 ncTimestamps.clear();
|
c@13
|
338 m_spectrogram.clear();
|
c@13
|
339 m_spectrogram = vector< vector<float> >(m_blockSize/2 + 1);
|
c@0
|
340 }
|
c@0
|
341
|
c@0
|
342 Tempogram::FeatureSet
|
c@0
|
343 Tempogram::process(const float *const *inputBuffers, Vamp::RealTime timestamp)
|
c@0
|
344 {
|
c@0
|
345 size_t n = m_blockSize/2 + 1;
|
c@0
|
346
|
c@0
|
347 FeatureSet featureSet;
|
c@0
|
348 Feature feature;
|
c@0
|
349
|
c@0
|
350 const float *in = inputBuffers[0];
|
c@3
|
351
|
c@9
|
352 //calculate magnitude of FrequencyDomain input
|
c@13
|
353 for (unsigned int i = 0; i < n; i++){
|
c@0
|
354 float magnitude = sqrt(in[2*i] * in[2*i] + in[2*i + 1] * in[2*i + 1]);
|
c@13
|
355 magnitude = magnitude > m_minDB ? magnitude : m_minDB;
|
c@13
|
356 m_spectrogram[i].push_back(magnitude);
|
c@0
|
357 }
|
c@0
|
358
|
c@13
|
359 m_numberOfBlocks++;
|
c@9
|
360 ncTimestamps.push_back(timestamp); //save timestamp
|
c@7
|
361
|
c@2
|
362 return featureSet;
|
c@0
|
363 }
|
c@0
|
364
|
c@11
|
365 Tempogram::FeatureSet
|
c@11
|
366 Tempogram::getRemainingFeatures()
|
c@11
|
367 {
|
c@0
|
368
|
c@13
|
369 float * hannWindowtN = new float[m_windowLength];
|
c@13
|
370 for (unsigned int i = 0; i < m_windowLength; i++){
|
c@7
|
371 hannWindowtN[i] = 0.0;
|
c@4
|
372 }
|
c@11
|
373
|
c@1
|
374 FeatureSet featureSet;
|
c@0
|
375
|
c@13
|
376 //initialise m_noveltyCurve processor
|
c@13
|
377 NoveltyCurve nc(m_inputSampleRate, m_blockSize, m_numberOfBlocks, m_compressionConstant);
|
c@13
|
378 m_noveltyCurve = nc.spectrogramToNoveltyCurve(m_spectrogram); //calculate novelty curve from magnitude data
|
c@4
|
379
|
c@9
|
380 //push novelty curve data to featureset 1 and set timestamps
|
c@13
|
381 for (unsigned int i = 0; i < m_numberOfBlocks; i++){
|
c@7
|
382 Feature feature;
|
c@13
|
383 feature.values.push_back(m_noveltyCurve[i]);
|
c@7
|
384 feature.hasTimestamp = true;
|
c@7
|
385 feature.timestamp = ncTimestamps[i];
|
c@7
|
386 featureSet[1].push_back(feature);
|
c@4
|
387 }
|
c@4
|
388
|
c@9
|
389 //window function for spectrogram
|
c@13
|
390 WindowFunction::hanning(hannWindowtN,m_windowLength);
|
c@9
|
391
|
c@9
|
392 //initialise spectrogram processor
|
c@13
|
393 SpectrogramProcessor spectrogramProcessor(m_numberOfBlocks, m_windowLength, m_fftLength, m_hopSize);
|
c@9
|
394 //compute spectrogram from novelty curve data (i.e., tempogram)
|
c@13
|
395 Spectrogram tempogram = spectrogramProcessor.process(&m_noveltyCurve[0], hannWindowtN);
|
c@0
|
396
|
c@13
|
397 int timePointer = m_hopSize-m_windowLength/2;
|
c@7
|
398 int tempogramLength = tempogram[0].size();
|
c@7
|
399
|
c@9
|
400 //push tempogram data to featureset 0 and set timestamps.
|
c@7
|
401 for (int block = 0; block < tempogramLength; block++){
|
c@0
|
402 Feature feature;
|
c@0
|
403
|
c@7
|
404 int timeMS = floor(1000*(m_stepSize*timePointer)/m_inputSampleRate + 0.5);
|
c@7
|
405
|
c@13
|
406 assert(tempogram.size() == (m_fftLength/2 + 1));
|
c@13
|
407 for(int k = m_minBin; k < (int)m_maxBin; k++){
|
c@7
|
408 feature.values.push_back(tempogram[k][block]);
|
c@13
|
409 //cout << tempogram[k][block] << endl;
|
c@0
|
410 }
|
c@7
|
411 feature.hasTimestamp = true;
|
c@7
|
412 feature.timestamp = RealTime::fromMilliseconds(timeMS);
|
c@7
|
413 featureSet[0].push_back(feature);
|
c@4
|
414
|
c@13
|
415 timePointer += m_hopSize;
|
c@0
|
416 }
|
c@0
|
417
|
c@11
|
418 //float func = [](){ cout << "Hello"; };
|
c@11
|
419
|
c@11
|
420 delete []hannWindowtN;
|
c@13
|
421 hannWindowtN = 0;
|
c@0
|
422
|
c@0
|
423 return featureSet;
|
c@0
|
424 }
|