c@41
|
1 /* -*- c-basic-offset: 4 indent-tabs-mode: nil -*- vi:set ts=8 sts=4 sw=4: */
|
c@41
|
2
|
c@41
|
3 /*
|
c@41
|
4 * SegmenterPlugin.cpp
|
c@41
|
5 *
|
c@41
|
6 * Copyright 2008 Centre for Digital Music, Queen Mary, University of London.
|
c@41
|
7 * All rights reserved.
|
c@41
|
8 */
|
c@41
|
9
|
c@41
|
10 #include <iostream>
|
c@41
|
11 #include <sstream>
|
c@41
|
12
|
c@41
|
13 #include "SimilarityPlugin.h"
|
c@42
|
14 #include "base/Pitch.h"
|
c@41
|
15 #include "dsp/mfcc/MFCC.h"
|
c@42
|
16 #include "dsp/chromagram/Chromagram.h"
|
c@41
|
17 #include "dsp/rateconversion/Decimator.h"
|
c@41
|
18
|
c@41
|
19 using std::string;
|
c@41
|
20 using std::vector;
|
c@41
|
21 using std::cerr;
|
c@41
|
22 using std::endl;
|
c@41
|
23 using std::ostringstream;
|
c@41
|
24
|
c@41
|
25 SimilarityPlugin::SimilarityPlugin(float inputSampleRate) :
|
c@41
|
26 Plugin(inputSampleRate),
|
c@42
|
27 m_type(TypeMFCC),
|
c@41
|
28 m_mfcc(0),
|
c@42
|
29 m_chromagram(0),
|
c@41
|
30 m_decimator(0),
|
c@42
|
31 m_featureColumnSize(20),
|
c@41
|
32 m_blockSize(0),
|
c@41
|
33 m_channels(0)
|
c@41
|
34 {
|
c@41
|
35
|
c@41
|
36 }
|
c@41
|
37
|
c@41
|
38 SimilarityPlugin::~SimilarityPlugin()
|
c@41
|
39 {
|
c@41
|
40 delete m_mfcc;
|
c@42
|
41 delete m_chromagram;
|
c@41
|
42 delete m_decimator;
|
c@41
|
43 }
|
c@41
|
44
|
c@41
|
45 string
|
c@41
|
46 SimilarityPlugin::getIdentifier() const
|
c@41
|
47 {
|
c@41
|
48 return "qm-similarity";
|
c@41
|
49 }
|
c@41
|
50
|
c@41
|
51 string
|
c@41
|
52 SimilarityPlugin::getName() const
|
c@41
|
53 {
|
c@41
|
54 return "Similarity";
|
c@41
|
55 }
|
c@41
|
56
|
c@41
|
57 string
|
c@41
|
58 SimilarityPlugin::getDescription() const
|
c@41
|
59 {
|
c@42
|
60 return "Return a distance matrix for similarity between the input audio channels";
|
c@41
|
61 }
|
c@41
|
62
|
c@41
|
63 string
|
c@41
|
64 SimilarityPlugin::getMaker() const
|
c@41
|
65 {
|
c@41
|
66 return "Chris Cannam, Queen Mary, University of London";
|
c@41
|
67 }
|
c@41
|
68
|
c@41
|
69 int
|
c@41
|
70 SimilarityPlugin::getPluginVersion() const
|
c@41
|
71 {
|
c@41
|
72 return 1;
|
c@41
|
73 }
|
c@41
|
74
|
c@41
|
75 string
|
c@41
|
76 SimilarityPlugin::getCopyright() const
|
c@41
|
77 {
|
c@41
|
78 return "Copyright (c) 2008 - All Rights Reserved";
|
c@41
|
79 }
|
c@41
|
80
|
c@41
|
81 size_t
|
c@41
|
82 SimilarityPlugin::getMinChannelCount() const
|
c@41
|
83 {
|
c@43
|
84 return 1;
|
c@41
|
85 }
|
c@41
|
86
|
c@41
|
87 size_t
|
c@41
|
88 SimilarityPlugin::getMaxChannelCount() const
|
c@41
|
89 {
|
c@41
|
90 return 1024;
|
c@41
|
91 }
|
c@41
|
92
|
c@41
|
93 bool
|
c@41
|
94 SimilarityPlugin::initialise(size_t channels, size_t stepSize, size_t blockSize)
|
c@41
|
95 {
|
c@41
|
96 if (channels < getMinChannelCount() ||
|
c@41
|
97 channels > getMaxChannelCount()) return false;
|
c@41
|
98
|
c@41
|
99 if (stepSize != getPreferredStepSize()) {
|
c@43
|
100 //!!! actually this perhaps shouldn't be an error... similarly
|
c@43
|
101 //using more than getMaxChannelCount channels
|
c@41
|
102 std::cerr << "SimilarityPlugin::initialise: supplied step size "
|
c@41
|
103 << stepSize << " differs from required step size "
|
c@41
|
104 << getPreferredStepSize() << std::endl;
|
c@41
|
105 return false;
|
c@41
|
106 }
|
c@41
|
107
|
c@41
|
108 if (blockSize != getPreferredBlockSize()) {
|
c@41
|
109 std::cerr << "SimilarityPlugin::initialise: supplied block size "
|
c@41
|
110 << blockSize << " differs from required block size "
|
c@41
|
111 << getPreferredBlockSize() << std::endl;
|
c@41
|
112 return false;
|
c@41
|
113 }
|
c@41
|
114
|
c@41
|
115 m_blockSize = blockSize;
|
c@41
|
116 m_channels = channels;
|
c@41
|
117
|
c@41
|
118 int decimationFactor = getDecimationFactor();
|
c@41
|
119 if (decimationFactor > 1) {
|
c@42
|
120 m_decimator = new Decimator(m_blockSize, decimationFactor);
|
c@41
|
121 }
|
c@41
|
122
|
c@42
|
123 if (m_type == TypeMFCC) {
|
c@42
|
124
|
c@42
|
125 m_featureColumnSize = 20;
|
c@42
|
126
|
c@42
|
127 MFCCConfig config;
|
c@42
|
128 config.FS = lrintf(m_inputSampleRate) / decimationFactor;
|
c@42
|
129 config.fftsize = 2048;
|
c@42
|
130 config.nceps = m_featureColumnSize - 1;
|
c@42
|
131 config.want_c0 = true;
|
c@42
|
132 m_mfcc = new MFCC(config);
|
c@42
|
133 m_fftSize = m_mfcc->getfftlength();
|
c@42
|
134
|
c@43
|
135 std::cerr << "MFCC FS = " << config.FS << ", FFT size = " << m_fftSize<< std::endl;
|
c@43
|
136
|
c@42
|
137 } else if (m_type == TypeChroma) {
|
c@42
|
138
|
c@42
|
139 m_featureColumnSize = 12;
|
c@42
|
140
|
c@42
|
141 ChromaConfig config;
|
c@42
|
142 config.FS = lrintf(m_inputSampleRate) / decimationFactor;
|
c@42
|
143 config.min = Pitch::getFrequencyForPitch(24, 0, 440);
|
c@42
|
144 config.max = Pitch::getFrequencyForPitch(96, 0, 440);
|
c@42
|
145 config.BPO = 12;
|
c@42
|
146 config.CQThresh = 0.0054;
|
c@42
|
147 config.isNormalised = true;
|
c@42
|
148 m_chromagram = new Chromagram(config);
|
c@42
|
149 m_fftSize = m_chromagram->getFrameSize();
|
c@42
|
150
|
c@42
|
151 std::cerr << "min = "<< config.min << ", max = " << config.max << std::endl;
|
c@42
|
152
|
c@42
|
153 } else {
|
c@42
|
154
|
c@42
|
155 std::cerr << "SimilarityPlugin::initialise: internal error: unknown type " << m_type << std::endl;
|
c@42
|
156 return false;
|
c@42
|
157 }
|
c@41
|
158
|
c@41
|
159 for (int i = 0; i < m_channels; ++i) {
|
c@42
|
160 m_values.push_back(FeatureMatrix());
|
c@41
|
161 }
|
c@41
|
162
|
c@41
|
163 return true;
|
c@41
|
164 }
|
c@41
|
165
|
c@41
|
166 void
|
c@41
|
167 SimilarityPlugin::reset()
|
c@41
|
168 {
|
c@41
|
169 //!!!
|
c@41
|
170 }
|
c@41
|
171
|
c@41
|
172 int
|
c@41
|
173 SimilarityPlugin::getDecimationFactor() const
|
c@41
|
174 {
|
c@41
|
175 int rate = lrintf(m_inputSampleRate);
|
c@41
|
176 int internalRate = 22050;
|
c@41
|
177 int decimationFactor = rate / internalRate;
|
c@41
|
178 if (decimationFactor < 1) decimationFactor = 1;
|
c@41
|
179
|
c@41
|
180 // must be a power of two
|
c@41
|
181 while (decimationFactor & (decimationFactor - 1)) ++decimationFactor;
|
c@41
|
182
|
c@41
|
183 return decimationFactor;
|
c@41
|
184 }
|
c@41
|
185
|
c@41
|
186 size_t
|
c@41
|
187 SimilarityPlugin::getPreferredStepSize() const
|
c@41
|
188 {
|
c@42
|
189 if (m_blockSize == 0) calculateBlockSize();
|
c@43
|
190 if (m_type == TypeChroma) {
|
c@43
|
191 return m_blockSize/2;
|
c@43
|
192 } else {
|
c@43
|
193 // for compatibility with old-skool Soundbite, which doesn't
|
c@43
|
194 // overlap blocks on input
|
c@43
|
195 return m_blockSize;
|
c@43
|
196 }
|
c@41
|
197 }
|
c@41
|
198
|
c@41
|
199 size_t
|
c@41
|
200 SimilarityPlugin::getPreferredBlockSize() const
|
c@41
|
201 {
|
c@42
|
202 if (m_blockSize == 0) calculateBlockSize();
|
c@42
|
203 return m_blockSize;
|
c@42
|
204 }
|
c@42
|
205
|
c@42
|
206 void
|
c@42
|
207 SimilarityPlugin::calculateBlockSize() const
|
c@42
|
208 {
|
c@42
|
209 if (m_blockSize != 0) return;
|
c@42
|
210 int decimationFactor = getDecimationFactor();
|
c@42
|
211 if (m_type == TypeChroma) {
|
c@42
|
212 ChromaConfig config;
|
c@42
|
213 config.FS = lrintf(m_inputSampleRate) / decimationFactor;
|
c@42
|
214 config.min = Pitch::getFrequencyForPitch(24, 0, 440);
|
c@42
|
215 config.max = Pitch::getFrequencyForPitch(96, 0, 440);
|
c@42
|
216 config.BPO = 12;
|
c@42
|
217 config.CQThresh = 0.0054;
|
c@42
|
218 config.isNormalised = false;
|
c@42
|
219 Chromagram *c = new Chromagram(config);
|
c@42
|
220 size_t sz = c->getFrameSize();
|
c@42
|
221 delete c;
|
c@42
|
222 m_blockSize = sz * decimationFactor;
|
c@42
|
223 } else {
|
c@42
|
224 m_blockSize = 2048 * decimationFactor;
|
c@42
|
225 }
|
c@41
|
226 }
|
c@41
|
227
|
c@41
|
228 SimilarityPlugin::ParameterList SimilarityPlugin::getParameterDescriptors() const
|
c@41
|
229 {
|
c@41
|
230 ParameterList list;
|
c@42
|
231
|
c@42
|
232 ParameterDescriptor desc;
|
c@42
|
233 desc.identifier = "featureType";
|
c@42
|
234 desc.name = "Feature Type";
|
c@42
|
235 desc.description = "";//!!!
|
c@42
|
236 desc.unit = "";
|
c@42
|
237 desc.minValue = 0;
|
c@42
|
238 desc.maxValue = 1;
|
c@42
|
239 desc.defaultValue = 0;
|
c@42
|
240 desc.isQuantized = true;
|
c@42
|
241 desc.quantizeStep = 1;
|
c@42
|
242 desc.valueNames.push_back("Timbral (MFCC)");
|
c@42
|
243 desc.valueNames.push_back("Chromatic (Chroma)");
|
c@42
|
244 list.push_back(desc);
|
c@42
|
245
|
c@41
|
246 return list;
|
c@41
|
247 }
|
c@41
|
248
|
c@41
|
249 float
|
c@41
|
250 SimilarityPlugin::getParameter(std::string param) const
|
c@41
|
251 {
|
c@42
|
252 if (param == "featureType") {
|
c@42
|
253 if (m_type == TypeMFCC) return 0;
|
c@42
|
254 else if (m_type == TypeChroma) return 1;
|
c@42
|
255 else return 0;
|
c@42
|
256 }
|
c@42
|
257
|
c@41
|
258 std::cerr << "WARNING: SimilarityPlugin::getParameter: unknown parameter \""
|
c@41
|
259 << param << "\"" << std::endl;
|
c@41
|
260 return 0.0;
|
c@41
|
261 }
|
c@41
|
262
|
c@41
|
263 void
|
c@41
|
264 SimilarityPlugin::setParameter(std::string param, float value)
|
c@41
|
265 {
|
c@42
|
266 if (param == "featureType") {
|
c@42
|
267 int v = int(value + 0.1);
|
c@42
|
268 Type prevType = m_type;
|
c@42
|
269 if (v == 0) m_type = TypeMFCC;
|
c@42
|
270 else if (v == 1) m_type = TypeChroma;
|
c@42
|
271 if (m_type != prevType) m_blockSize = 0;
|
c@42
|
272 return;
|
c@42
|
273 }
|
c@42
|
274
|
c@41
|
275 std::cerr << "WARNING: SimilarityPlugin::setParameter: unknown parameter \""
|
c@41
|
276 << param << "\"" << std::endl;
|
c@41
|
277 }
|
c@41
|
278
|
c@41
|
279 SimilarityPlugin::OutputList
|
c@41
|
280 SimilarityPlugin::getOutputDescriptors() const
|
c@41
|
281 {
|
c@41
|
282 OutputList list;
|
c@41
|
283
|
c@41
|
284 OutputDescriptor similarity;
|
c@43
|
285 similarity.identifier = "distancematrix";
|
c@43
|
286 similarity.name = "Distance Matrix";
|
c@43
|
287 similarity.description = "Distance matrix for similarity metric. Smaller = more similar. Should be symmetrical.";
|
c@41
|
288 similarity.unit = "";
|
c@41
|
289 similarity.hasFixedBinCount = true;
|
c@41
|
290 similarity.binCount = m_channels;
|
c@41
|
291 similarity.hasKnownExtents = false;
|
c@41
|
292 similarity.isQuantized = false;
|
c@41
|
293 similarity.sampleType = OutputDescriptor::FixedSampleRate;
|
c@41
|
294 similarity.sampleRate = 1;
|
c@41
|
295
|
c@43
|
296 m_distanceMatrixOutput = list.size();
|
c@41
|
297 list.push_back(similarity);
|
c@41
|
298
|
c@43
|
299 OutputDescriptor simvec;
|
c@43
|
300 simvec.identifier = "distancevector";
|
c@43
|
301 simvec.name = "Distance from First Channel";
|
c@43
|
302 simvec.description = "Distance vector for similarity of each channel to the first channel. Smaller = more similar.";
|
c@43
|
303 simvec.unit = "";
|
c@43
|
304 simvec.hasFixedBinCount = true;
|
c@43
|
305 simvec.binCount = m_channels;
|
c@43
|
306 simvec.hasKnownExtents = false;
|
c@43
|
307 simvec.isQuantized = false;
|
c@43
|
308 simvec.sampleType = OutputDescriptor::FixedSampleRate;
|
c@43
|
309 simvec.sampleRate = 1;
|
c@43
|
310
|
c@43
|
311 m_distanceVectorOutput = list.size();
|
c@43
|
312 list.push_back(simvec);
|
c@43
|
313
|
c@41
|
314 OutputDescriptor means;
|
c@41
|
315 means.identifier = "means";
|
c@42
|
316 means.name = "Feature Means";
|
c@43
|
317 means.description = "Means of the feature bins. Feature time (sec) corresponds to input channel. Number of bins depends on selected feature type.";
|
c@41
|
318 means.unit = "";
|
c@41
|
319 means.hasFixedBinCount = true;
|
c@43
|
320 means.binCount = m_featureColumnSize;
|
c@41
|
321 means.hasKnownExtents = false;
|
c@41
|
322 means.isQuantized = false;
|
c@43
|
323 means.sampleType = OutputDescriptor::FixedSampleRate;
|
c@43
|
324 means.sampleRate = 1;
|
c@41
|
325
|
c@43
|
326 m_meansOutput = list.size();
|
c@41
|
327 list.push_back(means);
|
c@41
|
328
|
c@41
|
329 OutputDescriptor variances;
|
c@41
|
330 variances.identifier = "variances";
|
c@42
|
331 variances.name = "Feature Variances";
|
c@43
|
332 variances.description = "Variances of the feature bins. Feature time (sec) corresponds to input channel. Number of bins depends on selected feature type.";
|
c@41
|
333 variances.unit = "";
|
c@41
|
334 variances.hasFixedBinCount = true;
|
c@43
|
335 variances.binCount = m_featureColumnSize;
|
c@41
|
336 variances.hasKnownExtents = false;
|
c@41
|
337 variances.isQuantized = false;
|
c@43
|
338 variances.sampleType = OutputDescriptor::FixedSampleRate;
|
c@43
|
339 variances.sampleRate = 1;
|
c@41
|
340
|
c@43
|
341 m_variancesOutput = list.size();
|
c@41
|
342 list.push_back(variances);
|
c@41
|
343
|
c@41
|
344 return list;
|
c@41
|
345 }
|
c@41
|
346
|
c@41
|
347 SimilarityPlugin::FeatureSet
|
c@41
|
348 SimilarityPlugin::process(const float *const *inputBuffers, Vamp::RealTime /* timestamp */)
|
c@41
|
349 {
|
c@41
|
350 double *dblbuf = new double[m_blockSize];
|
c@41
|
351 double *decbuf = dblbuf;
|
c@42
|
352 if (m_decimator) decbuf = new double[m_fftSize];
|
c@42
|
353
|
c@42
|
354 double *raw = 0;
|
c@42
|
355 bool ownRaw = false;
|
c@42
|
356
|
c@42
|
357 if (m_type == TypeMFCC) {
|
c@42
|
358 raw = new double[m_featureColumnSize];
|
c@42
|
359 ownRaw = true;
|
c@42
|
360 }
|
c@41
|
361
|
c@43
|
362 float threshold = 1e-10;
|
c@43
|
363
|
c@41
|
364 for (size_t c = 0; c < m_channels; ++c) {
|
c@41
|
365
|
c@43
|
366 bool empty = true;
|
c@43
|
367
|
c@41
|
368 for (int i = 0; i < m_blockSize; ++i) {
|
c@43
|
369 float val = inputBuffers[c][i];
|
c@43
|
370 if (fabs(val) > threshold) empty = false;
|
c@43
|
371 dblbuf[i] = val;
|
c@41
|
372 }
|
c@41
|
373
|
c@43
|
374 if (empty) continue;
|
c@43
|
375
|
c@41
|
376 if (m_decimator) {
|
c@41
|
377 m_decimator->process(dblbuf, decbuf);
|
c@41
|
378 }
|
c@42
|
379
|
c@42
|
380 if (m_type == TypeMFCC) {
|
c@42
|
381 m_mfcc->process(m_fftSize, decbuf, raw);
|
c@42
|
382 } else if (m_type == TypeChroma) {
|
c@42
|
383 raw = m_chromagram->process(decbuf);
|
c@42
|
384 }
|
c@41
|
385
|
c@42
|
386 FeatureColumn mf(m_featureColumnSize);
|
c@42
|
387 for (int i = 0; i < m_featureColumnSize; ++i) mf[i] = raw[i];
|
c@41
|
388
|
c@42
|
389 m_values[c].push_back(mf);
|
c@41
|
390 }
|
c@41
|
391
|
c@41
|
392 if (m_decimator) delete[] decbuf;
|
c@41
|
393 delete[] dblbuf;
|
c@42
|
394
|
c@42
|
395 if (ownRaw) delete[] raw;
|
c@41
|
396
|
c@41
|
397 return FeatureSet();
|
c@41
|
398 }
|
c@41
|
399
|
c@41
|
400 SimilarityPlugin::FeatureSet
|
c@41
|
401 SimilarityPlugin::getRemainingFeatures()
|
c@41
|
402 {
|
c@42
|
403 std::vector<FeatureColumn> m(m_channels);
|
c@42
|
404 std::vector<FeatureColumn> v(m_channels);
|
c@41
|
405
|
c@41
|
406 for (int i = 0; i < m_channels; ++i) {
|
c@41
|
407
|
c@42
|
408 FeatureColumn mean(m_featureColumnSize), variance(m_featureColumnSize);
|
c@41
|
409
|
c@42
|
410 for (int j = 0; j < m_featureColumnSize; ++j) {
|
c@41
|
411
|
c@43
|
412 mean[j] = 0.0;
|
c@43
|
413 variance[j] = 0.0;
|
c@41
|
414 int count;
|
c@41
|
415
|
c@43
|
416 // we need to use at least one value, but we want to
|
c@43
|
417 // disregard the final value because it may have come from
|
c@43
|
418 // incomplete data
|
c@43
|
419
|
c@43
|
420 int sz = m_values[i].size();
|
c@43
|
421 if (sz > 1) --sz;
|
c@43
|
422
|
c@43
|
423 // std::cout << "\nBin " << j << ":" << std::endl;
|
c@42
|
424
|
c@41
|
425 count = 0;
|
c@43
|
426 for (int k = 0; k < sz; ++k) {
|
c@42
|
427 double val = m_values[i][k][j];
|
c@42
|
428 // std::cout << val << " ";
|
c@41
|
429 if (isnan(val) || isinf(val)) continue;
|
c@41
|
430 mean[j] += val;
|
c@41
|
431 ++count;
|
c@41
|
432 }
|
c@41
|
433 if (count > 0) mean[j] /= count;
|
c@43
|
434 // std::cout << "\n" << count << " non-NaN non-inf values, so mean = " << mean[j] << std::endl;
|
c@41
|
435
|
c@41
|
436 count = 0;
|
c@43
|
437 for (int k = 0; k < sz; ++k) {
|
c@42
|
438 double val = ((m_values[i][k][j] - mean[j]) *
|
c@42
|
439 (m_values[i][k][j] - mean[j]));
|
c@41
|
440 if (isnan(val) || isinf(val)) continue;
|
c@41
|
441 variance[j] += val;
|
c@41
|
442 ++count;
|
c@41
|
443 }
|
c@41
|
444 if (count > 0) variance[j] /= count;
|
c@43
|
445 // std::cout << "... and variance = " << variance[j] << std::endl;
|
c@41
|
446 }
|
c@41
|
447
|
c@41
|
448 m[i] = mean;
|
c@41
|
449 v[i] = variance;
|
c@41
|
450 }
|
c@41
|
451
|
c@42
|
452 // we want to return a matrix of the distances between channels,
|
c@41
|
453 // but Vamp doesn't have a matrix return type so we actually
|
c@41
|
454 // return a series of vectors
|
c@41
|
455
|
c@41
|
456 std::vector<std::vector<double> > distances;
|
c@41
|
457
|
c@42
|
458 // "Despite the fact that MFCCs extracted from music are clearly
|
c@42
|
459 // not Gaussian, [14] showed, somewhat surprisingly, that a
|
c@42
|
460 // similarity function comparing single Gaussians modelling MFCCs
|
c@42
|
461 // for each track can perform as well as mixture models. A great
|
c@42
|
462 // advantage of using single Gaussians is that a simple closed
|
c@42
|
463 // form exists for the KL divergence." -- Mark Levy, "Lightweight
|
c@42
|
464 // measures for timbral similarity of musical audio"
|
c@42
|
465 // (http://www.elec.qmul.ac.uk/easaier/papers/mlevytimbralsimilarity.pdf)
|
c@42
|
466 //
|
c@42
|
467 // This code calculates a symmetrised distance metric based on the
|
c@42
|
468 // KL divergence of Gaussian models of the MFCC values.
|
c@42
|
469
|
c@41
|
470 for (int i = 0; i < m_channels; ++i) {
|
c@41
|
471 distances.push_back(std::vector<double>());
|
c@41
|
472 for (int j = 0; j < m_channels; ++j) {
|
c@42
|
473 double d = -2.0 * m_featureColumnSize;
|
c@42
|
474 for (int k = 0; k < m_featureColumnSize; ++k) {
|
c@42
|
475 // m[i][k] is the mean of feature bin k for channel i
|
c@42
|
476 // v[i][k] is the variance of feature bin k for channel i
|
c@41
|
477 d += v[i][k] / v[j][k] + v[j][k] / v[i][k];
|
c@41
|
478 d += (m[i][k] - m[j][k])
|
c@41
|
479 * (1.0 / v[i][k] + 1.0 / v[j][k])
|
c@41
|
480 * (m[i][k] - m[j][k]);
|
c@41
|
481 }
|
c@41
|
482 d /= 2.0;
|
c@41
|
483 distances[i].push_back(d);
|
c@41
|
484 }
|
c@41
|
485 }
|
c@41
|
486
|
c@41
|
487 FeatureSet returnFeatures;
|
c@41
|
488
|
c@43
|
489 Feature distanceVectorFeature;
|
c@43
|
490 distanceVectorFeature.label = "Distance from first channel";
|
c@43
|
491
|
c@41
|
492 for (int i = 0; i < m_channels; ++i) {
|
c@41
|
493
|
c@41
|
494 Feature feature;
|
c@41
|
495 feature.hasTimestamp = true; // otherwise hosts will tend to stamp them at the end of the file, which is annoying
|
c@41
|
496 feature.timestamp = Vamp::RealTime(i, 0);
|
c@41
|
497
|
c@41
|
498 feature.values.clear();
|
c@42
|
499 for (int k = 0; k < m_featureColumnSize; ++k) {
|
c@41
|
500 feature.values.push_back(m[i][k]);
|
c@41
|
501 }
|
c@41
|
502
|
c@43
|
503 returnFeatures[m_meansOutput].push_back(feature);
|
c@41
|
504
|
c@41
|
505 feature.values.clear();
|
c@42
|
506 for (int k = 0; k < m_featureColumnSize; ++k) {
|
c@41
|
507 feature.values.push_back(v[i][k]);
|
c@41
|
508 }
|
c@41
|
509
|
c@43
|
510 returnFeatures[m_variancesOutput].push_back(feature);
|
c@41
|
511
|
c@41
|
512 feature.values.clear();
|
c@41
|
513 for (int j = 0; j < m_channels; ++j) {
|
c@41
|
514 feature.values.push_back(distances[i][j]);
|
c@41
|
515 }
|
c@43
|
516
|
c@41
|
517 ostringstream oss;
|
c@41
|
518 oss << "Distance from " << (i + 1);
|
c@41
|
519 feature.label = oss.str();
|
c@41
|
520
|
c@43
|
521 returnFeatures[m_distanceMatrixOutput].push_back(feature);
|
c@43
|
522
|
c@43
|
523 distanceVectorFeature.values.push_back(distances[0][i]);
|
c@41
|
524 }
|
c@41
|
525
|
c@43
|
526 returnFeatures[m_distanceVectorOutput].push_back(distanceVectorFeature);
|
c@43
|
527
|
c@41
|
528 return returnFeatures;
|
c@41
|
529 }
|