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@14
|
7 #include "TempogramPlugin.h"
|
c@25
|
8
|
c@4
|
9
|
c@0
|
10 using Vamp::FFT;
|
c@7
|
11 using Vamp::RealTime;
|
c@0
|
12 using namespace std;
|
c@0
|
13
|
c@14
|
14 TempogramPlugin::TempogramPlugin(float inputSampleRate) :
|
c@0
|
15 Plugin(inputSampleRate),
|
c@18
|
16 m_inputBlockSize(0), //host parameter
|
c@18
|
17 m_inputStepSize(0), //host parameter
|
c@29
|
18 m_noveltyCurveMinDB(-74), //parameter
|
c@29
|
19 m_noveltyCurveMinV(0), //set in initialise()
|
c@18
|
20 m_noveltyCurveCompressionConstant(1000), //parameter
|
c@18
|
21 m_tempogramLog2WindowLength(10), //parameter
|
c@29
|
22 m_tempogramWindowLength(0), //set in initialise()
|
c@18
|
23 m_tempogramLog2FftLength(m_tempogramLog2WindowLength), //parameter
|
c@29
|
24 m_tempogramFftLength(0), //set in initialise()
|
c@18
|
25 m_tempogramLog2HopSize(6), //parameter
|
c@29
|
26 m_tempogramHopSize(0), //set in initialise()
|
c@18
|
27 m_tempogramMinBPM(30), //parameter
|
c@18
|
28 m_tempogramMaxBPM(480), //parameter
|
c@18
|
29 m_tempogramMinBin(0), //set in initialise()
|
c@18
|
30 m_tempogramMaxBin(0), //set in initialise()
|
c@29
|
31 m_tempogramMinLag(0), //set in initialise()
|
c@29
|
32 m_tempogramMaxLag(0), //set in initialise()
|
c@18
|
33 m_cyclicTempogramMinBPM(30), //reset in initialise()
|
c@18
|
34 m_cyclicTempogramNumberOfOctaves(0), //set in initialise()
|
c@18
|
35 m_cyclicTempogramOctaveDivider(30) //parameter
|
c@0
|
36
|
c@0
|
37 // Also be sure to set your plugin parameters (presumably stored
|
c@0
|
38 // in member variables) to their default values here -- the host
|
c@0
|
39 // will not do that for you
|
c@0
|
40 {
|
c@0
|
41 }
|
c@0
|
42
|
c@14
|
43 TempogramPlugin::~TempogramPlugin()
|
c@0
|
44 {
|
c@0
|
45 //delete stuff
|
c@0
|
46 }
|
c@0
|
47
|
c@0
|
48 string
|
c@14
|
49 TempogramPlugin::getIdentifier() const
|
c@0
|
50 {
|
c@0
|
51 return "tempogram";
|
c@0
|
52 }
|
c@0
|
53
|
c@0
|
54 string
|
c@14
|
55 TempogramPlugin::getName() const
|
c@0
|
56 {
|
c@0
|
57 return "Tempogram";
|
c@0
|
58 }
|
c@0
|
59
|
c@0
|
60 string
|
c@14
|
61 TempogramPlugin::getDescription() const
|
c@0
|
62 {
|
c@0
|
63 return "Cyclic Tempogram as described by Peter Grosche and Meinard Muller";
|
c@0
|
64 }
|
c@0
|
65
|
c@0
|
66 string
|
c@14
|
67 TempogramPlugin::getMaker() const
|
c@0
|
68 {
|
c@0
|
69 return "Carl Bussey";
|
c@0
|
70 }
|
c@0
|
71
|
c@0
|
72 int
|
c@14
|
73 TempogramPlugin::getPluginVersion() const
|
c@0
|
74 {
|
c@0
|
75 return 1;
|
c@0
|
76 }
|
c@0
|
77
|
c@0
|
78 string
|
c@14
|
79 TempogramPlugin::getCopyright() const
|
c@0
|
80 {
|
Chris@40
|
81 return "Copyright 2014 Queen Mary University of London. GPL licence.";
|
c@0
|
82 }
|
c@0
|
83
|
c@14
|
84 TempogramPlugin::InputDomain
|
c@14
|
85 TempogramPlugin::getInputDomain() const
|
c@0
|
86 {
|
c@0
|
87 return FrequencyDomain;
|
c@0
|
88 }
|
c@0
|
89
|
c@0
|
90 size_t
|
c@14
|
91 TempogramPlugin::getPreferredBlockSize() const
|
c@0
|
92 {
|
c@9
|
93 return 2048; // 0 means "I can handle any block size"
|
c@0
|
94 }
|
c@0
|
95
|
c@0
|
96 size_t
|
c@14
|
97 TempogramPlugin::getPreferredStepSize() const
|
c@0
|
98 {
|
c@9
|
99 return 1024; // 0 means "anything sensible"; in practice this
|
c@0
|
100 // means the same as the block size for TimeDomain
|
c@0
|
101 // plugins, or half of it for FrequencyDomain plugins
|
c@0
|
102 }
|
c@0
|
103
|
c@0
|
104 size_t
|
c@14
|
105 TempogramPlugin::getMinChannelCount() const
|
c@0
|
106 {
|
c@0
|
107 return 1;
|
c@0
|
108 }
|
c@0
|
109
|
c@0
|
110 size_t
|
c@14
|
111 TempogramPlugin::getMaxChannelCount() const
|
c@0
|
112 {
|
c@0
|
113 return 1;
|
c@0
|
114 }
|
c@0
|
115
|
c@14
|
116 TempogramPlugin::ParameterList
|
c@14
|
117 TempogramPlugin::getParameterDescriptors() const
|
c@0
|
118 {
|
c@0
|
119 ParameterList list;
|
c@0
|
120
|
c@0
|
121 // If the plugin has no adjustable parameters, return an empty
|
c@0
|
122 // list here (and there's no need to provide implementations of
|
c@0
|
123 // getParameter and setParameter in that case either).
|
c@0
|
124
|
c@0
|
125 // Note that it is your responsibility to make sure the parameters
|
c@0
|
126 // start off having their default values (e.g. in the constructor
|
c@0
|
127 // above). The host needs to know the default value so it can do
|
c@0
|
128 // things like provide a "reset to default" function, but it will
|
c@0
|
129 // not explicitly set your parameters to their defaults for you if
|
c@0
|
130 // they have not changed in the mean time.
|
c@0
|
131
|
c@14
|
132 ParameterDescriptor d1;
|
c@14
|
133 d1.identifier = "C";
|
c@15
|
134 d1.name = "Novelty Curve Spectrogram Compression Constant";
|
c@14
|
135 d1.description = "Spectrogram compression constant, C, used when retrieving the novelty curve from the audio.";
|
c@14
|
136 d1.unit = "";
|
c@14
|
137 d1.minValue = 2;
|
c@14
|
138 d1.maxValue = 10000;
|
c@14
|
139 d1.defaultValue = 1000;
|
c@14
|
140 d1.isQuantized = false;
|
c@14
|
141 list.push_back(d1);
|
c@29
|
142
|
c@29
|
143 ParameterDescriptor d2;
|
c@29
|
144 d2.identifier = "minDB";
|
c@29
|
145 d2.name = "Novelty Curve Minimum DB";
|
c@29
|
146 d2.description = "Spectrogram minimum DB used when removing unwanted peaks in the Spectrogram when retrieving the novelty curve from the audio.";
|
c@29
|
147 d2.unit = "";
|
c@29
|
148 d2.minValue = -100;
|
c@29
|
149 d2.maxValue = -50;
|
c@29
|
150 d2.defaultValue = -74;
|
c@29
|
151 d2.isQuantized = false;
|
c@29
|
152 list.push_back(d2);
|
c@9
|
153
|
c@14
|
154 ParameterDescriptor d3;
|
c@29
|
155 d3.identifier = "log2TN";
|
c@29
|
156 d3.name = "Tempogram Window Length";
|
c@29
|
157 d3.description = "FFT window length when analysing the novelty curve and extracting the tempogram time-frequency function.";
|
c@14
|
158 d3.unit = "";
|
c@29
|
159 d3.minValue = 7;
|
c@14
|
160 d3.maxValue = 12;
|
c@29
|
161 d3.defaultValue = 10;
|
c@14
|
162 d3.isQuantized = true;
|
c@14
|
163 d3.quantizeStep = 1;
|
c@14
|
164 for (int i = d3.minValue; i <= d3.maxValue; i++){
|
c@14
|
165 d3.valueNames.push_back(floatToString(pow((float)2,(float)i)));
|
c@14
|
166 }
|
c@14
|
167 list.push_back(d3);
|
c@9
|
168
|
c@14
|
169 ParameterDescriptor d4;
|
c@29
|
170 d4.identifier = "log2HopSize";
|
c@29
|
171 d4.name = "Tempogram Hopsize";
|
c@29
|
172 d4.description = "FFT hopsize when analysing the novelty curve and extracting the tempogram time-frequency function.";
|
c@14
|
173 d4.unit = "";
|
c@14
|
174 d4.minValue = 6;
|
c@14
|
175 d4.maxValue = 12;
|
c@29
|
176 d4.defaultValue = 6;
|
c@14
|
177 d4.isQuantized = true;
|
c@14
|
178 d4.quantizeStep = 1;
|
c@14
|
179 for (int i = d4.minValue; i <= d4.maxValue; i++){
|
c@14
|
180 d4.valueNames.push_back(floatToString(pow((float)2,(float)i)));
|
c@14
|
181 }
|
c@14
|
182 list.push_back(d4);
|
c@14
|
183
|
c@14
|
184 ParameterDescriptor d5;
|
c@29
|
185 d5.identifier = "log2FftLength";
|
c@29
|
186 d5.name = "Tempogram FFT Length";
|
c@29
|
187 d5.description = "FFT length when analysing the novelty curve and extracting the tempogram time-frequency function. This parameter determines the amount of zero padding.";
|
c@14
|
188 d5.unit = "";
|
c@29
|
189 d5.minValue = 6;
|
c@29
|
190 d5.maxValue = 12;
|
Chris@42
|
191 d5.defaultValue = 10;
|
c@14
|
192 d5.isQuantized = true;
|
c@29
|
193 d5.quantizeStep = 1;
|
c@29
|
194 for (int i = d5.minValue; i <= d5.maxValue; i++){
|
Chris@41
|
195 d5.valueNames.push_back(floatToString(pow((float)2,(float)i)));
|
c@29
|
196 }
|
c@14
|
197 list.push_back(d5);
|
c@14
|
198
|
c@14
|
199 ParameterDescriptor d6;
|
c@29
|
200 d6.identifier = "minBPM";
|
c@29
|
201 d6.name = "(Cyclic) Tempogram Minimum BPM";
|
c@29
|
202 d6.description = "The minimum BPM of the tempogram output bins.";
|
c@14
|
203 d6.unit = "";
|
c@29
|
204 d6.minValue = 0;
|
c@14
|
205 d6.maxValue = 2000;
|
c@29
|
206 d6.defaultValue = 30;
|
c@14
|
207 d6.isQuantized = true;
|
c@14
|
208 d6.quantizeStep = 5;
|
c@14
|
209 list.push_back(d6);
|
c@18
|
210
|
c@18
|
211 ParameterDescriptor d7;
|
c@29
|
212 d7.identifier = "maxBPM";
|
c@29
|
213 d7.name = "(Cyclic) Tempogram Maximum BPM";
|
c@29
|
214 d7.description = "The maximum BPM of the tempogram output bins.";
|
c@18
|
215 d7.unit = "";
|
c@29
|
216 d7.minValue = 30;
|
c@29
|
217 d7.maxValue = 2000;
|
c@29
|
218 d7.defaultValue = 480;
|
c@18
|
219 d7.isQuantized = true;
|
c@29
|
220 d7.quantizeStep = 5;
|
c@18
|
221 list.push_back(d7);
|
c@29
|
222
|
c@29
|
223 ParameterDescriptor d8;
|
c@29
|
224 d8.identifier = "octDiv";
|
c@29
|
225 d8.name = "Cyclic Tempogram Octave Divider";
|
c@29
|
226 d8.description = "The number bins within each octave.";
|
c@29
|
227 d8.unit = "";
|
c@29
|
228 d8.minValue = 5;
|
c@29
|
229 d8.maxValue = 60;
|
c@29
|
230 d8.defaultValue = 30;
|
c@29
|
231 d8.isQuantized = true;
|
c@29
|
232 d8.quantizeStep = 1;
|
c@29
|
233 list.push_back(d8);
|
c@0
|
234
|
c@0
|
235 return list;
|
c@0
|
236 }
|
c@0
|
237
|
c@0
|
238 float
|
c@14
|
239 TempogramPlugin::getParameter(string identifier) const
|
c@0
|
240 {
|
c@0
|
241 if (identifier == "C") {
|
c@18
|
242 return m_noveltyCurveCompressionConstant; // return the ACTUAL current value of your parameter here!
|
c@0
|
243 }
|
c@29
|
244 else if (identifier == "minDB"){
|
c@29
|
245 return m_noveltyCurveMinDB;
|
c@29
|
246 }
|
c@14
|
247 else if (identifier == "log2TN"){
|
c@18
|
248 return m_tempogramLog2WindowLength;
|
c@9
|
249 }
|
c@14
|
250 else if (identifier == "log2HopSize"){
|
c@18
|
251 return m_tempogramLog2HopSize;
|
c@14
|
252 }
|
c@14
|
253 else if (identifier == "log2FftLength"){
|
c@18
|
254 return m_tempogramLog2FftLength;
|
c@14
|
255 }
|
c@14
|
256 else if (identifier == "minBPM") {
|
c@18
|
257 return m_tempogramMinBPM;
|
c@9
|
258 }
|
c@14
|
259 else if (identifier == "maxBPM"){
|
c@18
|
260 return m_tempogramMaxBPM;
|
c@18
|
261 }
|
c@18
|
262 else if (identifier == "octDiv"){
|
c@18
|
263 return m_cyclicTempogramOctaveDivider;
|
c@0
|
264 }
|
c@0
|
265
|
c@0
|
266 return 0;
|
c@0
|
267 }
|
c@0
|
268
|
c@0
|
269 void
|
c@14
|
270 TempogramPlugin::setParameter(string identifier, float value)
|
c@0
|
271 {
|
c@9
|
272
|
c@0
|
273 if (identifier == "C") {
|
c@18
|
274 m_noveltyCurveCompressionConstant = value; // set the actual value of your parameter
|
c@0
|
275 }
|
c@29
|
276 else if (identifier == "minDB"){
|
c@29
|
277 m_noveltyCurveMinDB = value;
|
c@29
|
278 }
|
c@14
|
279 else if (identifier == "log2TN") {
|
c@18
|
280 m_tempogramLog2WindowLength = value;
|
c@0
|
281 }
|
c@14
|
282 else if (identifier == "log2HopSize"){
|
c@30
|
283 m_tempogramLog2HopSize = value;
|
c@14
|
284 }
|
c@18
|
285 else if (identifier == "log2FftLength"){
|
c@30
|
286 m_tempogramLog2FftLength = value;
|
c@14
|
287 }
|
c@14
|
288 else if (identifier == "minBPM") {
|
c@18
|
289 m_tempogramMinBPM = value;
|
c@9
|
290 }
|
c@14
|
291 else if (identifier == "maxBPM"){
|
c@18
|
292 m_tempogramMaxBPM = value;
|
c@18
|
293 }
|
c@18
|
294 else if (identifier == "octDiv"){
|
c@18
|
295 m_cyclicTempogramOctaveDivider = value;
|
c@9
|
296 }
|
c@9
|
297
|
c@9
|
298 }
|
c@9
|
299
|
c@14
|
300 TempogramPlugin::ProgramList
|
c@14
|
301 TempogramPlugin::getPrograms() const
|
c@0
|
302 {
|
c@0
|
303 ProgramList list;
|
c@0
|
304
|
c@0
|
305 // If you have no programs, return an empty list (or simply don't
|
c@0
|
306 // implement this function or getCurrentProgram/selectProgram)
|
c@0
|
307
|
c@0
|
308 return list;
|
c@0
|
309 }
|
c@0
|
310
|
c@0
|
311 string
|
c@14
|
312 TempogramPlugin::getCurrentProgram() const
|
c@0
|
313 {
|
c@0
|
314 return ""; // no programs
|
c@0
|
315 }
|
c@0
|
316
|
c@0
|
317 void
|
c@14
|
318 TempogramPlugin::selectProgram(string name)
|
c@0
|
319 {
|
c@0
|
320 }
|
c@0
|
321
|
c@14
|
322 TempogramPlugin::OutputList
|
c@14
|
323 TempogramPlugin::getOutputDescriptors() const
|
c@0
|
324 {
|
c@0
|
325 OutputList list;
|
c@0
|
326
|
c@0
|
327 // See OutputDescriptor documentation for the possibilities here.
|
c@0
|
328 // Every plugin must have at least one output.
|
c@1
|
329
|
c@7
|
330 float d_sampleRate;
|
c@18
|
331 float tempogramInputSampleRate = (float)m_inputSampleRate/m_inputStepSize;
|
c@25
|
332 OutputDescriptor d1;
|
c@25
|
333 d1.identifier = "cyclicTempogram";
|
c@25
|
334 d1.name = "Cyclic Tempogram";
|
c@25
|
335 d1.description = "Cyclic Tempogram";
|
c@25
|
336 d1.unit = "";
|
c@25
|
337 d1.hasFixedBinCount = true;
|
c@25
|
338 d1.binCount = m_cyclicTempogramOctaveDivider > 0 && !isnan(m_cyclicTempogramOctaveDivider) ? m_cyclicTempogramOctaveDivider : 0;
|
c@25
|
339 d1.hasKnownExtents = false;
|
c@25
|
340 d1.isQuantized = false;
|
c@25
|
341 d1.sampleType = OutputDescriptor::FixedSampleRate;
|
c@25
|
342 d_sampleRate = tempogramInputSampleRate/m_tempogramHopSize;
|
c@25
|
343 d1.sampleRate = d_sampleRate > 0.0 && !isnan(d_sampleRate) ? d_sampleRate : 0;
|
c@25
|
344 d1.hasDuration = false;
|
c@25
|
345 list.push_back(d1);
|
c@25
|
346
|
c@25
|
347 OutputDescriptor d2;
|
c@25
|
348 d2.identifier = "tempogramDFT";
|
c@25
|
349 d2.name = "Tempogram via DFT";
|
c@25
|
350 d2.description = "Tempogram via DFT";
|
c@25
|
351 d2.unit = "BPM";
|
c@25
|
352 d2.hasFixedBinCount = true;
|
c@25
|
353 d2.binCount = m_tempogramMaxBin - m_tempogramMinBin + 1;
|
c@25
|
354 d2.hasKnownExtents = false;
|
c@25
|
355 d2.isQuantized = false;
|
c@25
|
356 d2.sampleType = OutputDescriptor::FixedSampleRate;
|
c@25
|
357 d_sampleRate = tempogramInputSampleRate/m_tempogramHopSize;
|
c@25
|
358 d2.sampleRate = d_sampleRate > 0.0 && !isnan(d_sampleRate) ? d_sampleRate : 0.0;
|
c@25
|
359 for(int i = m_tempogramMinBin; i <= (int)m_tempogramMaxBin; i++){
|
c@25
|
360 float w = ((float)i/m_tempogramFftLength)*(tempogramInputSampleRate);
|
c@25
|
361 d2.binNames.push_back(floatToString(w*60));
|
c@25
|
362 }
|
c@25
|
363 d2.hasDuration = false;
|
c@25
|
364 list.push_back(d2);
|
c@25
|
365
|
c@21
|
366 OutputDescriptor d3;
|
c@25
|
367 d3.identifier = "tempogramACT";
|
c@25
|
368 d3.name = "Tempogram via ACT";
|
c@25
|
369 d3.description = "Tempogram via ACT";
|
c@25
|
370 d3.unit = "BPM";
|
c@21
|
371 d3.hasFixedBinCount = true;
|
c@28
|
372 d3.binCount = m_tempogramMaxLag - m_tempogramMinLag + 1;
|
c@21
|
373 d3.hasKnownExtents = false;
|
c@21
|
374 d3.isQuantized = false;
|
c@21
|
375 d3.sampleType = OutputDescriptor::FixedSampleRate;
|
c@21
|
376 d_sampleRate = tempogramInputSampleRate/m_tempogramHopSize;
|
c@25
|
377 d3.sampleRate = d_sampleRate > 0.0 && !isnan(d_sampleRate) ? d_sampleRate : 0.0;
|
c@28
|
378 for(int lag = m_tempogramMaxLag; lag >= (int)m_tempogramMinLag; lag--){
|
c@28
|
379 d3.binNames.push_back(floatToString(60/(m_inputStepSize*(lag/m_inputSampleRate))));
|
c@25
|
380 }
|
c@21
|
381 d3.hasDuration = false;
|
c@21
|
382 list.push_back(d3);
|
c@21
|
383
|
c@25
|
384 OutputDescriptor d4;
|
c@25
|
385 d4.identifier = "nc";
|
c@25
|
386 d4.name = "Novelty Curve";
|
c@25
|
387 d4.description = "Novelty Curve";
|
c@25
|
388 d4.unit = "";
|
c@25
|
389 d4.hasFixedBinCount = true;
|
c@25
|
390 d4.binCount = 1;
|
c@25
|
391 d4.hasKnownExtents = false;
|
c@25
|
392 d4.isQuantized = false;
|
c@25
|
393 d4.sampleType = OutputDescriptor::FixedSampleRate;
|
c@9
|
394 d_sampleRate = tempogramInputSampleRate;
|
c@25
|
395 d4.sampleRate = d_sampleRate > 0 && !isnan(d_sampleRate) ? d_sampleRate : 0;
|
c@25
|
396 d4.hasDuration = false;
|
c@25
|
397 list.push_back(d4);
|
c@18
|
398
|
c@0
|
399 return list;
|
c@0
|
400 }
|
c@0
|
401
|
c@20
|
402 bool
|
c@20
|
403 TempogramPlugin::initialise(size_t channels, size_t stepSize, size_t blockSize)
|
c@20
|
404 {
|
c@20
|
405 if (channels < getMinChannelCount() ||
|
c@20
|
406 channels > getMaxChannelCount()) return false;
|
c@20
|
407
|
c@20
|
408 // Real initialisation work goes here!
|
c@20
|
409 m_inputBlockSize = blockSize;
|
c@20
|
410 m_inputStepSize = stepSize;
|
c@20
|
411
|
c@24
|
412 //m_spectrogram = Spectrogram(m_inputBlockSize/2 + 1);
|
c@21
|
413 if (!handleParameterValues()) return false;
|
c@19
|
414 //cout << m_cyclicTempogramOctaveDivider << endl;
|
c@4
|
415
|
c@0
|
416 return true;
|
c@0
|
417 }
|
c@0
|
418
|
c@0
|
419 void
|
c@14
|
420 TempogramPlugin::reset()
|
c@0
|
421 {
|
c@0
|
422 // Clear buffers, reset stored values, etc
|
c@19
|
423 m_spectrogram.clear();
|
c@21
|
424 handleParameterValues();
|
c@0
|
425 }
|
c@0
|
426
|
c@14
|
427 TempogramPlugin::FeatureSet
|
c@14
|
428 TempogramPlugin::process(const float *const *inputBuffers, Vamp::RealTime timestamp)
|
c@0
|
429 {
|
c@23
|
430 int n = m_inputBlockSize/2 + 1;
|
c@0
|
431 const float *in = inputBuffers[0];
|
c@3
|
432
|
c@9
|
433 //calculate magnitude of FrequencyDomain input
|
c@22
|
434 vector<float> fftCoefficients;
|
c@23
|
435 for (int i = 0; i < n; i++){
|
c@0
|
436 float magnitude = sqrt(in[2*i] * in[2*i] + in[2*i + 1] * in[2*i + 1]);
|
c@29
|
437 magnitude = magnitude > m_noveltyCurveMinV ? magnitude : m_noveltyCurveMinV;
|
c@22
|
438 fftCoefficients.push_back(magnitude);
|
c@0
|
439 }
|
c@22
|
440 m_spectrogram.push_back(fftCoefficients);
|
c@24
|
441 //m_spectrogram.push_back(fftCoefficients);
|
c@21
|
442
|
c@23
|
443 return FeatureSet();
|
c@0
|
444 }
|
c@0
|
445
|
c@14
|
446 TempogramPlugin::FeatureSet
|
c@14
|
447 TempogramPlugin::getRemainingFeatures()
|
c@11
|
448 {
|
c@0
|
449
|
c@18
|
450 float * hannWindow = new float[m_tempogramWindowLength];
|
c@20
|
451 for (int i = 0; i < (int)m_tempogramWindowLength; i++){
|
c@14
|
452 hannWindow[i] = 0.0;
|
c@4
|
453 }
|
c@11
|
454
|
c@1
|
455 FeatureSet featureSet;
|
c@0
|
456
|
c@19
|
457 //initialise novelty curve processor
|
c@23
|
458 int numberOfBlocks = m_spectrogram.size();
|
c@20
|
459 //cerr << numberOfBlocks << endl;
|
c@22
|
460 NoveltyCurveProcessor nc(m_inputSampleRate, m_inputBlockSize, m_noveltyCurveCompressionConstant);
|
c@21
|
461 vector<float> noveltyCurve = nc.spectrogramToNoveltyCurve(m_spectrogram); //calculate novelty curvefrom magnitude data
|
c@4
|
462
|
c@9
|
463 //push novelty curve data to featureset 1 and set timestamps
|
c@23
|
464 for (int i = 0; i < numberOfBlocks; i++){
|
c@19
|
465 Feature noveltyCurveFeature;
|
c@19
|
466 noveltyCurveFeature.values.push_back(noveltyCurve[i]);
|
c@19
|
467 noveltyCurveFeature.hasTimestamp = false;
|
c@25
|
468 featureSet[3].push_back(noveltyCurveFeature);
|
c@21
|
469 assert(!isnan(noveltyCurveFeature.values.back()));
|
c@4
|
470 }
|
c@4
|
471
|
c@9
|
472 //window function for spectrogram
|
c@18
|
473 WindowFunction::hanning(hannWindow, m_tempogramWindowLength);
|
c@9
|
474
|
c@9
|
475 //initialise spectrogram processor
|
c@18
|
476 SpectrogramProcessor spectrogramProcessor(m_tempogramWindowLength, m_tempogramFftLength, m_tempogramHopSize);
|
c@9
|
477 //compute spectrogram from novelty curve data (i.e., tempogram)
|
c@25
|
478 Tempogram tempogramDFT = spectrogramProcessor.process(&noveltyCurve[0], numberOfBlocks, hannWindow);
|
c@18
|
479 delete []hannWindow;
|
c@18
|
480 hannWindow = 0;
|
c@0
|
481
|
c@25
|
482 int tempogramLength = tempogramDFT.size();
|
c@7
|
483
|
c@9
|
484 //push tempogram data to featureset 0 and set timestamps.
|
c@7
|
485 for (int block = 0; block < tempogramLength; block++){
|
c@25
|
486 Feature tempogramDFTFeature;
|
c@28
|
487
|
c@28
|
488 assert(tempogramDFT[block].size() == (m_tempogramFftLength/2 + 1));
|
c@28
|
489 for(int k = m_tempogramMinBin; k <= (int)m_tempogramMaxBin; k++){
|
c@28
|
490 tempogramDFTFeature.values.push_back(tempogramDFT[block][k]);
|
c@28
|
491 }
|
c@28
|
492 tempogramDFTFeature.hasTimestamp = false;
|
c@28
|
493 featureSet[1].push_back(tempogramDFTFeature);
|
c@28
|
494 }
|
c@28
|
495
|
c@28
|
496 AutocorrelationProcessor autocorrelationProcessor(m_tempogramWindowLength, m_tempogramHopSize);
|
c@28
|
497 Tempogram tempogramACT = autocorrelationProcessor.process(&noveltyCurve[0], numberOfBlocks);
|
c@28
|
498
|
c@28
|
499 for (int block = 0; block < tempogramLength; block++){
|
c@25
|
500 Feature tempogramACTFeature;
|
c@0
|
501
|
c@28
|
502 for(int k = m_tempogramMaxLag; k >= (int)m_tempogramMinLag; k--){
|
c@25
|
503 tempogramACTFeature.values.push_back(tempogramACT[block][k]);
|
c@0
|
504 }
|
c@25
|
505 tempogramACTFeature.hasTimestamp = false;
|
c@25
|
506 featureSet[2].push_back(tempogramACTFeature);
|
c@0
|
507 }
|
c@0
|
508
|
c@18
|
509 //Calculate cyclic tempogram
|
c@22
|
510 vector< vector<unsigned int> > logBins = calculateTempogramNearestNeighbourLogBins();
|
c@18
|
511
|
c@22
|
512 //assert((int)logBins.size() == m_cyclicTempogramOctaveDivider*m_cyclicTempogramNumberOfOctaves);
|
c@18
|
513 for (int block = 0; block < tempogramLength; block++){
|
c@19
|
514 Feature cyclicTempogramFeature;
|
c@18
|
515
|
c@23
|
516 for (int i = 0; i < m_cyclicTempogramOctaveDivider; i++){
|
c@18
|
517 float sum = 0;
|
c@21
|
518
|
c@23
|
519 for (int j = 0; j < m_cyclicTempogramNumberOfOctaves; j++){
|
Chris@34
|
520
|
Chris@34
|
521 if (block >= tempogramDFT.size()) {
|
Chris@34
|
522 cerr << "ERROR: at block = " << block << ", i = " << i << ", j = " << j << ": block " << block << " >= tempogramDFT.size() " << tempogramDFT.size() << endl;
|
Chris@34
|
523 } else if (j > logBins.size()) {
|
Chris@34
|
524 cerr << "ERROR: at block = " << block << ", i = " << i << ", j = " << j << ": j " << j << " >= logBins.size() " << logBins.size() << endl;
|
Chris@34
|
525 } else if (i > logBins[j].size()) {
|
Chris@34
|
526 cerr << "ERROR: at block = " << block << ", i = " << i << ", j = " << j << ": i " << i << " >= logBins[j].size() " << logBins[j].size() << endl;
|
Chris@34
|
527 } else if (logBins[j][i] >= tempogramDFT[block].size()) {
|
Chris@34
|
528 cerr << "ERROR: at block = " << block << ", i = " << i << ", j = " << j << ": logBins[j][i] " << logBins[j][i] << " >= tempogramDFT[block].size() " << tempogramDFT[block].size() << endl;
|
Chris@34
|
529 } else {
|
Chris@34
|
530 sum += tempogramDFT[block][logBins[j][i]];
|
Chris@34
|
531 }
|
c@18
|
532 }
|
c@19
|
533 cyclicTempogramFeature.values.push_back(sum/m_cyclicTempogramNumberOfOctaves);
|
c@21
|
534 assert(!isnan(cyclicTempogramFeature.values.back()));
|
c@18
|
535 }
|
c@18
|
536
|
c@19
|
537 cyclicTempogramFeature.hasTimestamp = false;
|
c@21
|
538 featureSet[0].push_back(cyclicTempogramFeature);
|
c@18
|
539 }
|
c@0
|
540
|
c@0
|
541 return featureSet;
|
c@0
|
542 }
|
c@22
|
543
|
c@22
|
544 vector< vector<unsigned int> > TempogramPlugin::calculateTempogramNearestNeighbourLogBins() const
|
c@22
|
545 {
|
c@22
|
546 vector< vector<unsigned int> > logBins;
|
c@22
|
547
|
c@22
|
548 for (int octave = 0; octave < (int)m_cyclicTempogramNumberOfOctaves; octave++){
|
c@22
|
549 vector<unsigned int> octaveBins;
|
c@22
|
550
|
c@22
|
551 for (int bin = 0; bin < (int)m_cyclicTempogramOctaveDivider; bin++){
|
c@22
|
552 float bpm = m_cyclicTempogramMinBPM*pow(2.0f, octave+(float)bin/m_cyclicTempogramOctaveDivider);
|
c@22
|
553 octaveBins.push_back(bpmToBin(bpm));
|
c@23
|
554 //cout << octaveBins.back() << endl;
|
c@22
|
555 }
|
c@22
|
556 logBins.push_back(octaveBins);
|
c@22
|
557 }
|
c@22
|
558
|
c@22
|
559 return logBins;
|
c@22
|
560 }
|
c@22
|
561
|
c@22
|
562 unsigned int TempogramPlugin::bpmToBin(const float &bpm) const
|
c@22
|
563 {
|
c@22
|
564 float w = (float)bpm/60;
|
c@22
|
565 float sampleRate = m_inputSampleRate/m_inputStepSize;
|
c@22
|
566 int bin = floor((float)m_tempogramFftLength*w/sampleRate + 0.5);
|
c@22
|
567
|
c@22
|
568 if(bin < 0) bin = 0;
|
c@22
|
569 else if(bin > m_tempogramFftLength/2.0f) bin = m_tempogramFftLength;
|
c@22
|
570
|
c@22
|
571 return bin;
|
c@22
|
572 }
|
c@22
|
573
|
c@22
|
574 float TempogramPlugin::binToBPM(const int &bin) const
|
c@22
|
575 {
|
c@22
|
576 float sampleRate = m_inputSampleRate/m_inputStepSize;
|
c@22
|
577
|
c@22
|
578 return (bin*sampleRate/m_tempogramFftLength)*60;
|
c@22
|
579 }
|
c@22
|
580
|
c@22
|
581 bool TempogramPlugin::handleParameterValues(){
|
c@22
|
582
|
Chris@42
|
583 if (m_tempogramLog2HopSize <= 0) {
|
Chris@42
|
584 cerr << "Tempogram log2 hop size " << m_tempogramLog2HopSize
|
Chris@42
|
585 << " <= 0, failing initialise" << endl;
|
Chris@42
|
586 return false;
|
Chris@42
|
587 }
|
Chris@42
|
588 if (m_tempogramLog2FftLength <= 0) {
|
Chris@42
|
589 cerr << "Tempogram log2 fft length " << m_tempogramLog2FftLength
|
Chris@42
|
590 << " <= 0, failing initialise" << endl;
|
Chris@42
|
591 return false;
|
Chris@42
|
592 }
|
c@22
|
593
|
Chris@42
|
594 if (m_tempogramMinBPM < 1) {
|
Chris@42
|
595 m_tempogramMinBPM = 1;
|
Chris@42
|
596 }
|
c@22
|
597 if (m_tempogramMinBPM >= m_tempogramMaxBPM){
|
c@22
|
598 m_tempogramMinBPM = 30;
|
c@22
|
599 m_tempogramMaxBPM = 480;
|
c@22
|
600 }
|
c@22
|
601
|
c@29
|
602 m_noveltyCurveMinV = pow(10,(float)m_noveltyCurveMinDB/20);
|
c@29
|
603
|
c@29
|
604 m_tempogramWindowLength = pow(2,m_tempogramLog2WindowLength);
|
c@29
|
605 m_tempogramHopSize = pow(2,m_tempogramLog2HopSize);
|
c@29
|
606 m_tempogramFftLength = pow(2,m_tempogramLog2FftLength);
|
c@29
|
607
|
c@30
|
608 if (m_tempogramFftLength < m_tempogramWindowLength){
|
c@30
|
609 m_tempogramFftLength = m_tempogramWindowLength;
|
c@30
|
610 }
|
c@30
|
611
|
c@22
|
612 float tempogramInputSampleRate = (float)m_inputSampleRate/m_inputStepSize;
|
c@28
|
613 m_tempogramMinBin = (max((int)floor(((m_tempogramMinBPM/60)/tempogramInputSampleRate)*m_tempogramFftLength), 0));
|
c@28
|
614 m_tempogramMaxBin = (min((int)ceil(((m_tempogramMaxBPM/60)/tempogramInputSampleRate)*m_tempogramFftLength), (int)(m_tempogramFftLength/2)));
|
Chris@32
|
615
|
Chris@32
|
616 if (m_tempogramMaxBin < m_tempogramMinBin) {
|
Chris@32
|
617 cerr << "At audio sample rate " << m_inputSampleRate
|
Chris@32
|
618 << ", tempogram sample rate " << tempogramInputSampleRate
|
Chris@32
|
619 << " with bpm range " << m_tempogramMinBPM << " -> "
|
Chris@32
|
620 << m_tempogramMaxBPM << ", min bin = " << m_tempogramMinBin
|
Chris@32
|
621 << " > max bin " << m_tempogramMaxBin
|
Chris@32
|
622 << ": can't proceed, failing initialise" << endl;
|
Chris@32
|
623 return false;
|
Chris@32
|
624 }
|
c@28
|
625
|
c@28
|
626 m_tempogramMinLag = max((int)ceil((60/(m_inputStepSize * m_tempogramMaxBPM))*m_inputSampleRate), 0);
|
c@28
|
627 m_tempogramMaxLag = min((int)floor((60/(m_inputStepSize * m_tempogramMinBPM))*m_inputSampleRate), (int)m_tempogramWindowLength);
|
Chris@32
|
628
|
Chris@32
|
629 if (m_tempogramMaxLag < m_tempogramMinLag) {
|
Chris@32
|
630 cerr << "At audio sample rate " << m_inputSampleRate
|
Chris@32
|
631 << ", tempogram sample rate " << tempogramInputSampleRate
|
Chris@42
|
632 << ", window length " << m_tempogramWindowLength
|
Chris@32
|
633 << " with bpm range " << m_tempogramMinBPM << " -> "
|
Chris@42
|
634 << m_tempogramMaxBPM << ", min lag = " << m_tempogramMinLag
|
Chris@42
|
635 << " > max lag " << m_tempogramMaxLag
|
Chris@32
|
636 << ": can't proceed, failing initialise" << endl;
|
Chris@32
|
637 return false;
|
Chris@32
|
638 }
|
c@22
|
639
|
Chris@42
|
640 m_cyclicTempogramMinBPM = m_tempogramMinBPM;
|
Chris@42
|
641 float cyclicTempogramMaxBPM = m_tempogramMaxBPM;
|
Chris@42
|
642
|
c@22
|
643 m_cyclicTempogramNumberOfOctaves = floor(log2(cyclicTempogramMaxBPM/m_cyclicTempogramMinBPM));
|
Chris@42
|
644
|
Chris@42
|
645 if (m_cyclicTempogramNumberOfOctaves < 1) {
|
Chris@42
|
646 cerr << "At audio sample rate " << m_inputSampleRate
|
Chris@42
|
647 << ", tempogram sample rate " << tempogramInputSampleRate
|
Chris@42
|
648 << " with bpm range " << m_tempogramMinBPM << " -> "
|
Chris@42
|
649 << m_tempogramMaxBPM << ", cyclic tempogram min bpm = "
|
Chris@42
|
650 << m_cyclicTempogramMinBPM << " and max bpm = "
|
Chris@42
|
651 << cyclicTempogramMaxBPM << " giving number of octaves = "
|
Chris@42
|
652 << m_cyclicTempogramNumberOfOctaves
|
Chris@42
|
653 << ": can't proceed, failing initialise" << endl;
|
Chris@42
|
654 return false;
|
Chris@42
|
655 }
|
c@22
|
656
|
c@22
|
657 return true;
|
c@22
|
658 }
|
c@22
|
659
|
c@22
|
660 string TempogramPlugin::floatToString(float value) const
|
c@22
|
661 {
|
c@22
|
662 ostringstream ss;
|
c@22
|
663
|
c@22
|
664 if(!(ss << value)) throw runtime_error("TempogramPlugin::floatToString(): invalid conversion from float to string");
|
c@22
|
665 return ss.str();
|
c@22
|
666 }
|