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