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