annotate plugins/AdaptiveSpectrogram.cpp @ 113:d0920575b48a

* pass allocator through to sub-cuts (duh)
author Chris Cannam <c.cannam@qmul.ac.uk>
date Wed, 20 May 2009 08:52:11 +0000
parents 2e25bdc9f826
children 496e6d6eb413
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@92 28 AdaptiveSpectrogram::AdaptiveSpectrogram(float inputSampleRate) :
c@92 29 Plugin(inputSampleRate),
c@104 30 m_w(8),
c@104 31 m_n(3),
c@109 32 m_threaded(true),
c@109 33 m_threadsInUse(false)
c@92 34 {
c@92 35 }
c@92 36
c@92 37 AdaptiveSpectrogram::~AdaptiveSpectrogram()
c@92 38 {
c@104 39 for (int i = 0; i < m_cutThreads.size(); ++i) {
c@104 40 delete m_cutThreads[i];
c@104 41 }
c@104 42 m_cutThreads.clear();
c@105 43
c@110 44 for (FFTMap::iterator i = m_fftThreads.begin();
c@110 45 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@109 158 ParameterDescriptor desc3;
c@109 159 desc3.identifier = "threaded";
c@109 160 desc3.name = "Multi-threaded processing";
c@110 161 desc3.description = "Perform calculations using several threads in parallel";
c@109 162 desc3.unit = "";
c@109 163 desc3.minValue = 0;
c@109 164 desc3.maxValue = 1;
c@109 165 desc3.defaultValue = 1;
c@109 166 desc3.isQuantized = true;
c@109 167 desc3.quantizeStep = 1;
c@109 168 list.push_back(desc3);
c@109 169
c@92 170 return list;
c@92 171 }
c@92 172
c@92 173 float
c@92 174 AdaptiveSpectrogram::getParameter(std::string id) const
c@92 175 {
c@92 176 if (id == "n") return m_n+1;
c@92 177 else if (id == "w") return m_w+1;
c@109 178 else if (id == "threaded") return (m_threaded ? 1 : 0);
c@92 179 return 0.f;
c@92 180 }
c@92 181
c@92 182 void
c@92 183 AdaptiveSpectrogram::setParameter(std::string id, float value)
c@92 184 {
c@92 185 if (id == "n") {
c@92 186 int n = lrintf(value);
c@92 187 if (n >= 1 && n <= 10) m_n = n-1;
c@92 188 } else if (id == "w") {
c@92 189 int w = lrintf(value);
c@92 190 if (w >= 1 && w <= 14) m_w = w-1;
c@109 191 } else if (id == "threaded") {
c@109 192 m_threaded = (value > 0.5);
c@109 193 }
c@92 194 }
c@92 195
c@92 196 AdaptiveSpectrogram::OutputList
c@92 197 AdaptiveSpectrogram::getOutputDescriptors() const
c@92 198 {
c@92 199 OutputList list;
c@92 200
c@92 201 OutputDescriptor d;
c@92 202 d.identifier = "output";
c@92 203 d.name = "Output";
c@92 204 d.description = "The output of the plugin";
c@92 205 d.unit = "";
c@92 206 d.hasFixedBinCount = true;
c@92 207 d.binCount = ((2 << m_w) << m_n) / 2;
c@92 208 d.hasKnownExtents = false;
c@92 209 d.isQuantized = false;
c@92 210 d.sampleType = OutputDescriptor::FixedSampleRate;
c@92 211 d.sampleRate = m_inputSampleRate / ((2 << m_w) / 2);
c@92 212 d.hasDuration = false;
c@112 213 char name[20];
c@112 214 for (int i = 0; i < d.binCount; ++i) {
c@112 215 float freq = (m_inputSampleRate / d.binCount) * (i + 1); // no DC bin
c@112 216 sprintf(name, "%d Hz", int(freq));
c@112 217 d.binNames.push_back(name);
c@112 218 }
c@92 219 list.push_back(d);
c@92 220
c@92 221 return list;
c@92 222 }
c@92 223
c@92 224 AdaptiveSpectrogram::FeatureSet
c@92 225 AdaptiveSpectrogram::getRemainingFeatures()
c@92 226 {
c@92 227 FeatureSet fs;
c@92 228 return fs;
c@92 229 }
c@92 230
c@100 231 AdaptiveSpectrogram::FeatureSet
c@100 232 AdaptiveSpectrogram::process(const float *const *inputBuffers, RealTime ts)
c@100 233 {
c@100 234 FeatureSet fs;
c@100 235
c@100 236 int minwid = (2 << m_w), maxwid = ((2 << m_w) << m_n);
c@100 237
c@101 238 #ifdef DEBUG_VERBOSE
c@100 239 cerr << "widths from " << minwid << " to " << maxwid << " ("
c@100 240 << minwid/2 << " to " << maxwid/2 << " in real parts)" << endl;
c@101 241 #endif
c@100 242
c@100 243 Spectrograms s(minwid/2, maxwid/2, 1);
c@100 244
c@100 245 int w = minwid;
c@100 246 int index = 0;
c@100 247
c@100 248 while (w <= maxwid) {
c@106 249 if (m_fftThreads.find(w) == m_fftThreads.end()) {
c@106 250 m_fftThreads[w] = new FFTThread(w);
c@106 251 }
c@109 252 if (m_threaded) {
c@109 253 m_fftThreads[w]->startCalculation(inputBuffers[0], s, index, maxwid);
c@109 254 } else {
c@109 255 m_fftThreads[w]->setParameters(inputBuffers[0], s, index, maxwid);
c@109 256 m_fftThreads[w]->performTask();
c@109 257 }
c@100 258 w *= 2;
c@100 259 ++index;
c@100 260 }
c@100 261
c@109 262 if (m_threaded) {
c@109 263 w = minwid;
c@109 264 while (w <= maxwid) {
c@109 265 m_fftThreads[w]->await();
c@109 266 w *= 2;
c@109 267 }
c@105 268 }
c@102 269
c@109 270 m_threadsInUse = false;
c@104 271
c@111 272 std::cerr << "maxwid/2 = " << maxwid/2 << ", minwid/2 = " << minwid/2 << ", n+1 = " << m_n+1 << ", 2^(n+1) = " << (2<<m_n) << std::endl;
c@110 273
c@110 274 Cutting *cutting = cut(s, maxwid/2, 0, 0, maxwid/2, 0);
c@100 275
c@101 276 #ifdef DEBUG_VERBOSE
c@100 277 printCutting(cutting, " ");
c@101 278 #endif
c@100 279
c@100 280 vector<vector<float> > rmat(maxwid/minwid);
c@100 281 for (int i = 0; i < maxwid/minwid; ++i) {
c@100 282 rmat[i] = vector<float>(maxwid/2);
c@100 283 }
c@100 284
c@100 285 assemble(s, cutting, rmat, 0, 0, maxwid/minwid, maxwid/2);
c@100 286
c@110 287 cutting->erase();
c@100 288
c@100 289 for (int i = 0; i < rmat.size(); ++i) {
c@100 290 Feature f;
c@100 291 f.hasTimestamp = false;
c@100 292 f.values = rmat[i];
c@100 293 fs[0].push_back(f);
c@100 294 }
c@100 295
c@104 296 // std::cerr << "process returning!\n" << std::endl;
c@104 297
c@100 298 return fs;
c@100 299 }
c@100 300
c@100 301 void
c@104 302 AdaptiveSpectrogram::printCutting(Cutting *c, string pfx) const
c@100 303 {
c@100 304 if (c->first) {
c@100 305 if (c->cut == Cutting::Horizontal) {
c@100 306 cerr << pfx << "H" << endl;
c@100 307 } else if (c->cut == Cutting::Vertical) {
c@100 308 cerr << pfx << "V" << endl;
c@100 309 }
c@100 310 printCutting(c->first, pfx + " ");
c@100 311 printCutting(c->second, pfx + " ");
c@100 312 } else {
c@100 313 cerr << pfx << "* " << c->value << endl;
c@100 314 }
c@100 315 }
c@100 316
c@104 317 void
c@104 318 AdaptiveSpectrogram::getSubCuts(const Spectrograms &s,
c@104 319 int res,
c@104 320 int x, int y, int h,
c@104 321 Cutting *&top, Cutting *&bottom,
c@113 322 Cutting *&left, Cutting *&right,
c@113 323 BlockAllocator *allocator) const
c@104 324 {
c@109 325 if (m_threaded && !m_threadsInUse) {
c@104 326
c@109 327 m_threadsInUse = true;
c@104 328
c@104 329 if (m_cutThreads.empty()) {
c@104 330 for (int i = 0; i < 4; ++i) {
c@104 331 CutThread *t = new CutThread(this);
c@104 332 m_cutThreads.push_back(t);
c@104 333 }
c@104 334 }
c@104 335
c@109 336 // Cut threads 0 and 1 calculate the top and bottom halves;
c@110 337 // threads 2 and 3 calculate left and right. See notes in
c@110 338 // unthreaded code below for more information.
c@104 339
c@110 340 m_cutThreads[0]->cut(s, res, x, y + h/2, h/2); // top
c@110 341 m_cutThreads[1]->cut(s, res, x, y, h/2); // bottom
c@110 342 m_cutThreads[2]->cut(s, res/2, 2 * x, y/2, h/2); // left
c@110 343 m_cutThreads[3]->cut(s, res/2, 2 * x + 1, y/2, h/2); // right
c@104 344
c@104 345 top = m_cutThreads[0]->get();
c@104 346 bottom = m_cutThreads[1]->get();
c@104 347 left = m_cutThreads[2]->get();
c@104 348 right = m_cutThreads[3]->get();
c@104 349
c@104 350 } else {
c@104 351
c@110 352 // Unthreaded version
c@104 353
c@104 354 // The "vertical" division is a top/bottom split.
c@104 355 // Splitting this way keeps us in the same resolution,
c@104 356 // but with two vertical subregions of height h/2.
c@104 357
c@113 358 top = cut(s, res, x, y + h/2, h/2, allocator);
c@113 359 bottom = cut(s, res, x, y, h/2, allocator);
c@104 360
c@104 361 // The "horizontal" division is a left/right split. Splitting
c@104 362 // this way places us in resolution res/2, which has lower
c@104 363 // vertical resolution but higher horizontal resolution. We
c@104 364 // need to double x accordingly.
c@104 365
c@113 366 left = cut(s, res/2, 2 * x, y/2, h/2, allocator);
c@113 367 right = cut(s, res/2, 2 * x + 1, y/2, h/2, allocator);
c@104 368 }
c@104 369 }
c@104 370
c@100 371 AdaptiveSpectrogram::Cutting *
c@100 372 AdaptiveSpectrogram::cut(const Spectrograms &s,
c@100 373 int res,
c@110 374 int x, int y, int h,
c@110 375 BlockAllocator *allocator) const
c@100 376 {
c@100 377 // cerr << "res = " << res << ", x = " << x << ", y = " << y << ", h = " << h << endl;
c@100 378
c@110 379 Cutting *cutting;
c@110 380 if (allocator) {
c@110 381 cutting = (Cutting *)(allocator->allocate());
c@110 382 cutting->allocator = allocator;
c@110 383 } else {
c@110 384 cutting = new Cutting;
c@110 385 cutting->allocator = 0;
c@110 386 }
c@110 387
c@100 388 if (h > 1 && res > s.minres) {
c@100 389
c@104 390 Cutting *top = 0, *bottom = 0, *left = 0, *right = 0;
c@113 391 getSubCuts(s, res, x, y, h, top, bottom, left, right, allocator);
c@100 392
c@100 393 double vcost = top->cost + bottom->cost;
c@100 394 double hcost = left->cost + right->cost;
c@100 395
c@101 396 bool normalize = true;
c@101 397
c@101 398 if (normalize) {
c@101 399
c@101 400 double venergy = top->value + bottom->value;
c@101 401 vcost = (vcost + (venergy * log(venergy))) / venergy;
c@101 402
c@101 403 double henergy = left->value + right->value;
c@101 404 hcost = (hcost + (henergy * log(henergy))) / henergy;
c@101 405 }
c@101 406
c@100 407 if (vcost > hcost) {
c@100 408
c@100 409 // cut horizontally (left/right)
c@100 410 cutting->cut = Cutting::Horizontal;
c@100 411 cutting->first = left;
c@100 412 cutting->second = right;
c@100 413 cutting->cost = hcost;
c@111 414 cutting->value = left->value + right->value;
c@110 415 top->erase();
c@110 416 bottom->erase();
c@100 417 return cutting;
c@100 418
c@100 419 } else {
c@100 420
c@110 421 // cut vertically (top/bottom)
c@100 422 cutting->cut = Cutting::Vertical;
c@100 423 cutting->first = top;
c@100 424 cutting->second = bottom;
c@100 425 cutting->cost = vcost;
c@111 426 cutting->value = top->value + bottom->value;
c@110 427 left->erase();
c@110 428 right->erase();
c@100 429 return cutting;
c@100 430 }
c@100 431
c@100 432 } else {
c@100 433
c@100 434 // no cuts possible from this level
c@100 435
c@100 436 cutting->cut = Cutting::Finished;
c@100 437 cutting->first = 0;
c@100 438 cutting->second = 0;
c@100 439
c@100 440 int n = 0;
c@100 441 for (int r = res; r > s.minres; r /= 2) ++n;
c@100 442 const Spectrogram *spectrogram = s.spectrograms[n];
c@100 443
c@100 444 cutting->cost = cost(*spectrogram, x, y);
c@100 445 cutting->value = value(*spectrogram, x, y);
c@100 446
c@100 447 // cerr << "cost for this cell: " << cutting->cost << endl;
c@100 448
c@100 449 return cutting;
c@100 450 }
c@100 451 }
c@100 452
c@100 453 void
c@100 454 AdaptiveSpectrogram::assemble(const Spectrograms &s,
c@100 455 const Cutting *cutting,
c@100 456 vector<vector<float> > &rmat,
c@104 457 int x, int y, int w, int h) const
c@100 458 {
c@100 459 switch (cutting->cut) {
c@100 460
c@100 461 case Cutting::Finished:
c@100 462 for (int i = 0; i < w; ++i) {
c@100 463 for (int j = 0; j < h; ++j) {
c@112 464 rmat[x+i][y+j] = cutting->value * cutting->value;
c@100 465 }
c@100 466 }
c@100 467 return;
c@100 468
c@100 469 case Cutting::Horizontal:
c@100 470 assemble(s, cutting->first, rmat, x, y, w/2, h);
c@100 471 assemble(s, cutting->second, rmat, x+w/2, y, w/2, h);
c@100 472 break;
c@100 473
c@100 474 case Cutting::Vertical:
c@100 475 assemble(s, cutting->first, rmat, x, y+h/2, w, h/2);
c@100 476 assemble(s, cutting->second, rmat, x, y, w, h/2);
c@100 477 break;
c@100 478 }
c@100 479 }
c@100 480