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