annotate layer/SpectrogramLayer.cpp @ 118:853a7fc542d0

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