annotate plugins/SegmenterPlugin.cpp @ 266:d04675d44928 tip master

Refer to SDK from Github
author Chris Cannam <cannam@all-day-breakfast.com>
date Wed, 02 Jun 2021 14:41:26 +0100
parents af6a5ba00a8f
children
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@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 }