Mercurial > hg > vamp-tempogram
comparison Tempogram.cpp @ 9:be59b4a73f49
* Added Spectrogram zero padding functionality
* Made output bins correspond to BPM
* User can now specify a range of output bins to view
* Comments added
author | Carl Bussey <c.bussey@se10.qmul.ac.uk> |
---|---|
date | Tue, 12 Aug 2014 14:40:37 +0100 |
parents | 4e429b9f2b4d |
children | 09fb76606b2b |
comparison
equal
deleted
inserted
replaced
8:4e429b9f2b4d | 9:be59b4a73f49 |
---|---|
3 // libraries. Replace MyPlugin and myPlugin throughout with the name | 3 // libraries. Replace MyPlugin and myPlugin throughout with the name |
4 // of your first plugin class, and fill in the gaps as appropriate. | 4 // of your first plugin class, and fill in the gaps as appropriate. |
5 | 5 |
6 | 6 |
7 #include "Tempogram.h" | 7 #include "Tempogram.h" |
8 #include <sstream> | |
9 #include <stdexcept> | |
8 | 10 |
9 using Vamp::FFT; | 11 using Vamp::FFT; |
10 using Vamp::RealTime; | 12 using Vamp::RealTime; |
11 using namespace std; | 13 using namespace std; |
12 | 14 |
13 Tempogram::Tempogram(float inputSampleRate) : | 15 Tempogram::Tempogram(float inputSampleRate) : |
14 Plugin(inputSampleRate), | 16 Plugin(inputSampleRate), |
15 m_blockSize(0), | 17 m_blockSize(0), |
16 m_stepSize(0), | 18 m_stepSize(0), |
17 compressionConstant(1000), //make param | 19 compressionConstant(1000), //parameter |
18 specMax(0), | |
19 minDB(0), | 20 minDB(0), |
20 tN(128), //make param | 21 windowLength(128), //parameter |
21 thopSize(64), //make param | 22 fftLength(4096), //parameter |
22 fftInput(NULL), | 23 thopSize(64), //parameter |
23 fftOutputReal(NULL), | 24 minBPM(30), |
24 fftOutputImag(NULL), | 25 maxBPM(480), |
25 numberOfBlocks(0) | 26 minBin(0), //set in initialise() |
27 maxBin(0), //set in initialise() | |
28 numberOfBlocks(0) //incremented in process() | |
26 | 29 |
27 // Also be sure to set your plugin parameters (presumably stored | 30 // Also be sure to set your plugin parameters (presumably stored |
28 // in member variables) to their default values here -- the host | 31 // in member variables) to their default values here -- the host |
29 // will not do that for you | 32 // will not do that for you |
30 { | 33 { |
87 } | 90 } |
88 | 91 |
89 size_t | 92 size_t |
90 Tempogram::getPreferredBlockSize() const | 93 Tempogram::getPreferredBlockSize() const |
91 { | 94 { |
92 return 0; // 0 means "I can handle any block size" | 95 return 2048; // 0 means "I can handle any block size" |
93 } | 96 } |
94 | 97 |
95 size_t | 98 size_t |
96 Tempogram::getPreferredStepSize() const | 99 Tempogram::getPreferredStepSize() const |
97 { | 100 { |
98 return 0; // 0 means "anything sensible"; in practice this | 101 return 1024; // 0 means "anything sensible"; in practice this |
99 // means the same as the block size for TimeDomain | 102 // means the same as the block size for TimeDomain |
100 // plugins, or half of it for FrequencyDomain plugins | 103 // plugins, or half of it for FrequencyDomain plugins |
101 } | 104 } |
102 | 105 |
103 size_t | 106 size_t |
126 // above). The host needs to know the default value so it can do | 129 // above). The host needs to know the default value so it can do |
127 // things like provide a "reset to default" function, but it will | 130 // things like provide a "reset to default" function, but it will |
128 // not explicitly set your parameters to their defaults for you if | 131 // not explicitly set your parameters to their defaults for you if |
129 // they have not changed in the mean time. | 132 // they have not changed in the mean time. |
130 | 133 |
131 ParameterDescriptor C; | 134 ParameterDescriptor d; |
132 C.identifier = "C"; | 135 d.identifier = "C"; |
133 C.name = "C"; | 136 d.name = "C"; |
134 C.description = "Spectrogram compression constant, C"; | 137 d.description = "Spectrogram compression constant, C, used when retrieving the novelty curve from the audio."; |
135 C.unit = ""; | 138 d.unit = ""; |
136 C.minValue = 2; | 139 d.minValue = 2; |
137 C.maxValue = 10000; | 140 d.maxValue = 10000; |
138 C.defaultValue = 1000; | 141 d.defaultValue = 1000; |
139 C.isQuantized = false; | 142 d.isQuantized = false; |
140 list.push_back(C); | 143 list.push_back(d); |
141 | 144 |
142 ParameterDescriptor tN; | 145 d.identifier = "TN"; |
143 tN.identifier = "tN"; | 146 d.name = "Tempogram Window Length"; |
144 tN.name = "Tempogram FFT length"; | 147 d.description = "FFT window length when analysing the novelty curve and extracting the tempogram time-frequency function."; |
145 tN.description = "Tempogram FFT length."; | 148 d.unit = ""; |
146 tN.unit = ""; | 149 d.minValue = 1024; |
147 tN.minValue = 128; | 150 d.maxValue = 4096; |
148 tN.maxValue = 4096; | 151 d.defaultValue = 128; |
149 tN.defaultValue = 128; | 152 d.isQuantized = true; |
150 tN.isQuantized = true; | 153 d.quantizeStep = 128; |
151 tN.quantizeStep = 128; | 154 list.push_back(d); |
152 list.push_back(tN); | 155 |
156 d.identifier = "minBPM"; | |
157 d.name = "Minimum BPM"; | |
158 d.description = "The minimum BPM of the tempogram output bins."; | |
159 d.unit = ""; | |
160 d.minValue = 0; | |
161 d.maxValue = 2000; | |
162 d.defaultValue = 30; | |
163 d.isQuantized = true; | |
164 d.quantizeStep = 5; | |
165 list.push_back(d); | |
166 | |
167 d.identifier = "maxBPM"; | |
168 d.name = "Maximum BPM"; | |
169 d.description = "The minimum BPM of the tempogram output bins."; | |
170 d.unit = ""; | |
171 d.minValue = 30; | |
172 d.maxValue = 2000; | |
173 d.defaultValue = 480; | |
174 d.isQuantized = true; | |
175 d.quantizeStep = 5; | |
176 list.push_back(d); | |
153 | 177 |
154 return list; | 178 return list; |
155 } | 179 } |
156 | 180 |
157 float | 181 float |
158 Tempogram::getParameter(string identifier) const | 182 Tempogram::getParameter(string identifier) const |
159 { | 183 { |
160 if (identifier == "C") { | 184 if (identifier == "C") { |
161 return compressionConstant; // return the ACTUAL current value of your parameter here! | 185 return compressionConstant; // return the ACTUAL current value of your parameter here! |
162 } | 186 } |
163 if (identifier == "tN"){ | 187 if (identifier == "TN"){ |
164 return tN; | 188 return windowLength; |
189 } | |
190 if (identifier == "minBPM") { | |
191 return minBPM; | |
192 } | |
193 if (identifier == "maxBPM"){ | |
194 return maxBPM; | |
165 } | 195 } |
166 | 196 |
167 return 0; | 197 return 0; |
168 } | 198 } |
169 | 199 |
170 void | 200 void |
171 Tempogram::setParameter(string identifier, float value) | 201 Tempogram::setParameter(string identifier, float value) |
172 { | 202 { |
203 | |
173 if (identifier == "C") { | 204 if (identifier == "C") { |
174 compressionConstant = value; // set the actual value of your parameter | 205 compressionConstant = value; // set the actual value of your parameter |
175 } | 206 } |
176 if (identifier == "tN") { | 207 if (identifier == "TN") { |
177 tN = value; | 208 windowLength = value; |
178 } | 209 } |
210 if (identifier == "minBPM") { | |
211 minBPM = value; | |
212 } | |
213 if (identifier == "maxBPM"){ | |
214 maxBPM = value; | |
215 } | |
216 | |
217 } | |
218 | |
219 void Tempogram::updateBPMParameters(){ | |
220 | |
179 } | 221 } |
180 | 222 |
181 Tempogram::ProgramList | 223 Tempogram::ProgramList |
182 Tempogram::getPrograms() const | 224 Tempogram::getPrograms() const |
183 { | 225 { |
198 void | 240 void |
199 Tempogram::selectProgram(string name) | 241 Tempogram::selectProgram(string name) |
200 { | 242 { |
201 } | 243 } |
202 | 244 |
245 string Tempogram::floatToString(float value) const | |
246 { | |
247 ostringstream ss; | |
248 | |
249 if(!(ss << value)) throw runtime_error("Tempogram::floatToString(): invalid conversion from float to string"); | |
250 return ss.str(); | |
251 } | |
252 | |
203 Tempogram::OutputList | 253 Tempogram::OutputList |
204 Tempogram::getOutputDescriptors() const | 254 Tempogram::getOutputDescriptors() const |
205 { | 255 { |
206 OutputList list; | 256 OutputList list; |
207 | 257 |
208 // See OutputDescriptor documentation for the possibilities here. | 258 // See OutputDescriptor documentation for the possibilities here. |
209 // Every plugin must have at least one output. | 259 // Every plugin must have at least one output. |
210 | 260 |
211 OutputDescriptor d; | 261 OutputDescriptor d; |
212 float d_sampleRate; | 262 float d_sampleRate; |
263 float tempogramInputSampleRate = (float)m_inputSampleRate/m_stepSize; | |
213 | 264 |
214 d.identifier = "tempogram"; | 265 d.identifier = "tempogram"; |
215 d.name = "Tempogram"; | 266 d.name = "Tempogram"; |
216 d.description = "Tempogram"; | 267 d.description = "Tempogram"; |
217 d.unit = ""; | 268 d.unit = "BPM"; |
218 d.hasFixedBinCount = true; | 269 d.hasFixedBinCount = true; |
219 d.binCount = tN/2 + 1; | 270 d.binCount = maxBin - minBin + 1; |
220 d.hasKnownExtents = false; | 271 d.hasKnownExtents = false; |
221 d.isQuantized = false; | 272 d.isQuantized = false; |
222 d.sampleType = OutputDescriptor::FixedSampleRate; | 273 d.sampleType = OutputDescriptor::FixedSampleRate; |
223 d_sampleRate = m_inputSampleRate/(m_stepSize * thopSize); | 274 d_sampleRate = tempogramInputSampleRate/thopSize; |
224 d.sampleRate = d_sampleRate > 0.0 && !isnan(d_sampleRate) ? d_sampleRate : 0.0; | 275 d.sampleRate = d_sampleRate > 0.0 && !isnan(d_sampleRate) ? d_sampleRate : 0.0; |
276 for(int i = minBin; i <= maxBin; i++){ | |
277 float w = ((float)i/fftLength)*(tempogramInputSampleRate); | |
278 d.binNames.push_back(floatToString(w*60)); | |
279 } | |
225 d.hasDuration = false; | 280 d.hasDuration = false; |
226 list.push_back(d); | 281 list.push_back(d); |
227 | 282 |
228 d.identifier = "nc"; | 283 d.identifier = "nc"; |
229 d.name = "Novelty Curve"; | 284 d.name = "Novelty Curve"; |
232 d.hasFixedBinCount = true; | 287 d.hasFixedBinCount = true; |
233 d.binCount = 1; | 288 d.binCount = 1; |
234 d.hasKnownExtents = false; | 289 d.hasKnownExtents = false; |
235 d.isQuantized = false; | 290 d.isQuantized = false; |
236 d.sampleType = OutputDescriptor::FixedSampleRate; | 291 d.sampleType = OutputDescriptor::FixedSampleRate; |
237 d_sampleRate = m_inputSampleRate/m_stepSize; | 292 d_sampleRate = tempogramInputSampleRate; |
238 d.sampleRate = d_sampleRate > 0 && !isnan(d_sampleRate) ? d_sampleRate : 0.0; | 293 d.sampleRate = d_sampleRate > 0 && !isnan(d_sampleRate) ? d_sampleRate : 0.0; |
239 d.hasDuration = false; | 294 d.hasDuration = false; |
240 list.push_back(d); | 295 list.push_back(d); |
241 | 296 |
242 return list; | 297 return list; |
245 bool | 300 bool |
246 Tempogram::initialise(size_t channels, size_t stepSize, size_t blockSize) | 301 Tempogram::initialise(size_t channels, size_t stepSize, size_t blockSize) |
247 { | 302 { |
248 if (channels < getMinChannelCount() || | 303 if (channels < getMinChannelCount() || |
249 channels > getMaxChannelCount()) return false; | 304 channels > getMaxChannelCount()) return false; |
250 | 305 |
251 // Real initialisation work goes here! | 306 // Real initialisation work goes here! |
252 m_blockSize = blockSize; | 307 m_blockSize = blockSize; |
253 m_stepSize = stepSize; | 308 m_stepSize = stepSize; |
254 minDB = pow(10,(float)-74/20); | 309 minDB = pow(10,(float)-74/20); |
310 | |
311 if (minBPM > maxBPM){ | |
312 minBPM = 30; | |
313 maxBPM = 480; | |
314 } | |
315 float tempogramInputSampleRate = (float)m_inputSampleRate/m_stepSize; | |
316 minBin = (unsigned int)(max(floor(((minBPM/60)/tempogramInputSampleRate)*fftLength), (float)0.0)); | |
317 maxBin = (unsigned int)(min(ceil(((maxBPM/60)/tempogramInputSampleRate)*fftLength), (float)fftLength/2)); | |
255 | 318 |
256 specData = vector< vector<float> >(m_blockSize/2 + 1); | 319 specData = vector< vector<float> >(m_blockSize/2 + 1); |
257 | 320 |
258 return true; | 321 return true; |
259 } | 322 } |
280 FeatureSet featureSet; | 343 FeatureSet featureSet; |
281 Feature feature; | 344 Feature feature; |
282 | 345 |
283 const float *in = inputBuffers[0]; | 346 const float *in = inputBuffers[0]; |
284 | 347 |
348 //calculate magnitude of FrequencyDomain input | |
285 for (int i = 0; i < n; i++){ | 349 for (int i = 0; i < n; i++){ |
286 float magnitude = sqrt(in[2*i] * in[2*i] + in[2*i + 1] * in[2*i + 1]); | 350 float magnitude = sqrt(in[2*i] * in[2*i] + in[2*i + 1] * in[2*i + 1]); |
287 magnitude = magnitude > minDB ? magnitude : minDB; | 351 magnitude = magnitude > minDB ? magnitude : minDB; |
288 specData[i].push_back(magnitude); | 352 specData[i].push_back(magnitude); |
289 } | 353 } |
290 | 354 |
291 numberOfBlocks++; | 355 numberOfBlocks++; |
292 ncTimestamps.push_back(timestamp); | 356 ncTimestamps.push_back(timestamp); //save timestamp |
293 | 357 |
294 return featureSet; | 358 return featureSet; |
295 } | 359 } |
296 | 360 |
297 void | 361 void |
298 Tempogram::initialiseForGRF(){ | 362 Tempogram::initialiseForGRF(){ |
299 hannWindowtN = new float[tN]; | 363 hannWindowtN = new float[windowLength]; |
300 fftInput = new double[tN]; | 364 |
301 fftOutputReal = new double[tN]; | 365 for (int i = 0; i < windowLength; i++){ |
302 fftOutputImag = new double[tN]; | |
303 | |
304 for (int i = 0; i < tN; i ++){ | |
305 hannWindowtN[i] = 0.0; | 366 hannWindowtN[i] = 0.0; |
306 fftInput[i] = 0.0; | |
307 fftOutputReal[i] = 0.0; | |
308 fftOutputImag[i] = 0.0; | |
309 } | 367 } |
310 } | 368 } |
311 | 369 |
312 void | 370 void |
313 Tempogram::cleanupForGRF(){ | 371 Tempogram::cleanupForGRF(){ |
314 delete []hannWindowtN; | 372 delete []hannWindowtN; |
315 hannWindowtN = NULL; | 373 hannWindowtN = NULL; |
316 fftInput = fftOutputReal = fftOutputImag = NULL; | 374 } |
317 } | 375 |
376 | |
318 | 377 |
319 Tempogram::FeatureSet | 378 Tempogram::FeatureSet |
320 Tempogram::getRemainingFeatures() | 379 Tempogram::getRemainingFeatures() |
321 { | 380 { |
322 //Make sure this is called at the beginning of the function | 381 //Make sure this is called at the beginning of the function |
323 initialiseForGRF(); | 382 initialiseForGRF(); |
324 FeatureSet featureSet; | 383 FeatureSet featureSet; |
325 | 384 |
385 //initialise noveltycurve processor | |
326 NoveltyCurve nc(m_inputSampleRate, m_blockSize, numberOfBlocks, compressionConstant); | 386 NoveltyCurve nc(m_inputSampleRate, m_blockSize, numberOfBlocks, compressionConstant); |
327 noveltyCurve = nc.spectrogramToNoveltyCurve(specData); | 387 noveltyCurve = nc.spectrogramToNoveltyCurve(specData); //calculate novelty curve from magnitude data |
328 | 388 |
389 //push novelty curve data to featureset 1 and set timestamps | |
329 for (int i = 0; i < numberOfBlocks; i++){ | 390 for (int i = 0; i < numberOfBlocks; i++){ |
330 Feature feature; | 391 Feature feature; |
331 feature.values.push_back(noveltyCurve[i]); | 392 feature.values.push_back(noveltyCurve[i]); |
332 feature.hasTimestamp = true; | 393 feature.hasTimestamp = true; |
333 feature.timestamp = ncTimestamps[i]; | 394 feature.timestamp = ncTimestamps[i]; |
334 featureSet[1].push_back(feature); | 395 featureSet[1].push_back(feature); |
335 } | 396 } |
336 | 397 |
337 WindowFunction::hanning(hannWindowtN, tN); | 398 //window function for spectrogram |
338 Spectrogram * spectrogramProcessor = new Spectrogram(numberOfBlocks, tN, thopSize); | 399 WindowFunction::hanning(hannWindowtN,windowLength); |
400 | |
401 //initialise spectrogram processor | |
402 Spectrogram * spectrogramProcessor = new Spectrogram(numberOfBlocks, windowLength, fftLength, thopSize); | |
403 //compute spectrogram from novelty curve data (i.e., tempogram) | |
339 vector< vector<float> > tempogram = spectrogramProcessor->audioToMagnitudeSpectrogram(&noveltyCurve[0], hannWindowtN); | 404 vector< vector<float> > tempogram = spectrogramProcessor->audioToMagnitudeSpectrogram(&noveltyCurve[0], hannWindowtN); |
340 delete spectrogramProcessor; | 405 delete spectrogramProcessor; |
341 spectrogramProcessor = NULL; | 406 spectrogramProcessor = NULL; |
342 | 407 |
343 int timePointer = thopSize-tN/2; | 408 int timePointer = thopSize-windowLength/2; |
344 int tempogramLength = tempogram[0].size(); | 409 int tempogramLength = tempogram[0].size(); |
345 | 410 |
411 //push tempogram data to featureset 0 and set timestamps. | |
346 for (int block = 0; block < tempogramLength; block++){ | 412 for (int block = 0; block < tempogramLength; block++){ |
347 Feature feature; | 413 Feature feature; |
348 | 414 |
349 int timeMS = floor(1000*(m_stepSize*timePointer)/m_inputSampleRate + 0.5); | 415 int timeMS = floor(1000*(m_stepSize*timePointer)/m_inputSampleRate + 0.5); |
350 | 416 |
351 for(int k = 0; k < tN/2 + 1; k++){ | 417 assert(tempogram.size() == (fftLength/2 + 1)); |
418 for(int k = minBin; k < maxBin; k++){ | |
352 feature.values.push_back(tempogram[k][block]); | 419 feature.values.push_back(tempogram[k][block]); |
353 } | 420 } |
354 feature.hasTimestamp = true; | 421 feature.hasTimestamp = true; |
355 feature.timestamp = RealTime::fromMilliseconds(timeMS); | 422 feature.timestamp = RealTime::fromMilliseconds(timeMS); |
356 featureSet[0].push_back(feature); | 423 featureSet[0].push_back(feature); |