c@38
|
1 /* -*- c-basic-offset: 4 indent-tabs-mode: nil -*- vi:set ts=8 sts=4 sw=4: */
|
c@38
|
2
|
c@36
|
3 /*
|
c@38
|
4 * SegmenterPlugin.cpp
|
c@36
|
5 *
|
c@38
|
6 * Created by Mark Levy on 24/03/2006.
|
c@38
|
7 * Copyright 2006 Centre for Digital Music, Queen Mary, University of London.
|
c@135
|
8
|
c@135
|
9 This program is free software; you can redistribute it and/or
|
c@135
|
10 modify it under the terms of the GNU General Public License as
|
c@135
|
11 published by the Free Software Foundation; either version 2 of the
|
c@135
|
12 License, or (at your option) any later version. See the file
|
c@135
|
13 COPYING included with this distribution for more information.
|
c@36
|
14 */
|
c@36
|
15
|
c@36
|
16 #include <iostream>
|
c@36
|
17 #include <sstream>
|
c@36
|
18
|
c@36
|
19 #include "SegmenterPlugin.h"
|
c@37
|
20 #include "dsp/segmentation/ClusterMeltSegmenter.h"
|
c@36
|
21
|
c@36
|
22 using std::string;
|
c@36
|
23 using std::vector;
|
c@36
|
24 using std::cerr;
|
c@36
|
25 using std::endl;
|
c@36
|
26 using std::ostringstream;
|
c@36
|
27
|
c@36
|
28 SegmenterPlugin::SegmenterPlugin(float inputSampleRate) :
|
c@38
|
29 Plugin(inputSampleRate),
|
c@38
|
30 segmenter(0),
|
c@178
|
31 hopsize(0),
|
c@178
|
32 windowsize(0),
|
c@178
|
33 neighbourhoodLimit(4),
|
c@38
|
34 nSegmentTypes(10),
|
c@38
|
35 featureType(feature_types(1))
|
c@36
|
36 {
|
c@36
|
37
|
c@36
|
38 }
|
c@36
|
39
|
c@36
|
40 SegmenterPlugin::~SegmenterPlugin()
|
c@36
|
41 {
|
c@38
|
42 delete segmenter;
|
c@36
|
43 }
|
c@36
|
44
|
c@45
|
45 std::string SegmenterPlugin::getIdentifier() const
|
c@45
|
46 {
|
c@45
|
47 return "qm-segmenter";
|
c@45
|
48 }
|
c@45
|
49
|
c@45
|
50 std::string SegmenterPlugin::getName() const
|
c@45
|
51 {
|
c@45
|
52 return "Segmenter";
|
c@45
|
53 }
|
c@45
|
54
|
c@45
|
55 std::string SegmenterPlugin::getDescription() const
|
c@45
|
56 {
|
c@45
|
57 return "Divide the track into a sequence of consistent segments";
|
c@45
|
58 }
|
c@45
|
59
|
c@36
|
60 string
|
c@36
|
61 SegmenterPlugin::getMaker() const
|
c@36
|
62 {
|
c@50
|
63 return "Queen Mary, University of London";
|
c@36
|
64 }
|
c@36
|
65
|
c@36
|
66 int
|
c@36
|
67 SegmenterPlugin::getPluginVersion() const
|
c@36
|
68 {
|
c@150
|
69 return 3;
|
c@36
|
70 }
|
c@36
|
71
|
c@36
|
72 string
|
c@36
|
73 SegmenterPlugin::getCopyright() const
|
c@36
|
74 {
|
c@150
|
75 return "Plugin by Mark Levy. Copyright (c) 2006-2013 QMUL - All Rights Reserved";
|
c@36
|
76 }
|
c@36
|
77
|
c@36
|
78 bool
|
c@36
|
79 SegmenterPlugin::initialise(size_t channels, size_t stepSize, size_t blockSize)
|
c@36
|
80 {
|
c@36
|
81 if (channels < getMinChannelCount() ||
|
c@36
|
82 channels > getMaxChannelCount()) return false;
|
c@36
|
83
|
c@36
|
84 if (!segmenter) makeSegmenter();
|
c@36
|
85
|
c@178
|
86 if (int(stepSize) != hopsize) {
|
c@39
|
87 std::cerr << "SegmenterPlugin::initialise: supplied step size "
|
c@39
|
88 << stepSize << " differs from required step size " << hopsize
|
c@39
|
89 << std::endl;
|
c@39
|
90 return false;
|
c@39
|
91 }
|
c@39
|
92
|
c@178
|
93 if (int(blockSize) != windowsize) {
|
c@39
|
94 std::cerr << "SegmenterPlugin::initialise: supplied block size "
|
c@39
|
95 << blockSize << " differs from required block size " << windowsize
|
c@39
|
96 << std::endl;
|
c@39
|
97 return false;
|
c@39
|
98 }
|
c@36
|
99
|
c@36
|
100 return true;
|
c@36
|
101 }
|
c@36
|
102
|
c@36
|
103 void
|
c@36
|
104 SegmenterPlugin::reset()
|
c@36
|
105 {
|
c@57
|
106 // re-make segmenter only if it has already been made; otherwise
|
c@57
|
107 // there's nothing to reset
|
c@57
|
108 if (segmenter) makeSegmenter();
|
c@36
|
109 }
|
c@36
|
110
|
c@36
|
111 size_t
|
c@36
|
112 SegmenterPlugin::getPreferredStepSize() const
|
c@36
|
113 {
|
c@38
|
114 if (!segmenter) makeSegmenter();
|
c@38
|
115 return hopsize;
|
c@36
|
116 }
|
c@36
|
117
|
c@36
|
118 size_t
|
c@36
|
119 SegmenterPlugin::getPreferredBlockSize() const
|
c@36
|
120 {
|
c@38
|
121 if (!segmenter) makeSegmenter();
|
c@38
|
122 return windowsize;
|
c@36
|
123 }
|
c@36
|
124
|
c@36
|
125 SegmenterPlugin::ParameterList SegmenterPlugin::getParameterDescriptors() const
|
c@36
|
126 {
|
c@36
|
127 ParameterList list;
|
c@36
|
128
|
c@36
|
129 ParameterDescriptor desc;
|
c@36
|
130 desc.identifier = "nSegmentTypes";
|
c@36
|
131 desc.name = "Number of segment-types";
|
c@36
|
132 desc.description = "Maximum number of different kinds of segment to find";
|
c@36
|
133 desc.unit = "";
|
c@36
|
134 desc.minValue = 2;
|
c@36
|
135 desc.maxValue = 12;
|
c@36
|
136 desc.defaultValue = 10;
|
c@36
|
137 desc.isQuantized = true;
|
c@36
|
138 desc.quantizeStep = 1;
|
c@36
|
139 list.push_back(desc);
|
c@36
|
140
|
c@36
|
141 ParameterDescriptor desc2;
|
c@36
|
142 desc2.identifier = "featureType";
|
c@36
|
143 desc2.name = "Feature Type";
|
c@39
|
144 desc2.description = "Try Chromatic for acoustic or pre-1980 recordings, otherwise use Hybrid";
|
c@36
|
145 desc2.unit = "";
|
c@36
|
146 desc2.minValue = 1;
|
c@39
|
147 desc2.maxValue = 3;
|
c@36
|
148 desc2.defaultValue = 1;
|
c@36
|
149 desc2.isQuantized = true;
|
c@36
|
150 desc2.quantizeStep = 1;
|
c@39
|
151 desc2.valueNames.push_back("Hybrid (Constant-Q)");
|
c@39
|
152 desc2.valueNames.push_back("Chromatic (Chroma)");
|
c@39
|
153 desc2.valueNames.push_back("Timbral (MFCC)");
|
c@36
|
154 list.push_back(desc2);
|
c@36
|
155
|
c@49
|
156 ParameterDescriptor desc3;
|
c@49
|
157 desc3.identifier = "neighbourhoodLimit";
|
c@49
|
158 desc3.name = "Minimum segment duration";
|
c@49
|
159 desc3.description = "Approximate expected minimum duration for each segment";
|
c@49
|
160 desc3.unit = "s";
|
c@49
|
161 desc3.minValue = 1;
|
c@49
|
162 desc3.maxValue = 15;
|
c@49
|
163 desc3.defaultValue = 4;
|
c@49
|
164 desc3.isQuantized = true;
|
c@49
|
165 desc3.quantizeStep = 0.2;
|
c@49
|
166 list.push_back(desc3);
|
c@49
|
167
|
c@36
|
168 return list;
|
c@36
|
169 }
|
c@36
|
170
|
c@36
|
171 float
|
c@36
|
172 SegmenterPlugin::getParameter(std::string param) const
|
c@36
|
173 {
|
c@36
|
174 if (param == "nSegmentTypes") {
|
c@36
|
175 return nSegmentTypes;
|
c@36
|
176 }
|
c@36
|
177
|
c@38
|
178 if (param == "featureType") {
|
c@38
|
179 return featureType;
|
c@38
|
180 }
|
c@49
|
181
|
c@49
|
182 if (param == "neighbourhoodLimit") {
|
c@49
|
183 return neighbourhoodLimit;
|
c@49
|
184 }
|
c@36
|
185
|
c@38
|
186 std::cerr << "WARNING: SegmenterPlugin::getParameter: unknown parameter \""
|
c@38
|
187 << param << "\"" << std::endl;
|
c@36
|
188 return 0.0;
|
c@36
|
189 }
|
c@36
|
190
|
c@36
|
191 void
|
c@36
|
192 SegmenterPlugin::setParameter(std::string param, float value)
|
c@36
|
193 {
|
c@36
|
194 if (param == "nSegmentTypes") {
|
c@38
|
195
|
c@42
|
196 nSegmentTypes = int(value + 0.0001);
|
c@49
|
197 return;
|
c@49
|
198 }
|
c@38
|
199
|
c@49
|
200 if (param == "featureType") {
|
c@190
|
201 int nval = int(value + 0.5);
|
c@190
|
202 if (featureType != feature_types(nval)) { // feature type changed, create a new segmenter
|
c@190
|
203 featureType = feature_types(nval);
|
c@49
|
204 makeSegmenter();
|
c@49
|
205 }
|
c@49
|
206 return;
|
c@49
|
207 }
|
c@38
|
208
|
c@49
|
209 if (param == "neighbourhoodLimit") {
|
c@49
|
210 if (neighbourhoodLimit != value) {
|
c@49
|
211 neighbourhoodLimit = value;
|
c@49
|
212 makeSegmenter();
|
c@38
|
213 }
|
c@49
|
214 return;
|
c@38
|
215 }
|
c@49
|
216
|
c@49
|
217 std::cerr << "WARNING: SegmenterPlugin::setParameter: unknown parameter \""
|
c@49
|
218 << param << "\"" << std::endl;
|
c@36
|
219 }
|
c@36
|
220
|
c@36
|
221 void
|
c@36
|
222 SegmenterPlugin::makeSegmenter() const
|
c@36
|
223 {
|
c@38
|
224 ClusterMeltSegmenterParams params = ClusterMeltSegmenterParams();
|
c@38
|
225 params.featureType = (feature_types) featureType;
|
c@38
|
226
|
c@38
|
227 if (params.featureType == FEATURE_TYPE_CONSTQ)
|
c@38
|
228 {
|
c@38
|
229 params.ncomponents = 20;
|
c@38
|
230 }
|
c@38
|
231 if (params.featureType == FEATURE_TYPE_CHROMA)
|
c@38
|
232 {
|
c@38
|
233 params.hopSize = 0.1;
|
c@38
|
234 params.windowSize = 0.372;
|
c@38
|
235 params.nbins = 12;
|
c@38
|
236 params.histogramLength = 20;
|
c@38
|
237 }
|
c@39
|
238 if (params.featureType == FEATURE_TYPE_MFCC)
|
c@39
|
239 {
|
c@39
|
240 params.ncomponents = 20;
|
c@39
|
241 }
|
c@38
|
242 delete segmenter;
|
c@38
|
243
|
c@49
|
244 params.neighbourhoodLimit =
|
c@49
|
245 int(neighbourhoodLimit / params.hopSize + 0.0001);
|
c@49
|
246
|
c@38
|
247 segmenter = new ClusterMeltSegmenter(params);
|
c@38
|
248 segmenter->initialise(m_inputSampleRate);
|
c@38
|
249 hopsize = segmenter->getHopsize();
|
c@38
|
250 windowsize = segmenter->getWindowsize();
|
c@38
|
251
|
c@45
|
252 // std::cerr << "segmenter window size: " << segmenter->getWindowsize()
|
c@45
|
253 // << std::endl;
|
c@36
|
254 }
|
c@36
|
255
|
c@36
|
256 SegmenterPlugin::OutputList
|
c@36
|
257 SegmenterPlugin::getOutputDescriptors() const
|
c@36
|
258 {
|
c@36
|
259 OutputList list;
|
c@36
|
260
|
c@38
|
261 OutputDescriptor segmentation;
|
c@38
|
262 segmentation.identifier = "segmentation";
|
c@36
|
263 segmentation.name = "Segmentation";
|
c@36
|
264 segmentation.description = "Segmentation";
|
c@36
|
265 segmentation.unit = "segment-type";
|
c@36
|
266 segmentation.hasFixedBinCount = true;
|
c@36
|
267 segmentation.binCount = 1;
|
c@40
|
268 segmentation.hasKnownExtents = true;
|
c@38
|
269 segmentation.minValue = 1;
|
c@38
|
270 segmentation.maxValue = nSegmentTypes;
|
c@38
|
271 segmentation.isQuantized = true;
|
c@38
|
272 segmentation.quantizeStep = 1;
|
c@36
|
273 segmentation.sampleType = OutputDescriptor::VariableSampleRate;
|
c@36
|
274 segmentation.sampleRate = m_inputSampleRate / getPreferredStepSize();
|
c@150
|
275 segmentation.hasDuration = true;
|
c@36
|
276
|
c@36
|
277 list.push_back(segmentation);
|
c@36
|
278
|
c@38
|
279 return list;
|
c@36
|
280 }
|
c@36
|
281
|
c@36
|
282 SegmenterPlugin::FeatureSet
|
c@150
|
283 SegmenterPlugin::process(const float *const *inputBuffers, Vamp::RealTime timestamp)
|
c@36
|
284 {
|
c@36
|
285 // convert float* to double*
|
c@36
|
286 double *tempBuffer = new double[windowsize];
|
c@178
|
287 for (int i = 0; i < windowsize; ++i) {
|
c@38
|
288 tempBuffer[i] = inputBuffers[0][i];
|
c@36
|
289 }
|
c@38
|
290
|
c@38
|
291 segmenter->extractFeatures(tempBuffer, segmenter->getWindowsize());
|
c@38
|
292
|
c@38
|
293 delete [] tempBuffer;
|
c@150
|
294
|
c@150
|
295 m_endTime = timestamp;
|
c@36
|
296
|
c@38
|
297 return FeatureSet();
|
c@36
|
298 }
|
c@36
|
299
|
c@36
|
300 SegmenterPlugin::FeatureSet
|
c@36
|
301 SegmenterPlugin::getRemainingFeatures()
|
c@36
|
302 {
|
c@36
|
303 segmenter->segment(nSegmentTypes);
|
c@38
|
304 Segmentation segm = segmenter->getSegmentation();
|
c@36
|
305
|
c@38
|
306 FeatureSet returnFeatures;
|
c@150
|
307
|
c@150
|
308 // Map the segment types onto a dense series starting at 1
|
c@36
|
309
|
c@150
|
310 std::map<int, int> typeMap;
|
c@150
|
311 int nextType = 1;
|
c@150
|
312
|
c@178
|
313 for (int i = 0; i < int(segm.segments.size()); ++i) {
|
c@150
|
314 Segment s = segm.segments[i];
|
c@150
|
315 if (typeMap.find(s.type) == typeMap.end()) {
|
c@150
|
316 typeMap[s.type] = nextType;
|
c@150
|
317 ++nextType;
|
c@150
|
318 }
|
c@150
|
319 }
|
c@150
|
320
|
c@178
|
321 for (int i = 0; i < int(segm.segments.size()); ++i) {
|
c@36
|
322
|
c@38
|
323 Segment s = segm.segments[i];
|
c@36
|
324
|
c@38
|
325 Feature feature;
|
c@38
|
326 feature.hasTimestamp = true;
|
c@150
|
327 feature.timestamp = Vamp::RealTime::frame2RealTime
|
c@150
|
328 (s.start, (int)m_inputSampleRate);
|
c@150
|
329 feature.hasDuration = true;
|
c@150
|
330
|
c@178
|
331 if (i + 1 < int(segm.segments.size())) {
|
c@150
|
332 feature.duration = Vamp::RealTime::frame2RealTime
|
c@150
|
333 (segm.segments[i+1].start - s.start, (int)m_inputSampleRate);
|
c@150
|
334 } else {
|
c@150
|
335 feature.duration = m_endTime - feature.timestamp;
|
c@150
|
336 }
|
c@150
|
337
|
c@150
|
338 int type = typeMap[s.type];
|
c@36
|
339
|
c@38
|
340 vector<float> floatval;
|
c@150
|
341 floatval.push_back(type);
|
c@38
|
342 feature.values = floatval;
|
c@36
|
343
|
c@38
|
344 ostringstream oss;
|
c@150
|
345 oss << char('A' + type - 1);
|
c@38
|
346 feature.label = oss.str();
|
c@36
|
347
|
c@38
|
348 returnFeatures[0].push_back(feature);
|
c@36
|
349 }
|
c@36
|
350
|
c@36
|
351 return returnFeatures;
|
c@36
|
352 }
|