1 /* -*- c-basic-offset: 4 indent-tabs-mode: nil -*- vi:set ts=8 sts=4 sw=4: */
4 * ClusterMeltSegmenter.cpp
6 * Created by Mark Levy on 23/03/2006.
7 * Copyright 2006 Centre for Digital Music, Queen Mary, University of London.
9 This program is free software; you can redistribute it and/or
10 modify it under the terms of the GNU General Public License as
11 published by the Free Software Foundation; either version 2 of the
12 License, or (at your option) any later version. See the file
13 COPYING included with this distribution for more information.
19 #include "ClusterMeltSegmenter.h"
20 #include "cluster_segmenter.h"
23 #include "dsp/transforms/FFT.h"
24 #include "dsp/chromagram/ConstantQ.h"
25 #include "dsp/rateconversion/Decimator.h"
26 #include "dsp/mfcc/MFCC.h"
28 ClusterMeltSegmenter::ClusterMeltSegmenter(ClusterMeltSegmenterParams params) :
33 featureType(params.featureType),
34 hopSize(params.hopSize),
35 windowSize(params.windowSize),
39 ncomponents(params.ncomponents), // NB currently not passed - no. of PCA components is set in cluser_segmenter.c
40 nHMMStates(params.nHMMStates),
41 nclusters(params.nclusters),
42 histogramLength(params.histogramLength),
43 neighbourhoodLimit(params.neighbourhoodLimit),
48 void ClusterMeltSegmenter::initialise(int fs)
52 if (featureType == FEATURE_TYPE_CONSTQ ||
53 featureType == FEATURE_TYPE_CHROMA) {
55 // run internal processing at 11025 or thereabouts
56 int internalRate = 11025;
57 int decimationFactor = samplerate / internalRate;
58 if (decimationFactor < 1) decimationFactor = 1;
60 // must be a power of two
61 while (decimationFactor & (decimationFactor - 1)) ++decimationFactor;
63 if (decimationFactor > Decimator::getHighestSupportedFactor()) {
64 decimationFactor = Decimator::getHighestSupportedFactor();
67 if (decimationFactor > 1) {
68 decimator = new Decimator(getWindowsize(), decimationFactor);
72 config.FS = samplerate / decimationFactor;
76 config.CQThresh = 0.0054;
78 constq = new ConstantQ(config);
79 constq->sparsekernel();
81 ncoeff = constq->getK();
83 fft = new FFTReal(constq->getfftlength());
85 } else if (featureType == FEATURE_TYPE_MFCC) {
87 // run internal processing at 22050 or thereabouts
88 int internalRate = 22050;
89 int decimationFactor = samplerate / internalRate;
90 if (decimationFactor < 1) decimationFactor = 1;
92 // must be a power of two
93 while (decimationFactor & (decimationFactor - 1)) ++decimationFactor;
95 if (decimationFactor > Decimator::getHighestSupportedFactor()) {
96 decimationFactor = Decimator::getHighestSupportedFactor();
99 if (decimationFactor > 1) {
100 decimator = new Decimator(getWindowsize(), decimationFactor);
103 MFCCConfig config(samplerate / decimationFactor);
104 config.fftsize = 2048;
106 config.want_c0 = true;
108 mfcc = new MFCC(config);
109 ncoeff = config.nceps + 1;
113 ClusterMeltSegmenter::~ClusterMeltSegmenter()
122 ClusterMeltSegmenter::getWindowsize()
124 return static_cast<int>(windowSize * samplerate + 0.001);
128 ClusterMeltSegmenter::getHopsize()
130 return static_cast<int>(hopSize * samplerate + 0.001);
133 void ClusterMeltSegmenter::extractFeatures(const double* samples, int nsamples)
135 if (featureType == FEATURE_TYPE_CONSTQ ||
136 featureType == FEATURE_TYPE_CHROMA) {
137 extractFeaturesConstQ(samples, nsamples);
138 } else if (featureType == FEATURE_TYPE_MFCC) {
139 extractFeaturesMFCC(samples, nsamples);
143 void ClusterMeltSegmenter::extractFeaturesConstQ(const double* samples, int nsamples)
146 std::cerr << "ERROR: ClusterMeltSegmenter::extractFeaturesConstQ: "
147 << "No const-q: initialise not called?"
152 if (nsamples < getWindowsize()) {
153 std::cerr << "ERROR: ClusterMeltSegmenter::extractFeatures: nsamples < windowsize (" << nsamples << " < " << getWindowsize() << ")" << std::endl;
157 int fftsize = constq->getfftlength();
159 if (!window || window->getSize() != fftsize) {
161 window = new Window<double>(HammingWindow, fftsize);
164 vector<double> cq(ncoeff);
166 for (int i = 0; i < ncoeff; ++i) cq[i] = 0.0;
168 const double *psource = samples;
169 int pcount = nsamples;
172 pcount = nsamples / decimator->getFactor();
173 double *decout = new double[pcount];
174 decimator->process(samples, decout);
180 // std::cerr << "nsamples = " << nsamples << ", pcount = " << pcount << std::endl;
184 double *frame = new double[fftsize];
185 double *real = new double[fftsize];
186 double *imag = new double[fftsize];
187 double *cqre = new double[ncoeff];
188 double *cqim = new double[ncoeff];
190 while (origin <= pcount) {
192 // always need at least one fft window per block, but after
193 // that we want to avoid having any incomplete ones
194 if (origin > 0 && origin + fftsize >= pcount) break;
196 for (int i = 0; i < fftsize; ++i) {
197 if (origin + i < pcount) {
198 frame[i] = psource[origin + i];
204 for (int i = 0; i < fftsize/2; ++i) {
205 double value = frame[i];
206 frame[i] = frame[i + fftsize/2];
207 frame[i + fftsize/2] = value;
212 fft->forward(frame, real, imag);
214 constq->process(real, imag, cqre, cqim);
216 for (int i = 0; i < ncoeff; ++i) {
217 cq[i] += sqrt(cqre[i] * cqre[i] + cqim[i] * cqim[i]);
230 for (int i = 0; i < ncoeff; ++i) {
234 if (decimator) delete[] psource;
236 features.push_back(cq);
239 void ClusterMeltSegmenter::extractFeaturesMFCC(const double* samples, int nsamples)
242 std::cerr << "ERROR: ClusterMeltSegmenter::extractFeaturesMFCC: "
243 << "No mfcc: initialise not called?"
248 if (nsamples < getWindowsize()) {
249 std::cerr << "ERROR: ClusterMeltSegmenter::extractFeatures: nsamples < windowsize (" << nsamples << " < " << getWindowsize() << ")" << std::endl;
253 int fftsize = mfcc->getfftlength();
255 vector<double> cc(ncoeff);
257 for (int i = 0; i < ncoeff; ++i) cc[i] = 0.0;
259 const double *psource = samples;
260 int pcount = nsamples;
263 pcount = nsamples / decimator->getFactor();
264 double *decout = new double[pcount];
265 decimator->process(samples, decout);
272 double *frame = new double[fftsize];
273 double *ccout = new double[ncoeff];
275 while (origin <= pcount) {
277 // always need at least one fft window per block, but after
278 // that we want to avoid having any incomplete ones
279 if (origin > 0 && origin + fftsize >= pcount) break;
281 for (int i = 0; i < fftsize; ++i) {
282 if (origin + i < pcount) {
283 frame[i] = psource[origin + i];
289 mfcc->process(frame, ccout);
291 for (int i = 0; i < ncoeff; ++i) {
302 for (int i = 0; i < ncoeff; ++i) {
306 if (decimator) delete[] psource;
308 features.push_back(cc);
311 void ClusterMeltSegmenter::segment(int m)
317 void ClusterMeltSegmenter::setFeatures(const vector<vector<double> >& f)
320 featureType = FEATURE_TYPE_UNKNOWN;
323 void ClusterMeltSegmenter::segment()
332 if (features.size() < histogramLength) return;
334 std::cerr << "ClusterMeltSegmenter::segment: have " << features.size()
335 << " features with " << features[0].size() << " coefficients (ncoeff = " << ncoeff << ", ncomponents = " << ncomponents << ")" << std::endl;
337 // copy the features to a native array and use the existing C segmenter...
338 double** arrFeatures = new double*[features.size()];
339 for (int i = 0; i < features.size(); i++)
341 if (featureType == FEATURE_TYPE_UNKNOWN) {
342 arrFeatures[i] = new double[features[0].size()];
343 for (int j = 0; j < features[0].size(); j++)
344 arrFeatures[i][j] = features[i][j];
346 arrFeatures[i] = new double[ncoeff+1]; // allow space for the normalised envelope
347 for (int j = 0; j < ncoeff; j++)
348 arrFeatures[i][j] = features[i][j];
352 q = new int[features.size()];
354 if (featureType == FEATURE_TYPE_UNKNOWN ||
355 featureType == FEATURE_TYPE_MFCC)
356 cluster_segment(q, arrFeatures, features.size(), features[0].size(), nHMMStates, histogramLength,
357 nclusters, neighbourhoodLimit);
359 constq_segment(q, arrFeatures, features.size(), nbins, ncoeff, featureType,
360 nHMMStates, histogramLength, nclusters, neighbourhoodLimit);
362 // convert the cluster assignment sequence to a segmentation
363 makeSegmentation(q, features.size());
365 // de-allocate arrays
367 for (int i = 0; i < features.size(); i++)
368 delete [] arrFeatures[i];
369 delete [] arrFeatures;
371 // clear the features
375 void ClusterMeltSegmenter::makeSegmentation(int* q, int len)
377 segmentation.segments.clear();
378 segmentation.nsegtypes = nclusters;
379 segmentation.samplerate = samplerate;
385 for (int i = 1; i < len; i++)
389 segment.end = i * getHopsize();
390 segmentation.segments.push_back(segment);
392 segment.start = segment.end;
395 segment.end = len * getHopsize();
396 segmentation.segments.push_back(segment);