c@38: /* -*- c-basic-offset: 4 indent-tabs-mode: nil -*- vi:set ts=8 sts=4 sw=4: */ c@38: c@36: /* c@38: * SegmenterPlugin.cpp c@36: * c@38: * Created by Mark Levy on 24/03/2006. c@38: * Copyright 2006 Centre for Digital Music, Queen Mary, University of London. c@135: c@135: This program is free software; you can redistribute it and/or c@135: modify it under the terms of the GNU General Public License as c@135: published by the Free Software Foundation; either version 2 of the c@135: License, or (at your option) any later version. See the file c@135: COPYING included with this distribution for more information. c@36: */ c@36: c@36: #include c@36: #include c@36: c@36: #include "SegmenterPlugin.h" c@37: #include "dsp/segmentation/ClusterMeltSegmenter.h" c@36: c@36: using std::string; c@36: using std::vector; c@36: using std::cerr; c@36: using std::endl; c@36: using std::ostringstream; c@36: c@36: SegmenterPlugin::SegmenterPlugin(float inputSampleRate) : c@38: Plugin(inputSampleRate), c@38: segmenter(0), c@178: hopsize(0), c@178: windowsize(0), c@178: neighbourhoodLimit(4), c@38: nSegmentTypes(10), c@38: featureType(feature_types(1)) c@36: { c@36: c@36: } c@36: c@36: SegmenterPlugin::~SegmenterPlugin() c@36: { c@38: delete segmenter; c@36: } c@36: c@45: std::string SegmenterPlugin::getIdentifier() const c@45: { c@45: return "qm-segmenter"; c@45: } c@45: c@45: std::string SegmenterPlugin::getName() const c@45: { c@45: return "Segmenter"; c@45: } c@45: c@45: std::string SegmenterPlugin::getDescription() const c@45: { c@45: return "Divide the track into a sequence of consistent segments"; c@45: } c@45: c@36: string c@36: SegmenterPlugin::getMaker() const c@36: { c@50: return "Queen Mary, University of London"; c@36: } c@36: c@36: int c@36: SegmenterPlugin::getPluginVersion() const c@36: { c@150: return 3; c@36: } c@36: c@36: string c@36: SegmenterPlugin::getCopyright() const c@36: { c@150: return "Plugin by Mark Levy. Copyright (c) 2006-2013 QMUL - All Rights Reserved"; c@36: } c@36: c@36: bool c@36: SegmenterPlugin::initialise(size_t channels, size_t stepSize, size_t blockSize) c@36: { c@36: if (channels < getMinChannelCount() || c@36: channels > getMaxChannelCount()) return false; c@36: c@36: if (!segmenter) makeSegmenter(); c@36: c@178: if (int(stepSize) != hopsize) { c@39: std::cerr << "SegmenterPlugin::initialise: supplied step size " c@39: << stepSize << " differs from required step size " << hopsize c@39: << std::endl; c@39: return false; c@39: } c@39: c@178: if (int(blockSize) != windowsize) { c@39: std::cerr << "SegmenterPlugin::initialise: supplied block size " c@39: << blockSize << " differs from required block size " << windowsize c@39: << std::endl; c@39: return false; c@39: } c@36: c@36: return true; c@36: } c@36: c@36: void c@36: SegmenterPlugin::reset() c@36: { c@57: // re-make segmenter only if it has already been made; otherwise c@57: // there's nothing to reset c@57: if (segmenter) makeSegmenter(); c@36: } c@36: c@36: size_t c@36: SegmenterPlugin::getPreferredStepSize() const c@36: { c@38: if (!segmenter) makeSegmenter(); c@38: return hopsize; c@36: } c@36: c@36: size_t c@36: SegmenterPlugin::getPreferredBlockSize() const c@36: { c@38: if (!segmenter) makeSegmenter(); c@38: return windowsize; c@36: } c@36: c@36: SegmenterPlugin::ParameterList SegmenterPlugin::getParameterDescriptors() const c@36: { c@36: ParameterList list; c@36: c@36: ParameterDescriptor desc; c@36: desc.identifier = "nSegmentTypes"; c@36: desc.name = "Number of segment-types"; c@36: desc.description = "Maximum number of different kinds of segment to find"; c@36: desc.unit = ""; c@36: desc.minValue = 2; c@36: desc.maxValue = 12; c@36: desc.defaultValue = 10; c@36: desc.isQuantized = true; c@36: desc.quantizeStep = 1; c@36: list.push_back(desc); c@36: c@36: ParameterDescriptor desc2; c@36: desc2.identifier = "featureType"; c@36: desc2.name = "Feature Type"; c@39: desc2.description = "Try Chromatic for acoustic or pre-1980 recordings, otherwise use Hybrid"; c@36: desc2.unit = ""; c@36: desc2.minValue = 1; c@39: desc2.maxValue = 3; c@36: desc2.defaultValue = 1; c@36: desc2.isQuantized = true; c@36: desc2.quantizeStep = 1; c@39: desc2.valueNames.push_back("Hybrid (Constant-Q)"); c@39: desc2.valueNames.push_back("Chromatic (Chroma)"); c@39: desc2.valueNames.push_back("Timbral (MFCC)"); c@36: list.push_back(desc2); c@36: c@49: ParameterDescriptor desc3; c@49: desc3.identifier = "neighbourhoodLimit"; c@49: desc3.name = "Minimum segment duration"; c@49: desc3.description = "Approximate expected minimum duration for each segment"; c@49: desc3.unit = "s"; c@49: desc3.minValue = 1; c@49: desc3.maxValue = 15; c@49: desc3.defaultValue = 4; c@49: desc3.isQuantized = true; c@49: desc3.quantizeStep = 0.2; c@49: list.push_back(desc3); c@49: c@36: return list; c@36: } c@36: c@36: float c@36: SegmenterPlugin::getParameter(std::string param) const c@36: { c@36: if (param == "nSegmentTypes") { c@36: return nSegmentTypes; c@36: } c@36: c@38: if (param == "featureType") { c@38: return featureType; c@38: } c@49: c@49: if (param == "neighbourhoodLimit") { c@49: return neighbourhoodLimit; c@49: } c@36: c@38: std::cerr << "WARNING: SegmenterPlugin::getParameter: unknown parameter \"" c@38: << param << "\"" << std::endl; c@36: return 0.0; c@36: } c@36: c@36: void c@36: SegmenterPlugin::setParameter(std::string param, float value) c@36: { c@36: if (param == "nSegmentTypes") { c@38: c@42: nSegmentTypes = int(value + 0.0001); c@49: return; c@49: } c@38: c@49: if (param == "featureType") { c@190: int nval = int(value + 0.5); c@190: if (featureType != feature_types(nval)) { // feature type changed, create a new segmenter c@190: featureType = feature_types(nval); c@49: makeSegmenter(); c@49: } c@49: return; c@49: } c@38: c@49: if (param == "neighbourhoodLimit") { c@49: if (neighbourhoodLimit != value) { c@49: neighbourhoodLimit = value; c@49: makeSegmenter(); c@38: } c@49: return; c@38: } c@49: c@49: std::cerr << "WARNING: SegmenterPlugin::setParameter: unknown parameter \"" c@49: << param << "\"" << std::endl; c@36: } c@36: c@36: void c@36: SegmenterPlugin::makeSegmenter() const c@36: { c@38: ClusterMeltSegmenterParams params = ClusterMeltSegmenterParams(); c@38: params.featureType = (feature_types) featureType; c@38: c@38: if (params.featureType == FEATURE_TYPE_CONSTQ) c@38: { c@38: params.ncomponents = 20; c@38: } c@38: if (params.featureType == FEATURE_TYPE_CHROMA) c@38: { c@38: params.hopSize = 0.1; c@38: params.windowSize = 0.372; c@38: params.nbins = 12; c@38: params.histogramLength = 20; c@38: } c@39: if (params.featureType == FEATURE_TYPE_MFCC) c@39: { c@39: params.ncomponents = 20; c@39: } c@38: delete segmenter; c@38: c@49: params.neighbourhoodLimit = c@49: int(neighbourhoodLimit / params.hopSize + 0.0001); c@49: c@38: segmenter = new ClusterMeltSegmenter(params); c@38: segmenter->initialise(m_inputSampleRate); c@38: hopsize = segmenter->getHopsize(); c@38: windowsize = segmenter->getWindowsize(); c@38: c@45: // std::cerr << "segmenter window size: " << segmenter->getWindowsize() c@45: // << std::endl; c@36: } c@36: c@36: SegmenterPlugin::OutputList c@36: SegmenterPlugin::getOutputDescriptors() const c@36: { c@36: OutputList list; c@36: c@38: OutputDescriptor segmentation; c@38: segmentation.identifier = "segmentation"; c@36: segmentation.name = "Segmentation"; c@36: segmentation.description = "Segmentation"; c@36: segmentation.unit = "segment-type"; c@36: segmentation.hasFixedBinCount = true; c@36: segmentation.binCount = 1; c@40: segmentation.hasKnownExtents = true; c@38: segmentation.minValue = 1; c@38: segmentation.maxValue = nSegmentTypes; c@38: segmentation.isQuantized = true; c@38: segmentation.quantizeStep = 1; c@36: segmentation.sampleType = OutputDescriptor::VariableSampleRate; c@36: segmentation.sampleRate = m_inputSampleRate / getPreferredStepSize(); c@150: segmentation.hasDuration = true; c@36: c@36: list.push_back(segmentation); c@36: c@38: return list; c@36: } c@36: c@36: SegmenterPlugin::FeatureSet c@150: SegmenterPlugin::process(const float *const *inputBuffers, Vamp::RealTime timestamp) c@36: { c@36: // convert float* to double* c@36: double *tempBuffer = new double[windowsize]; c@178: for (int i = 0; i < windowsize; ++i) { c@38: tempBuffer[i] = inputBuffers[0][i]; c@36: } c@38: c@38: segmenter->extractFeatures(tempBuffer, segmenter->getWindowsize()); c@38: c@38: delete [] tempBuffer; c@150: c@150: m_endTime = timestamp; c@36: c@38: return FeatureSet(); c@36: } c@36: c@36: SegmenterPlugin::FeatureSet c@36: SegmenterPlugin::getRemainingFeatures() c@36: { c@36: segmenter->segment(nSegmentTypes); c@38: Segmentation segm = segmenter->getSegmentation(); c@36: c@38: FeatureSet returnFeatures; c@150: c@150: // Map the segment types onto a dense series starting at 1 c@36: c@150: std::map typeMap; c@150: int nextType = 1; c@150: c@178: for (int i = 0; i < int(segm.segments.size()); ++i) { c@150: Segment s = segm.segments[i]; c@150: if (typeMap.find(s.type) == typeMap.end()) { c@150: typeMap[s.type] = nextType; c@150: ++nextType; c@150: } c@150: } c@150: c@178: for (int i = 0; i < int(segm.segments.size()); ++i) { c@36: c@38: Segment s = segm.segments[i]; c@36: c@38: Feature feature; c@38: feature.hasTimestamp = true; c@150: feature.timestamp = Vamp::RealTime::frame2RealTime c@150: (s.start, (int)m_inputSampleRate); c@150: feature.hasDuration = true; c@150: c@178: if (i + 1 < int(segm.segments.size())) { c@150: feature.duration = Vamp::RealTime::frame2RealTime c@150: (segm.segments[i+1].start - s.start, (int)m_inputSampleRate); c@150: } else { c@150: feature.duration = m_endTime - feature.timestamp; c@150: } c@150: c@150: int type = typeMap[s.type]; c@36: c@38: vector floatval; c@150: floatval.push_back(type); c@38: feature.values = floatval; c@36: c@38: ostringstream oss; c@150: oss << char('A' + type - 1); c@38: feature.label = oss.str(); c@36: c@38: returnFeatures[0].push_back(feature); c@36: } c@36: c@36: return returnFeatures; c@36: }