Mercurial > hg > vamp-tempogram
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 } |