Mercurial > hg > vamp-tempogram
comparison Tempogram.cpp @ 0:31d2a7e07786
Moved all to folder "tempogram".
author | Carl Bussey <c.bussey@se10.qmul.ac.uk> |
---|---|
date | Mon, 07 Jul 2014 10:08:14 +0100 |
parents | |
children | 3fd1a41b089b |
comparison
equal
deleted
inserted
replaced
-1:000000000000 | 0:31d2a7e07786 |
---|---|
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 "Tempogram.h" | |
8 #include "FIRFilter.h" | |
9 #include "WindowFunction.h" | |
10 #include <vamp-sdk/FFT.h> | |
11 #include <cmath> | |
12 #include <fstream> | |
13 #include <assert.h> | |
14 using Vamp::FFT; | |
15 using namespace std; | |
16 | |
17 Tempogram::Tempogram(float inputSampleRate) : | |
18 Plugin(inputSampleRate), | |
19 m_blockSize(0), | |
20 compressionConstant(1000), //make param | |
21 previousY(NULL), | |
22 currentY(NULL), | |
23 tN(1024), //make param | |
24 thopSize(512), //make param | |
25 fftInput(NULL), | |
26 fftOutputReal(NULL), | |
27 fftOutputImag(NULL), | |
28 ncLength(0) | |
29 | |
30 // Also be sure to set your plugin parameters (presumably stored | |
31 // in member variables) to their default values here -- the host | |
32 // will not do that for you | |
33 { | |
34 } | |
35 | |
36 Tempogram::~Tempogram() | |
37 { | |
38 //delete stuff | |
39 } | |
40 | |
41 string | |
42 Tempogram::getIdentifier() const | |
43 { | |
44 return "tempogram"; | |
45 } | |
46 | |
47 string | |
48 Tempogram::getName() const | |
49 { | |
50 return "Tempogram"; | |
51 } | |
52 | |
53 string | |
54 Tempogram::getDescription() const | |
55 { | |
56 // Return something helpful here! | |
57 return "Cyclic Tempogram as described by Peter Grosche and Meinard Muller"; | |
58 } | |
59 | |
60 string | |
61 Tempogram::getMaker() const | |
62 { | |
63 //Your name here | |
64 return "Carl Bussey"; | |
65 } | |
66 | |
67 int | |
68 Tempogram::getPluginVersion() const | |
69 { | |
70 // Increment this each time you release a version that behaves | |
71 // differently from the previous one | |
72 return 1; | |
73 } | |
74 | |
75 string | |
76 Tempogram::getCopyright() const | |
77 { | |
78 // This function is not ideally named. It does not necessarily | |
79 // need to say who made the plugin -- getMaker does that -- but it | |
80 // should indicate the terms under which it is distributed. For | |
81 // example, "Copyright (year). All Rights Reserved", or "GPL" | |
82 return ""; | |
83 } | |
84 | |
85 Tempogram::InputDomain | |
86 Tempogram::getInputDomain() const | |
87 { | |
88 return FrequencyDomain; | |
89 } | |
90 | |
91 size_t | |
92 Tempogram::getPreferredBlockSize() const | |
93 { | |
94 return 0; // 0 means "I can handle any block size" | |
95 } | |
96 | |
97 size_t | |
98 Tempogram::getPreferredStepSize() const | |
99 { | |
100 //23 ms? | |
101 return 0; // 0 means "anything sensible"; in practice this | |
102 // means the same as the block size for TimeDomain | |
103 // plugins, or half of it for FrequencyDomain plugins | |
104 } | |
105 | |
106 size_t | |
107 Tempogram::getMinChannelCount() const | |
108 { | |
109 return 1; | |
110 } | |
111 | |
112 size_t | |
113 Tempogram::getMaxChannelCount() const | |
114 { | |
115 return 1; | |
116 } | |
117 | |
118 Tempogram::ParameterList | |
119 Tempogram::getParameterDescriptors() const | |
120 { | |
121 ParameterList list; | |
122 | |
123 // If the plugin has no adjustable parameters, return an empty | |
124 // list here (and there's no need to provide implementations of | |
125 // getParameter and setParameter in that case either). | |
126 | |
127 // Note that it is your responsibility to make sure the parameters | |
128 // start off having their default values (e.g. in the constructor | |
129 // above). The host needs to know the default value so it can do | |
130 // things like provide a "reset to default" function, but it will | |
131 // not explicitly set your parameters to their defaults for you if | |
132 // they have not changed in the mean time. | |
133 | |
134 ParameterDescriptor C; | |
135 C.identifier = "C"; | |
136 C.name = "C"; | |
137 C.description = "Spectrogram compression constant, C"; | |
138 C.unit = ""; | |
139 C.minValue = 2; | |
140 C.maxValue = 10000; | |
141 C.defaultValue = 1000; | |
142 C.isQuantized = false; | |
143 list.push_back(C); | |
144 | |
145 ParameterDescriptor tN; | |
146 tN.identifier = "tN"; | |
147 tN.name = "Tempogram FFT length"; | |
148 tN.description = "Tempogram FFT length."; | |
149 tN.unit = ""; | |
150 tN.minValue = 128; | |
151 tN.maxValue = 4096; | |
152 tN.defaultValue = 1024; | |
153 tN.isQuantized = true; | |
154 tN.quantizeStep = 128; | |
155 list.push_back(tN); | |
156 | |
157 return list; | |
158 } | |
159 | |
160 float | |
161 Tempogram::getParameter(string identifier) const | |
162 { | |
163 if (identifier == "C") { | |
164 return compressionConstant; // return the ACTUAL current value of your parameter here! | |
165 } | |
166 if (identifier == "tN"){ | |
167 return tN; | |
168 } | |
169 | |
170 return 0; | |
171 } | |
172 | |
173 void | |
174 Tempogram::setParameter(string identifier, float value) | |
175 { | |
176 if (identifier == "C") { | |
177 compressionConstant = value;// set the actual value of your parameter | |
178 } | |
179 if (identifier == "tN") { | |
180 tN = value; | |
181 } | |
182 } | |
183 | |
184 Tempogram::ProgramList | |
185 Tempogram::getPrograms() const | |
186 { | |
187 ProgramList list; | |
188 | |
189 // If you have no programs, return an empty list (or simply don't | |
190 // implement this function or getCurrentProgram/selectProgram) | |
191 | |
192 return list; | |
193 } | |
194 | |
195 string | |
196 Tempogram::getCurrentProgram() const | |
197 { | |
198 return ""; // no programs | |
199 } | |
200 | |
201 void | |
202 Tempogram::selectProgram(string name) | |
203 { | |
204 } | |
205 | |
206 Tempogram::OutputList | |
207 Tempogram::getOutputDescriptors() const | |
208 { | |
209 OutputList list; | |
210 | |
211 // See OutputDescriptor documentation for the possibilities here. | |
212 // Every plugin must have at least one output. | |
213 | |
214 OutputDescriptor d; | |
215 d.identifier = "output"; | |
216 d.name = "Cyclic Tempogram"; | |
217 d.description = "Cyclic Tempogram"; | |
218 d.unit = ""; | |
219 d.hasFixedBinCount = false; | |
220 //d.binCount = 1; | |
221 d.hasKnownExtents = false; | |
222 d.isQuantized = false; | |
223 d.sampleType = OutputDescriptor::VariableSampleRate; | |
224 d.sampleRate = 0.0; | |
225 d.hasDuration = false; | |
226 list.push_back(d); | |
227 | |
228 return list; | |
229 } | |
230 | |
231 bool | |
232 Tempogram::initialise(size_t channels, size_t stepSize, size_t blockSize) | |
233 { | |
234 if (channels < getMinChannelCount() || | |
235 channels > getMaxChannelCount()) return false; | |
236 | |
237 // Real initialisation work goes here! | |
238 m_blockSize = blockSize; | |
239 currentY = new float[m_blockSize]; | |
240 previousY = new float[m_blockSize]; | |
241 | |
242 return true; | |
243 } | |
244 | |
245 void | |
246 Tempogram::reset() | |
247 { | |
248 // Clear buffers, reset stored values, etc | |
249 } | |
250 | |
251 Tempogram::FeatureSet | |
252 Tempogram::process(const float *const *inputBuffers, Vamp::RealTime timestamp) | |
253 { | |
254 size_t n = m_blockSize/2 + 1; | |
255 | |
256 FeatureSet featureSet; | |
257 Feature feature; | |
258 feature.hasTimestamp = false; | |
259 | |
260 const float *in = inputBuffers[0]; | |
261 | |
262 //Calculate log magnitude | |
263 float sum = 0; | |
264 for (int i = 0; i < n; i++){ | |
265 float magnitude = sqrt(in[2*i] * in[2*i] + in[2*i + 1] * in[2*i + 1]); | |
266 currentY[i] = log(1+compressionConstant*magnitude); //should be 1+C*magnitude | |
267 if(currentY[i] >= previousY[i]){ | |
268 sum += (currentY[i] - previousY[i]); | |
269 } | |
270 } | |
271 | |
272 noveltyCurve.push_back(sum); | |
273 | |
274 float *tmpY = currentY; | |
275 currentY = previousY; | |
276 previousY = tmpY; | |
277 | |
278 ncTimestamps.push_back(timestamp); | |
279 | |
280 return FeatureSet(); | |
281 } | |
282 | |
283 void | |
284 Tempogram::initialiseForGRF(){ | |
285 hannN = 129; | |
286 hannWindow = new float[hannN]; | |
287 hannWindowtN = new float[tN]; | |
288 fftInput = new double[tN]; | |
289 fftOutputReal = new double[tN]; | |
290 fftOutputImag = new double[tN]; | |
291 ncLength = noveltyCurve.size(); | |
292 | |
293 WindowFunction::hanning(hannWindow, hannN, true); | |
294 } | |
295 | |
296 void | |
297 Tempogram::cleanupForGRF(){ | |
298 delete []hannWindow; | |
299 hannWindow = NULL; | |
300 delete []hannWindowtN; | |
301 hannWindowtN = NULL; | |
302 delete []fftInput; | |
303 fftInput = NULL; | |
304 delete []fftOutputReal; | |
305 fftOutputReal = NULL; | |
306 delete []fftOutputImag; | |
307 fftOutputImag = NULL; | |
308 } | |
309 | |
310 Tempogram::FeatureSet | |
311 Tempogram::getRemainingFeatures() | |
312 { | |
313 //Make sure this is called at the beginning of the function | |
314 initialiseForGRF(); | |
315 | |
316 vector<float> noveltyCurveLocalAverage(ncLength); | |
317 | |
318 FIRFilter *filter = new FIRFilter(ncLength, hannN); | |
319 filter->process(&noveltyCurve[0], hannWindow, &noveltyCurveLocalAverage[0]); | |
320 delete filter; | |
321 | |
322 for(int i = 0; i < ncLength; i++){ | |
323 noveltyCurve[i] -= noveltyCurveLocalAverage[i]; | |
324 noveltyCurve[i] = noveltyCurve[i] >= 0 ? noveltyCurve[i] : 0; | |
325 } | |
326 | |
327 int i=0; | |
328 WindowFunction::hanning(hannWindowtN, tN); | |
329 | |
330 int index; | |
331 int start = floor(tN/2 + 0.5); | |
332 int timestampInc = floor((((float)ncTimestamps[1].nsec - ncTimestamps[0].nsec)/1e9)*(thopSize) + 0.5); | |
333 //cout << timestampInc << endl; | |
334 | |
335 FeatureSet featureSet; | |
336 | |
337 while(i < ncLength){ | |
338 Feature feature; | |
339 Vamp::RealTime timestamp; | |
340 | |
341 for (int n = start; n < tN; n++){ | |
342 index = i + n - tN/2; | |
343 assert (index >= 0); | |
344 | |
345 if(index < ncLength){ | |
346 fftInput[n] = noveltyCurve[i + n] * hannWindowtN[n]; | |
347 } | |
348 else if(index >= ncLength){ | |
349 fftInput[n] = 0.0; //pad the end with zeros | |
350 } | |
351 //cout << fftInput[n] << endl; | |
352 } | |
353 if (i+tN/2 > ncLength){ | |
354 timestamp = Vamp::RealTime::fromSeconds(ncTimestamps[i].sec + timestampInc); | |
355 } | |
356 else{ | |
357 timestamp = ncTimestamps[i + tN/2]; | |
358 } | |
359 | |
360 FFT::forward(tN, fftInput, NULL, fftOutputReal, fftOutputImag); | |
361 | |
362 //TODO: sample at logarithmic spacing | |
363 for(int k = 0; k < tN; k++){ | |
364 double fftOutputPower = (fftOutputReal[k]*fftOutputReal[k] + fftOutputImag[k]*fftOutputImag[k]); //Magnitude or power? | |
365 assert (!isinf(fftOutputPower)); | |
366 | |
367 feature.values.push_back(fftOutputPower); | |
368 } | |
369 | |
370 i += thopSize; | |
371 start = 0; | |
372 | |
373 feature.timestamp = timestamp; | |
374 feature.hasTimestamp = true; | |
375 featureSet[0].push_back(feature); | |
376 } | |
377 | |
378 //Make sure this is called at the end of the function | |
379 cleanupForGRF(); | |
380 | |
381 return featureSet; | |
382 } |