comparison TempogramPlugin.cpp @ 14:c11367df624d

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