annotate layer/SpectrogramLayer.cpp @ 107:bf196d6e8998

* Separate out window and FFT size variables. Not necessarily correct for frequency estimation code.
author Chris Cannam
date Mon, 19 Jun 2006 16:14:16 +0000
parents 1348818e7be7
children 12340cb6e6cb
rev   line source
Chris@58 1 /* -*- c-basic-offset: 4 indent-tabs-mode: nil -*- vi:set ts=8 sts=4 sw=4: */
Chris@0 2
Chris@0 3 /*
Chris@59 4 Sonic Visualiser
Chris@59 5 An audio file viewer and annotation editor.
Chris@59 6 Centre for Digital Music, Queen Mary, University of London.
Chris@59 7 This file copyright 2006 Chris Cannam.
Chris@0 8
Chris@59 9 This program is free software; you can redistribute it and/or
Chris@59 10 modify it under the terms of the GNU General Public License as
Chris@59 11 published by the Free Software Foundation; either version 2 of the
Chris@59 12 License, or (at your option) any later version. See the file
Chris@59 13 COPYING included with this distribution for more information.
Chris@0 14 */
Chris@0 15
Chris@0 16 #include "SpectrogramLayer.h"
Chris@0 17
Chris@0 18 #include "base/View.h"
Chris@0 19 #include "base/Profiler.h"
Chris@0 20 #include "base/AudioLevel.h"
Chris@0 21 #include "base/Window.h"
Chris@24 22 #include "base/Pitch.h"
Chris@89 23 #include "fileio/FFTFileCache.h"
Chris@0 24
Chris@0 25 #include <QPainter>
Chris@0 26 #include <QImage>
Chris@0 27 #include <QPixmap>
Chris@0 28 #include <QRect>
Chris@0 29 #include <QTimer>
Chris@92 30 #include <QApplication>
Chris@0 31
Chris@0 32 #include <iostream>
Chris@0 33
Chris@0 34 #include <cassert>
Chris@0 35 #include <cmath>
Chris@0 36
Chris@101 37 //#define DEBUG_SPECTROGRAM_REPAINT 1
Chris@0 38
Chris@75 39 static double mod(double x, double y)
Chris@75 40 {
Chris@75 41 double a = floor(x / y);
Chris@75 42 double b = x - (y * a);
Chris@75 43 return b;
Chris@75 44 }
Chris@75 45
Chris@104 46 static float modf(float x, float y)
Chris@104 47 {
Chris@104 48 float a = floorf(x / y);
Chris@104 49 float b = x - (y * a);
Chris@104 50 return b;
Chris@104 51 }
Chris@104 52
Chris@75 53 static double princarg(double ang)
Chris@75 54 {
Chris@75 55 return mod(ang + M_PI, -2 * M_PI) + M_PI;
Chris@75 56 }
Chris@75 57
Chris@104 58 static float princargf(float ang)
Chris@104 59 {
Chris@104 60 return modf(ang + M_PI, -2 * M_PI) + M_PI;
Chris@104 61 }
Chris@104 62
Chris@0 63
Chris@44 64 SpectrogramLayer::SpectrogramLayer(Configuration config) :
Chris@44 65 Layer(),
Chris@0 66 m_model(0),
Chris@0 67 m_channel(0),
Chris@0 68 m_windowSize(1024),
Chris@0 69 m_windowType(HanningWindow),
Chris@97 70 m_windowHopLevel(2),
Chris@107 71 m_fftSize(1024),
Chris@0 72 m_gain(1.0),
Chris@37 73 m_threshold(0.0),
Chris@9 74 m_colourRotation(0),
Chris@37 75 m_minFrequency(0),
Chris@0 76 m_maxFrequency(8000),
Chris@0 77 m_colourScale(dBColourScale),
Chris@0 78 m_colourScheme(DefaultColours),
Chris@0 79 m_frequencyScale(LinearFrequencyScale),
Chris@37 80 m_binDisplay(AllBins),
Chris@36 81 m_normalizeColumns(false),
Chris@0 82 m_cache(0),
Chris@86 83 m_writeCache(0),
Chris@0 84 m_cacheInvalid(true),
Chris@0 85 m_fillThread(0),
Chris@0 86 m_updateTimer(0),
Chris@44 87 m_candidateFillStartFrame(0),
Chris@0 88 m_lastFillExtent(0),
Chris@0 89 m_exiting(false)
Chris@0 90 {
Chris@0 91 if (config == MelodicRange) {
Chris@0 92 setWindowSize(8192);
Chris@97 93 setWindowHopLevel(4);
Chris@0 94 setWindowType(ParzenWindow);
Chris@0 95 setMaxFrequency(1000);
Chris@0 96 setColourScale(LinearColourScale);
Chris@37 97 } else if (config == MelodicPeaks) {
Chris@37 98 setWindowSize(4096);
Chris@97 99 setWindowHopLevel(5);
Chris@37 100 setWindowType(BlackmanWindow);
Chris@40 101 setMaxFrequency(2000);
Chris@37 102 setMinFrequency(40);
Chris@37 103 setFrequencyScale(LogFrequencyScale);
Chris@41 104 setColourScale(MeterColourScale);
Chris@37 105 setBinDisplay(PeakFrequencies);
Chris@37 106 setNormalizeColumns(true);
Chris@0 107 }
Chris@0 108 }
Chris@0 109
Chris@0 110 SpectrogramLayer::~SpectrogramLayer()
Chris@0 111 {
Chris@0 112 delete m_updateTimer;
Chris@0 113 m_updateTimer = 0;
Chris@0 114
Chris@0 115 m_exiting = true;
Chris@0 116 m_condition.wakeAll();
Chris@0 117 if (m_fillThread) m_fillThread->wait();
Chris@0 118 delete m_fillThread;
Chris@0 119
Chris@86 120 delete m_writeCache;
Chris@0 121 delete m_cache;
Chris@0 122 }
Chris@0 123
Chris@0 124 void
Chris@0 125 SpectrogramLayer::setModel(const DenseTimeValueModel *model)
Chris@0 126 {
Chris@101 127 // std::cerr << "SpectrogramLayer(" << this << "): setModel(" << model << ")" << std::endl;
Chris@34 128
Chris@0 129 m_mutex.lock();
Chris@35 130 m_cacheInvalid = true;
Chris@0 131 m_model = model;
Chris@0 132 m_mutex.unlock();
Chris@0 133
Chris@0 134 if (!m_model || !m_model->isOK()) return;
Chris@0 135
Chris@0 136 connect(m_model, SIGNAL(modelChanged()), this, SIGNAL(modelChanged()));
Chris@0 137 connect(m_model, SIGNAL(modelChanged(size_t, size_t)),
Chris@0 138 this, SIGNAL(modelChanged(size_t, size_t)));
Chris@0 139
Chris@0 140 connect(m_model, SIGNAL(completionChanged()),
Chris@0 141 this, SIGNAL(modelCompletionChanged()));
Chris@0 142
Chris@0 143 connect(m_model, SIGNAL(modelChanged()), this, SLOT(cacheInvalid()));
Chris@0 144 connect(m_model, SIGNAL(modelChanged(size_t, size_t)),
Chris@0 145 this, SLOT(cacheInvalid(size_t, size_t)));
Chris@0 146
Chris@0 147 emit modelReplaced();
Chris@0 148 fillCache();
Chris@0 149 }
Chris@0 150
Chris@0 151 Layer::PropertyList
Chris@0 152 SpectrogramLayer::getProperties() const
Chris@0 153 {
Chris@0 154 PropertyList list;
Chris@87 155 list.push_back("Colour");
Chris@87 156 list.push_back("Colour Scale");
Chris@87 157 list.push_back("Window Type");
Chris@87 158 list.push_back("Window Size");
Chris@97 159 list.push_back("Window Increment");
Chris@87 160 list.push_back("Normalize Columns");
Chris@87 161 list.push_back("Bin Display");
Chris@87 162 list.push_back("Threshold");
Chris@87 163 list.push_back("Gain");
Chris@87 164 list.push_back("Colour Rotation");
Chris@87 165 list.push_back("Min Frequency");
Chris@87 166 list.push_back("Max Frequency");
Chris@87 167 list.push_back("Frequency Scale");
Chris@0 168 return list;
Chris@0 169 }
Chris@0 170
Chris@87 171 QString
Chris@87 172 SpectrogramLayer::getPropertyLabel(const PropertyName &name) const
Chris@87 173 {
Chris@87 174 if (name == "Colour") return tr("Colour");
Chris@87 175 if (name == "Colour Scale") return tr("Colour Scale");
Chris@87 176 if (name == "Window Type") return tr("Window Type");
Chris@87 177 if (name == "Window Size") return tr("Window Size");
Chris@97 178 if (name == "Window Increment") return tr("Window Increment");
Chris@87 179 if (name == "Normalize Columns") return tr("Normalize Columns");
Chris@87 180 if (name == "Bin Display") return tr("Bin Display");
Chris@87 181 if (name == "Threshold") return tr("Threshold");
Chris@87 182 if (name == "Gain") return tr("Gain");
Chris@87 183 if (name == "Colour Rotation") return tr("Colour Rotation");
Chris@87 184 if (name == "Min Frequency") return tr("Min Frequency");
Chris@87 185 if (name == "Max Frequency") return tr("Max Frequency");
Chris@87 186 if (name == "Frequency Scale") return tr("Frequency Scale");
Chris@87 187 return "";
Chris@87 188 }
Chris@87 189
Chris@0 190 Layer::PropertyType
Chris@0 191 SpectrogramLayer::getPropertyType(const PropertyName &name) const
Chris@0 192 {
Chris@87 193 if (name == "Gain") return RangeProperty;
Chris@87 194 if (name == "Colour Rotation") return RangeProperty;
Chris@87 195 if (name == "Normalize Columns") return ToggleProperty;
Chris@87 196 if (name == "Threshold") return RangeProperty;
Chris@0 197 return ValueProperty;
Chris@0 198 }
Chris@0 199
Chris@0 200 QString
Chris@0 201 SpectrogramLayer::getPropertyGroupName(const PropertyName &name) const
Chris@0 202 {
Chris@87 203 if (name == "Window Size" ||
Chris@87 204 name == "Window Type" ||
Chris@97 205 name == "Window Increment") return tr("Window");
Chris@87 206 if (name == "Colour" ||
Chris@87 207 name == "Gain" ||
Chris@87 208 name == "Threshold" ||
Chris@87 209 name == "Colour Rotation") return tr("Colour");
Chris@87 210 if (name == "Normalize Columns" ||
Chris@87 211 name == "Bin Display" ||
Chris@87 212 name == "Colour Scale") return tr("Scale");
Chris@87 213 if (name == "Max Frequency" ||
Chris@87 214 name == "Min Frequency" ||
Chris@87 215 name == "Frequency Scale" ||
Chris@87 216 name == "Frequency Adjustment") return tr("Range");
Chris@0 217 return QString();
Chris@0 218 }
Chris@0 219
Chris@0 220 int
Chris@0 221 SpectrogramLayer::getPropertyRangeAndValue(const PropertyName &name,
Chris@55 222 int *min, int *max) const
Chris@0 223 {
Chris@0 224 int deft = 0;
Chris@0 225
Chris@55 226 int garbage0, garbage1;
Chris@55 227 if (!min) min = &garbage0;
Chris@55 228 if (!max) max = &garbage1;
Chris@10 229
Chris@87 230 if (name == "Gain") {
Chris@0 231
Chris@0 232 *min = -50;
Chris@0 233 *max = 50;
Chris@0 234
Chris@0 235 deft = lrint(log10(m_gain) * 20.0);
Chris@0 236 if (deft < *min) deft = *min;
Chris@0 237 if (deft > *max) deft = *max;
Chris@0 238
Chris@87 239 } else if (name == "Threshold") {
Chris@37 240
Chris@37 241 *min = -50;
Chris@37 242 *max = 0;
Chris@37 243
Chris@37 244 deft = lrintf(AudioLevel::multiplier_to_dB(m_threshold));
Chris@37 245 if (deft < *min) deft = *min;
Chris@37 246 if (deft > *max) deft = *max;
Chris@37 247
Chris@87 248 } else if (name == "Colour Rotation") {
Chris@9 249
Chris@9 250 *min = 0;
Chris@9 251 *max = 256;
Chris@9 252
Chris@9 253 deft = m_colourRotation;
Chris@9 254
Chris@87 255 } else if (name == "Colour Scale") {
Chris@0 256
Chris@0 257 *min = 0;
Chris@0 258 *max = 3;
Chris@0 259
Chris@0 260 deft = (int)m_colourScale;
Chris@0 261
Chris@87 262 } else if (name == "Colour") {
Chris@0 263
Chris@0 264 *min = 0;
Chris@71 265 *max = 6;
Chris@0 266
Chris@0 267 deft = (int)m_colourScheme;
Chris@0 268
Chris@87 269 } else if (name == "Window Type") {
Chris@0 270
Chris@0 271 *min = 0;
Chris@0 272 *max = 6;
Chris@0 273
Chris@0 274 deft = (int)m_windowType;
Chris@0 275
Chris@87 276 } else if (name == "Window Size") {
Chris@0 277
Chris@0 278 *min = 0;
Chris@0 279 *max = 10;
Chris@0 280
Chris@0 281 deft = 0;
Chris@0 282 int ws = m_windowSize;
Chris@0 283 while (ws > 32) { ws >>= 1; deft ++; }
Chris@0 284
Chris@97 285 } else if (name == "Window Increment") {
Chris@0 286
Chris@0 287 *min = 0;
Chris@97 288 *max = 5;
Chris@0 289
Chris@97 290 deft = m_windowHopLevel;
Chris@0 291
Chris@87 292 } else if (name == "Min Frequency") {
Chris@37 293
Chris@37 294 *min = 0;
Chris@37 295 *max = 9;
Chris@37 296
Chris@37 297 switch (m_minFrequency) {
Chris@37 298 case 0: default: deft = 0; break;
Chris@37 299 case 10: deft = 1; break;
Chris@37 300 case 20: deft = 2; break;
Chris@37 301 case 40: deft = 3; break;
Chris@37 302 case 100: deft = 4; break;
Chris@37 303 case 250: deft = 5; break;
Chris@37 304 case 500: deft = 6; break;
Chris@37 305 case 1000: deft = 7; break;
Chris@37 306 case 4000: deft = 8; break;
Chris@37 307 case 10000: deft = 9; break;
Chris@37 308 }
Chris@37 309
Chris@87 310 } else if (name == "Max Frequency") {
Chris@0 311
Chris@0 312 *min = 0;
Chris@0 313 *max = 9;
Chris@0 314
Chris@0 315 switch (m_maxFrequency) {
Chris@0 316 case 500: deft = 0; break;
Chris@0 317 case 1000: deft = 1; break;
Chris@0 318 case 1500: deft = 2; break;
Chris@0 319 case 2000: deft = 3; break;
Chris@0 320 case 4000: deft = 4; break;
Chris@0 321 case 6000: deft = 5; break;
Chris@0 322 case 8000: deft = 6; break;
Chris@0 323 case 12000: deft = 7; break;
Chris@0 324 case 16000: deft = 8; break;
Chris@0 325 default: deft = 9; break;
Chris@0 326 }
Chris@0 327
Chris@87 328 } else if (name == "Frequency Scale") {
Chris@0 329
Chris@0 330 *min = 0;
Chris@0 331 *max = 1;
Chris@0 332 deft = (int)m_frequencyScale;
Chris@0 333
Chris@87 334 } else if (name == "Bin Display") {
Chris@35 335
Chris@35 336 *min = 0;
Chris@35 337 *max = 2;
Chris@37 338 deft = (int)m_binDisplay;
Chris@35 339
Chris@87 340 } else if (name == "Normalize Columns") {
Chris@36 341
Chris@36 342 deft = (m_normalizeColumns ? 1 : 0);
Chris@36 343
Chris@0 344 } else {
Chris@0 345 deft = Layer::getPropertyRangeAndValue(name, min, max);
Chris@0 346 }
Chris@0 347
Chris@0 348 return deft;
Chris@0 349 }
Chris@0 350
Chris@0 351 QString
Chris@0 352 SpectrogramLayer::getPropertyValueLabel(const PropertyName &name,
Chris@9 353 int value) const
Chris@0 354 {
Chris@87 355 if (name == "Colour") {
Chris@0 356 switch (value) {
Chris@0 357 default:
Chris@0 358 case 0: return tr("Default");
Chris@0 359 case 1: return tr("White on Black");
Chris@0 360 case 2: return tr("Black on White");
Chris@0 361 case 3: return tr("Red on Blue");
Chris@0 362 case 4: return tr("Yellow on Black");
Chris@71 363 case 5: return tr("Blue on Black");
Chris@71 364 case 6: return tr("Fruit Salad");
Chris@0 365 }
Chris@0 366 }
Chris@87 367 if (name == "Colour Scale") {
Chris@0 368 switch (value) {
Chris@0 369 default:
Chris@37 370 case 0: return tr("Linear");
Chris@37 371 case 1: return tr("Meter");
Chris@37 372 case 2: return tr("dB");
Chris@0 373 case 3: return tr("Phase");
Chris@0 374 }
Chris@0 375 }
Chris@87 376 if (name == "Window Type") {
Chris@0 377 switch ((WindowType)value) {
Chris@0 378 default:
Chris@35 379 case RectangularWindow: return tr("Rectangle");
Chris@0 380 case BartlettWindow: return tr("Bartlett");
Chris@0 381 case HammingWindow: return tr("Hamming");
Chris@0 382 case HanningWindow: return tr("Hanning");
Chris@0 383 case BlackmanWindow: return tr("Blackman");
Chris@0 384 case GaussianWindow: return tr("Gaussian");
Chris@0 385 case ParzenWindow: return tr("Parzen");
Chris@0 386 }
Chris@0 387 }
Chris@87 388 if (name == "Window Size") {
Chris@0 389 return QString("%1").arg(32 << value);
Chris@0 390 }
Chris@97 391 if (name == "Window Increment") {
Chris@0 392 switch (value) {
Chris@0 393 default:
Chris@97 394 case 0: return tr("1/1");
Chris@97 395 case 1: return tr("3/4");
Chris@97 396 case 2: return tr("1/2");
Chris@97 397 case 3: return tr("1/4");
Chris@97 398 case 4: return tr("1/8");
Chris@97 399 case 5: return tr("1/16");
Chris@0 400 }
Chris@0 401 }
Chris@87 402 if (name == "Min Frequency") {
Chris@37 403 switch (value) {
Chris@37 404 default:
Chris@38 405 case 0: return tr("No min");
Chris@37 406 case 1: return tr("10 Hz");
Chris@37 407 case 2: return tr("20 Hz");
Chris@37 408 case 3: return tr("40 Hz");
Chris@37 409 case 4: return tr("100 Hz");
Chris@37 410 case 5: return tr("250 Hz");
Chris@37 411 case 6: return tr("500 Hz");
Chris@37 412 case 7: return tr("1 KHz");
Chris@37 413 case 8: return tr("4 KHz");
Chris@37 414 case 9: return tr("10 KHz");
Chris@37 415 }
Chris@37 416 }
Chris@87 417 if (name == "Max Frequency") {
Chris@0 418 switch (value) {
Chris@0 419 default:
Chris@0 420 case 0: return tr("500 Hz");
Chris@0 421 case 1: return tr("1 KHz");
Chris@0 422 case 2: return tr("1.5 KHz");
Chris@0 423 case 3: return tr("2 KHz");
Chris@0 424 case 4: return tr("4 KHz");
Chris@0 425 case 5: return tr("6 KHz");
Chris@0 426 case 6: return tr("8 KHz");
Chris@0 427 case 7: return tr("12 KHz");
Chris@0 428 case 8: return tr("16 KHz");
Chris@38 429 case 9: return tr("No max");
Chris@0 430 }
Chris@0 431 }
Chris@87 432 if (name == "Frequency Scale") {
Chris@0 433 switch (value) {
Chris@0 434 default:
Chris@0 435 case 0: return tr("Linear");
Chris@0 436 case 1: return tr("Log");
Chris@0 437 }
Chris@0 438 }
Chris@87 439 if (name == "Bin Display") {
Chris@35 440 switch (value) {
Chris@35 441 default:
Chris@37 442 case 0: return tr("All Bins");
Chris@37 443 case 1: return tr("Peak Bins");
Chris@37 444 case 2: return tr("Frequencies");
Chris@35 445 }
Chris@35 446 }
Chris@0 447 return tr("<unknown>");
Chris@0 448 }
Chris@0 449
Chris@0 450 void
Chris@0 451 SpectrogramLayer::setProperty(const PropertyName &name, int value)
Chris@0 452 {
Chris@87 453 if (name == "Gain") {
Chris@0 454 setGain(pow(10, float(value)/20.0));
Chris@87 455 } else if (name == "Threshold") {
Chris@37 456 if (value == -50) setThreshold(0.0);
Chris@37 457 else setThreshold(AudioLevel::dB_to_multiplier(value));
Chris@87 458 } else if (name == "Colour Rotation") {
Chris@9 459 setColourRotation(value);
Chris@87 460 } else if (name == "Colour") {
Chris@0 461 switch (value) {
Chris@0 462 default:
Chris@0 463 case 0: setColourScheme(DefaultColours); break;
Chris@0 464 case 1: setColourScheme(WhiteOnBlack); break;
Chris@0 465 case 2: setColourScheme(BlackOnWhite); break;
Chris@0 466 case 3: setColourScheme(RedOnBlue); break;
Chris@0 467 case 4: setColourScheme(YellowOnBlack); break;
Chris@71 468 case 5: setColourScheme(BlueOnBlack); break;
Chris@71 469 case 6: setColourScheme(Rainbow); break;
Chris@0 470 }
Chris@87 471 } else if (name == "Window Type") {
Chris@0 472 setWindowType(WindowType(value));
Chris@87 473 } else if (name == "Window Size") {
Chris@0 474 setWindowSize(32 << value);
Chris@97 475 } else if (name == "Window Increment") {
Chris@97 476 setWindowHopLevel(value);
Chris@87 477 } else if (name == "Min Frequency") {
Chris@37 478 switch (value) {
Chris@37 479 default:
Chris@37 480 case 0: setMinFrequency(0); break;
Chris@37 481 case 1: setMinFrequency(10); break;
Chris@37 482 case 2: setMinFrequency(20); break;
Chris@37 483 case 3: setMinFrequency(40); break;
Chris@37 484 case 4: setMinFrequency(100); break;
Chris@37 485 case 5: setMinFrequency(250); break;
Chris@37 486 case 6: setMinFrequency(500); break;
Chris@37 487 case 7: setMinFrequency(1000); break;
Chris@37 488 case 8: setMinFrequency(4000); break;
Chris@37 489 case 9: setMinFrequency(10000); break;
Chris@37 490 }
Chris@87 491 } else if (name == "Max Frequency") {
Chris@0 492 switch (value) {
Chris@0 493 case 0: setMaxFrequency(500); break;
Chris@0 494 case 1: setMaxFrequency(1000); break;
Chris@0 495 case 2: setMaxFrequency(1500); break;
Chris@0 496 case 3: setMaxFrequency(2000); break;
Chris@0 497 case 4: setMaxFrequency(4000); break;
Chris@0 498 case 5: setMaxFrequency(6000); break;
Chris@0 499 case 6: setMaxFrequency(8000); break;
Chris@0 500 case 7: setMaxFrequency(12000); break;
Chris@0 501 case 8: setMaxFrequency(16000); break;
Chris@0 502 default:
Chris@0 503 case 9: setMaxFrequency(0); break;
Chris@0 504 }
Chris@87 505 } else if (name == "Colour Scale") {
Chris@0 506 switch (value) {
Chris@0 507 default:
Chris@0 508 case 0: setColourScale(LinearColourScale); break;
Chris@0 509 case 1: setColourScale(MeterColourScale); break;
Chris@0 510 case 2: setColourScale(dBColourScale); break;
Chris@0 511 case 3: setColourScale(PhaseColourScale); break;
Chris@0 512 }
Chris@87 513 } else if (name == "Frequency Scale") {
Chris@0 514 switch (value) {
Chris@0 515 default:
Chris@0 516 case 0: setFrequencyScale(LinearFrequencyScale); break;
Chris@0 517 case 1: setFrequencyScale(LogFrequencyScale); break;
Chris@0 518 }
Chris@87 519 } else if (name == "Bin Display") {
Chris@35 520 switch (value) {
Chris@35 521 default:
Chris@37 522 case 0: setBinDisplay(AllBins); break;
Chris@37 523 case 1: setBinDisplay(PeakBins); break;
Chris@37 524 case 2: setBinDisplay(PeakFrequencies); break;
Chris@35 525 }
Chris@82 526 } else if (name == "Normalize Columns") {
Chris@36 527 setNormalizeColumns(value ? true : false);
Chris@0 528 }
Chris@0 529 }
Chris@0 530
Chris@0 531 void
Chris@95 532 SpectrogramLayer::invalidatePixmapCaches()
Chris@95 533 {
Chris@95 534 for (ViewPixmapCache::iterator i = m_pixmapCaches.begin();
Chris@95 535 i != m_pixmapCaches.end(); ++i) {
Chris@95 536 i->second.validArea = QRect();
Chris@95 537 }
Chris@95 538 }
Chris@95 539
Chris@95 540 void
Chris@95 541 SpectrogramLayer::invalidatePixmapCaches(size_t startFrame, size_t endFrame)
Chris@95 542 {
Chris@95 543 for (ViewPixmapCache::iterator i = m_pixmapCaches.begin();
Chris@95 544 i != m_pixmapCaches.end(); ++i) {
Chris@95 545 //!!! when are views removed from the map? on setLayerDormant?
Chris@95 546 const View *v = i->first;
Chris@95 547
Chris@95 548 if (startFrame < v->getEndFrame() && endFrame >= v->getStartFrame()) {
Chris@95 549 i->second.validArea = QRect();
Chris@95 550 }
Chris@95 551 }
Chris@95 552 }
Chris@95 553
Chris@95 554 void
Chris@0 555 SpectrogramLayer::setChannel(int ch)
Chris@0 556 {
Chris@0 557 if (m_channel == ch) return;
Chris@0 558
Chris@0 559 m_mutex.lock();
Chris@0 560 m_cacheInvalid = true;
Chris@95 561 invalidatePixmapCaches();
Chris@0 562
Chris@0 563 m_channel = ch;
Chris@9 564
Chris@9 565 m_mutex.unlock();
Chris@9 566
Chris@0 567 emit layerParametersChanged();
Chris@9 568
Chris@0 569 fillCache();
Chris@0 570 }
Chris@0 571
Chris@0 572 int
Chris@0 573 SpectrogramLayer::getChannel() const
Chris@0 574 {
Chris@0 575 return m_channel;
Chris@0 576 }
Chris@0 577
Chris@0 578 void
Chris@0 579 SpectrogramLayer::setWindowSize(size_t ws)
Chris@0 580 {
Chris@0 581 if (m_windowSize == ws) return;
Chris@0 582
Chris@0 583 m_mutex.lock();
Chris@0 584 m_cacheInvalid = true;
Chris@95 585 invalidatePixmapCaches();
Chris@0 586
Chris@0 587 m_windowSize = ws;
Chris@107 588 m_fftSize = ws;
Chris@0 589
Chris@0 590 m_mutex.unlock();
Chris@9 591
Chris@9 592 emit layerParametersChanged();
Chris@9 593
Chris@0 594 fillCache();
Chris@0 595 }
Chris@0 596
Chris@0 597 size_t
Chris@0 598 SpectrogramLayer::getWindowSize() const
Chris@0 599 {
Chris@0 600 return m_windowSize;
Chris@0 601 }
Chris@0 602
Chris@0 603 void
Chris@97 604 SpectrogramLayer::setWindowHopLevel(size_t v)
Chris@0 605 {
Chris@97 606 if (m_windowHopLevel == v) return;
Chris@0 607
Chris@0 608 m_mutex.lock();
Chris@0 609 m_cacheInvalid = true;
Chris@95 610 invalidatePixmapCaches();
Chris@0 611
Chris@97 612 m_windowHopLevel = v;
Chris@0 613
Chris@0 614 m_mutex.unlock();
Chris@9 615
Chris@9 616 emit layerParametersChanged();
Chris@9 617
Chris@0 618 fillCache();
Chris@0 619 }
Chris@0 620
Chris@0 621 size_t
Chris@97 622 SpectrogramLayer::getWindowHopLevel() const
Chris@0 623 {
Chris@97 624 return m_windowHopLevel;
Chris@0 625 }
Chris@0 626
Chris@0 627 void
Chris@0 628 SpectrogramLayer::setWindowType(WindowType w)
Chris@0 629 {
Chris@0 630 if (m_windowType == w) return;
Chris@0 631
Chris@0 632 m_mutex.lock();
Chris@0 633 m_cacheInvalid = true;
Chris@95 634 invalidatePixmapCaches();
Chris@0 635
Chris@0 636 m_windowType = w;
Chris@0 637
Chris@0 638 m_mutex.unlock();
Chris@9 639
Chris@9 640 emit layerParametersChanged();
Chris@9 641
Chris@0 642 fillCache();
Chris@0 643 }
Chris@0 644
Chris@0 645 WindowType
Chris@0 646 SpectrogramLayer::getWindowType() const
Chris@0 647 {
Chris@0 648 return m_windowType;
Chris@0 649 }
Chris@0 650
Chris@0 651 void
Chris@0 652 SpectrogramLayer::setGain(float gain)
Chris@0 653 {
Chris@101 654 // std::cerr << "SpectrogramLayer::setGain(" << gain << ") (my gain is now "
Chris@101 655 // << m_gain << ")" << std::endl;
Chris@55 656
Chris@40 657 if (m_gain == gain) return;
Chris@0 658
Chris@0 659 m_mutex.lock();
Chris@95 660 invalidatePixmapCaches();
Chris@0 661
Chris@0 662 m_gain = gain;
Chris@0 663
Chris@0 664 m_mutex.unlock();
Chris@9 665
Chris@9 666 emit layerParametersChanged();
Chris@9 667
Chris@0 668 fillCache();
Chris@0 669 }
Chris@0 670
Chris@0 671 float
Chris@0 672 SpectrogramLayer::getGain() const
Chris@0 673 {
Chris@0 674 return m_gain;
Chris@0 675 }
Chris@0 676
Chris@0 677 void
Chris@37 678 SpectrogramLayer::setThreshold(float threshold)
Chris@37 679 {
Chris@40 680 if (m_threshold == threshold) return;
Chris@37 681
Chris@37 682 m_mutex.lock();
Chris@95 683 invalidatePixmapCaches();
Chris@37 684
Chris@37 685 m_threshold = threshold;
Chris@37 686
Chris@37 687 m_mutex.unlock();
Chris@37 688
Chris@37 689 emit layerParametersChanged();
Chris@37 690
Chris@37 691 fillCache();
Chris@37 692 }
Chris@37 693
Chris@37 694 float
Chris@37 695 SpectrogramLayer::getThreshold() const
Chris@37 696 {
Chris@37 697 return m_threshold;
Chris@37 698 }
Chris@37 699
Chris@37 700 void
Chris@37 701 SpectrogramLayer::setMinFrequency(size_t mf)
Chris@37 702 {
Chris@37 703 if (m_minFrequency == mf) return;
Chris@37 704
Chris@37 705 m_mutex.lock();
Chris@95 706 invalidatePixmapCaches();
Chris@37 707
Chris@37 708 m_minFrequency = mf;
Chris@37 709
Chris@37 710 m_mutex.unlock();
Chris@37 711
Chris@37 712 emit layerParametersChanged();
Chris@37 713 }
Chris@37 714
Chris@37 715 size_t
Chris@37 716 SpectrogramLayer::getMinFrequency() const
Chris@37 717 {
Chris@37 718 return m_minFrequency;
Chris@37 719 }
Chris@37 720
Chris@37 721 void
Chris@0 722 SpectrogramLayer::setMaxFrequency(size_t mf)
Chris@0 723 {
Chris@0 724 if (m_maxFrequency == mf) return;
Chris@0 725
Chris@0 726 m_mutex.lock();
Chris@95 727 invalidatePixmapCaches();
Chris@0 728
Chris@0 729 m_maxFrequency = mf;
Chris@0 730
Chris@0 731 m_mutex.unlock();
Chris@9 732
Chris@9 733 emit layerParametersChanged();
Chris@0 734 }
Chris@0 735
Chris@0 736 size_t
Chris@0 737 SpectrogramLayer::getMaxFrequency() const
Chris@0 738 {
Chris@0 739 return m_maxFrequency;
Chris@0 740 }
Chris@0 741
Chris@0 742 void
Chris@9 743 SpectrogramLayer::setColourRotation(int r)
Chris@9 744 {
Chris@9 745 m_mutex.lock();
Chris@95 746 invalidatePixmapCaches();
Chris@9 747
Chris@9 748 if (r < 0) r = 0;
Chris@9 749 if (r > 256) r = 256;
Chris@9 750 int distance = r - m_colourRotation;
Chris@9 751
Chris@9 752 if (distance != 0) {
Chris@90 753 rotateColourmap(-distance);
Chris@9 754 m_colourRotation = r;
Chris@9 755 }
Chris@9 756
Chris@9 757 m_mutex.unlock();
Chris@9 758
Chris@9 759 emit layerParametersChanged();
Chris@9 760 }
Chris@9 761
Chris@9 762 void
Chris@0 763 SpectrogramLayer::setColourScale(ColourScale colourScale)
Chris@0 764 {
Chris@0 765 if (m_colourScale == colourScale) return;
Chris@0 766
Chris@0 767 m_mutex.lock();
Chris@95 768 invalidatePixmapCaches();
Chris@0 769
Chris@0 770 m_colourScale = colourScale;
Chris@0 771
Chris@0 772 m_mutex.unlock();
Chris@0 773 fillCache();
Chris@9 774
Chris@9 775 emit layerParametersChanged();
Chris@0 776 }
Chris@0 777
Chris@0 778 SpectrogramLayer::ColourScale
Chris@0 779 SpectrogramLayer::getColourScale() const
Chris@0 780 {
Chris@0 781 return m_colourScale;
Chris@0 782 }
Chris@0 783
Chris@0 784 void
Chris@0 785 SpectrogramLayer::setColourScheme(ColourScheme scheme)
Chris@0 786 {
Chris@0 787 if (m_colourScheme == scheme) return;
Chris@0 788
Chris@0 789 m_mutex.lock();
Chris@95 790 invalidatePixmapCaches();
Chris@0 791
Chris@0 792 m_colourScheme = scheme;
Chris@90 793 setColourmap();
Chris@9 794
Chris@9 795 m_mutex.unlock();
Chris@9 796
Chris@0 797 emit layerParametersChanged();
Chris@0 798 }
Chris@0 799
Chris@0 800 SpectrogramLayer::ColourScheme
Chris@0 801 SpectrogramLayer::getColourScheme() const
Chris@0 802 {
Chris@0 803 return m_colourScheme;
Chris@0 804 }
Chris@0 805
Chris@0 806 void
Chris@0 807 SpectrogramLayer::setFrequencyScale(FrequencyScale frequencyScale)
Chris@0 808 {
Chris@0 809 if (m_frequencyScale == frequencyScale) return;
Chris@0 810
Chris@0 811 m_mutex.lock();
Chris@35 812
Chris@95 813 invalidatePixmapCaches();
Chris@0 814
Chris@0 815 m_frequencyScale = frequencyScale;
Chris@0 816
Chris@0 817 m_mutex.unlock();
Chris@9 818
Chris@9 819 emit layerParametersChanged();
Chris@0 820 }
Chris@0 821
Chris@0 822 SpectrogramLayer::FrequencyScale
Chris@0 823 SpectrogramLayer::getFrequencyScale() const
Chris@0 824 {
Chris@0 825 return m_frequencyScale;
Chris@0 826 }
Chris@0 827
Chris@0 828 void
Chris@37 829 SpectrogramLayer::setBinDisplay(BinDisplay binDisplay)
Chris@35 830 {
Chris@37 831 if (m_binDisplay == binDisplay) return;
Chris@35 832
Chris@35 833 m_mutex.lock();
Chris@35 834
Chris@95 835 invalidatePixmapCaches();
Chris@35 836
Chris@37 837 m_binDisplay = binDisplay;
Chris@35 838
Chris@35 839 m_mutex.unlock();
Chris@35 840
Chris@35 841 fillCache();
Chris@35 842
Chris@35 843 emit layerParametersChanged();
Chris@35 844 }
Chris@35 845
Chris@37 846 SpectrogramLayer::BinDisplay
Chris@37 847 SpectrogramLayer::getBinDisplay() const
Chris@35 848 {
Chris@37 849 return m_binDisplay;
Chris@35 850 }
Chris@35 851
Chris@35 852 void
Chris@36 853 SpectrogramLayer::setNormalizeColumns(bool n)
Chris@36 854 {
Chris@36 855 if (m_normalizeColumns == n) return;
Chris@36 856 m_mutex.lock();
Chris@36 857
Chris@95 858 invalidatePixmapCaches();
Chris@36 859 m_normalizeColumns = n;
Chris@36 860 m_mutex.unlock();
Chris@36 861
Chris@36 862 fillCache();
Chris@36 863 emit layerParametersChanged();
Chris@36 864 }
Chris@36 865
Chris@36 866 bool
Chris@36 867 SpectrogramLayer::getNormalizeColumns() const
Chris@36 868 {
Chris@36 869 return m_normalizeColumns;
Chris@36 870 }
Chris@36 871
Chris@36 872 void
Chris@47 873 SpectrogramLayer::setLayerDormant(const View *v, bool dormant)
Chris@29 874 {
Chris@47 875 QMutexLocker locker(&m_mutex);
Chris@47 876
Chris@47 877 if (dormant == m_dormancy[v]) return;
Chris@33 878
Chris@33 879 if (dormant) {
Chris@33 880
Chris@47 881 m_dormancy[v] = true;
Chris@33 882
Chris@34 883 // delete m_cache;
Chris@34 884 // m_cache = 0;
Chris@33 885
Chris@34 886 m_cacheInvalid = true;
Chris@95 887 invalidatePixmapCaches();
Chris@95 888 m_pixmapCaches.erase(v);
Chris@33 889
Chris@33 890 } else {
Chris@33 891
Chris@47 892 m_dormancy[v] = false;
Chris@33 893 }
Chris@29 894 }
Chris@29 895
Chris@29 896 void
Chris@0 897 SpectrogramLayer::cacheInvalid()
Chris@0 898 {
Chris@0 899 m_cacheInvalid = true;
Chris@95 900 invalidatePixmapCaches();
Chris@0 901 fillCache();
Chris@0 902 }
Chris@0 903
Chris@0 904 void
Chris@0 905 SpectrogramLayer::cacheInvalid(size_t, size_t)
Chris@0 906 {
Chris@0 907 // for now (or forever?)
Chris@0 908 cacheInvalid();
Chris@0 909 }
Chris@0 910
Chris@0 911 void
Chris@0 912 SpectrogramLayer::fillCache()
Chris@0 913 {
Chris@0 914 #ifdef DEBUG_SPECTROGRAM_REPAINT
Chris@0 915 std::cerr << "SpectrogramLayer::fillCache" << std::endl;
Chris@0 916 #endif
Chris@0 917 QMutexLocker locker(&m_mutex);
Chris@0 918
Chris@0 919 m_lastFillExtent = 0;
Chris@0 920
Chris@0 921 delete m_updateTimer;
Chris@0 922 m_updateTimer = new QTimer(this);
Chris@0 923 connect(m_updateTimer, SIGNAL(timeout()), this, SLOT(fillTimerTimedOut()));
Chris@0 924 m_updateTimer->start(200);
Chris@0 925
Chris@0 926 if (!m_fillThread) {
Chris@0 927 std::cerr << "SpectrogramLayer::fillCache creating thread" << std::endl;
Chris@0 928 m_fillThread = new CacheFillThread(*this);
Chris@0 929 m_fillThread->start();
Chris@0 930 }
Chris@0 931
Chris@0 932 m_condition.wakeAll();
Chris@0 933 }
Chris@0 934
Chris@0 935 void
Chris@0 936 SpectrogramLayer::fillTimerTimedOut()
Chris@0 937 {
Chris@0 938 if (m_fillThread && m_model) {
Chris@0 939 size_t fillExtent = m_fillThread->getFillExtent();
Chris@0 940 #ifdef DEBUG_SPECTROGRAM_REPAINT
Chris@0 941 std::cerr << "SpectrogramLayer::fillTimerTimedOut: extent " << fillExtent << ", last " << m_lastFillExtent << ", total " << m_model->getEndFrame() << std::endl;
Chris@0 942 #endif
Chris@0 943 if (fillExtent >= m_lastFillExtent) {
Chris@0 944 if (fillExtent >= m_model->getEndFrame() && m_lastFillExtent > 0) {
Chris@0 945 #ifdef DEBUG_SPECTROGRAM_REPAINT
Chris@0 946 std::cerr << "complete!" << std::endl;
Chris@0 947 #endif
Chris@95 948 invalidatePixmapCaches();
Chris@0 949 emit modelChanged();
Chris@0 950 delete m_updateTimer;
Chris@0 951 m_updateTimer = 0;
Chris@0 952 m_lastFillExtent = 0;
Chris@0 953 } else if (fillExtent > m_lastFillExtent) {
Chris@0 954 #ifdef DEBUG_SPECTROGRAM_REPAINT
Chris@0 955 std::cerr << "SpectrogramLayer: emitting modelChanged("
Chris@0 956 << m_lastFillExtent << "," << fillExtent << ")" << std::endl;
Chris@0 957 #endif
Chris@95 958 invalidatePixmapCaches(m_lastFillExtent, fillExtent);
Chris@0 959 emit modelChanged(m_lastFillExtent, fillExtent);
Chris@0 960 m_lastFillExtent = fillExtent;
Chris@0 961 }
Chris@0 962 } else {
Chris@44 963 // if (v) {
Chris@0 964 size_t sf = 0;
Chris@44 965 //!!! if (v->getStartFrame() > 0) sf = v->getStartFrame();
Chris@0 966 #ifdef DEBUG_SPECTROGRAM_REPAINT
Chris@0 967 std::cerr << "SpectrogramLayer: going backwards, emitting modelChanged("
Chris@44 968 << sf << "," << m_model->getEndFrame() << ")" << std::endl;
Chris@0 969 #endif
Chris@95 970 invalidatePixmapCaches();
Chris@44 971 emit modelChanged(sf, m_model->getEndFrame());
Chris@44 972 // }
Chris@0 973 m_lastFillExtent = fillExtent;
Chris@0 974 }
Chris@0 975 }
Chris@0 976 }
Chris@0 977
Chris@0 978 void
Chris@90 979 SpectrogramLayer::setColourmap()
Chris@0 980 {
Chris@10 981 int formerRotation = m_colourRotation;
Chris@10 982
Chris@38 983 if (m_colourScheme == BlackOnWhite) {
Chris@86 984 m_colourMap.setColour(NO_VALUE, Qt::white);
Chris@38 985 } else {
Chris@86 986 m_colourMap.setColour(NO_VALUE, Qt::black);
Chris@38 987 }
Chris@0 988
Chris@0 989 for (int pixel = 1; pixel < 256; ++pixel) {
Chris@0 990
Chris@0 991 QColor colour;
Chris@0 992 int hue, px;
Chris@0 993
Chris@0 994 switch (m_colourScheme) {
Chris@0 995
Chris@0 996 default:
Chris@0 997 case DefaultColours:
Chris@0 998 hue = 256 - pixel;
Chris@0 999 colour = QColor::fromHsv(hue, pixel/2 + 128, pixel);
Chris@77 1000 m_crosshairColour = QColor(255, 150, 50);
Chris@77 1001 // m_crosshairColour = QColor::fromHsv(240, 160, 255);
Chris@0 1002 break;
Chris@0 1003
Chris@0 1004 case WhiteOnBlack:
Chris@0 1005 colour = QColor(pixel, pixel, pixel);
Chris@77 1006 m_crosshairColour = Qt::red;
Chris@0 1007 break;
Chris@0 1008
Chris@0 1009 case BlackOnWhite:
Chris@0 1010 colour = QColor(256-pixel, 256-pixel, 256-pixel);
Chris@77 1011 m_crosshairColour = Qt::darkGreen;
Chris@0 1012 break;
Chris@0 1013
Chris@0 1014 case RedOnBlue:
Chris@0 1015 colour = QColor(pixel > 128 ? (pixel - 128) * 2 : 0, 0,
Chris@0 1016 pixel < 128 ? pixel : (256 - pixel));
Chris@77 1017 m_crosshairColour = Qt::green;
Chris@0 1018 break;
Chris@0 1019
Chris@0 1020 case YellowOnBlack:
Chris@0 1021 px = 256 - pixel;
Chris@0 1022 colour = QColor(px < 64 ? 255 - px/2 :
Chris@0 1023 px < 128 ? 224 - (px - 64) :
Chris@0 1024 px < 192 ? 160 - (px - 128) * 3 / 2 :
Chris@0 1025 256 - px,
Chris@0 1026 pixel,
Chris@0 1027 pixel / 4);
Chris@77 1028 m_crosshairColour = QColor::fromHsv(240, 255, 255);
Chris@0 1029 break;
Chris@0 1030
Chris@71 1031 case BlueOnBlack:
Chris@71 1032 colour = QColor::fromHsv
Chris@71 1033 (240, pixel > 226 ? 256 - (pixel - 226) * 8 : 255,
Chris@71 1034 (pixel * pixel) / 255);
Chris@77 1035 m_crosshairColour = Qt::red;
Chris@71 1036 break;
Chris@71 1037
Chris@40 1038 case Rainbow:
Chris@40 1039 hue = 250 - pixel;
Chris@40 1040 if (hue < 0) hue += 256;
Chris@40 1041 colour = QColor::fromHsv(pixel, 255, 255);
Chris@77 1042 m_crosshairColour = Qt::white;
Chris@0 1043 break;
Chris@0 1044 }
Chris@0 1045
Chris@86 1046 m_colourMap.setColour(pixel, colour);
Chris@0 1047 }
Chris@9 1048
Chris@9 1049 m_colourRotation = 0;
Chris@90 1050 rotateColourmap(m_colourRotation - formerRotation);
Chris@10 1051 m_colourRotation = formerRotation;
Chris@9 1052 }
Chris@9 1053
Chris@9 1054 void
Chris@90 1055 SpectrogramLayer::rotateColourmap(int distance)
Chris@9 1056 {
Chris@10 1057 if (!m_cache) return;
Chris@10 1058
Chris@31 1059 QColor newPixels[256];
Chris@9 1060
Chris@86 1061 newPixels[NO_VALUE] = m_colourMap.getColour(NO_VALUE);
Chris@9 1062
Chris@9 1063 for (int pixel = 1; pixel < 256; ++pixel) {
Chris@9 1064 int target = pixel + distance;
Chris@9 1065 while (target < 1) target += 255;
Chris@9 1066 while (target > 255) target -= 255;
Chris@86 1067 newPixels[target] = m_colourMap.getColour(pixel);
Chris@9 1068 }
Chris@9 1069
Chris@9 1070 for (int pixel = 0; pixel < 256; ++pixel) {
Chris@86 1071 m_colourMap.setColour(pixel, newPixels[pixel]);
Chris@9 1072 }
Chris@0 1073 }
Chris@0 1074
Chris@38 1075 float
Chris@38 1076 SpectrogramLayer::calculateFrequency(size_t bin,
Chris@38 1077 size_t windowSize,
Chris@38 1078 size_t windowIncrement,
Chris@38 1079 size_t sampleRate,
Chris@38 1080 float oldPhase,
Chris@38 1081 float newPhase,
Chris@38 1082 bool &steadyState)
Chris@38 1083 {
Chris@38 1084 // At frequency f, phase shift of 2pi (one cycle) happens in 1/f sec.
Chris@38 1085 // At hopsize h and sample rate sr, one hop happens in h/sr sec.
Chris@38 1086 // At window size w, for bin b, f is b*sr/w.
Chris@38 1087 // thus 2pi phase shift happens in w/(b*sr) sec.
Chris@38 1088 // We need to know what phase shift we expect from h/sr sec.
Chris@38 1089 // -> 2pi * ((h/sr) / (w/(b*sr)))
Chris@38 1090 // = 2pi * ((h * b * sr) / (w * sr))
Chris@38 1091 // = 2pi * (h * b) / w.
Chris@38 1092
Chris@38 1093 float frequency = (float(bin) * sampleRate) / windowSize;
Chris@38 1094
Chris@38 1095 float expectedPhase =
Chris@38 1096 oldPhase + (2.0 * M_PI * bin * windowIncrement) / windowSize;
Chris@38 1097
Chris@104 1098 float phaseError = princargf(newPhase - expectedPhase);
Chris@38 1099
Chris@38 1100 if (fabs(phaseError) < (1.1 * (windowIncrement * M_PI) / windowSize)) {
Chris@38 1101
Chris@38 1102 // The new frequency estimate based on the phase error
Chris@38 1103 // resulting from assuming the "native" frequency of this bin
Chris@38 1104
Chris@38 1105 float newFrequency =
Chris@38 1106 (sampleRate * (expectedPhase + phaseError - oldPhase)) /
Chris@38 1107 (2 * M_PI * windowIncrement);
Chris@38 1108
Chris@38 1109 steadyState = true;
Chris@38 1110 return newFrequency;
Chris@38 1111 }
Chris@38 1112
Chris@38 1113 steadyState = false;
Chris@38 1114 return frequency;
Chris@38 1115 }
Chris@38 1116
Chris@38 1117 void
Chris@107 1118 SpectrogramLayer::fillCacheColumn(int column,
Chris@107 1119 fftsample *input,
Chris@104 1120 fftwf_complex *output,
Chris@104 1121 fftwf_plan plan,
Chris@9 1122 size_t windowSize,
Chris@107 1123 size_t fftSize,
Chris@9 1124 size_t increment,
Chris@86 1125 float *workbuffer,
Chris@104 1126 const Window<fftsample> &windower) const
Chris@0 1127 {
Chris@38 1128 //!!! we _do_ need a lock for these references to the model
Chris@38 1129 // though, don't we?
Chris@35 1130
Chris@0 1131 int startFrame = increment * column;
Chris@9 1132 int endFrame = startFrame + windowSize;
Chris@0 1133
Chris@9 1134 startFrame -= int(windowSize - increment) / 2;
Chris@9 1135 endFrame -= int(windowSize - increment) / 2;
Chris@0 1136 size_t pfx = 0;
Chris@0 1137
Chris@107 1138 size_t off = (m_fftSize - m_windowSize) / 2;
Chris@107 1139
Chris@107 1140 for (size_t i = 0; i < off; ++i) {
Chris@107 1141 input[i] = 0.0;
Chris@107 1142 input[m_fftSize - i - 1] = 0.0;
Chris@107 1143 }
Chris@107 1144
Chris@0 1145 if (startFrame < 0) {
Chris@0 1146 pfx = size_t(-startFrame);
Chris@0 1147 for (size_t i = 0; i < pfx; ++i) {
Chris@107 1148 input[off + i] = 0.0;
Chris@0 1149 }
Chris@0 1150 }
Chris@0 1151
Chris@0 1152 size_t got = m_model->getValues(m_channel, startFrame + pfx,
Chris@0 1153 endFrame, input + pfx);
Chris@9 1154 while (got + pfx < windowSize) {
Chris@107 1155 input[off + got + pfx] = 0.0;
Chris@0 1156 ++got;
Chris@0 1157 }
Chris@0 1158
Chris@37 1159 if (m_channel == -1) {
Chris@37 1160 int channels = m_model->getChannelCount();
Chris@37 1161 if (channels > 1) {
Chris@37 1162 for (size_t i = 0; i < windowSize; ++i) {
Chris@107 1163 input[off + i] /= channels;
Chris@37 1164 }
Chris@37 1165 }
Chris@37 1166 }
Chris@37 1167
Chris@0 1168 windower.cut(input);
Chris@0 1169
Chris@35 1170 for (size_t i = 0; i < windowSize/2; ++i) {
Chris@107 1171 fftsample temp = input[off + i];
Chris@107 1172 input[off + i] = input[off + i + windowSize/2];
Chris@107 1173 input[off + i + windowSize/2] = temp;
Chris@35 1174 }
Chris@107 1175
Chris@104 1176 fftwf_execute(plan);
Chris@104 1177
Chris@104 1178 fftsample factor = 0.0;
Chris@0 1179
Chris@107 1180 for (size_t i = 0; i < fftSize/2; ++i) {
Chris@35 1181
Chris@104 1182 fftsample mag = sqrtf(output[i][0] * output[i][0] +
Chris@104 1183 output[i][1] * output[i][1]);
Chris@107 1184 mag /= fftSize / 2;
Chris@37 1185
Chris@38 1186 if (mag > factor) factor = mag;
Chris@37 1187
Chris@104 1188 fftsample phase = atan2f(output[i][1], output[i][0]);
Chris@104 1189 phase = princargf(phase);
Chris@37 1190
Chris@86 1191 workbuffer[i] = mag;
Chris@107 1192 workbuffer[i + fftSize/2] = phase;
Chris@38 1193 }
Chris@35 1194
Chris@86 1195 m_writeCache->setColumnAt(column, workbuffer,
Chris@107 1196 workbuffer + fftSize/2, factor);
Chris@38 1197 }
Chris@35 1198
Chris@38 1199 unsigned char
Chris@38 1200 SpectrogramLayer::getDisplayValue(float input) const
Chris@38 1201 {
Chris@38 1202 int value;
Chris@37 1203
Chris@40 1204 switch (m_colourScale) {
Chris@40 1205
Chris@40 1206 default:
Chris@40 1207 case LinearColourScale:
Chris@40 1208 value = int
Chris@40 1209 (input * (m_normalizeColumns ? 1.0 : 50.0) * 255.0) + 1;
Chris@40 1210 break;
Chris@40 1211
Chris@40 1212 case MeterColourScale:
Chris@40 1213 value = AudioLevel::multiplier_to_preview
Chris@40 1214 (input * (m_normalizeColumns ? 1.0 : 50.0), 255) + 1;
Chris@40 1215 break;
Chris@40 1216
Chris@40 1217 case dBColourScale:
Chris@40 1218 input = 20.0 * log10(input);
Chris@40 1219 input = (input + 80.0) / 80.0;
Chris@40 1220 if (input < 0.0) input = 0.0;
Chris@40 1221 if (input > 1.0) input = 1.0;
Chris@40 1222 value = int(input * 255.0) + 1;
Chris@40 1223 break;
Chris@40 1224
Chris@40 1225 case PhaseColourScale:
Chris@40 1226 value = int((input * 127.0 / M_PI) + 128);
Chris@40 1227 break;
Chris@0 1228 }
Chris@38 1229
Chris@38 1230 if (value > UCHAR_MAX) value = UCHAR_MAX;
Chris@38 1231 if (value < 0) value = 0;
Chris@38 1232 return value;
Chris@0 1233 }
Chris@0 1234
Chris@40 1235 float
Chris@40 1236 SpectrogramLayer::getInputForDisplayValue(unsigned char uc) const
Chris@40 1237 {
Chris@40 1238 int value = uc;
Chris@40 1239 float input;
Chris@40 1240
Chris@40 1241 switch (m_colourScale) {
Chris@40 1242
Chris@40 1243 default:
Chris@40 1244 case LinearColourScale:
Chris@40 1245 input = float(value - 1) / 255.0 / (m_normalizeColumns ? 1 : 50);
Chris@40 1246 break;
Chris@40 1247
Chris@40 1248 case MeterColourScale:
Chris@40 1249 input = AudioLevel::preview_to_multiplier(value - 1, 255)
Chris@40 1250 / (m_normalizeColumns ? 1.0 : 50.0);
Chris@40 1251 break;
Chris@40 1252
Chris@40 1253 case dBColourScale:
Chris@40 1254 input = float(value - 1) / 255.0;
Chris@40 1255 input = (input * 80.0) - 80.0;
Chris@40 1256 input = powf(10.0, input) / 20.0;
Chris@40 1257 value = int(input);
Chris@40 1258 break;
Chris@40 1259
Chris@40 1260 case PhaseColourScale:
Chris@40 1261 input = float(value - 128) * M_PI / 127.0;
Chris@40 1262 break;
Chris@40 1263 }
Chris@40 1264
Chris@40 1265 return input;
Chris@40 1266 }
Chris@40 1267
Chris@0 1268 void
Chris@0 1269 SpectrogramLayer::CacheFillThread::run()
Chris@0 1270 {
Chris@0 1271 // std::cerr << "SpectrogramLayer::CacheFillThread::run" << std::endl;
Chris@0 1272
Chris@0 1273 m_layer.m_mutex.lock();
Chris@0 1274
Chris@0 1275 while (!m_layer.m_exiting) {
Chris@0 1276
Chris@0 1277 bool interrupted = false;
Chris@0 1278
Chris@0 1279 // std::cerr << "SpectrogramLayer::CacheFillThread::run in loop" << std::endl;
Chris@0 1280
Chris@48 1281 bool haveUndormantViews = false;
Chris@48 1282
Chris@48 1283 for (std::map<const void *, bool>::iterator i =
Chris@48 1284 m_layer.m_dormancy.begin();
Chris@48 1285 i != m_layer.m_dormancy.end(); ++i) {
Chris@48 1286
Chris@48 1287 if (!i->second) {
Chris@48 1288 haveUndormantViews = true;
Chris@48 1289 break;
Chris@48 1290 }
Chris@48 1291 }
Chris@48 1292
Chris@48 1293 if (!haveUndormantViews) {
Chris@48 1294
Chris@48 1295 if (m_layer.m_cacheInvalid && m_layer.m_cache) {
Chris@48 1296 std::cerr << "All views dormant, freeing spectrogram cache"
Chris@48 1297 << std::endl;
Chris@47 1298
Chris@34 1299 delete m_layer.m_cache;
Chris@34 1300 m_layer.m_cache = 0;
Chris@34 1301 }
Chris@34 1302
Chris@34 1303 } else if (m_layer.m_model && m_layer.m_cacheInvalid) {
Chris@0 1304
Chris@0 1305 // std::cerr << "SpectrogramLayer::CacheFillThread::run: something to do" << std::endl;
Chris@0 1306
Chris@0 1307 while (!m_layer.m_model->isReady()) {
Chris@0 1308 m_layer.m_condition.wait(&m_layer.m_mutex, 100);
Chris@48 1309 if (m_layer.m_exiting) break;
Chris@0 1310 }
Chris@0 1311
Chris@48 1312 if (m_layer.m_exiting) break;
Chris@48 1313
Chris@0 1314 m_layer.m_cacheInvalid = false;
Chris@0 1315 m_fillExtent = 0;
Chris@0 1316 m_fillCompletion = 0;
Chris@0 1317
Chris@101 1318 // std::cerr << "SpectrogramLayer::CacheFillThread::run: model is ready" << std::endl;
Chris@0 1319
Chris@0 1320 size_t start = m_layer.m_model->getStartFrame();
Chris@0 1321 size_t end = m_layer.m_model->getEndFrame();
Chris@9 1322
Chris@101 1323 // std::cerr << "start = " << start << ", end = " << end << std::endl;
Chris@41 1324
Chris@9 1325 WindowType windowType = m_layer.m_windowType;
Chris@0 1326 size_t windowSize = m_layer.m_windowSize;
Chris@0 1327 size_t windowIncrement = m_layer.getWindowIncrement();
Chris@107 1328 size_t fftSize = m_layer.m_fftSize;
Chris@0 1329
Chris@101 1330 // std::cerr << "\nWINDOW INCREMENT: " << windowIncrement << " (for hop level " << m_layer.m_windowHopLevel << ")\n" << std::endl;
Chris@97 1331
Chris@44 1332 size_t visibleStart = m_layer.m_candidateFillStartFrame;
Chris@44 1333 visibleStart = (visibleStart / windowIncrement) * windowIncrement;
Chris@0 1334
Chris@9 1335 size_t width = (end - start) / windowIncrement + 1;
Chris@107 1336 size_t height = fftSize / 2;
Chris@35 1337
Chris@86 1338 //!!! if (!m_layer.m_cache) {
Chris@86 1339 // m_layer.m_cache = new FFTMemoryCache;
Chris@86 1340 // }
Chris@86 1341 if (!m_layer.m_writeCache) {
Chris@86 1342 m_layer.m_writeCache = new FFTFileCache
Chris@86 1343 (QString("%1").arg(getObjectExportId(&m_layer)),
Chris@88 1344 MatrixFile::ReadWrite);
Chris@86 1345 }
Chris@86 1346 m_layer.m_writeCache->resize(width, height);
Chris@86 1347 if (m_layer.m_cache) delete m_layer.m_cache;
Chris@86 1348 m_layer.m_cache = new FFTFileCache
Chris@86 1349 (QString("%1").arg(getObjectExportId(&m_layer)),
Chris@88 1350 MatrixFile::ReadOnly);
Chris@86 1351
Chris@90 1352 m_layer.setColourmap();
Chris@86 1353 //!!! m_layer.m_writeCache->reset();
Chris@35 1354
Chris@104 1355 fftsample *input = (fftsample *)
Chris@107 1356 fftwf_malloc(fftSize * sizeof(fftsample));
Chris@104 1357
Chris@104 1358 fftwf_complex *output = (fftwf_complex *)
Chris@107 1359 fftwf_malloc(fftSize * sizeof(fftwf_complex));
Chris@102 1360
Chris@102 1361 float *workbuffer = (float *)
Chris@107 1362 fftwf_malloc(fftSize * sizeof(float));
Chris@107 1363
Chris@107 1364 fftwf_plan plan = fftwf_plan_dft_r2c_1d(fftSize, input,
Chris@104 1365 output, FFTW_ESTIMATE);
Chris@102 1366
Chris@102 1367 if (!plan) {
Chris@104 1368 std::cerr << "WARNING: fftwf_plan_dft_r2c_1d(" << windowSize << ") failed!" << std::endl;
Chris@104 1369 fftwf_free(input);
Chris@104 1370 fftwf_free(output);
Chris@104 1371 fftwf_free(workbuffer);
Chris@102 1372 continue;
Chris@102 1373 }
Chris@102 1374
Chris@33 1375 // We don't need a lock when writing to or reading from
Chris@38 1376 // the pixels in the cache. We do need to ensure we have
Chris@38 1377 // the width and height of the cache and the FFT
Chris@38 1378 // parameters known before we unlock, in case they change
Chris@38 1379 // in the model while we aren't holding a lock. It's safe
Chris@38 1380 // for us to continue to use the "old" values if that
Chris@38 1381 // happens, because they will continue to match the
Chris@80 1382 // dimensions of the actual cache (which this thread
Chris@80 1383 // manages, not the layer's).
Chris@0 1384 m_layer.m_mutex.unlock();
Chris@0 1385
Chris@104 1386 Window<fftsample> windower(windowType, windowSize);
Chris@0 1387
Chris@0 1388 int counter = 0;
Chris@0 1389 int updateAt = (end / windowIncrement) / 20;
Chris@0 1390 if (updateAt < 100) updateAt = 100;
Chris@0 1391
Chris@44 1392 bool doVisibleFirst = (visibleStart != start);
Chris@0 1393
Chris@0 1394 if (doVisibleFirst) {
Chris@0 1395
Chris@44 1396 for (size_t f = visibleStart; f < end; f += windowIncrement) {
Chris@0 1397
Chris@38 1398 m_layer.fillCacheColumn(int((f - start) / windowIncrement),
Chris@38 1399 input, output, plan,
Chris@107 1400 windowSize, fftSize,
Chris@107 1401 windowIncrement,
Chris@86 1402 workbuffer, windower);
Chris@38 1403
Chris@38 1404 if (m_layer.m_cacheInvalid || m_layer.m_exiting) {
Chris@0 1405 interrupted = true;
Chris@0 1406 m_fillExtent = 0;
Chris@0 1407 break;
Chris@0 1408 }
Chris@0 1409
Chris@38 1410 if (++counter == updateAt) {
Chris@37 1411 m_fillExtent = f;
Chris@0 1412 m_fillCompletion = size_t(100 * fabsf(float(f - visibleStart) /
Chris@0 1413 float(end - start)));
Chris@0 1414 counter = 0;
Chris@0 1415 }
Chris@0 1416 }
Chris@0 1417 }
Chris@0 1418
Chris@0 1419 if (!interrupted) {
Chris@0 1420
Chris@0 1421 size_t remainingEnd = end;
Chris@0 1422 if (doVisibleFirst) {
Chris@0 1423 remainingEnd = visibleStart;
Chris@0 1424 if (remainingEnd > start) --remainingEnd;
Chris@0 1425 else remainingEnd = start;
Chris@0 1426 }
Chris@0 1427 size_t baseCompletion = m_fillCompletion;
Chris@0 1428
Chris@0 1429 for (size_t f = start; f < remainingEnd; f += windowIncrement) {
Chris@0 1430
Chris@38 1431 m_layer.fillCacheColumn(int((f - start) / windowIncrement),
Chris@38 1432 input, output, plan,
Chris@107 1433 windowSize, fftSize,
Chris@107 1434 windowIncrement,
Chris@86 1435 workbuffer, windower);
Chris@38 1436
Chris@38 1437 if (m_layer.m_cacheInvalid || m_layer.m_exiting) {
Chris@0 1438 interrupted = true;
Chris@0 1439 m_fillExtent = 0;
Chris@0 1440 break;
Chris@0 1441 }
Chris@0 1442
Chris@44 1443 if (++counter == updateAt) {
Chris@0 1444 m_fillExtent = f;
Chris@0 1445 m_fillCompletion = baseCompletion +
Chris@0 1446 size_t(100 * fabsf(float(f - start) /
Chris@0 1447 float(end - start)));
Chris@0 1448 counter = 0;
Chris@0 1449 }
Chris@0 1450 }
Chris@0 1451 }
Chris@0 1452
Chris@104 1453 fftwf_destroy_plan(plan);
Chris@104 1454 fftwf_free(output);
Chris@104 1455 fftwf_free(input);
Chris@104 1456 fftwf_free(workbuffer);
Chris@0 1457
Chris@0 1458 if (!interrupted) {
Chris@0 1459 m_fillExtent = end;
Chris@0 1460 m_fillCompletion = 100;
Chris@0 1461 }
Chris@0 1462
Chris@0 1463 m_layer.m_mutex.lock();
Chris@0 1464 }
Chris@0 1465
Chris@0 1466 if (!interrupted) m_layer.m_condition.wait(&m_layer.m_mutex, 2000);
Chris@0 1467 }
Chris@0 1468 }
Chris@0 1469
Chris@40 1470 float
Chris@40 1471 SpectrogramLayer::getEffectiveMinFrequency() const
Chris@40 1472 {
Chris@40 1473 int sr = m_model->getSampleRate();
Chris@107 1474 float minf = float(sr) / m_fftSize;
Chris@40 1475
Chris@40 1476 if (m_minFrequency > 0.0) {
Chris@107 1477 size_t minbin = size_t((double(m_minFrequency) * m_fftSize) / sr + 0.01);
Chris@40 1478 if (minbin < 1) minbin = 1;
Chris@107 1479 minf = minbin * sr / m_fftSize;
Chris@40 1480 }
Chris@40 1481
Chris@40 1482 return minf;
Chris@40 1483 }
Chris@40 1484
Chris@40 1485 float
Chris@40 1486 SpectrogramLayer::getEffectiveMaxFrequency() const
Chris@40 1487 {
Chris@40 1488 int sr = m_model->getSampleRate();
Chris@40 1489 float maxf = float(sr) / 2;
Chris@40 1490
Chris@40 1491 if (m_maxFrequency > 0.0) {
Chris@107 1492 size_t maxbin = size_t((double(m_maxFrequency) * m_fftSize) / sr + 0.1);
Chris@107 1493 if (maxbin > m_fftSize / 2) maxbin = m_fftSize / 2;
Chris@107 1494 maxf = maxbin * sr / m_fftSize;
Chris@40 1495 }
Chris@40 1496
Chris@40 1497 return maxf;
Chris@40 1498 }
Chris@40 1499
Chris@0 1500 bool
Chris@44 1501 SpectrogramLayer::getYBinRange(View *v, int y, float &q0, float &q1) const
Chris@0 1502 {
Chris@44 1503 int h = v->height();
Chris@0 1504 if (y < 0 || y >= h) return false;
Chris@0 1505
Chris@38 1506 int sr = m_model->getSampleRate();
Chris@40 1507 float minf = getEffectiveMinFrequency();
Chris@40 1508 float maxf = getEffectiveMaxFrequency();
Chris@0 1509
Chris@38 1510 bool logarithmic = (m_frequencyScale == LogFrequencyScale);
Chris@38 1511
Chris@44 1512 q0 = v->getFrequencyForY(y, minf, maxf, logarithmic);
Chris@44 1513 q1 = v->getFrequencyForY(y - 1, minf, maxf, logarithmic);
Chris@38 1514
Chris@38 1515 // Now map these on to actual bins
Chris@38 1516
Chris@107 1517 int b0 = int((q0 * m_fftSize) / sr);
Chris@107 1518 int b1 = int((q1 * m_fftSize) / sr);
Chris@0 1519
Chris@40 1520 //!!! this is supposed to return fractions-of-bins, as it were, hence the floats
Chris@38 1521 q0 = b0;
Chris@38 1522 q1 = b1;
Chris@38 1523
Chris@107 1524 // q0 = (b0 * sr) / m_fftSize;
Chris@107 1525 // q1 = (b1 * sr) / m_fftSize;
Chris@0 1526
Chris@0 1527 return true;
Chris@0 1528 }
Chris@38 1529
Chris@0 1530 bool
Chris@44 1531 SpectrogramLayer::getXBinRange(View *v, int x, float &s0, float &s1) const
Chris@0 1532 {
Chris@21 1533 size_t modelStart = m_model->getStartFrame();
Chris@21 1534 size_t modelEnd = m_model->getEndFrame();
Chris@0 1535
Chris@0 1536 // Each pixel column covers an exact range of sample frames:
Chris@44 1537 int f0 = v->getFrameForX(x) - modelStart;
Chris@44 1538 int f1 = v->getFrameForX(x + 1) - modelStart - 1;
Chris@20 1539
Chris@41 1540 if (f1 < int(modelStart) || f0 > int(modelEnd)) {
Chris@41 1541 return false;
Chris@41 1542 }
Chris@20 1543
Chris@0 1544 // And that range may be drawn from a possibly non-integral
Chris@0 1545 // range of spectrogram windows:
Chris@0 1546
Chris@0 1547 size_t windowIncrement = getWindowIncrement();
Chris@0 1548 s0 = float(f0) / windowIncrement;
Chris@0 1549 s1 = float(f1) / windowIncrement;
Chris@0 1550
Chris@0 1551 return true;
Chris@0 1552 }
Chris@0 1553
Chris@0 1554 bool
Chris@44 1555 SpectrogramLayer::getXBinSourceRange(View *v, int x, RealTime &min, RealTime &max) const
Chris@0 1556 {
Chris@0 1557 float s0 = 0, s1 = 0;
Chris@44 1558 if (!getXBinRange(v, x, s0, s1)) return false;
Chris@0 1559
Chris@0 1560 int s0i = int(s0 + 0.001);
Chris@0 1561 int s1i = int(s1);
Chris@0 1562
Chris@0 1563 int windowIncrement = getWindowIncrement();
Chris@0 1564 int w0 = s0i * windowIncrement - (m_windowSize - windowIncrement)/2;
Chris@0 1565 int w1 = s1i * windowIncrement + windowIncrement +
Chris@0 1566 (m_windowSize - windowIncrement)/2 - 1;
Chris@0 1567
Chris@0 1568 min = RealTime::frame2RealTime(w0, m_model->getSampleRate());
Chris@0 1569 max = RealTime::frame2RealTime(w1, m_model->getSampleRate());
Chris@0 1570 return true;
Chris@0 1571 }
Chris@0 1572
Chris@0 1573 bool
Chris@44 1574 SpectrogramLayer::getYBinSourceRange(View *v, int y, float &freqMin, float &freqMax)
Chris@0 1575 const
Chris@0 1576 {
Chris@0 1577 float q0 = 0, q1 = 0;
Chris@44 1578 if (!getYBinRange(v, y, q0, q1)) return false;
Chris@0 1579
Chris@0 1580 int q0i = int(q0 + 0.001);
Chris@0 1581 int q1i = int(q1);
Chris@0 1582
Chris@0 1583 int sr = m_model->getSampleRate();
Chris@0 1584
Chris@0 1585 for (int q = q0i; q <= q1i; ++q) {
Chris@107 1586 int binfreq = (sr * q) / m_fftSize;
Chris@0 1587 if (q == q0i) freqMin = binfreq;
Chris@0 1588 if (q == q1i) freqMax = binfreq;
Chris@0 1589 }
Chris@0 1590 return true;
Chris@0 1591 }
Chris@35 1592
Chris@35 1593 bool
Chris@44 1594 SpectrogramLayer::getAdjustedYBinSourceRange(View *v, int x, int y,
Chris@35 1595 float &freqMin, float &freqMax,
Chris@35 1596 float &adjFreqMin, float &adjFreqMax)
Chris@35 1597 const
Chris@35 1598 {
Chris@35 1599 float s0 = 0, s1 = 0;
Chris@44 1600 if (!getXBinRange(v, x, s0, s1)) return false;
Chris@35 1601
Chris@35 1602 float q0 = 0, q1 = 0;
Chris@44 1603 if (!getYBinRange(v, y, q0, q1)) return false;
Chris@35 1604
Chris@35 1605 int s0i = int(s0 + 0.001);
Chris@35 1606 int s1i = int(s1);
Chris@35 1607
Chris@35 1608 int q0i = int(q0 + 0.001);
Chris@35 1609 int q1i = int(q1);
Chris@35 1610
Chris@35 1611 int sr = m_model->getSampleRate();
Chris@35 1612
Chris@38 1613 size_t windowSize = m_windowSize;
Chris@38 1614 size_t windowIncrement = getWindowIncrement();
Chris@38 1615
Chris@35 1616 bool haveAdj = false;
Chris@35 1617
Chris@37 1618 bool peaksOnly = (m_binDisplay == PeakBins ||
Chris@37 1619 m_binDisplay == PeakFrequencies);
Chris@37 1620
Chris@35 1621 for (int q = q0i; q <= q1i; ++q) {
Chris@35 1622
Chris@35 1623 for (int s = s0i; s <= s1i; ++s) {
Chris@35 1624
Chris@35 1625 float binfreq = (sr * q) / m_windowSize;
Chris@35 1626 if (q == q0i) freqMin = binfreq;
Chris@35 1627 if (q == q1i) freqMax = binfreq;
Chris@37 1628
Chris@38 1629 if (!m_cache || m_cacheInvalid) break; //!!! lock?
Chris@38 1630
Chris@38 1631 if (peaksOnly && !m_cache->isLocalPeak(s, q)) continue;
Chris@38 1632
Chris@38 1633 if (!m_cache->isOverThreshold(s, q, m_threshold)) continue;
Chris@38 1634
Chris@38 1635 float freq = binfreq;
Chris@38 1636 bool steady = false;
Chris@40 1637
Chris@40 1638 if (s < int(m_cache->getWidth()) - 1) {
Chris@38 1639
Chris@38 1640 freq = calculateFrequency(q,
Chris@38 1641 windowSize,
Chris@38 1642 windowIncrement,
Chris@38 1643 sr,
Chris@38 1644 m_cache->getPhaseAt(s, q),
Chris@38 1645 m_cache->getPhaseAt(s+1, q),
Chris@38 1646 steady);
Chris@35 1647
Chris@38 1648 if (!haveAdj || freq < adjFreqMin) adjFreqMin = freq;
Chris@38 1649 if (!haveAdj || freq > adjFreqMax) adjFreqMax = freq;
Chris@35 1650
Chris@35 1651 haveAdj = true;
Chris@35 1652 }
Chris@35 1653 }
Chris@35 1654 }
Chris@35 1655
Chris@35 1656 if (!haveAdj) {
Chris@40 1657 adjFreqMin = adjFreqMax = 0.0;
Chris@35 1658 }
Chris@35 1659
Chris@35 1660 return haveAdj;
Chris@35 1661 }
Chris@0 1662
Chris@0 1663 bool
Chris@44 1664 SpectrogramLayer::getXYBinSourceRange(View *v, int x, int y,
Chris@38 1665 float &min, float &max,
Chris@38 1666 float &phaseMin, float &phaseMax) const
Chris@0 1667 {
Chris@0 1668 float q0 = 0, q1 = 0;
Chris@44 1669 if (!getYBinRange(v, y, q0, q1)) return false;
Chris@0 1670
Chris@0 1671 float s0 = 0, s1 = 0;
Chris@44 1672 if (!getXBinRange(v, x, s0, s1)) return false;
Chris@0 1673
Chris@0 1674 int q0i = int(q0 + 0.001);
Chris@0 1675 int q1i = int(q1);
Chris@0 1676
Chris@0 1677 int s0i = int(s0 + 0.001);
Chris@0 1678 int s1i = int(s1);
Chris@0 1679
Chris@37 1680 bool rv = false;
Chris@37 1681
Chris@0 1682 if (m_mutex.tryLock()) {
Chris@0 1683 if (m_cache && !m_cacheInvalid) {
Chris@0 1684
Chris@31 1685 int cw = m_cache->getWidth();
Chris@31 1686 int ch = m_cache->getHeight();
Chris@0 1687
Chris@38 1688 min = 0.0;
Chris@38 1689 max = 0.0;
Chris@38 1690 phaseMin = 0.0;
Chris@38 1691 phaseMax = 0.0;
Chris@38 1692 bool have = false;
Chris@0 1693
Chris@0 1694 for (int q = q0i; q <= q1i; ++q) {
Chris@0 1695 for (int s = s0i; s <= s1i; ++s) {
Chris@0 1696 if (s >= 0 && q >= 0 && s < cw && q < ch) {
Chris@38 1697
Chris@97 1698 if (!m_cache->haveSetColumnAt(s)) continue;
Chris@91 1699
Chris@38 1700 float value;
Chris@38 1701
Chris@38 1702 value = m_cache->getPhaseAt(s, q);
Chris@38 1703 if (!have || value < phaseMin) { phaseMin = value; }
Chris@38 1704 if (!have || value > phaseMax) { phaseMax = value; }
Chris@38 1705
Chris@38 1706 value = m_cache->getMagnitudeAt(s, q);
Chris@38 1707 if (!have || value < min) { min = value; }
Chris@38 1708 if (!have || value > max) { max = value; }
Chris@38 1709
Chris@38 1710 have = true;
Chris@0 1711 }
Chris@0 1712 }
Chris@0 1713 }
Chris@0 1714
Chris@38 1715 if (have) {
Chris@37 1716 rv = true;
Chris@37 1717 }
Chris@0 1718 }
Chris@0 1719
Chris@0 1720 m_mutex.unlock();
Chris@0 1721 }
Chris@0 1722
Chris@37 1723 return rv;
Chris@0 1724 }
Chris@0 1725
Chris@0 1726 void
Chris@44 1727 SpectrogramLayer::paint(View *v, QPainter &paint, QRect rect) const
Chris@0 1728 {
Chris@55 1729 if (m_colourScheme == BlackOnWhite) {
Chris@55 1730 v->setLightBackground(true);
Chris@55 1731 } else {
Chris@55 1732 v->setLightBackground(false);
Chris@55 1733 }
Chris@55 1734
Chris@0 1735 // Profiler profiler("SpectrogramLayer::paint", true);
Chris@0 1736 #ifdef DEBUG_SPECTROGRAM_REPAINT
Chris@95 1737 std::cerr << "SpectrogramLayer::paint(): m_model is " << m_model << ", zoom level is " << v->getZoomLevel() << ", m_updateTimer " << m_updateTimer << std::endl;
Chris@95 1738
Chris@95 1739 std::cerr << "rect is " << rect.x() << "," << rect.y() << " " << rect.width() << "x" << rect.height() << std::endl;
Chris@0 1740 #endif
Chris@95 1741
Chris@45 1742 long sf = v->getStartFrame();
Chris@45 1743 if (sf < 0) m_candidateFillStartFrame = 0;
Chris@45 1744 else m_candidateFillStartFrame = sf;
Chris@44 1745
Chris@0 1746 if (!m_model || !m_model->isOK() || !m_model->isReady()) {
Chris@0 1747 return;
Chris@0 1748 }
Chris@0 1749
Chris@47 1750 if (isLayerDormant(v)) {
Chris@48 1751 std::cerr << "SpectrogramLayer::paint(): Layer is dormant, making it undormant again" << std::endl;
Chris@29 1752 }
Chris@29 1753
Chris@48 1754 // Need to do this even if !isLayerDormant, as that could mean v
Chris@48 1755 // is not in the dormancy map at all -- we need it to be present
Chris@48 1756 // and accountable for when determining whether we need the cache
Chris@48 1757 // in the cache-fill thread above.
Chris@48 1758 m_dormancy[v] = false;
Chris@48 1759
Chris@0 1760 #ifdef DEBUG_SPECTROGRAM_REPAINT
Chris@95 1761 // std::cerr << "SpectrogramLayer::paint(): About to lock" << std::endl;
Chris@0 1762 #endif
Chris@0 1763
Chris@37 1764 m_mutex.lock();
Chris@0 1765
Chris@0 1766 #ifdef DEBUG_SPECTROGRAM_REPAINT
Chris@95 1767 // std::cerr << "SpectrogramLayer::paint(): locked" << std::endl;
Chris@0 1768 #endif
Chris@0 1769
Chris@0 1770 if (m_cacheInvalid) { // lock the mutex before checking this
Chris@0 1771 m_mutex.unlock();
Chris@0 1772 #ifdef DEBUG_SPECTROGRAM_REPAINT
Chris@0 1773 std::cerr << "SpectrogramLayer::paint(): Cache invalid, returning" << std::endl;
Chris@0 1774 #endif
Chris@0 1775 return;
Chris@0 1776 }
Chris@0 1777
Chris@95 1778 PixmapCache &cache = m_pixmapCaches[v];
Chris@95 1779
Chris@95 1780 #ifdef DEBUG_SPECTROGRAM_REPAINT
Chris@95 1781 std::cerr << "SpectrogramLayer::paint(): pixmap cache valid area " << cache.validArea.x() << ", " << cache.validArea.y() << ", " << cache.validArea.width() << "x" << cache.validArea.height() << std::endl;
Chris@95 1782 #endif
Chris@95 1783
Chris@0 1784 bool stillCacheing = (m_updateTimer != 0);
Chris@0 1785
Chris@0 1786 #ifdef DEBUG_SPECTROGRAM_REPAINT
Chris@0 1787 std::cerr << "SpectrogramLayer::paint(): Still cacheing = " << stillCacheing << std::endl;
Chris@0 1788 #endif
Chris@0 1789
Chris@44 1790 long startFrame = v->getStartFrame();
Chris@44 1791 int zoomLevel = v->getZoomLevel();
Chris@0 1792
Chris@0 1793 int x0 = 0;
Chris@44 1794 int x1 = v->width();
Chris@0 1795 int y0 = 0;
Chris@44 1796 int y1 = v->height();
Chris@0 1797
Chris@0 1798 bool recreateWholePixmapCache = true;
Chris@0 1799
Chris@95 1800 x0 = rect.left();
Chris@95 1801 x1 = rect.right() + 1;
Chris@95 1802 y0 = rect.top();
Chris@95 1803 y1 = rect.bottom() + 1;
Chris@95 1804
Chris@95 1805 if (cache.validArea.width() > 0) {
Chris@95 1806
Chris@95 1807 if (int(cache.zoomLevel) == zoomLevel &&
Chris@95 1808 cache.pixmap.width() == v->width() &&
Chris@95 1809 cache.pixmap.height() == v->height()) {
Chris@95 1810
Chris@95 1811 if (v->getXForFrame(cache.startFrame) ==
Chris@95 1812 v->getXForFrame(startFrame) &&
Chris@95 1813 cache.validArea.x() <= x0 &&
Chris@95 1814 cache.validArea.x() + cache.validArea.width() >= x1) {
Chris@0 1815
Chris@0 1816 #ifdef DEBUG_SPECTROGRAM_REPAINT
Chris@0 1817 std::cerr << "SpectrogramLayer: pixmap cache good" << std::endl;
Chris@0 1818 #endif
Chris@0 1819
Chris@0 1820 m_mutex.unlock();
Chris@95 1821 paint.drawPixmap(rect, cache.pixmap, rect);
Chris@0 1822 return;
Chris@0 1823
Chris@0 1824 } else {
Chris@0 1825
Chris@0 1826 #ifdef DEBUG_SPECTROGRAM_REPAINT
Chris@0 1827 std::cerr << "SpectrogramLayer: pixmap cache partially OK" << std::endl;
Chris@0 1828 #endif
Chris@0 1829
Chris@0 1830 recreateWholePixmapCache = false;
Chris@0 1831
Chris@95 1832 int dx = v->getXForFrame(cache.startFrame) -
Chris@44 1833 v->getXForFrame(startFrame);
Chris@0 1834
Chris@0 1835 #ifdef DEBUG_SPECTROGRAM_REPAINT
Chris@95 1836 std::cerr << "SpectrogramLayer: dx = " << dx << " (pixmap cache " << cache.pixmap.width() << "x" << cache.pixmap.height() << ")" << std::endl;
Chris@0 1837 #endif
Chris@0 1838
Chris@95 1839 if (dx != 0 &&
Chris@95 1840 dx > -cache.pixmap.width() &&
Chris@95 1841 dx < cache.pixmap.width()) {
Chris@0 1842
Chris@0 1843 #if defined(Q_WS_WIN32) || defined(Q_WS_MAC)
Chris@0 1844 // Copying a pixmap to itself doesn't work
Chris@0 1845 // properly on Windows or Mac (it only works when
Chris@0 1846 // moving in one direction).
Chris@0 1847
Chris@0 1848 //!!! Need a utility function for this
Chris@0 1849
Chris@0 1850 static QPixmap *tmpPixmap = 0;
Chris@0 1851 if (!tmpPixmap ||
Chris@95 1852 tmpPixmap->width() != cache.pixmap.width() ||
Chris@95 1853 tmpPixmap->height() != cache.pixmap.height()) {
Chris@0 1854 delete tmpPixmap;
Chris@95 1855 tmpPixmap = new QPixmap(cache.pixmap.width(),
Chris@95 1856 cache.pixmap.height());
Chris@0 1857 }
Chris@0 1858 QPainter cachePainter;
Chris@0 1859 cachePainter.begin(tmpPixmap);
Chris@95 1860 cachePainter.drawPixmap(0, 0, cache.pixmap);
Chris@0 1861 cachePainter.end();
Chris@95 1862 cachePainter.begin(&cache.pixmap);
Chris@0 1863 cachePainter.drawPixmap(dx, 0, *tmpPixmap);
Chris@0 1864 cachePainter.end();
Chris@0 1865 #else
Chris@95 1866 QPainter cachePainter(&cache.pixmap);
Chris@95 1867 cachePainter.drawPixmap(dx, 0, cache.pixmap);
Chris@0 1868 cachePainter.end();
Chris@0 1869 #endif
Chris@0 1870
Chris@95 1871 int px = cache.validArea.x();
Chris@95 1872 int pw = cache.validArea.width();
Chris@0 1873
Chris@0 1874 if (dx < 0) {
Chris@95 1875 x0 = cache.pixmap.width() + dx;
Chris@95 1876 x1 = cache.pixmap.width();
Chris@95 1877 px += dx;
Chris@95 1878 if (px < 0) {
Chris@95 1879 pw += px;
Chris@95 1880 px = 0;
Chris@95 1881 if (pw < 0) pw = 0;
Chris@95 1882 }
Chris@0 1883 } else {
Chris@0 1884 x0 = 0;
Chris@0 1885 x1 = dx;
Chris@95 1886 px += dx;
Chris@95 1887 if (px + pw > cache.pixmap.width()) {
Chris@95 1888 pw = int(cache.pixmap.width()) - px;
Chris@95 1889 if (pw < 0) pw = 0;
Chris@95 1890 }
Chris@0 1891 }
Chris@95 1892
Chris@95 1893 cache.validArea =
Chris@95 1894 QRect(px, cache.validArea.y(),
Chris@95 1895 pw, cache.validArea.height());
Chris@95 1896
Chris@95 1897 paint.drawPixmap(rect & cache.validArea,
Chris@95 1898 cache.pixmap,
Chris@95 1899 rect & cache.validArea);
Chris@0 1900 }
Chris@0 1901 }
Chris@0 1902 } else {
Chris@0 1903 #ifdef DEBUG_SPECTROGRAM_REPAINT
Chris@0 1904 std::cerr << "SpectrogramLayer: pixmap cache useless" << std::endl;
Chris@0 1905 #endif
Chris@95 1906 cache.validArea = QRect();
Chris@0 1907 }
Chris@0 1908 }
Chris@95 1909
Chris@92 1910 /*
Chris@0 1911 if (stillCacheing) {
Chris@0 1912 x0 = rect.left();
Chris@0 1913 x1 = rect.right() + 1;
Chris@0 1914 y0 = rect.top();
Chris@0 1915 y1 = rect.bottom() + 1;
Chris@0 1916 }
Chris@92 1917 */
Chris@95 1918
Chris@95 1919 if (recreateWholePixmapCache) {
Chris@95 1920 x0 = 0;
Chris@95 1921 x1 = v->width();
Chris@95 1922 }
Chris@95 1923
Chris@96 1924 int paintBlockWidth = (300000 / zoomLevel);
Chris@96 1925 if (paintBlockWidth < 20) paintBlockWidth = 20;
Chris@96 1926
Chris@96 1927 if (cache.validArea.width() > 0) {
Chris@96 1928
Chris@96 1929 int vx0 = 0, vx1 = 0;
Chris@96 1930 vx0 = cache.validArea.x();
Chris@96 1931 vx1 = cache.validArea.x() + cache.validArea.width();
Chris@96 1932
Chris@96 1933 #ifdef DEBUG_SPECTROGRAM_REPAINT
Chris@96 1934 std::cerr << "x0 " << x0 << ", x1 " << x1 << ", vx0 " << vx0 << ", vx1 " << vx1 << ", paintBlockWidth " << paintBlockWidth << std::endl;
Chris@96 1935 #endif
Chris@96 1936 if (x0 < vx0) {
Chris@96 1937 if (x0 + paintBlockWidth < vx0) {
Chris@96 1938 x0 = vx0 - paintBlockWidth;
Chris@96 1939 } else {
Chris@96 1940 x0 = 0;
Chris@96 1941 }
Chris@96 1942 } else if (x0 > vx1) {
Chris@96 1943 x0 = vx1;
Chris@96 1944 }
Chris@95 1945
Chris@96 1946 if (x1 < vx0) {
Chris@96 1947 x1 = vx0;
Chris@96 1948 } else if (x1 > vx1) {
Chris@96 1949 if (vx1 + paintBlockWidth < x1) {
Chris@96 1950 x1 = vx1 + paintBlockWidth;
Chris@96 1951 } else {
Chris@96 1952 x1 = v->width();
Chris@95 1953 }
Chris@96 1954 }
Chris@95 1955
Chris@96 1956 cache.validArea = QRect
Chris@96 1957 (std::min(vx0, x0), cache.validArea.y(),
Chris@96 1958 std::max(vx1 - std::min(vx0, x0),
Chris@96 1959 x1 - std::min(vx0, x0)),
Chris@96 1960 cache.validArea.height());
Chris@95 1961
Chris@96 1962 } else {
Chris@96 1963 if (x1 > x0 + paintBlockWidth) {
Chris@96 1964 x1 = x0 + paintBlockWidth;
Chris@95 1965 }
Chris@96 1966 cache.validArea = QRect(x0, 0, x1 - x0, v->height());
Chris@95 1967 }
Chris@95 1968
Chris@0 1969 int w = x1 - x0;
Chris@0 1970 int h = y1 - y0;
Chris@0 1971
Chris@95 1972 #ifdef DEBUG_SPECTROGRAM_REPAINT
Chris@95 1973 std::cerr << "x0 " << x0 << ", x1 " << x1 << ", w " << w << ", h " << h << std::endl;
Chris@95 1974 #endif
Chris@95 1975
Chris@95 1976 if (m_drawBuffer.width() < w || m_drawBuffer.height() < h) {
Chris@95 1977 m_drawBuffer = QImage(w, h, QImage::Format_RGB32);
Chris@95 1978 }
Chris@95 1979
Chris@97 1980 m_drawBuffer.fill(m_colourMap.getColour(0).rgb());
Chris@35 1981
Chris@37 1982 int sr = m_model->getSampleRate();
Chris@35 1983
Chris@107 1984 size_t bins = m_fftSize / 2;
Chris@35 1985 if (m_maxFrequency > 0) {
Chris@107 1986 bins = int((double(m_maxFrequency) * m_fftSize) / sr + 0.1);
Chris@107 1987 if (bins > m_fftSize / 2) bins = m_fftSize / 2;
Chris@35 1988 }
Chris@35 1989
Chris@40 1990 size_t minbin = 1;
Chris@37 1991 if (m_minFrequency > 0) {
Chris@107 1992 minbin = int((double(m_minFrequency) * m_fftSize) / sr + 0.1);
Chris@40 1993 if (minbin < 1) minbin = 1;
Chris@37 1994 if (minbin >= bins) minbin = bins - 1;
Chris@37 1995 }
Chris@37 1996
Chris@107 1997 float minFreq = (float(minbin) * sr) / m_fftSize;
Chris@107 1998 float maxFreq = (float(bins) * sr) / m_fftSize;
Chris@0 1999
Chris@92 2000 float ymag[h];
Chris@92 2001 float ydiv[h];
Chris@92 2002 float yval[bins + 1];
Chris@92 2003
Chris@38 2004 size_t increment = getWindowIncrement();
Chris@40 2005
Chris@40 2006 bool logarithmic = (m_frequencyScale == LogFrequencyScale);
Chris@38 2007
Chris@0 2008 m_mutex.unlock();
Chris@0 2009
Chris@92 2010 for (size_t q = minbin; q <= bins; ++q) {
Chris@107 2011 float f0 = (float(q) * sr) / m_fftSize;
Chris@92 2012 yval[q] = v->getYForFrequency(f0, minFreq, maxFreq, logarithmic);
Chris@92 2013 }
Chris@92 2014
Chris@95 2015 m_mutex.lock();
Chris@95 2016
Chris@35 2017 for (int x = 0; x < w; ++x) {
Chris@35 2018
Chris@95 2019 if (x % 10 == 0) {
Chris@95 2020 m_mutex.unlock();
Chris@95 2021 m_mutex.lock();
Chris@95 2022 if (m_cacheInvalid) {
Chris@95 2023 break;
Chris@95 2024 }
Chris@95 2025 }
Chris@35 2026
Chris@35 2027 for (int y = 0; y < h; ++y) {
Chris@40 2028 ymag[y] = 0.0;
Chris@40 2029 ydiv[y] = 0.0;
Chris@35 2030 }
Chris@35 2031
Chris@35 2032 float s0 = 0, s1 = 0;
Chris@35 2033
Chris@44 2034 if (!getXBinRange(v, x0 + x, s0, s1)) {
Chris@95 2035 assert(x <= m_drawBuffer.width());
Chris@35 2036 continue;
Chris@35 2037 }
Chris@35 2038
Chris@35 2039 int s0i = int(s0 + 0.001);
Chris@35 2040 int s1i = int(s1);
Chris@35 2041
Chris@45 2042 if (s1i >= m_cache->getWidth()) {
Chris@45 2043 if (s0i >= m_cache->getWidth()) {
Chris@45 2044 continue;
Chris@45 2045 } else {
Chris@45 2046 s1i = s0i;
Chris@45 2047 }
Chris@45 2048 }
Chris@92 2049
Chris@92 2050 for (int s = s0i; s <= s1i; ++s) {
Chris@92 2051
Chris@97 2052 if (!m_cache->haveSetColumnAt(s)) continue;
Chris@92 2053
Chris@92 2054 for (size_t q = minbin; q < bins; ++q) {
Chris@92 2055
Chris@92 2056 float y0 = yval[q + 1];
Chris@92 2057 float y1 = yval[q];
Chris@92 2058
Chris@40 2059 if (m_binDisplay == PeakBins ||
Chris@40 2060 m_binDisplay == PeakFrequencies) {
Chris@40 2061 if (!m_cache->isLocalPeak(s, q)) continue;
Chris@40 2062 }
Chris@40 2063
Chris@40 2064 if (!m_cache->isOverThreshold(s, q, m_threshold)) continue;
Chris@40 2065
Chris@35 2066 float sprop = 1.0;
Chris@35 2067 if (s == s0i) sprop *= (s + 1) - s0;
Chris@35 2068 if (s == s1i) sprop *= s1 - s;
Chris@35 2069
Chris@38 2070 if (m_binDisplay == PeakFrequencies &&
Chris@40 2071 s < int(m_cache->getWidth()) - 1) {
Chris@35 2072
Chris@38 2073 bool steady = false;
Chris@92 2074 float f = calculateFrequency(q,
Chris@38 2075 m_windowSize,
Chris@38 2076 increment,
Chris@38 2077 sr,
Chris@38 2078 m_cache->getPhaseAt(s, q),
Chris@38 2079 m_cache->getPhaseAt(s+1, q),
Chris@38 2080 steady);
Chris@40 2081
Chris@44 2082 y0 = y1 = v->getYForFrequency
Chris@92 2083 (f, minFreq, maxFreq, logarithmic);
Chris@35 2084 }
Chris@38 2085
Chris@35 2086 int y0i = int(y0 + 0.001);
Chris@35 2087 int y1i = int(y1);
Chris@35 2088
Chris@92 2089 float value;
Chris@92 2090
Chris@92 2091 if (m_colourScale == PhaseColourScale) {
Chris@92 2092 value = m_cache->getPhaseAt(s, q);
Chris@92 2093 } else if (m_normalizeColumns) {
Chris@92 2094 value = m_cache->getNormalizedMagnitudeAt(s, q) * m_gain;
Chris@92 2095 } else {
Chris@92 2096 value = m_cache->getMagnitudeAt(s, q) * m_gain;
Chris@92 2097 }
Chris@92 2098
Chris@35 2099 for (int y = y0i; y <= y1i; ++y) {
Chris@35 2100
Chris@35 2101 if (y < 0 || y >= h) continue;
Chris@35 2102
Chris@35 2103 float yprop = sprop;
Chris@35 2104 if (y == y0i) yprop *= (y + 1) - y0;
Chris@35 2105 if (y == y1i) yprop *= y1 - y;
Chris@37 2106 ymag[y] += yprop * value;
Chris@35 2107 ydiv[y] += yprop;
Chris@35 2108 }
Chris@35 2109 }
Chris@35 2110 }
Chris@35 2111
Chris@35 2112 for (int y = 0; y < h; ++y) {
Chris@35 2113
Chris@35 2114 if (ydiv[y] > 0.0) {
Chris@40 2115
Chris@40 2116 unsigned char pixel = 0;
Chris@40 2117
Chris@38 2118 float avg = ymag[y] / ydiv[y];
Chris@38 2119 pixel = getDisplayValue(avg);
Chris@40 2120
Chris@95 2121 assert(x <= m_drawBuffer.width());
Chris@86 2122 QColor c = m_colourMap.getColour(pixel);
Chris@95 2123 m_drawBuffer.setPixel(x, y,
Chris@95 2124 qRgb(c.red(), c.green(), c.blue()));
Chris@35 2125 }
Chris@35 2126 }
Chris@35 2127 }
Chris@35 2128
Chris@95 2129 m_mutex.unlock();
Chris@95 2130
Chris@95 2131 paint.drawImage(x0, y0, m_drawBuffer, 0, 0, w, h);
Chris@0 2132
Chris@0 2133 if (recreateWholePixmapCache) {
Chris@95 2134 cache.pixmap = QPixmap(v->width(), v->height());
Chris@0 2135 }
Chris@0 2136
Chris@95 2137 QPainter cachePainter(&cache.pixmap);
Chris@95 2138 cachePainter.drawImage(x0, y0, m_drawBuffer, 0, 0, w, h);
Chris@0 2139 cachePainter.end();
Chris@0 2140
Chris@95 2141 // m_pixmapCacheInvalid = false;
Chris@95 2142 cache.startFrame = startFrame;
Chris@95 2143 cache.zoomLevel = zoomLevel;
Chris@95 2144
Chris@95 2145 if (cache.validArea.x() > 0) {
Chris@95 2146 #ifdef DEBUG_SPECTROGRAM_REPAINT
Chris@95 2147 std::cerr << "SpectrogramLayer::paint() updating left" << std::endl;
Chris@95 2148 #endif
Chris@95 2149 v->update(0, 0, cache.validArea.x(), v->height());
Chris@95 2150 }
Chris@95 2151
Chris@95 2152 if (cache.validArea.x() + cache.validArea.width() <
Chris@95 2153 cache.pixmap.width()) {
Chris@95 2154 #ifdef DEBUG_SPECTROGRAM_REPAINT
Chris@95 2155 std::cerr << "SpectrogramLayer::paint() updating right ("
Chris@95 2156 << cache.validArea.x() + cache.validArea.width()
Chris@95 2157 << ", "
Chris@95 2158 << cache.pixmap.width() - (cache.validArea.x() +
Chris@95 2159 cache.validArea.width())
Chris@95 2160 << ")" << std::endl;
Chris@95 2161 #endif
Chris@95 2162 v->update(cache.validArea.x() + cache.validArea.width(),
Chris@95 2163 0,
Chris@95 2164 cache.pixmap.width() - (cache.validArea.x() +
Chris@95 2165 cache.validArea.width()),
Chris@95 2166 v->height());
Chris@95 2167 }
Chris@0 2168
Chris@0 2169 #ifdef DEBUG_SPECTROGRAM_REPAINT
Chris@0 2170 std::cerr << "SpectrogramLayer::paint() returning" << std::endl;
Chris@0 2171 #endif
Chris@0 2172 }
Chris@0 2173
Chris@42 2174 float
Chris@44 2175 SpectrogramLayer::getYForFrequency(View *v, float frequency) const
Chris@42 2176 {
Chris@44 2177 return v->getYForFrequency(frequency,
Chris@44 2178 getEffectiveMinFrequency(),
Chris@44 2179 getEffectiveMaxFrequency(),
Chris@44 2180 m_frequencyScale == LogFrequencyScale);
Chris@42 2181 }
Chris@42 2182
Chris@42 2183 float
Chris@44 2184 SpectrogramLayer::getFrequencyForY(View *v, int y) const
Chris@42 2185 {
Chris@44 2186 return v->getFrequencyForY(y,
Chris@44 2187 getEffectiveMinFrequency(),
Chris@44 2188 getEffectiveMaxFrequency(),
Chris@44 2189 m_frequencyScale == LogFrequencyScale);
Chris@42 2190 }
Chris@42 2191
Chris@0 2192 int
Chris@0 2193 SpectrogramLayer::getCompletion() const
Chris@0 2194 {
Chris@0 2195 if (m_updateTimer == 0) return 100;
Chris@0 2196 size_t completion = m_fillThread->getFillCompletion();
Chris@0 2197 // std::cerr << "SpectrogramLayer::getCompletion: completion = " << completion << std::endl;
Chris@0 2198 return completion;
Chris@0 2199 }
Chris@0 2200
Chris@28 2201 bool
Chris@101 2202 SpectrogramLayer::getValueExtents(float &min, float &max,
Chris@101 2203 bool &logarithmic, QString &unit) const
Chris@79 2204 {
Chris@79 2205 min = getEffectiveMinFrequency();
Chris@79 2206 max = getEffectiveMaxFrequency();
Chris@101 2207 logarithmic = (m_frequencyScale == LogFrequencyScale);
Chris@79 2208 unit = "Hz";
Chris@79 2209 return true;
Chris@79 2210 }
Chris@79 2211
Chris@79 2212 bool
Chris@101 2213 SpectrogramLayer::getDisplayExtents(float &min, float &max) const
Chris@101 2214 {
Chris@101 2215 min = getEffectiveMinFrequency();
Chris@101 2216 max = getEffectiveMaxFrequency();
Chris@101 2217 return true;
Chris@101 2218 }
Chris@101 2219
Chris@101 2220 bool
Chris@44 2221 SpectrogramLayer::snapToFeatureFrame(View *v, int &frame,
Chris@28 2222 size_t &resolution,
Chris@28 2223 SnapType snap) const
Chris@13 2224 {
Chris@13 2225 resolution = getWindowIncrement();
Chris@28 2226 int left = (frame / resolution) * resolution;
Chris@28 2227 int right = left + resolution;
Chris@28 2228
Chris@28 2229 switch (snap) {
Chris@28 2230 case SnapLeft: frame = left; break;
Chris@28 2231 case SnapRight: frame = right; break;
Chris@28 2232 case SnapNearest:
Chris@28 2233 case SnapNeighbouring:
Chris@28 2234 if (frame - left > right - frame) frame = right;
Chris@28 2235 else frame = left;
Chris@28 2236 break;
Chris@28 2237 }
Chris@28 2238
Chris@28 2239 return true;
Chris@28 2240 }
Chris@13 2241
Chris@77 2242 bool
Chris@77 2243 SpectrogramLayer::getCrosshairExtents(View *v, QPainter &paint,
Chris@77 2244 QPoint cursorPos,
Chris@77 2245 std::vector<QRect> &extents) const
Chris@77 2246 {
Chris@77 2247 QRect vertical(cursorPos.x() - 12, 0, 12, v->height());
Chris@77 2248 extents.push_back(vertical);
Chris@77 2249
Chris@77 2250 QRect horizontal(0, cursorPos.y(), cursorPos.x(), 1);
Chris@77 2251 extents.push_back(horizontal);
Chris@77 2252
Chris@77 2253 return true;
Chris@77 2254 }
Chris@77 2255
Chris@77 2256 void
Chris@77 2257 SpectrogramLayer::paintCrosshairs(View *v, QPainter &paint,
Chris@77 2258 QPoint cursorPos) const
Chris@77 2259 {
Chris@77 2260 paint.save();
Chris@77 2261 paint.setPen(m_crosshairColour);
Chris@77 2262
Chris@77 2263 paint.drawLine(0, cursorPos.y(), cursorPos.x() - 1, cursorPos.y());
Chris@77 2264 paint.drawLine(cursorPos.x(), 0, cursorPos.x(), v->height());
Chris@77 2265
Chris@77 2266 float fundamental = getFrequencyForY(v, cursorPos.y());
Chris@77 2267
Chris@77 2268 int harmonic = 2;
Chris@77 2269
Chris@77 2270 while (harmonic < 100) {
Chris@77 2271
Chris@77 2272 float hy = lrintf(getYForFrequency(v, fundamental * harmonic));
Chris@77 2273 if (hy < 0 || hy > v->height()) break;
Chris@77 2274
Chris@77 2275 int len = 7;
Chris@77 2276
Chris@77 2277 if (harmonic % 2 == 0) {
Chris@77 2278 if (harmonic % 4 == 0) {
Chris@77 2279 len = 12;
Chris@77 2280 } else {
Chris@77 2281 len = 10;
Chris@77 2282 }
Chris@77 2283 }
Chris@77 2284
Chris@77 2285 paint.drawLine(cursorPos.x() - len,
Chris@77 2286 hy,
Chris@77 2287 cursorPos.x(),
Chris@77 2288 hy);
Chris@77 2289
Chris@77 2290 ++harmonic;
Chris@77 2291 }
Chris@77 2292
Chris@77 2293 paint.restore();
Chris@77 2294 }
Chris@77 2295
Chris@25 2296 QString
Chris@44 2297 SpectrogramLayer::getFeatureDescription(View *v, QPoint &pos) const
Chris@25 2298 {
Chris@25 2299 int x = pos.x();
Chris@25 2300 int y = pos.y();
Chris@0 2301
Chris@25 2302 if (!m_model || !m_model->isOK()) return "";
Chris@0 2303
Chris@38 2304 float magMin = 0, magMax = 0;
Chris@38 2305 float phaseMin = 0, phaseMax = 0;
Chris@0 2306 float freqMin = 0, freqMax = 0;
Chris@35 2307 float adjFreqMin = 0, adjFreqMax = 0;
Chris@25 2308 QString pitchMin, pitchMax;
Chris@0 2309 RealTime rtMin, rtMax;
Chris@0 2310
Chris@38 2311 bool haveValues = false;
Chris@0 2312
Chris@44 2313 if (!getXBinSourceRange(v, x, rtMin, rtMax)) {
Chris@38 2314 return "";
Chris@38 2315 }
Chris@44 2316 if (getXYBinSourceRange(v, x, y, magMin, magMax, phaseMin, phaseMax)) {
Chris@38 2317 haveValues = true;
Chris@38 2318 }
Chris@0 2319
Chris@35 2320 QString adjFreqText = "", adjPitchText = "";
Chris@35 2321
Chris@38 2322 if (m_binDisplay == PeakFrequencies) {
Chris@35 2323
Chris@44 2324 if (!getAdjustedYBinSourceRange(v, x, y, freqMin, freqMax,
Chris@38 2325 adjFreqMin, adjFreqMax)) {
Chris@38 2326 return "";
Chris@38 2327 }
Chris@35 2328
Chris@35 2329 if (adjFreqMin != adjFreqMax) {
Chris@65 2330 adjFreqText = tr("Peak Frequency:\t%1 - %2 Hz\n")
Chris@35 2331 .arg(adjFreqMin).arg(adjFreqMax);
Chris@35 2332 } else {
Chris@65 2333 adjFreqText = tr("Peak Frequency:\t%1 Hz\n")
Chris@35 2334 .arg(adjFreqMin);
Chris@38 2335 }
Chris@38 2336
Chris@38 2337 QString pmin = Pitch::getPitchLabelForFrequency(adjFreqMin);
Chris@38 2338 QString pmax = Pitch::getPitchLabelForFrequency(adjFreqMax);
Chris@38 2339
Chris@38 2340 if (pmin != pmax) {
Chris@65 2341 adjPitchText = tr("Peak Pitch:\t%3 - %4\n").arg(pmin).arg(pmax);
Chris@38 2342 } else {
Chris@65 2343 adjPitchText = tr("Peak Pitch:\t%2\n").arg(pmin);
Chris@35 2344 }
Chris@35 2345
Chris@35 2346 } else {
Chris@35 2347
Chris@44 2348 if (!getYBinSourceRange(v, y, freqMin, freqMax)) return "";
Chris@35 2349 }
Chris@35 2350
Chris@25 2351 QString text;
Chris@25 2352
Chris@25 2353 if (rtMin != rtMax) {
Chris@25 2354 text += tr("Time:\t%1 - %2\n")
Chris@25 2355 .arg(rtMin.toText(true).c_str())
Chris@25 2356 .arg(rtMax.toText(true).c_str());
Chris@25 2357 } else {
Chris@25 2358 text += tr("Time:\t%1\n")
Chris@25 2359 .arg(rtMin.toText(true).c_str());
Chris@0 2360 }
Chris@0 2361
Chris@25 2362 if (freqMin != freqMax) {
Chris@65 2363 text += tr("%1Bin Frequency:\t%2 - %3 Hz\n%4Bin Pitch:\t%5 - %6\n")
Chris@65 2364 .arg(adjFreqText)
Chris@25 2365 .arg(freqMin)
Chris@25 2366 .arg(freqMax)
Chris@65 2367 .arg(adjPitchText)
Chris@65 2368 .arg(Pitch::getPitchLabelForFrequency(freqMin))
Chris@65 2369 .arg(Pitch::getPitchLabelForFrequency(freqMax));
Chris@65 2370 } else {
Chris@65 2371 text += tr("%1Bin Frequency:\t%2 Hz\n%3Bin Pitch:\t%4\n")
Chris@35 2372 .arg(adjFreqText)
Chris@25 2373 .arg(freqMin)
Chris@65 2374 .arg(adjPitchText)
Chris@65 2375 .arg(Pitch::getPitchLabelForFrequency(freqMin));
Chris@25 2376 }
Chris@25 2377
Chris@38 2378 if (haveValues) {
Chris@38 2379 float dbMin = AudioLevel::multiplier_to_dB(magMin);
Chris@38 2380 float dbMax = AudioLevel::multiplier_to_dB(magMax);
Chris@43 2381 QString dbMinString;
Chris@43 2382 QString dbMaxString;
Chris@43 2383 if (dbMin == AudioLevel::DB_FLOOR) {
Chris@43 2384 dbMinString = tr("-Inf");
Chris@43 2385 } else {
Chris@43 2386 dbMinString = QString("%1").arg(lrintf(dbMin));
Chris@43 2387 }
Chris@43 2388 if (dbMax == AudioLevel::DB_FLOOR) {
Chris@43 2389 dbMaxString = tr("-Inf");
Chris@43 2390 } else {
Chris@43 2391 dbMaxString = QString("%1").arg(lrintf(dbMax));
Chris@43 2392 }
Chris@25 2393 if (lrintf(dbMin) != lrintf(dbMax)) {
Chris@25 2394 text += tr("dB:\t%1 - %2").arg(lrintf(dbMin)).arg(lrintf(dbMax));
Chris@25 2395 } else {
Chris@25 2396 text += tr("dB:\t%1").arg(lrintf(dbMin));
Chris@25 2397 }
Chris@38 2398 if (phaseMin != phaseMax) {
Chris@38 2399 text += tr("\nPhase:\t%1 - %2").arg(phaseMin).arg(phaseMax);
Chris@38 2400 } else {
Chris@38 2401 text += tr("\nPhase:\t%1").arg(phaseMin);
Chris@38 2402 }
Chris@25 2403 }
Chris@25 2404
Chris@25 2405 return text;
Chris@0 2406 }
Chris@25 2407
Chris@0 2408 int
Chris@40 2409 SpectrogramLayer::getColourScaleWidth(QPainter &paint) const
Chris@40 2410 {
Chris@40 2411 int cw;
Chris@40 2412
Chris@40 2413 switch (m_colourScale) {
Chris@40 2414 default:
Chris@40 2415 case LinearColourScale:
Chris@40 2416 cw = paint.fontMetrics().width(QString("0.00"));
Chris@40 2417 break;
Chris@40 2418
Chris@40 2419 case MeterColourScale:
Chris@40 2420 case dBColourScale:
Chris@40 2421 cw = std::max(paint.fontMetrics().width(tr("-Inf")),
Chris@40 2422 paint.fontMetrics().width(tr("-90")));
Chris@40 2423 break;
Chris@40 2424
Chris@40 2425 case PhaseColourScale:
Chris@40 2426 cw = paint.fontMetrics().width(QString("-") + QChar(0x3c0));
Chris@40 2427 break;
Chris@40 2428 }
Chris@40 2429
Chris@40 2430 return cw;
Chris@40 2431 }
Chris@40 2432
Chris@40 2433 int
Chris@44 2434 SpectrogramLayer::getVerticalScaleWidth(View *v, QPainter &paint) const
Chris@0 2435 {
Chris@0 2436 if (!m_model || !m_model->isOK()) return 0;
Chris@0 2437
Chris@40 2438 int cw = getColourScaleWidth(paint);
Chris@40 2439
Chris@0 2440 int tw = paint.fontMetrics().width(QString("%1")
Chris@0 2441 .arg(m_maxFrequency > 0 ?
Chris@0 2442 m_maxFrequency - 1 :
Chris@0 2443 m_model->getSampleRate() / 2));
Chris@0 2444
Chris@0 2445 int fw = paint.fontMetrics().width(QString("43Hz"));
Chris@0 2446 if (tw < fw) tw = fw;
Chris@40 2447
Chris@40 2448 int tickw = (m_frequencyScale == LogFrequencyScale ? 10 : 4);
Chris@0 2449
Chris@40 2450 return cw + tickw + tw + 13;
Chris@0 2451 }
Chris@0 2452
Chris@0 2453 void
Chris@44 2454 SpectrogramLayer::paintVerticalScale(View *v, QPainter &paint, QRect rect) const
Chris@0 2455 {
Chris@0 2456 if (!m_model || !m_model->isOK()) {
Chris@0 2457 return;
Chris@0 2458 }
Chris@0 2459
Chris@0 2460 int h = rect.height(), w = rect.width();
Chris@0 2461
Chris@40 2462 int tickw = (m_frequencyScale == LogFrequencyScale ? 10 : 4);
Chris@40 2463 int pkw = (m_frequencyScale == LogFrequencyScale ? 10 : 0);
Chris@40 2464
Chris@107 2465 size_t bins = m_fftSize / 2;
Chris@0 2466 int sr = m_model->getSampleRate();
Chris@0 2467
Chris@0 2468 if (m_maxFrequency > 0) {
Chris@107 2469 bins = int((double(m_maxFrequency) * m_fftSize) / sr + 0.1);
Chris@107 2470 if (bins > m_fftSize / 2) bins = m_fftSize / 2;
Chris@0 2471 }
Chris@0 2472
Chris@40 2473 int cw = getColourScaleWidth(paint);
Chris@40 2474
Chris@0 2475 int py = -1;
Chris@0 2476 int textHeight = paint.fontMetrics().height();
Chris@0 2477 int toff = -textHeight + paint.fontMetrics().ascent() + 2;
Chris@0 2478
Chris@40 2479 if (m_cache && !m_cacheInvalid && h > textHeight * 2 + 10) { //!!! lock?
Chris@40 2480
Chris@40 2481 int ch = h - textHeight * 2 - 8;
Chris@40 2482 paint.drawRect(4, textHeight + 4, cw - 1, ch + 1);
Chris@40 2483
Chris@40 2484 QString top, bottom;
Chris@40 2485
Chris@40 2486 switch (m_colourScale) {
Chris@40 2487 default:
Chris@40 2488 case LinearColourScale:
Chris@40 2489 top = (m_normalizeColumns ? "1.0" : "0.02");
Chris@40 2490 bottom = (m_normalizeColumns ? "0.0" : "0.00");
Chris@40 2491 break;
Chris@40 2492
Chris@40 2493 case MeterColourScale:
Chris@40 2494 top = (m_normalizeColumns ? QString("0") :
Chris@40 2495 QString("%1").arg(int(AudioLevel::multiplier_to_dB(0.02))));
Chris@40 2496 bottom = QString("%1").
Chris@40 2497 arg(int(AudioLevel::multiplier_to_dB
Chris@40 2498 (AudioLevel::preview_to_multiplier(0, 255))));
Chris@40 2499 break;
Chris@40 2500
Chris@40 2501 case dBColourScale:
Chris@40 2502 top = "0";
Chris@40 2503 bottom = "-80";
Chris@40 2504 break;
Chris@40 2505
Chris@40 2506 case PhaseColourScale:
Chris@40 2507 top = QChar(0x3c0);
Chris@40 2508 bottom = "-" + top;
Chris@40 2509 break;
Chris@40 2510 }
Chris@40 2511
Chris@40 2512 paint.drawText((cw + 6 - paint.fontMetrics().width(top)) / 2,
Chris@40 2513 2 + textHeight + toff, top);
Chris@40 2514
Chris@40 2515 paint.drawText((cw + 6 - paint.fontMetrics().width(bottom)) / 2,
Chris@40 2516 h + toff - 3, bottom);
Chris@40 2517
Chris@40 2518 paint.save();
Chris@40 2519 paint.setBrush(Qt::NoBrush);
Chris@40 2520 for (int i = 0; i < ch; ++i) {
Chris@40 2521 int v = (i * 255) / ch + 1;
Chris@86 2522 paint.setPen(m_colourMap.getColour(v));
Chris@40 2523 paint.drawLine(5, 4 + textHeight + ch - i,
Chris@40 2524 cw + 2, 4 + textHeight + ch - i);
Chris@40 2525 }
Chris@40 2526 paint.restore();
Chris@40 2527 }
Chris@40 2528
Chris@40 2529 paint.drawLine(cw + 7, 0, cw + 7, h);
Chris@40 2530
Chris@0 2531 int bin = -1;
Chris@0 2532
Chris@44 2533 for (int y = 0; y < v->height(); ++y) {
Chris@0 2534
Chris@0 2535 float q0, q1;
Chris@44 2536 if (!getYBinRange(v, v->height() - y, q0, q1)) continue;
Chris@0 2537
Chris@0 2538 int vy;
Chris@0 2539
Chris@0 2540 if (int(q0) > bin) {
Chris@0 2541 vy = y;
Chris@0 2542 bin = int(q0);
Chris@0 2543 } else {
Chris@0 2544 continue;
Chris@0 2545 }
Chris@0 2546
Chris@107 2547 int freq = (sr * bin) / m_fftSize;
Chris@0 2548
Chris@0 2549 if (py >= 0 && (vy - py) < textHeight - 1) {
Chris@40 2550 if (m_frequencyScale == LinearFrequencyScale) {
Chris@40 2551 paint.drawLine(w - tickw, h - vy, w, h - vy);
Chris@40 2552 }
Chris@0 2553 continue;
Chris@0 2554 }
Chris@0 2555
Chris@0 2556 QString text = QString("%1").arg(freq);
Chris@40 2557 if (bin == 1) text = QString("%1Hz").arg(freq); // bin 0 is DC
Chris@40 2558 paint.drawLine(cw + 7, h - vy, w - pkw - 1, h - vy);
Chris@0 2559
Chris@0 2560 if (h - vy - textHeight >= -2) {
Chris@40 2561 int tx = w - 3 - paint.fontMetrics().width(text) - std::max(tickw, pkw);
Chris@0 2562 paint.drawText(tx, h - vy + toff, text);
Chris@0 2563 }
Chris@0 2564
Chris@0 2565 py = vy;
Chris@0 2566 }
Chris@40 2567
Chris@40 2568 if (m_frequencyScale == LogFrequencyScale) {
Chris@40 2569
Chris@40 2570 paint.drawLine(w - pkw - 1, 0, w - pkw - 1, h);
Chris@40 2571
Chris@40 2572 int sr = m_model->getSampleRate();//!!! lock?
Chris@40 2573 float minf = getEffectiveMinFrequency();
Chris@40 2574 float maxf = getEffectiveMaxFrequency();
Chris@40 2575
Chris@40 2576 int py = h;
Chris@40 2577 paint.setBrush(paint.pen().color());
Chris@40 2578
Chris@40 2579 for (int i = 0; i < 128; ++i) {
Chris@40 2580
Chris@40 2581 float f = Pitch::getFrequencyForPitch(i);
Chris@44 2582 int y = lrintf(v->getYForFrequency(f, minf, maxf, true));
Chris@40 2583 int n = (i % 12);
Chris@40 2584 if (n == 1 || n == 3 || n == 6 || n == 8 || n == 10) {
Chris@40 2585 // black notes
Chris@40 2586 paint.drawLine(w - pkw, y, w, y);
Chris@41 2587 int rh = ((py - y) / 4) * 2;
Chris@41 2588 if (rh < 2) rh = 2;
Chris@41 2589 paint.drawRect(w - pkw, y - (py-y)/4, pkw/2, rh);
Chris@40 2590 } else if (n == 0 || n == 5) {
Chris@40 2591 // C, A
Chris@40 2592 if (py < h) {
Chris@40 2593 paint.drawLine(w - pkw, (y + py) / 2, w, (y + py) / 2);
Chris@40 2594 }
Chris@40 2595 }
Chris@40 2596
Chris@40 2597 py = y;
Chris@40 2598 }
Chris@40 2599 }
Chris@0 2600 }
Chris@0 2601
Chris@6 2602 QString
Chris@6 2603 SpectrogramLayer::toXmlString(QString indent, QString extraAttributes) const
Chris@6 2604 {
Chris@6 2605 QString s;
Chris@6 2606
Chris@6 2607 s += QString("channel=\"%1\" "
Chris@6 2608 "windowSize=\"%2\" "
Chris@6 2609 "windowType=\"%3\" "
Chris@97 2610 "windowHopLevel=\"%4\" "
Chris@37 2611 "gain=\"%5\" "
Chris@37 2612 "threshold=\"%6\" ")
Chris@6 2613 .arg(m_channel)
Chris@6 2614 .arg(m_windowSize)
Chris@6 2615 .arg(m_windowType)
Chris@97 2616 .arg(m_windowHopLevel)
Chris@37 2617 .arg(m_gain)
Chris@37 2618 .arg(m_threshold);
Chris@37 2619
Chris@37 2620 s += QString("minFrequency=\"%1\" "
Chris@37 2621 "maxFrequency=\"%2\" "
Chris@37 2622 "colourScale=\"%3\" "
Chris@37 2623 "colourScheme=\"%4\" "
Chris@37 2624 "colourRotation=\"%5\" "
Chris@37 2625 "frequencyScale=\"%6\" "
Chris@37 2626 "binDisplay=\"%7\" "
Chris@37 2627 "normalizeColumns=\"%8\"")
Chris@37 2628 .arg(m_minFrequency)
Chris@6 2629 .arg(m_maxFrequency)
Chris@6 2630 .arg(m_colourScale)
Chris@6 2631 .arg(m_colourScheme)
Chris@37 2632 .arg(m_colourRotation)
Chris@35 2633 .arg(m_frequencyScale)
Chris@37 2634 .arg(m_binDisplay)
Chris@36 2635 .arg(m_normalizeColumns ? "true" : "false");
Chris@6 2636
Chris@6 2637 return Layer::toXmlString(indent, extraAttributes + " " + s);
Chris@6 2638 }
Chris@6 2639
Chris@11 2640 void
Chris@11 2641 SpectrogramLayer::setProperties(const QXmlAttributes &attributes)
Chris@11 2642 {
Chris@11 2643 bool ok = false;
Chris@11 2644
Chris@11 2645 int channel = attributes.value("channel").toInt(&ok);
Chris@11 2646 if (ok) setChannel(channel);
Chris@11 2647
Chris@11 2648 size_t windowSize = attributes.value("windowSize").toUInt(&ok);
Chris@11 2649 if (ok) setWindowSize(windowSize);
Chris@11 2650
Chris@11 2651 WindowType windowType = (WindowType)
Chris@11 2652 attributes.value("windowType").toInt(&ok);
Chris@11 2653 if (ok) setWindowType(windowType);
Chris@11 2654
Chris@97 2655 size_t windowHopLevel = attributes.value("windowHopLevel").toUInt(&ok);
Chris@97 2656 if (ok) setWindowHopLevel(windowHopLevel);
Chris@97 2657 else {
Chris@97 2658 size_t windowOverlap = attributes.value("windowOverlap").toUInt(&ok);
Chris@97 2659 // a percentage value
Chris@97 2660 if (ok) {
Chris@97 2661 if (windowOverlap == 0) setWindowHopLevel(0);
Chris@97 2662 else if (windowOverlap == 25) setWindowHopLevel(1);
Chris@97 2663 else if (windowOverlap == 50) setWindowHopLevel(2);
Chris@97 2664 else if (windowOverlap == 75) setWindowHopLevel(3);
Chris@97 2665 else if (windowOverlap == 90) setWindowHopLevel(4);
Chris@97 2666 }
Chris@97 2667 }
Chris@11 2668
Chris@11 2669 float gain = attributes.value("gain").toFloat(&ok);
Chris@11 2670 if (ok) setGain(gain);
Chris@11 2671
Chris@37 2672 float threshold = attributes.value("threshold").toFloat(&ok);
Chris@37 2673 if (ok) setThreshold(threshold);
Chris@37 2674
Chris@37 2675 size_t minFrequency = attributes.value("minFrequency").toUInt(&ok);
Chris@37 2676 if (ok) setMinFrequency(minFrequency);
Chris@37 2677
Chris@11 2678 size_t maxFrequency = attributes.value("maxFrequency").toUInt(&ok);
Chris@11 2679 if (ok) setMaxFrequency(maxFrequency);
Chris@11 2680
Chris@11 2681 ColourScale colourScale = (ColourScale)
Chris@11 2682 attributes.value("colourScale").toInt(&ok);
Chris@11 2683 if (ok) setColourScale(colourScale);
Chris@11 2684
Chris@11 2685 ColourScheme colourScheme = (ColourScheme)
Chris@11 2686 attributes.value("colourScheme").toInt(&ok);
Chris@11 2687 if (ok) setColourScheme(colourScheme);
Chris@11 2688
Chris@37 2689 int colourRotation = attributes.value("colourRotation").toInt(&ok);
Chris@37 2690 if (ok) setColourRotation(colourRotation);
Chris@37 2691
Chris@11 2692 FrequencyScale frequencyScale = (FrequencyScale)
Chris@11 2693 attributes.value("frequencyScale").toInt(&ok);
Chris@11 2694 if (ok) setFrequencyScale(frequencyScale);
Chris@35 2695
Chris@37 2696 BinDisplay binDisplay = (BinDisplay)
Chris@37 2697 attributes.value("binDisplay").toInt(&ok);
Chris@37 2698 if (ok) setBinDisplay(binDisplay);
Chris@36 2699
Chris@36 2700 bool normalizeColumns =
Chris@36 2701 (attributes.value("normalizeColumns").trimmed() == "true");
Chris@36 2702 setNormalizeColumns(normalizeColumns);
Chris@11 2703 }
Chris@11 2704
Chris@11 2705
Chris@0 2706 #ifdef INCLUDE_MOCFILES
Chris@0 2707 #include "SpectrogramLayer.moc.cpp"
Chris@0 2708 #endif
Chris@0 2709