c@110
|
1 /* -*- c-basic-offset: 4 indent-tabs-mode: nil -*- vi:set ts=8 sts=4 sw=4: */
|
c@110
|
2 /*
|
c@110
|
3 Constant-Q library
|
c@110
|
4 Copyright (c) 2013-2014 Queen Mary, University of London
|
c@110
|
5
|
c@110
|
6 Permission is hereby granted, free of charge, to any person
|
c@110
|
7 obtaining a copy of this software and associated documentation
|
c@110
|
8 files (the "Software"), to deal in the Software without
|
c@110
|
9 restriction, including without limitation the rights to use, copy,
|
c@110
|
10 modify, merge, publish, distribute, sublicense, and/or sell copies
|
c@110
|
11 of the Software, and to permit persons to whom the Software is
|
c@110
|
12 furnished to do so, subject to the following conditions:
|
c@110
|
13
|
c@110
|
14 The above copyright notice and this permission notice shall be
|
c@110
|
15 included in all copies or substantial portions of the Software.
|
c@110
|
16
|
c@110
|
17 THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
c@110
|
18 EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
c@110
|
19 MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
c@110
|
20 NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY
|
c@110
|
21 CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF
|
c@110
|
22 CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
c@110
|
23 WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
c@110
|
24
|
c@110
|
25 Except as contained in this notice, the names of the Centre for
|
c@110
|
26 Digital Music; Queen Mary, University of London; and Chris Cannam
|
c@110
|
27 shall not be used in advertising or otherwise to promote the sale,
|
c@110
|
28 use or other dealings in this Software without prior written
|
c@110
|
29 authorization.
|
c@110
|
30 */
|
c@110
|
31
|
c@110
|
32 #include "CQChromaVamp.h"
|
c@110
|
33
|
c@170
|
34 #include "cq/Chromagram.h"
|
c@110
|
35
|
c@110
|
36 #include <algorithm>
|
c@110
|
37
|
c@110
|
38 using std::string;
|
c@110
|
39 using std::vector;
|
c@110
|
40 using std::cerr;
|
c@110
|
41 using std::endl;
|
c@110
|
42
|
c@113
|
43 static const int defaultLowestOctave = 0;
|
c@113
|
44 static const int defaultOctaveCount = 7;
|
c@110
|
45 static const int defaultBPO = 36;
|
c@110
|
46 static const float defaultTuningFrequency = 440.f;
|
c@110
|
47
|
c@110
|
48 CQChromaVamp::CQChromaVamp(float inputSampleRate) :
|
c@110
|
49 Vamp::Plugin(inputSampleRate),
|
c@110
|
50 m_lowestOctave(defaultLowestOctave),
|
c@110
|
51 m_octaveCount(defaultOctaveCount),
|
c@110
|
52 m_tuningFrequency(defaultTuningFrequency),
|
c@110
|
53 m_bpo(defaultBPO),
|
c@170
|
54 m_chroma(0),
|
c@110
|
55 m_haveStartTime(false),
|
c@110
|
56 m_columnCount(0)
|
c@110
|
57 {
|
c@110
|
58 }
|
c@110
|
59
|
c@110
|
60 CQChromaVamp::~CQChromaVamp()
|
c@110
|
61 {
|
c@170
|
62 delete m_chroma;
|
c@110
|
63 }
|
c@110
|
64
|
c@110
|
65 string
|
c@110
|
66 CQChromaVamp::getIdentifier() const
|
c@110
|
67 {
|
c@110
|
68 return "cqchromavamp";
|
c@110
|
69 }
|
c@110
|
70
|
c@110
|
71 string
|
c@110
|
72 CQChromaVamp::getName() const
|
c@110
|
73 {
|
c@169
|
74 return "CQ Chromagram";
|
c@110
|
75 }
|
c@110
|
76
|
c@110
|
77 string
|
c@110
|
78 CQChromaVamp::getDescription() const
|
c@110
|
79 {
|
c@155
|
80 return "Extract a Constant-Q spectrogram with constant ratio of centre frequency to resolution from the audio, then wrap it around into a single-octave chromagram.";
|
c@110
|
81 }
|
c@110
|
82
|
c@110
|
83 string
|
c@110
|
84 CQChromaVamp::getMaker() const
|
c@110
|
85 {
|
c@110
|
86 return "Queen Mary, University of London";
|
c@110
|
87 }
|
c@110
|
88
|
c@110
|
89 int
|
c@110
|
90 CQChromaVamp::getPluginVersion() const
|
c@110
|
91 {
|
c@182
|
92 return 2;
|
c@110
|
93 }
|
c@110
|
94
|
c@110
|
95 string
|
c@110
|
96 CQChromaVamp::getCopyright() const
|
c@110
|
97 {
|
c@182
|
98 return "Plugin by Chris Cannam. Method by Christian Schörkhuber and Anssi Klapuri. Copyright (c) 2015 QMUL. BSD/MIT licence.";
|
c@110
|
99 }
|
c@110
|
100
|
c@110
|
101 CQChromaVamp::ParameterList
|
c@110
|
102 CQChromaVamp::getParameterDescriptors() const
|
c@110
|
103 {
|
c@110
|
104 ParameterList list;
|
c@110
|
105
|
c@110
|
106 ParameterDescriptor desc;
|
c@110
|
107
|
c@110
|
108 desc.identifier = "lowestoct";
|
c@110
|
109 desc.name = "Lowest Contributing Octave";
|
c@110
|
110 desc.unit = "";
|
c@110
|
111 desc.description = "Octave number of the lowest octave to include in the chromagram. Octave numbering is ASA standard, with -1 as the first octave in the MIDI range and middle-C being C4. The octave starts at C.";
|
c@110
|
112 desc.minValue = -1;
|
c@110
|
113 desc.maxValue = 12;
|
c@110
|
114 desc.defaultValue = defaultLowestOctave;
|
c@110
|
115 desc.isQuantized = true;
|
c@110
|
116 desc.quantizeStep = 1;
|
c@110
|
117 list.push_back(desc);
|
c@110
|
118
|
c@110
|
119 desc.identifier = "octaves";
|
c@110
|
120 desc.name = "Contributing Octave Count";
|
c@110
|
121 desc.unit = "octaves";
|
c@110
|
122 desc.description = "Number of octaves to use when generating the Constant-Q transform. All octaves are wrapped around and summed to produce a single octave chromagram as output.";
|
c@110
|
123 desc.minValue = 1;
|
c@110
|
124 desc.maxValue = 12;
|
c@110
|
125 desc.defaultValue = defaultOctaveCount;
|
c@110
|
126 desc.isQuantized = true;
|
c@110
|
127 desc.quantizeStep = 1;
|
c@110
|
128 list.push_back(desc);
|
c@110
|
129
|
c@110
|
130 desc.identifier = "tuning";
|
c@110
|
131 desc.name = "Tuning Frequency";
|
c@110
|
132 desc.unit = "Hz";
|
c@110
|
133 desc.description = "Frequency of concert A";
|
c@110
|
134 desc.minValue = 360;
|
c@110
|
135 desc.maxValue = 500;
|
c@151
|
136 desc.defaultValue = defaultTuningFrequency;
|
c@110
|
137 desc.isQuantized = false;
|
c@110
|
138 list.push_back(desc);
|
c@110
|
139
|
c@110
|
140 desc.identifier = "bpo";
|
c@110
|
141 desc.name = "Bins per Octave";
|
c@110
|
142 desc.unit = "bins";
|
c@110
|
143 desc.description = "Number of constant-Q transform bins per octave";
|
c@110
|
144 desc.minValue = 2;
|
c@110
|
145 desc.maxValue = 480;
|
c@110
|
146 desc.defaultValue = defaultBPO;
|
c@110
|
147 desc.isQuantized = true;
|
c@110
|
148 desc.quantizeStep = 1;
|
c@110
|
149 list.push_back(desc);
|
c@110
|
150
|
c@110
|
151 return list;
|
c@110
|
152 }
|
c@110
|
153
|
c@110
|
154 float
|
c@110
|
155 CQChromaVamp::getParameter(std::string param) const
|
c@110
|
156 {
|
c@110
|
157 if (param == "lowestoct") {
|
c@110
|
158 return m_lowestOctave;
|
c@110
|
159 }
|
c@110
|
160 if (param == "octaves") {
|
c@110
|
161 return m_octaveCount;
|
c@110
|
162 }
|
c@110
|
163 if (param == "tuning") {
|
c@110
|
164 return m_tuningFrequency;
|
c@110
|
165 }
|
c@110
|
166 if (param == "bpo") {
|
c@110
|
167 return m_bpo;
|
c@110
|
168 }
|
c@110
|
169 std::cerr << "WARNING: CQChromaVamp::getParameter: unknown parameter \""
|
c@110
|
170 << param << "\"" << std::endl;
|
c@110
|
171 return 0.0;
|
c@110
|
172 }
|
c@110
|
173
|
c@110
|
174 void
|
c@110
|
175 CQChromaVamp::setParameter(std::string param, float value)
|
c@110
|
176 {
|
c@110
|
177 if (param == "lowestoct") {
|
c@164
|
178 m_lowestOctave = int(value + 0.5f);
|
c@110
|
179 } else if (param == "octaves") {
|
c@164
|
180 m_octaveCount = int(value + 0.5f);
|
c@110
|
181 } else if (param == "tuning") {
|
c@110
|
182 m_tuningFrequency = value;
|
c@110
|
183 } else if (param == "bpo") {
|
c@164
|
184 m_bpo = int(value + 0.5f);
|
c@110
|
185 } else {
|
c@110
|
186 std::cerr << "WARNING: CQChromaVamp::setParameter: unknown parameter \""
|
c@110
|
187 << param << "\"" << std::endl;
|
c@110
|
188 }
|
c@110
|
189 }
|
c@110
|
190
|
c@110
|
191 bool
|
c@110
|
192 CQChromaVamp::initialise(size_t channels, size_t stepSize, size_t blockSize)
|
c@110
|
193 {
|
c@170
|
194 if (m_chroma) {
|
c@170
|
195 delete m_chroma;
|
c@170
|
196 m_chroma = 0;
|
c@110
|
197 }
|
c@110
|
198
|
c@110
|
199 if (channels < getMinChannelCount() ||
|
c@110
|
200 channels > getMaxChannelCount()) return false;
|
c@110
|
201
|
c@110
|
202 m_stepSize = stepSize;
|
c@110
|
203 m_blockSize = blockSize;
|
c@110
|
204
|
c@147
|
205 reset();
|
c@147
|
206
|
c@170
|
207 if (!m_chroma || !m_chroma->isValid()) {
|
c@147
|
208 cerr << "CQVamp::initialise: Constant-Q parameters not valid! Not initialising" << endl;
|
c@147
|
209 return false;
|
c@147
|
210 }
|
c@110
|
211
|
c@110
|
212 return true;
|
c@110
|
213 }
|
c@110
|
214
|
c@110
|
215 void
|
c@110
|
216 CQChromaVamp::reset()
|
c@110
|
217 {
|
c@170
|
218 delete m_chroma;
|
c@170
|
219 Chromagram::Parameters p(m_inputSampleRate);
|
c@170
|
220 p.lowestOctave = m_lowestOctave;
|
c@171
|
221 p.octaveCount = m_octaveCount;
|
c@171
|
222 p.binsPerOctave = m_bpo;
|
c@170
|
223 p.tuningFrequency = m_tuningFrequency;
|
c@170
|
224
|
c@170
|
225 m_chroma = new Chromagram(p);
|
c@147
|
226
|
c@110
|
227 m_haveStartTime = false;
|
c@147
|
228 m_startTime = Vamp::RealTime::zeroTime;
|
c@110
|
229 m_columnCount = 0;
|
c@110
|
230 }
|
c@110
|
231
|
c@110
|
232 size_t
|
c@110
|
233 CQChromaVamp::getPreferredStepSize() const
|
c@110
|
234 {
|
c@110
|
235 return 0;
|
c@110
|
236 }
|
c@110
|
237
|
c@110
|
238 size_t
|
c@110
|
239 CQChromaVamp::getPreferredBlockSize() const
|
c@110
|
240 {
|
c@110
|
241 return 0;
|
c@110
|
242 }
|
c@110
|
243
|
c@110
|
244 CQChromaVamp::OutputList
|
c@110
|
245 CQChromaVamp::getOutputDescriptors() const
|
c@110
|
246 {
|
c@110
|
247 OutputList list;
|
c@110
|
248
|
c@110
|
249 OutputDescriptor d;
|
c@110
|
250 d.identifier = "chromagram";
|
c@110
|
251 d.name = "Chromagram";
|
c@110
|
252 d.unit = "";
|
c@110
|
253 d.description = "Chromagram obtained from output of constant-Q transform, folding over each process block into a single-octave vector";
|
c@110
|
254 d.hasFixedBinCount = true;
|
c@110
|
255 d.binCount = m_bpo;
|
c@110
|
256
|
c@170
|
257 if (m_chroma) {
|
c@110
|
258 for (int i = 0; i < (int)d.binCount; ++i) {
|
c@170
|
259 d.binNames.push_back(m_chroma->getBinName(i));
|
c@110
|
260 }
|
c@110
|
261 }
|
c@110
|
262
|
c@110
|
263 d.hasKnownExtents = false;
|
c@110
|
264 d.isQuantized = false;
|
c@110
|
265 d.sampleType = OutputDescriptor::FixedSampleRate;
|
c@170
|
266 d.sampleRate = m_inputSampleRate / (m_chroma ? m_chroma->getColumnHop() : 256);
|
c@110
|
267 list.push_back(d);
|
c@110
|
268
|
c@110
|
269 return list;
|
c@110
|
270 }
|
c@110
|
271
|
c@110
|
272 CQChromaVamp::FeatureSet
|
c@110
|
273 CQChromaVamp::process(const float *const *inputBuffers,
|
c@110
|
274 Vamp::RealTime timestamp)
|
c@110
|
275 {
|
c@170
|
276 if (!m_chroma) {
|
c@110
|
277 cerr << "ERROR: CQChromaVamp::process: "
|
c@110
|
278 << "Plugin has not been initialised"
|
c@110
|
279 << endl;
|
c@110
|
280 return FeatureSet();
|
c@110
|
281 }
|
c@110
|
282
|
c@110
|
283 if (!m_haveStartTime) {
|
c@110
|
284 m_startTime = timestamp;
|
c@110
|
285 m_haveStartTime = true;
|
c@110
|
286 }
|
c@110
|
287
|
c@110
|
288 vector<double> data;
|
c@110
|
289 for (int i = 0; i < m_blockSize; ++i) data.push_back(inputBuffers[0][i]);
|
c@110
|
290
|
c@170
|
291 vector<vector<double> > chromaout = m_chroma->process(data);
|
c@170
|
292 return convertToFeatures(chromaout);
|
c@110
|
293 }
|
c@110
|
294
|
c@110
|
295 CQChromaVamp::FeatureSet
|
c@110
|
296 CQChromaVamp::getRemainingFeatures()
|
c@110
|
297 {
|
c@170
|
298 vector<vector<double> > chromaout = m_chroma->getRemainingOutput();
|
c@170
|
299 return convertToFeatures(chromaout);
|
c@110
|
300 }
|
c@110
|
301
|
c@110
|
302 CQChromaVamp::FeatureSet
|
c@170
|
303 CQChromaVamp::convertToFeatures(const vector<vector<double> > &chromaout)
|
c@110
|
304 {
|
c@110
|
305 FeatureSet returnFeatures;
|
c@110
|
306
|
c@170
|
307 int width = chromaout.size();
|
c@110
|
308
|
c@110
|
309 for (int i = 0; i < width; ++i) {
|
c@110
|
310
|
c@170
|
311 vector<float> column(chromaout[i].begin(), chromaout[i].end());
|
c@110
|
312
|
c@110
|
313 Feature feature;
|
c@110
|
314 feature.hasTimestamp = true;
|
c@110
|
315 feature.timestamp = m_startTime + Vamp::RealTime::frame2RealTime
|
c@170
|
316 (m_columnCount * m_chroma->getColumnHop() - m_chroma->getLatency(),
|
c@110
|
317 m_inputSampleRate);
|
c@110
|
318 feature.values = column;
|
c@110
|
319 feature.label = "";
|
c@110
|
320
|
c@110
|
321 if (feature.timestamp >= m_startTime) {
|
c@110
|
322 returnFeatures[0].push_back(feature);
|
c@110
|
323 }
|
c@110
|
324
|
c@110
|
325 ++m_columnCount;
|
c@110
|
326 }
|
c@110
|
327
|
c@110
|
328 return returnFeatures;
|
c@110
|
329 }
|
c@110
|
330
|