annotate plugins/AdaptiveSpectrogram.cpp @ 106:35f2138c6891

* Update to new FFT api
author Chris Cannam <c.cannam@qmul.ac.uk>
date Wed, 13 May 2009 09:19:30 +0000
parents abbc482aaad2
children 0dd97d053053
rev   line source
c@92 1 /* -*- c-basic-offset: 4 indent-tabs-mode: nil -*- vi:set ts=8 sts=4 sw=4: */
c@92 2
c@92 3 /*
c@92 4 QM Vamp Plugin Set
c@92 5
c@92 6 Centre for Digital Music, Queen Mary, University of London.
c@92 7 All rights reserved.
c@92 8 */
c@92 9
c@92 10 #include "AdaptiveSpectrogram.h"
c@92 11
c@92 12 #include <cstdlib>
c@92 13 #include <cstring>
c@92 14
c@92 15 #include <iostream>
c@92 16
c@92 17 #include <dsp/transforms/FFT.h>
c@92 18
c@92 19 using std::string;
c@92 20 using std::vector;
c@92 21 using std::cerr;
c@92 22 using std::endl;
c@92 23
c@92 24 using Vamp::RealTime;
c@92 25
c@99 26 //#define DEBUG_VERBOSE 1
c@99 27
c@104 28 static const int cutThreadCount = 4;
c@104 29
c@92 30 AdaptiveSpectrogram::AdaptiveSpectrogram(float inputSampleRate) :
c@92 31 Plugin(inputSampleRate),
c@104 32 m_w(8),
c@104 33 m_n(3),
c@104 34 m_first(true)
c@92 35 {
c@92 36 }
c@92 37
c@92 38 AdaptiveSpectrogram::~AdaptiveSpectrogram()
c@92 39 {
c@104 40 for (int i = 0; i < m_cutThreads.size(); ++i) {
c@104 41 delete m_cutThreads[i];
c@104 42 }
c@104 43 m_cutThreads.clear();
c@105 44
c@106 45 for (FFTMap::iterator i = m_fftThreads.begin(); i != m_fftThreads.end(); ++i) {
c@106 46 delete i->second;
c@105 47 }
c@105 48 m_fftThreads.clear();
c@92 49 }
c@92 50
c@92 51 string
c@92 52 AdaptiveSpectrogram::getIdentifier() const
c@92 53 {
c@93 54 return "qm-adaptivespectrogram";
c@92 55 }
c@92 56
c@92 57 string
c@92 58 AdaptiveSpectrogram::getName() const
c@92 59 {
c@92 60 return "Adaptive Spectrogram";
c@92 61 }
c@92 62
c@92 63 string
c@92 64 AdaptiveSpectrogram::getDescription() const
c@92 65 {
c@92 66 return "Produce an adaptive spectrogram by adaptive selection from spectrograms at multiple resolutions";
c@92 67 }
c@92 68
c@92 69 string
c@92 70 AdaptiveSpectrogram::getMaker() const
c@92 71 {
c@92 72 return "Queen Mary, University of London";
c@92 73 }
c@92 74
c@92 75 int
c@92 76 AdaptiveSpectrogram::getPluginVersion() const
c@92 77 {
c@92 78 return 1;
c@92 79 }
c@92 80
c@92 81 string
c@92 82 AdaptiveSpectrogram::getCopyright() const
c@92 83 {
c@92 84 return "Plugin by Wen Xue and Chris Cannam. Copyright (c) 2009 Wen Xue and QMUL - All Rights Reserved";
c@92 85 }
c@92 86
c@92 87 size_t
c@92 88 AdaptiveSpectrogram::getPreferredStepSize() const
c@92 89 {
c@92 90 return ((2 << m_w) << m_n) / 2;
c@92 91 }
c@92 92
c@92 93 size_t
c@92 94 AdaptiveSpectrogram::getPreferredBlockSize() const
c@92 95 {
c@92 96 return (2 << m_w) << m_n;
c@92 97 }
c@92 98
c@92 99 bool
c@92 100 AdaptiveSpectrogram::initialise(size_t channels, size_t stepSize, size_t blockSize)
c@92 101 {
c@92 102 if (channels < getMinChannelCount() ||
c@92 103 channels > getMaxChannelCount()) return false;
c@92 104
c@92 105 return true;
c@92 106 }
c@92 107
c@92 108 void
c@92 109 AdaptiveSpectrogram::reset()
c@92 110 {
c@92 111
c@92 112 }
c@92 113
c@92 114 AdaptiveSpectrogram::ParameterList
c@92 115 AdaptiveSpectrogram::getParameterDescriptors() const
c@92 116 {
c@92 117 ParameterList list;
c@92 118
c@92 119 ParameterDescriptor desc;
c@92 120 desc.identifier = "n";
c@92 121 desc.name = "Number of resolutions";
c@92 122 desc.description = "Number of consecutive powers of two to use as spectrogram resolutions, starting with the minimum resolution specified";
c@92 123 desc.unit = "";
c@92 124 desc.minValue = 1;
c@92 125 desc.maxValue = 10;
c@104 126 desc.defaultValue = 4;
c@92 127 desc.isQuantized = true;
c@92 128 desc.quantizeStep = 1;
c@92 129 list.push_back(desc);
c@92 130
c@92 131 ParameterDescriptor desc2;
c@92 132 desc2.identifier = "w";
c@92 133 desc2.name = "Smallest resolution";
c@92 134 desc2.description = "Smallest of the consecutive powers of two to use as spectrogram resolutions";
c@92 135 desc2.unit = "";
c@92 136 desc2.minValue = 1;
c@92 137 desc2.maxValue = 14;
c@104 138 desc2.defaultValue = 9;
c@92 139 desc2.isQuantized = true;
c@92 140 desc2.quantizeStep = 1;
c@92 141 // I am so lazy
c@92 142 desc2.valueNames.push_back("2");
c@92 143 desc2.valueNames.push_back("4");
c@92 144 desc2.valueNames.push_back("8");
c@92 145 desc2.valueNames.push_back("16");
c@92 146 desc2.valueNames.push_back("32");
c@92 147 desc2.valueNames.push_back("64");
c@92 148 desc2.valueNames.push_back("128");
c@92 149 desc2.valueNames.push_back("256");
c@92 150 desc2.valueNames.push_back("512");
c@92 151 desc2.valueNames.push_back("1024");
c@92 152 desc2.valueNames.push_back("2048");
c@92 153 desc2.valueNames.push_back("4096");
c@92 154 desc2.valueNames.push_back("8192");
c@92 155 desc2.valueNames.push_back("16384");
c@92 156 list.push_back(desc2);
c@92 157
c@92 158 return list;
c@92 159 }
c@92 160
c@92 161 float
c@92 162 AdaptiveSpectrogram::getParameter(std::string id) const
c@92 163 {
c@92 164 if (id == "n") return m_n+1;
c@92 165 else if (id == "w") return m_w+1;
c@92 166 return 0.f;
c@92 167 }
c@92 168
c@92 169 void
c@92 170 AdaptiveSpectrogram::setParameter(std::string id, float value)
c@92 171 {
c@92 172 if (id == "n") {
c@92 173 int n = lrintf(value);
c@92 174 if (n >= 1 && n <= 10) m_n = n-1;
c@92 175 } else if (id == "w") {
c@92 176 int w = lrintf(value);
c@92 177 if (w >= 1 && w <= 14) m_w = w-1;
c@92 178 }
c@92 179 }
c@92 180
c@92 181 AdaptiveSpectrogram::OutputList
c@92 182 AdaptiveSpectrogram::getOutputDescriptors() const
c@92 183 {
c@92 184 OutputList list;
c@92 185
c@92 186 OutputDescriptor d;
c@92 187 d.identifier = "output";
c@92 188 d.name = "Output";
c@92 189 d.description = "The output of the plugin";
c@92 190 d.unit = "";
c@92 191 d.hasFixedBinCount = true;
c@92 192 d.binCount = ((2 << m_w) << m_n) / 2;
c@92 193 d.hasKnownExtents = false;
c@92 194 d.isQuantized = false;
c@92 195 d.sampleType = OutputDescriptor::FixedSampleRate;
c@92 196 d.sampleRate = m_inputSampleRate / ((2 << m_w) / 2);
c@92 197 d.hasDuration = false;
c@92 198 list.push_back(d);
c@92 199
c@92 200 return list;
c@92 201 }
c@92 202
c@92 203 AdaptiveSpectrogram::FeatureSet
c@92 204 AdaptiveSpectrogram::getRemainingFeatures()
c@92 205 {
c@92 206 FeatureSet fs;
c@92 207 return fs;
c@92 208 }
c@92 209
c@100 210 AdaptiveSpectrogram::FeatureSet
c@100 211 AdaptiveSpectrogram::process(const float *const *inputBuffers, RealTime ts)
c@100 212 {
c@100 213 FeatureSet fs;
c@100 214
c@100 215 int minwid = (2 << m_w), maxwid = ((2 << m_w) << m_n);
c@100 216
c@101 217 #ifdef DEBUG_VERBOSE
c@100 218 cerr << "widths from " << minwid << " to " << maxwid << " ("
c@100 219 << minwid/2 << " to " << maxwid/2 << " in real parts)" << endl;
c@101 220 #endif
c@100 221
c@100 222 Spectrograms s(minwid/2, maxwid/2, 1);
c@100 223
c@100 224 int w = minwid;
c@100 225 int index = 0;
c@100 226
c@100 227 while (w <= maxwid) {
c@106 228 if (m_fftThreads.find(w) == m_fftThreads.end()) {
c@106 229 m_fftThreads[w] = new FFTThread(w);
c@106 230 }
c@106 231 m_fftThreads[w]->calculate(inputBuffers[0], s, index, maxwid);
c@100 232 w *= 2;
c@100 233 ++index;
c@100 234 }
c@100 235
c@105 236 w = minwid;
c@105 237 while (w <= maxwid) {
c@106 238 m_fftThreads[w]->await();
c@105 239 w *= 2;
c@105 240 }
c@102 241
c@104 242 m_first = true;//!!!
c@104 243
c@100 244 Cutting *cutting = cut(s, maxwid/2, 0, 0, maxwid/2);
c@100 245
c@101 246 #ifdef DEBUG_VERBOSE
c@100 247 printCutting(cutting, " ");
c@101 248 #endif
c@100 249
c@100 250 vector<vector<float> > rmat(maxwid/minwid);
c@100 251 for (int i = 0; i < maxwid/minwid; ++i) {
c@100 252 rmat[i] = vector<float>(maxwid/2);
c@100 253 }
c@100 254
c@100 255 assemble(s, cutting, rmat, 0, 0, maxwid/minwid, maxwid/2);
c@100 256
c@100 257 delete cutting;
c@100 258
c@100 259 for (int i = 0; i < rmat.size(); ++i) {
c@100 260 Feature f;
c@100 261 f.hasTimestamp = false;
c@100 262 f.values = rmat[i];
c@100 263 fs[0].push_back(f);
c@100 264 }
c@100 265
c@104 266 // std::cerr << "process returning!\n" << std::endl;
c@104 267
c@100 268 return fs;
c@100 269 }
c@100 270
c@100 271 void
c@104 272 AdaptiveSpectrogram::printCutting(Cutting *c, string pfx) const
c@100 273 {
c@100 274 if (c->first) {
c@100 275 if (c->cut == Cutting::Horizontal) {
c@100 276 cerr << pfx << "H" << endl;
c@100 277 } else if (c->cut == Cutting::Vertical) {
c@100 278 cerr << pfx << "V" << endl;
c@100 279 }
c@100 280 printCutting(c->first, pfx + " ");
c@100 281 printCutting(c->second, pfx + " ");
c@100 282 } else {
c@100 283 cerr << pfx << "* " << c->value << endl;
c@100 284 }
c@100 285 }
c@100 286
c@104 287 void
c@104 288 AdaptiveSpectrogram::getSubCuts(const Spectrograms &s,
c@104 289 int res,
c@104 290 int x, int y, int h,
c@104 291 Cutting *&top, Cutting *&bottom,
c@104 292 Cutting *&left, Cutting *&right) const
c@104 293 {
c@104 294 if (m_first) {//!!!
c@104 295
c@104 296 m_first = false;
c@104 297
c@104 298 if (m_cutThreads.empty()) {
c@104 299 for (int i = 0; i < 4; ++i) {
c@104 300 // for (int i = 0; i < 1; ++i) {
c@104 301 CutThread *t = new CutThread(this);
c@105 302 // t->start();
c@104 303 m_cutThreads.push_back(t);
c@104 304 }
c@104 305 // sleep(1); //!!!
c@104 306 }
c@104 307
c@104 308 // int threadIndices[4];
c@104 309 // int found = 0;
c@104 310 // for (int i = 0; i < m_cutThreads.size(); ++i) {
c@104 311 // if (!m_cutThreads[i]->busy()) {
c@104 312 // threadIndices[found] = i;
c@104 313 // if (++found == 4) break;
c@104 314 // }
c@104 315 // }
c@104 316
c@104 317 // if (found == 4) {
c@104 318
c@104 319 // enough threads available; use them. Need to avoid threads calling back on cut() in this class before we have made all of our threads busy (otherwise the recursive call is likely to claim threads further down our threadIndices before we do) -- hence m_threadMutex
c@104 320
c@104 321 //!!! no, thread mutex not a good way, need a claim() call on each thread or something
c@104 322
c@104 323 // m_threadMutex.lock();
c@104 324 m_cutThreads[0]->cut(s, res, x, y + h/2, h/2); // top
c@104 325 m_cutThreads[1]->cut(s, res, x, y, h/2); // bottom
c@104 326 m_cutThreads[2]->cut(s, res/2, 2 * x, y/2, h/2); // left
c@104 327 m_cutThreads[3]->cut(s, res/2, 2 * x + 1, y/2, h/2); // right
c@104 328
c@104 329 // std::cerr << "set up all four" << std::endl;
c@104 330
c@104 331 top = m_cutThreads[0]->get();
c@104 332 bottom = m_cutThreads[1]->get();
c@104 333 left = m_cutThreads[2]->get();
c@104 334 right = m_cutThreads[3]->get();
c@104 335 /*
c@104 336 bottom = cut(s, res, x, y, h/2);
c@104 337
c@104 338 // The "horizontal" division is a left/right split. Splitting
c@104 339 // this way places us in resolution res/2, which has lower
c@104 340 // vertical resolution but higher horizontal resolution. We
c@104 341 // need to double x accordingly.
c@104 342
c@104 343 left = cut(s, res/2, 2 * x, y/2, h/2);
c@104 344 right = cut(s, res/2, 2 * x + 1, y/2, h/2);
c@104 345 */
c@104 346 // std::cerr << "got all four" << std::endl;
c@104 347
c@104 348 } else {
c@104 349
c@104 350 // unthreaded
c@104 351
c@104 352 // The "vertical" division is a top/bottom split.
c@104 353 // Splitting this way keeps us in the same resolution,
c@104 354 // but with two vertical subregions of height h/2.
c@104 355
c@104 356 top = cut(s, res, x, y + h/2, h/2);
c@104 357 bottom = cut(s, res, x, y, h/2);
c@104 358
c@104 359 // The "horizontal" division is a left/right split. Splitting
c@104 360 // this way places us in resolution res/2, which has lower
c@104 361 // vertical resolution but higher horizontal resolution. We
c@104 362 // need to double x accordingly.
c@104 363
c@104 364 left = cut(s, res/2, 2 * x, y/2, h/2);
c@104 365 right = cut(s, res/2, 2 * x + 1, y/2, h/2);
c@104 366 }
c@104 367 }
c@104 368
c@100 369 AdaptiveSpectrogram::Cutting *
c@100 370 AdaptiveSpectrogram::cut(const Spectrograms &s,
c@100 371 int res,
c@104 372 int x, int y, int h) const
c@100 373 {
c@100 374 // cerr << "res = " << res << ", x = " << x << ", y = " << y << ", h = " << h << endl;
c@100 375
c@100 376 if (h > 1 && res > s.minres) {
c@100 377
c@104 378 Cutting *top = 0, *bottom = 0, *left = 0, *right = 0;
c@104 379 getSubCuts(s, res, x, y, h, top, bottom, left, right);
c@100 380
c@100 381 double vcost = top->cost + bottom->cost;
c@100 382 double hcost = left->cost + right->cost;
c@100 383
c@101 384 bool normalize = true;
c@101 385
c@101 386 if (normalize) {
c@101 387
c@101 388 double venergy = top->value + bottom->value;
c@101 389 vcost = (vcost + (venergy * log(venergy))) / venergy;
c@101 390
c@101 391 double henergy = left->value + right->value;
c@101 392 hcost = (hcost + (henergy * log(henergy))) / henergy;
c@101 393 }
c@101 394
c@100 395 if (vcost > hcost) {
c@100 396
c@100 397 // cut horizontally (left/right)
c@100 398
c@100 399 Cutting *cutting = new Cutting;
c@100 400 cutting->cut = Cutting::Horizontal;
c@100 401 cutting->first = left;
c@100 402 cutting->second = right;
c@100 403 cutting->cost = hcost;
c@100 404 cutting->value = left->value + right->value;
c@100 405 delete top;
c@100 406 delete bottom;
c@100 407 return cutting;
c@100 408
c@100 409 } else {
c@100 410
c@100 411 Cutting *cutting = new Cutting;
c@100 412 cutting->cut = Cutting::Vertical;
c@100 413 cutting->first = top;
c@100 414 cutting->second = bottom;
c@100 415 cutting->cost = vcost;
c@100 416 cutting->value = top->value + bottom->value;
c@100 417 delete left;
c@100 418 delete right;
c@100 419 return cutting;
c@100 420 }
c@100 421
c@100 422 } else {
c@100 423
c@100 424 // no cuts possible from this level
c@100 425
c@100 426 Cutting *cutting = new Cutting;
c@100 427 cutting->cut = Cutting::Finished;
c@100 428 cutting->first = 0;
c@100 429 cutting->second = 0;
c@100 430
c@100 431 int n = 0;
c@100 432 for (int r = res; r > s.minres; r /= 2) ++n;
c@100 433 const Spectrogram *spectrogram = s.spectrograms[n];
c@100 434
c@100 435 cutting->cost = cost(*spectrogram, x, y);
c@100 436 cutting->value = value(*spectrogram, x, y);
c@100 437
c@100 438 // cerr << "cost for this cell: " << cutting->cost << endl;
c@100 439
c@100 440 return cutting;
c@100 441 }
c@100 442 }
c@100 443
c@100 444 void
c@100 445 AdaptiveSpectrogram::assemble(const Spectrograms &s,
c@100 446 const Cutting *cutting,
c@100 447 vector<vector<float> > &rmat,
c@104 448 int x, int y, int w, int h) const
c@100 449 {
c@100 450 switch (cutting->cut) {
c@100 451
c@100 452 case Cutting::Finished:
c@100 453 for (int i = 0; i < w; ++i) {
c@100 454 for (int j = 0; j < h; ++j) {
c@100 455 rmat[x+i][y+j] = cutting->value;
c@100 456 }
c@100 457 }
c@100 458 return;
c@100 459
c@100 460 case Cutting::Horizontal:
c@100 461 assemble(s, cutting->first, rmat, x, y, w/2, h);
c@100 462 assemble(s, cutting->second, rmat, x+w/2, y, w/2, h);
c@100 463 break;
c@100 464
c@100 465 case Cutting::Vertical:
c@100 466 assemble(s, cutting->first, rmat, x, y+h/2, w, h/2);
c@100 467 assemble(s, cutting->second, rmat, x, y, w, h/2);
c@100 468 break;
c@100 469 }
c@100 470 }
c@100 471