fix crash during first-run configuration of the application, caused by using an incom...
[ardour.git] / libs / vamp-plugins / ChromagramPlugin.cpp
1 /* -*- c-basic-offset: 4 indent-tabs-mode: nil -*-  vi:set ts=8 sts=4 sw=4: */
2
3 /*
4     QM Vamp Plugin Set
5
6     Centre for Digital Music, Queen Mary, University of London.
7
8     This program is free software; you can redistribute it and/or
9     modify it under the terms of the GNU General Public License as
10     published by the Free Software Foundation; either version 2 of the
11     License, or (at your option) any later version.  See the file
12     COPYING included with this distribution for more information.
13 */
14
15 #include "ChromagramPlugin.h"
16
17 #include <base/Pitch.h>
18 #include <dsp/chromagram/Chromagram.h>
19
20 using std::string;
21 using std::vector;
22 using std::cerr;
23 using std::endl;
24
25 ChromagramPlugin::ChromagramPlugin(float inputSampleRate) :
26     Vamp::Plugin(inputSampleRate),
27     m_chromagram(0),
28     m_step(0),
29     m_block(0)
30 {
31     m_minMIDIPitch = 36;
32     m_maxMIDIPitch = 96;
33     m_tuningFrequency = 440;
34     m_normalise = MathUtilities::NormaliseNone;
35     m_bpo = 12;
36
37     setupConfig();
38 }
39
40 void
41 ChromagramPlugin::setupConfig()
42 {
43     m_config.FS = lrintf(m_inputSampleRate);
44     m_config.min = Pitch::getFrequencyForPitch
45         (m_minMIDIPitch, 0, m_tuningFrequency);
46     m_config.max = Pitch::getFrequencyForPitch
47         (m_maxMIDIPitch, 0, m_tuningFrequency);
48     m_config.BPO = m_bpo;
49     m_config.CQThresh = 0.0054;
50     m_config.normalise = m_normalise;
51
52     m_step = 0;
53     m_block = 0;
54 }
55
56 ChromagramPlugin::~ChromagramPlugin()
57 {
58     delete m_chromagram;
59 }
60
61 string
62 ChromagramPlugin::getIdentifier() const
63 {
64     return "qm-chromagram";
65 }
66
67 string
68 ChromagramPlugin::getName() const
69 {
70     return "Chromagram";
71 }
72
73 string
74 ChromagramPlugin::getDescription() const
75 {
76     return "Extract a series of tonal chroma vectors from the audio";
77 }
78
79 string
80 ChromagramPlugin::getMaker() const
81 {
82     return "Queen Mary, University of London";
83 }
84
85 int
86 ChromagramPlugin::getPluginVersion() const
87 {
88     return 4;
89 }
90
91 string
92 ChromagramPlugin::getCopyright() const
93 {
94     return "Plugin by Chris Cannam and Christian Landone.  Copyright (c) 2006-2009 QMUL - All Rights Reserved";
95 }
96
97 ChromagramPlugin::ParameterList
98 ChromagramPlugin::getParameterDescriptors() const
99 {
100     ParameterList list;
101
102     ParameterDescriptor desc;
103     desc.identifier = "minpitch";
104     desc.name = "Minimum Pitch";
105     desc.unit = "MIDI units";
106     desc.description = "MIDI pitch corresponding to the lowest frequency to be included in the chromagram";
107     desc.minValue = 0;
108     desc.maxValue = 127;
109     desc.defaultValue = 36;
110     desc.isQuantized = true;
111     desc.quantizeStep = 1;
112     list.push_back(desc);
113
114     desc.identifier = "maxpitch";
115     desc.name = "Maximum Pitch";
116     desc.unit = "MIDI units";
117     desc.description = "MIDI pitch corresponding to the highest frequency to be included in the chromagram";
118     desc.minValue = 0;
119     desc.maxValue = 127;
120     desc.defaultValue = 96;
121     desc.isQuantized = true;
122     desc.quantizeStep = 1;
123     list.push_back(desc);
124
125     desc.identifier = "tuning";
126     desc.name = "Tuning Frequency";
127     desc.unit = "Hz";
128     desc.description = "Frequency of concert A";
129     desc.minValue = 360;
130     desc.maxValue = 500;
131     desc.defaultValue = 440;
132     desc.isQuantized = false;
133     list.push_back(desc);
134     
135     desc.identifier = "bpo";
136     desc.name = "Bins per Octave";
137     desc.unit = "bins";
138     desc.description = "Number of constant-Q transform bins per octave, and the number of bins for the chromagram outputs";
139     desc.minValue = 2;
140     desc.maxValue = 480;
141     desc.defaultValue = 12;
142     desc.isQuantized = true;
143     desc.quantizeStep = 1;
144     list.push_back(desc);
145
146     desc.identifier = "normalization";
147     desc.name = "Normalization";
148     desc.unit = "";
149     desc.description = "Normalization for each chromagram output column";
150     desc.minValue = 0;
151     desc.maxValue = 2;
152     desc.defaultValue = 0;
153     desc.isQuantized = true;
154     desc.quantizeStep = 1;
155     desc.valueNames.push_back("None");
156     desc.valueNames.push_back("Unit Sum");
157     desc.valueNames.push_back("Unit Maximum");
158     list.push_back(desc);
159
160     return list;
161 }
162
163 float
164 ChromagramPlugin::getParameter(std::string param) const
165 {
166     if (param == "minpitch") {
167         return m_minMIDIPitch;
168     }
169     if (param == "maxpitch") {
170         return m_maxMIDIPitch;
171     }
172     if (param == "tuning") {
173         return m_tuningFrequency;
174     }
175     if (param == "bpo") {
176         return m_bpo;
177     }
178     if (param == "normalization") {
179         return int(m_normalise);
180     }
181     std::cerr << "WARNING: ChromagramPlugin::getParameter: unknown parameter \""
182               << param << "\"" << std::endl;
183     return 0.0;
184 }
185
186 void
187 ChromagramPlugin::setParameter(std::string param, float value)
188 {
189     if (param == "minpitch") {
190         m_minMIDIPitch = lrintf(value);
191     } else if (param == "maxpitch") {
192         m_maxMIDIPitch = lrintf(value);
193     } else if (param == "tuning") {
194         m_tuningFrequency = value;
195     } else if (param == "bpo") {
196         m_bpo = lrintf(value);
197     } else if (param == "normalization") {
198         m_normalise = MathUtilities::NormaliseType(int(value + 0.0001));
199     } else {
200         std::cerr << "WARNING: ChromagramPlugin::setParameter: unknown parameter \""
201                   << param << "\"" << std::endl;
202     }
203
204     setupConfig();
205 }
206
207
208 bool
209 ChromagramPlugin::initialise(size_t channels, size_t stepSize, size_t blockSize)
210 {
211     if (m_chromagram) {
212         delete m_chromagram;
213         m_chromagram = 0;
214     }
215
216     if (channels < getMinChannelCount() ||
217         channels > getMaxChannelCount()) return false;
218
219     m_chromagram = new Chromagram(m_config);
220     m_binsums = vector<double>(m_config.BPO);
221
222     for (int i = 0; i < m_config.BPO; ++i) {
223         m_binsums[i] = 0.0;
224     }
225
226     m_count = 0;
227
228     m_step = m_chromagram->getHopSize();
229     m_block = m_chromagram->getFrameSize();
230     if (m_step < 1) m_step = 1;
231
232     if (blockSize != m_block) {
233         std::cerr << "ChromagramPlugin::initialise: ERROR: supplied block size " << blockSize << " differs from required block size " << m_block << ", initialise failing" << std::endl;
234         delete m_chromagram;
235         m_chromagram = 0;
236         return false;
237     }
238
239     if (stepSize != m_step) {
240         std::cerr << "ChromagramPlugin::initialise: NOTE: supplied step size " << stepSize << " differs from expected step size " << m_step << " (for block size = " << m_block << ")" << std::endl;
241     }
242
243     return true;
244 }
245
246 void
247 ChromagramPlugin::reset()
248 {
249     if (m_chromagram) {
250         delete m_chromagram;
251         m_chromagram = new Chromagram(m_config);
252         for (int i = 0; i < m_config.BPO; ++i) {
253             m_binsums[i] = 0.0;
254         }
255         m_count = 0;
256     }
257 }
258
259 size_t
260 ChromagramPlugin::getPreferredStepSize() const
261 {
262     if (!m_step) {
263         Chromagram chroma(m_config);
264         m_step = chroma.getHopSize();
265         m_block = chroma.getFrameSize();
266         if (m_step < 1) m_step = 1;
267     }
268
269     return m_step;
270 }
271
272 size_t
273 ChromagramPlugin::getPreferredBlockSize() const
274 {
275     if (!m_block) {
276         Chromagram chroma(m_config);
277         m_step = chroma.getHopSize();
278         m_block = chroma.getFrameSize();
279         if (m_step < 1) m_step = 1;
280     }
281
282     return m_block;
283 }
284
285 ChromagramPlugin::OutputList
286 ChromagramPlugin::getOutputDescriptors() const
287 {
288     OutputList list;
289
290     OutputDescriptor d;
291     d.identifier = "chromagram";
292     d.name = "Chromagram";
293     d.unit = "";
294     d.description = "Output of chromagram, as a single vector per process block";
295     d.hasFixedBinCount = true;
296     d.binCount = m_config.BPO;
297     
298     const char *names[] =
299         { "C", "C#", "D", "D#", "E", "F", "F#", "G", "G#", "A", "A#", "B" };
300
301     if (d.binCount % 12 == 0) {
302         for (int i = 0; i < 12; ++i) {
303             int ipc = m_minMIDIPitch % 12;
304             int index = (i + ipc) % 12;
305             d.binNames.push_back(names[index]);
306             for (int j = 0; j < int(d.binCount) / 12 - 1; ++j) {
307                 d.binNames.push_back("");
308             }
309         }
310     } else {
311         d.binNames.push_back(names[m_minMIDIPitch % 12]);
312     }
313
314     d.hasKnownExtents = (m_normalise != MathUtilities::NormaliseNone);
315     d.minValue = 0.0;
316     d.maxValue = (d.hasKnownExtents ? 1.0 : 0.0);
317     d.isQuantized = false;
318     d.sampleType = OutputDescriptor::OneSamplePerStep;
319     list.push_back(d);
320
321     d.identifier = "chromameans";
322     d.name = "Chroma Means";
323     d.description = "Mean values of chromagram bins across the duration of the input audio";
324     d.sampleType = OutputDescriptor::FixedSampleRate;
325     d.sampleRate = 1;
326     list.push_back(d);
327
328     return list;
329 }
330
331 ChromagramPlugin::FeatureSet
332 ChromagramPlugin::process(const float *const *inputBuffers,
333                           Vamp::RealTime )
334 {
335     if (!m_chromagram) {
336         cerr << "ERROR: ChromagramPlugin::process: "
337              << "Chromagram has not been initialised"
338              << endl;
339         return FeatureSet();
340     }
341
342     double *real = new double[m_block];
343     double *imag = new double[m_block];
344
345     for (size_t i = 0; i <= m_block/2; ++i) {
346         real[i] = inputBuffers[0][i*2];
347         if (i > 0) real[m_block - i] = real[i];
348         imag[i] = inputBuffers[0][i*2+1];
349         if (i > 0) imag[m_block - i] = imag[i];
350     }
351
352 //    cerr << "chromagram: timestamp = " << timestamp << endl;
353 /*
354     bool printThis = false;
355
356     if (timestamp.sec == 3 && timestamp.nsec < 250000000) {
357         printThis = true;
358     } 
359     if (printThis) {
360         cerr << "\n\nchromagram: timestamp " << timestamp << ": input data starts:" << endl;
361         for (int i = 0; i < m_block && i < 1000; ++i) {
362             cerr << real[i] << "," << imag[i] << " ";
363         }
364         cerr << endl << "values:" << endl;
365     }
366 */
367     double *output = m_chromagram->process(real, imag);
368
369     delete[] real;
370     delete[] imag;
371
372     Feature feature;
373     feature.hasTimestamp = false;
374     for (int i = 0; i < m_config.BPO; ++i) {
375         double value = output[i];
376 /*
377         if (printThis) {
378             cerr << value << " ";
379         }
380 */
381         if (ISNAN(value)) value = 0.0;
382         m_binsums[i] += value;
383         feature.values.push_back(value);
384     }
385     feature.label = "";
386     ++m_count;
387 /*
388     if (printThis) {
389         cerr << endl;
390     }
391 */
392
393     FeatureSet returnFeatures;
394     returnFeatures[0].push_back(feature);
395     return returnFeatures;
396 }
397
398 ChromagramPlugin::FeatureSet
399 ChromagramPlugin::getRemainingFeatures()
400 {
401     Feature feature;
402     feature.hasTimestamp = true;
403     feature.timestamp = Vamp::RealTime::zeroTime;
404   
405     for (int i = 0; i < m_config.BPO; ++i) {
406         double v = m_binsums[i];
407         if (m_count > 0) v /= m_count;
408         feature.values.push_back(v);
409     }
410     feature.label = "Chromagram bin means";
411
412     FeatureSet returnFeatures;
413     returnFeatures[1].push_back(feature);
414     return returnFeatures;
415 }
416