annotate plugins/SegmenterPlugin.cpp @ 150:7c7881bbb6ca

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