annotate layer/SpectrogramLayer.cpp @ 473:4f4f943bfdfc

* Merge from one-fftdataserver-per-fftmodel branch. This bit of reworking (which is not described very accurately by the title of the branch) turns the MatrixFile object into something that either reads or writes, but not both, and separates the FFT file cache reader and writer implementations separately. This allows the FFT data server to have a single thread owning writers and one reader per "customer" thread, and for all locking to be vastly simplified and concentrated in the data server alone (because none of the classes it makes use of is used in more than one thread at a time). The result is faster and more trustworthy code.
author Chris Cannam
date Tue, 27 Jan 2009 13:25:10 +0000
parents 0acf803e2c79
children 92f4d88241b8
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@182 7 This file copyright 2006 Chris Cannam and QMUL.
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@128 18 #include "view/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@167 24 #include "base/RangeMapper.h"
Chris@253 25 #include "base/LogRange.h"
Chris@376 26 #include "widgets/CommandHistory.h"
Chris@376 27 #include "ColourMapper.h"
Chris@283 28 #include "ImageRegionFinder.h"
Chris@0 29
Chris@0 30 #include <QPainter>
Chris@0 31 #include <QImage>
Chris@0 32 #include <QPixmap>
Chris@0 33 #include <QRect>
Chris@0 34 #include <QTimer>
Chris@92 35 #include <QApplication>
Chris@178 36 #include <QMessageBox>
Chris@283 37 #include <QMouseEvent>
Chris@316 38 #include <QTextStream>
Chris@0 39
Chris@0 40 #include <iostream>
Chris@0 41
Chris@0 42 #include <cassert>
Chris@0 43 #include <cmath>
Chris@0 44
Chris@174 45 //#define DEBUG_SPECTROGRAM_REPAINT 1
Chris@0 46
Chris@44 47 SpectrogramLayer::SpectrogramLayer(Configuration config) :
Chris@0 48 m_model(0),
Chris@0 49 m_channel(0),
Chris@0 50 m_windowSize(1024),
Chris@0 51 m_windowType(HanningWindow),
Chris@97 52 m_windowHopLevel(2),
Chris@109 53 m_zeroPadLevel(0),
Chris@107 54 m_fftSize(1024),
Chris@0 55 m_gain(1.0),
Chris@215 56 m_initialGain(1.0),
Chris@37 57 m_threshold(0.0),
Chris@215 58 m_initialThreshold(0.0),
Chris@9 59 m_colourRotation(0),
Chris@215 60 m_initialRotation(0),
Chris@119 61 m_minFrequency(10),
Chris@0 62 m_maxFrequency(8000),
Chris@135 63 m_initialMaxFrequency(8000),
Chris@0 64 m_colourScale(dBColourScale),
Chris@197 65 m_colourMap(0),
Chris@0 66 m_frequencyScale(LinearFrequencyScale),
Chris@37 67 m_binDisplay(AllBins),
Chris@36 68 m_normalizeColumns(false),
Chris@120 69 m_normalizeVisibleArea(false),
Chris@133 70 m_lastEmittedZoomStep(-1),
Chris@390 71 m_synchronous(false),
Chris@215 72 m_lastPaintBlockWidth(0),
Chris@0 73 m_updateTimer(0),
Chris@44 74 m_candidateFillStartFrame(0),
Chris@193 75 m_exiting(false),
Chris@193 76 m_sliceableModel(0)
Chris@0 77 {
Chris@215 78 if (config == FullRangeDb) {
Chris@215 79 m_initialMaxFrequency = 0;
Chris@215 80 setMaxFrequency(0);
Chris@215 81 } else if (config == MelodicRange) {
Chris@0 82 setWindowSize(8192);
Chris@97 83 setWindowHopLevel(4);
Chris@215 84 m_initialMaxFrequency = 1500;
Chris@215 85 setMaxFrequency(1500);
Chris@215 86 setMinFrequency(40);
Chris@0 87 setColourScale(LinearColourScale);
Chris@215 88 setColourMap(ColourMapper::Sunset);
Chris@215 89 setFrequencyScale(LogFrequencyScale);
Chris@224 90 // setGain(20);
Chris@37 91 } else if (config == MelodicPeaks) {
Chris@37 92 setWindowSize(4096);
Chris@97 93 setWindowHopLevel(5);
Chris@135 94 m_initialMaxFrequency = 2000;
Chris@40 95 setMaxFrequency(2000);
Chris@37 96 setMinFrequency(40);
Chris@37 97 setFrequencyScale(LogFrequencyScale);
Chris@215 98 setColourScale(LinearColourScale);
Chris@37 99 setBinDisplay(PeakFrequencies);
Chris@37 100 setNormalizeColumns(true);
Chris@0 101 }
Chris@110 102
Chris@122 103 Preferences *prefs = Preferences::getInstance();
Chris@122 104 connect(prefs, SIGNAL(propertyChanged(PropertyContainer::PropertyName)),
Chris@122 105 this, SLOT(preferenceChanged(PropertyContainer::PropertyName)));
Chris@122 106 setWindowType(prefs->getWindowType());
Chris@122 107
Chris@197 108 initialisePalette();
Chris@0 109 }
Chris@0 110
Chris@0 111 SpectrogramLayer::~SpectrogramLayer()
Chris@0 112 {
Chris@0 113 delete m_updateTimer;
Chris@0 114 m_updateTimer = 0;
Chris@0 115
Chris@130 116 invalidateFFTModels();
Chris@0 117 }
Chris@0 118
Chris@0 119 void
Chris@0 120 SpectrogramLayer::setModel(const DenseTimeValueModel *model)
Chris@0 121 {
Chris@101 122 // std::cerr << "SpectrogramLayer(" << this << "): setModel(" << model << ")" << std::endl;
Chris@34 123
Chris@110 124 if (model == m_model) return;
Chris@110 125
Chris@0 126 m_model = model;
Chris@130 127 invalidateFFTModels();
Chris@0 128
Chris@0 129 if (!m_model || !m_model->isOK()) return;
Chris@0 130
Chris@320 131 connectSignals(m_model);
Chris@0 132
Chris@0 133 connect(m_model, SIGNAL(modelChanged()), this, SLOT(cacheInvalid()));
Chris@0 134 connect(m_model, SIGNAL(modelChanged(size_t, size_t)),
Chris@0 135 this, SLOT(cacheInvalid(size_t, size_t)));
Chris@0 136
Chris@0 137 emit modelReplaced();
Chris@110 138 }
Chris@115 139
Chris@0 140 Layer::PropertyList
Chris@0 141 SpectrogramLayer::getProperties() const
Chris@0 142 {
Chris@0 143 PropertyList list;
Chris@87 144 list.push_back("Colour");
Chris@87 145 list.push_back("Colour Scale");
Chris@87 146 list.push_back("Window Size");
Chris@97 147 list.push_back("Window Increment");
Chris@87 148 list.push_back("Normalize Columns");
Chris@120 149 list.push_back("Normalize Visible Area");
Chris@87 150 list.push_back("Bin Display");
Chris@87 151 list.push_back("Threshold");
Chris@87 152 list.push_back("Gain");
Chris@87 153 list.push_back("Colour Rotation");
Chris@153 154 // list.push_back("Min Frequency");
Chris@153 155 // list.push_back("Max Frequency");
Chris@87 156 list.push_back("Frequency Scale");
Chris@153 157 //// list.push_back("Zero Padding");
Chris@0 158 return list;
Chris@0 159 }
Chris@0 160
Chris@87 161 QString
Chris@87 162 SpectrogramLayer::getPropertyLabel(const PropertyName &name) const
Chris@87 163 {
Chris@87 164 if (name == "Colour") return tr("Colour");
Chris@87 165 if (name == "Colour Scale") return tr("Colour Scale");
Chris@87 166 if (name == "Window Size") return tr("Window Size");
Chris@112 167 if (name == "Window Increment") return tr("Window Overlap");
Chris@87 168 if (name == "Normalize Columns") return tr("Normalize Columns");
Chris@120 169 if (name == "Normalize Visible Area") return tr("Normalize Visible Area");
Chris@87 170 if (name == "Bin Display") return tr("Bin Display");
Chris@87 171 if (name == "Threshold") return tr("Threshold");
Chris@87 172 if (name == "Gain") return tr("Gain");
Chris@87 173 if (name == "Colour Rotation") return tr("Colour Rotation");
Chris@87 174 if (name == "Min Frequency") return tr("Min Frequency");
Chris@87 175 if (name == "Max Frequency") return tr("Max Frequency");
Chris@87 176 if (name == "Frequency Scale") return tr("Frequency Scale");
Chris@109 177 if (name == "Zero Padding") return tr("Smoothing");
Chris@87 178 return "";
Chris@87 179 }
Chris@87 180
Chris@335 181 QString
Chris@335 182 SpectrogramLayer::getPropertyIconName(const PropertyName &name) const
Chris@335 183 {
Chris@335 184 if (name == "Normalize Columns") return "normalise-columns";
Chris@335 185 if (name == "Normalize Visible Area") return "normalise";
Chris@335 186 return "";
Chris@335 187 }
Chris@335 188
Chris@0 189 Layer::PropertyType
Chris@0 190 SpectrogramLayer::getPropertyType(const PropertyName &name) const
Chris@0 191 {
Chris@87 192 if (name == "Gain") return RangeProperty;
Chris@87 193 if (name == "Colour Rotation") return RangeProperty;
Chris@87 194 if (name == "Normalize Columns") return ToggleProperty;
Chris@120 195 if (name == "Normalize Visible Area") return ToggleProperty;
Chris@87 196 if (name == "Threshold") return RangeProperty;
Chris@109 197 if (name == "Zero Padding") return ToggleProperty;
Chris@0 198 return ValueProperty;
Chris@0 199 }
Chris@0 200
Chris@0 201 QString
Chris@0 202 SpectrogramLayer::getPropertyGroupName(const PropertyName &name) const
Chris@0 203 {
Chris@153 204 if (name == "Bin Display" ||
Chris@153 205 name == "Frequency Scale") return tr("Bins");
Chris@87 206 if (name == "Window Size" ||
Chris@109 207 name == "Window Increment" ||
Chris@109 208 name == "Zero Padding") return tr("Window");
Chris@87 209 if (name == "Colour" ||
Chris@87 210 name == "Threshold" ||
Chris@87 211 name == "Colour Rotation") return tr("Colour");
Chris@87 212 if (name == "Normalize Columns" ||
Chris@120 213 name == "Normalize Visible Area" ||
Chris@153 214 name == "Gain" ||
Chris@87 215 name == "Colour Scale") return tr("Scale");
Chris@0 216 return QString();
Chris@0 217 }
Chris@0 218
Chris@0 219 int
Chris@0 220 SpectrogramLayer::getPropertyRangeAndValue(const PropertyName &name,
Chris@216 221 int *min, int *max, int *deflt) const
Chris@0 222 {
Chris@216 223 int val = 0;
Chris@216 224
Chris@216 225 int garbage0, garbage1, garbage2;
Chris@55 226 if (!min) min = &garbage0;
Chris@55 227 if (!max) max = &garbage1;
Chris@216 228 if (!deflt) deflt = &garbage2;
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@216 235 *deflt = lrintf(log10(m_initialGain) * 20.0);;
Chris@216 236 if (*deflt < *min) *deflt = *min;
Chris@216 237 if (*deflt > *max) *deflt = *max;
Chris@216 238
Chris@216 239 val = lrintf(log10(m_gain) * 20.0);
Chris@216 240 if (val < *min) val = *min;
Chris@216 241 if (val > *max) val = *max;
Chris@0 242
Chris@87 243 } else if (name == "Threshold") {
Chris@37 244
Chris@37 245 *min = -50;
Chris@37 246 *max = 0;
Chris@37 247
Chris@216 248 *deflt = lrintf(AudioLevel::multiplier_to_dB(m_initialThreshold));
Chris@216 249 if (*deflt < *min) *deflt = *min;
Chris@216 250 if (*deflt > *max) *deflt = *max;
Chris@216 251
Chris@216 252 val = lrintf(AudioLevel::multiplier_to_dB(m_threshold));
Chris@216 253 if (val < *min) val = *min;
Chris@216 254 if (val > *max) val = *max;
Chris@37 255
Chris@87 256 } else if (name == "Colour Rotation") {
Chris@9 257
Chris@9 258 *min = 0;
Chris@9 259 *max = 256;
Chris@216 260 *deflt = m_initialRotation;
Chris@216 261
Chris@216 262 val = m_colourRotation;
Chris@9 263
Chris@87 264 } else if (name == "Colour Scale") {
Chris@0 265
Chris@0 266 *min = 0;
Chris@176 267 *max = 4;
Chris@216 268 *deflt = int(dBColourScale);
Chris@216 269
Chris@216 270 val = (int)m_colourScale;
Chris@0 271
Chris@87 272 } else if (name == "Colour") {
Chris@0 273
Chris@0 274 *min = 0;
Chris@196 275 *max = ColourMapper::getColourMapCount() - 1;
Chris@216 276 *deflt = 0;
Chris@216 277
Chris@216 278 val = m_colourMap;
Chris@0 279
Chris@87 280 } else if (name == "Window Size") {
Chris@0 281
Chris@0 282 *min = 0;
Chris@0 283 *max = 10;
Chris@216 284 *deflt = 5;
Chris@0 285
Chris@216 286 val = 0;
Chris@0 287 int ws = m_windowSize;
Chris@216 288 while (ws > 32) { ws >>= 1; val ++; }
Chris@0 289
Chris@97 290 } else if (name == "Window Increment") {
Chris@0 291
Chris@0 292 *min = 0;
Chris@97 293 *max = 5;
Chris@216 294 *deflt = 2;
Chris@216 295
Chris@216 296 val = m_windowHopLevel;
Chris@0 297
Chris@109 298 } else if (name == "Zero Padding") {
Chris@109 299
Chris@109 300 *min = 0;
Chris@109 301 *max = 1;
Chris@216 302 *deflt = 0;
Chris@109 303
Chris@216 304 val = m_zeroPadLevel > 0 ? 1 : 0;
Chris@109 305
Chris@87 306 } else if (name == "Min Frequency") {
Chris@37 307
Chris@37 308 *min = 0;
Chris@37 309 *max = 9;
Chris@216 310 *deflt = 1;
Chris@37 311
Chris@37 312 switch (m_minFrequency) {
Chris@216 313 case 0: default: val = 0; break;
Chris@216 314 case 10: val = 1; break;
Chris@216 315 case 20: val = 2; break;
Chris@216 316 case 40: val = 3; break;
Chris@216 317 case 100: val = 4; break;
Chris@216 318 case 250: val = 5; break;
Chris@216 319 case 500: val = 6; break;
Chris@216 320 case 1000: val = 7; break;
Chris@216 321 case 4000: val = 8; break;
Chris@216 322 case 10000: val = 9; break;
Chris@37 323 }
Chris@37 324
Chris@87 325 } else if (name == "Max Frequency") {
Chris@0 326
Chris@0 327 *min = 0;
Chris@0 328 *max = 9;
Chris@216 329 *deflt = 6;
Chris@0 330
Chris@0 331 switch (m_maxFrequency) {
Chris@216 332 case 500: val = 0; break;
Chris@216 333 case 1000: val = 1; break;
Chris@216 334 case 1500: val = 2; break;
Chris@216 335 case 2000: val = 3; break;
Chris@216 336 case 4000: val = 4; break;
Chris@216 337 case 6000: val = 5; break;
Chris@216 338 case 8000: val = 6; break;
Chris@216 339 case 12000: val = 7; break;
Chris@216 340 case 16000: val = 8; break;
Chris@216 341 default: val = 9; break;
Chris@0 342 }
Chris@0 343
Chris@87 344 } else if (name == "Frequency Scale") {
Chris@0 345
Chris@0 346 *min = 0;
Chris@0 347 *max = 1;
Chris@216 348 *deflt = int(LinearFrequencyScale);
Chris@216 349 val = (int)m_frequencyScale;
Chris@0 350
Chris@87 351 } else if (name == "Bin Display") {
Chris@35 352
Chris@35 353 *min = 0;
Chris@35 354 *max = 2;
Chris@216 355 *deflt = int(AllBins);
Chris@216 356 val = (int)m_binDisplay;
Chris@35 357
Chris@87 358 } else if (name == "Normalize Columns") {
Chris@36 359
Chris@216 360 *deflt = 0;
Chris@216 361 val = (m_normalizeColumns ? 1 : 0);
Chris@36 362
Chris@120 363 } else if (name == "Normalize Visible Area") {
Chris@120 364
Chris@216 365 *deflt = 0;
Chris@216 366 val = (m_normalizeVisibleArea ? 1 : 0);
Chris@120 367
Chris@0 368 } else {
Chris@216 369 val = Layer::getPropertyRangeAndValue(name, min, max, deflt);
Chris@0 370 }
Chris@0 371
Chris@216 372 return val;
Chris@0 373 }
Chris@0 374
Chris@0 375 QString
Chris@0 376 SpectrogramLayer::getPropertyValueLabel(const PropertyName &name,
Chris@9 377 int value) const
Chris@0 378 {
Chris@87 379 if (name == "Colour") {
Chris@196 380 return ColourMapper::getColourMapName(value);
Chris@0 381 }
Chris@87 382 if (name == "Colour Scale") {
Chris@0 383 switch (value) {
Chris@0 384 default:
Chris@37 385 case 0: return tr("Linear");
Chris@37 386 case 1: return tr("Meter");
Chris@215 387 case 2: return tr("dBV^2");
Chris@215 388 case 3: return tr("dBV");
Chris@119 389 case 4: return tr("Phase");
Chris@0 390 }
Chris@0 391 }
Chris@87 392 if (name == "Window Size") {
Chris@0 393 return QString("%1").arg(32 << value);
Chris@0 394 }
Chris@97 395 if (name == "Window Increment") {
Chris@0 396 switch (value) {
Chris@0 397 default:
Chris@112 398 case 0: return tr("None");
Chris@112 399 case 1: return tr("25 %");
Chris@112 400 case 2: return tr("50 %");
Chris@112 401 case 3: return tr("75 %");
Chris@112 402 case 4: return tr("87.5 %");
Chris@112 403 case 5: return tr("93.75 %");
Chris@0 404 }
Chris@0 405 }
Chris@109 406 if (name == "Zero Padding") {
Chris@109 407 if (value == 0) return tr("None");
Chris@109 408 return QString("%1x").arg(value + 1);
Chris@109 409 }
Chris@87 410 if (name == "Min Frequency") {
Chris@37 411 switch (value) {
Chris@37 412 default:
Chris@38 413 case 0: return tr("No min");
Chris@37 414 case 1: return tr("10 Hz");
Chris@37 415 case 2: return tr("20 Hz");
Chris@37 416 case 3: return tr("40 Hz");
Chris@37 417 case 4: return tr("100 Hz");
Chris@37 418 case 5: return tr("250 Hz");
Chris@37 419 case 6: return tr("500 Hz");
Chris@37 420 case 7: return tr("1 KHz");
Chris@37 421 case 8: return tr("4 KHz");
Chris@37 422 case 9: return tr("10 KHz");
Chris@37 423 }
Chris@37 424 }
Chris@87 425 if (name == "Max Frequency") {
Chris@0 426 switch (value) {
Chris@0 427 default:
Chris@0 428 case 0: return tr("500 Hz");
Chris@0 429 case 1: return tr("1 KHz");
Chris@0 430 case 2: return tr("1.5 KHz");
Chris@0 431 case 3: return tr("2 KHz");
Chris@0 432 case 4: return tr("4 KHz");
Chris@0 433 case 5: return tr("6 KHz");
Chris@0 434 case 6: return tr("8 KHz");
Chris@0 435 case 7: return tr("12 KHz");
Chris@0 436 case 8: return tr("16 KHz");
Chris@38 437 case 9: return tr("No max");
Chris@0 438 }
Chris@0 439 }
Chris@87 440 if (name == "Frequency Scale") {
Chris@0 441 switch (value) {
Chris@0 442 default:
Chris@0 443 case 0: return tr("Linear");
Chris@0 444 case 1: return tr("Log");
Chris@0 445 }
Chris@0 446 }
Chris@87 447 if (name == "Bin Display") {
Chris@35 448 switch (value) {
Chris@35 449 default:
Chris@37 450 case 0: return tr("All Bins");
Chris@37 451 case 1: return tr("Peak Bins");
Chris@37 452 case 2: return tr("Frequencies");
Chris@35 453 }
Chris@35 454 }
Chris@0 455 return tr("<unknown>");
Chris@0 456 }
Chris@0 457
Chris@167 458 RangeMapper *
Chris@167 459 SpectrogramLayer::getNewPropertyRangeMapper(const PropertyName &name) const
Chris@167 460 {
Chris@167 461 if (name == "Gain") {
Chris@167 462 return new LinearRangeMapper(-50, 50, -25, 25, tr("dB"));
Chris@167 463 }
Chris@167 464 if (name == "Threshold") {
Chris@167 465 return new LinearRangeMapper(-50, 0, -50, 0, tr("dB"));
Chris@167 466 }
Chris@167 467 return 0;
Chris@167 468 }
Chris@167 469
Chris@0 470 void
Chris@0 471 SpectrogramLayer::setProperty(const PropertyName &name, int value)
Chris@0 472 {
Chris@87 473 if (name == "Gain") {
Chris@0 474 setGain(pow(10, float(value)/20.0));
Chris@87 475 } else if (name == "Threshold") {
Chris@37 476 if (value == -50) setThreshold(0.0);
Chris@37 477 else setThreshold(AudioLevel::dB_to_multiplier(value));
Chris@87 478 } else if (name == "Colour Rotation") {
Chris@9 479 setColourRotation(value);
Chris@87 480 } else if (name == "Colour") {
Chris@197 481 setColourMap(value);
Chris@87 482 } else if (name == "Window Size") {
Chris@0 483 setWindowSize(32 << value);
Chris@97 484 } else if (name == "Window Increment") {
Chris@97 485 setWindowHopLevel(value);
Chris@109 486 } else if (name == "Zero Padding") {
Chris@109 487 setZeroPadLevel(value > 0.1 ? 3 : 0);
Chris@87 488 } else if (name == "Min Frequency") {
Chris@37 489 switch (value) {
Chris@37 490 default:
Chris@37 491 case 0: setMinFrequency(0); break;
Chris@37 492 case 1: setMinFrequency(10); break;
Chris@37 493 case 2: setMinFrequency(20); break;
Chris@37 494 case 3: setMinFrequency(40); break;
Chris@37 495 case 4: setMinFrequency(100); break;
Chris@37 496 case 5: setMinFrequency(250); break;
Chris@37 497 case 6: setMinFrequency(500); break;
Chris@37 498 case 7: setMinFrequency(1000); break;
Chris@37 499 case 8: setMinFrequency(4000); break;
Chris@37 500 case 9: setMinFrequency(10000); break;
Chris@37 501 }
Chris@133 502 int vs = getCurrentVerticalZoomStep();
Chris@133 503 if (vs != m_lastEmittedZoomStep) {
Chris@133 504 emit verticalZoomChanged();
Chris@133 505 m_lastEmittedZoomStep = vs;
Chris@133 506 }
Chris@87 507 } else if (name == "Max Frequency") {
Chris@0 508 switch (value) {
Chris@0 509 case 0: setMaxFrequency(500); break;
Chris@0 510 case 1: setMaxFrequency(1000); break;
Chris@0 511 case 2: setMaxFrequency(1500); break;
Chris@0 512 case 3: setMaxFrequency(2000); break;
Chris@0 513 case 4: setMaxFrequency(4000); break;
Chris@0 514 case 5: setMaxFrequency(6000); break;
Chris@0 515 case 6: setMaxFrequency(8000); break;
Chris@0 516 case 7: setMaxFrequency(12000); break;
Chris@0 517 case 8: setMaxFrequency(16000); break;
Chris@0 518 default:
Chris@0 519 case 9: setMaxFrequency(0); break;
Chris@0 520 }
Chris@133 521 int vs = getCurrentVerticalZoomStep();
Chris@133 522 if (vs != m_lastEmittedZoomStep) {
Chris@133 523 emit verticalZoomChanged();
Chris@133 524 m_lastEmittedZoomStep = vs;
Chris@133 525 }
Chris@87 526 } else if (name == "Colour Scale") {
Chris@0 527 switch (value) {
Chris@0 528 default:
Chris@0 529 case 0: setColourScale(LinearColourScale); break;
Chris@0 530 case 1: setColourScale(MeterColourScale); break;
Chris@215 531 case 2: setColourScale(dBSquaredColourScale); break;
Chris@215 532 case 3: setColourScale(dBColourScale); break;
Chris@119 533 case 4: setColourScale(PhaseColourScale); break;
Chris@0 534 }
Chris@87 535 } else if (name == "Frequency Scale") {
Chris@0 536 switch (value) {
Chris@0 537 default:
Chris@0 538 case 0: setFrequencyScale(LinearFrequencyScale); break;
Chris@0 539 case 1: setFrequencyScale(LogFrequencyScale); break;
Chris@0 540 }
Chris@87 541 } else if (name == "Bin Display") {
Chris@35 542 switch (value) {
Chris@35 543 default:
Chris@37 544 case 0: setBinDisplay(AllBins); break;
Chris@37 545 case 1: setBinDisplay(PeakBins); break;
Chris@37 546 case 2: setBinDisplay(PeakFrequencies); break;
Chris@35 547 }
Chris@82 548 } else if (name == "Normalize Columns") {
Chris@36 549 setNormalizeColumns(value ? true : false);
Chris@120 550 } else if (name == "Normalize Visible Area") {
Chris@120 551 setNormalizeVisibleArea(value ? true : false);
Chris@0 552 }
Chris@0 553 }
Chris@0 554
Chris@0 555 void
Chris@95 556 SpectrogramLayer::invalidatePixmapCaches()
Chris@95 557 {
Chris@95 558 for (ViewPixmapCache::iterator i = m_pixmapCaches.begin();
Chris@95 559 i != m_pixmapCaches.end(); ++i) {
Chris@95 560 i->second.validArea = QRect();
Chris@95 561 }
Chris@95 562 }
Chris@95 563
Chris@95 564 void
Chris@95 565 SpectrogramLayer::invalidatePixmapCaches(size_t startFrame, size_t endFrame)
Chris@95 566 {
Chris@95 567 for (ViewPixmapCache::iterator i = m_pixmapCaches.begin();
Chris@95 568 i != m_pixmapCaches.end(); ++i) {
Chris@131 569
Chris@95 570 //!!! when are views removed from the map? on setLayerDormant?
Chris@95 571 const View *v = i->first;
Chris@95 572
Chris@391 573 #ifdef DEBUG_SPECTROGRAM_REPAINT
Chris@391 574 std::cerr << "SpectrogramLayer::invalidatePixmapCaches("
Chris@391 575 << startFrame << ", " << endFrame << "): view range is "
Chris@391 576 << v->getStartFrame() << ", " << v->getEndFrame()
Chris@391 577 << std::endl;
Chris@391 578
Chris@391 579 std::cerr << "Valid area was: " << i->second.validArea.x() << ", "
Chris@391 580 << i->second.validArea.y() << " "
Chris@391 581 << i->second.validArea.width() << "x"
Chris@391 582 << i->second.validArea.height() << std::endl;
Chris@391 583 #endif
Chris@391 584
Chris@391 585 if (long(startFrame) > v->getStartFrame()) {
Chris@391 586 if (startFrame >= v->getEndFrame()) {
Chris@391 587 #ifdef DEBUG_SPECTROGRAM_REPAINT
Chris@391 588 std::cerr << "Modified start frame is off right of view" << std::endl;
Chris@391 589 #endif
Chris@391 590 return;
Chris@391 591 }
Chris@391 592 int x = v->getXForFrame(startFrame);
Chris@407 593 #ifdef DEBUG_SPECTROGRAM_REPAINT
Chris@391 594 std::cerr << "clipping from 0 to " << x-1 << std::endl;
Chris@407 595 #endif
Chris@391 596 if (x > 1) {
Chris@391 597 i->second.validArea &=
Chris@391 598 QRect(0, 0, x-1, v->height());
Chris@391 599 } else {
Chris@391 600 i->second.validArea = QRect();
Chris@391 601 }
Chris@391 602 } else {
Chris@391 603 if (long(endFrame) < v->getStartFrame()) {
Chris@391 604 #ifdef DEBUG_SPECTROGRAM_REPAINT
Chris@391 605 std::cerr << "Modified end frame is off left of view" << std::endl;
Chris@391 606 #endif
Chris@391 607 return;
Chris@391 608 }
Chris@391 609 int x = v->getXForFrame(endFrame);
Chris@391 610 #ifdef DEBUG_SPECTROGRAM_REPAINT
Chris@391 611 std::cerr << "clipping from " << x+1 << " to " << v->width()
Chris@391 612 << std::endl;
Chris@391 613 #endif
Chris@391 614 if (x < v->width()) {
Chris@391 615 i->second.validArea &=
Chris@391 616 QRect(x+1, 0, v->width()-(x+1), v->height());
Chris@391 617 } else {
Chris@391 618 i->second.validArea = QRect();
Chris@391 619 }
Chris@95 620 }
Chris@391 621
Chris@391 622 #ifdef DEBUG_SPECTROGRAM_REPAINT
Chris@391 623 std::cerr << "Valid area is now: " << i->second.validArea.x() << ", "
Chris@391 624 << i->second.validArea.y() << " "
Chris@391 625 << i->second.validArea.width() << "x"
Chris@391 626 << i->second.validArea.height() << std::endl;
Chris@391 627 #endif
Chris@95 628 }
Chris@95 629 }
Chris@95 630
Chris@95 631 void
Chris@122 632 SpectrogramLayer::preferenceChanged(PropertyContainer::PropertyName name)
Chris@122 633 {
Chris@122 634 std::cerr << "SpectrogramLayer::preferenceChanged(" << name.toStdString() << ")" << std::endl;
Chris@122 635
Chris@122 636 if (name == "Window Type") {
Chris@122 637 setWindowType(Preferences::getInstance()->getWindowType());
Chris@122 638 return;
Chris@122 639 }
Chris@221 640 if (name == "Spectrogram Smoothing") {
Chris@122 641 invalidatePixmapCaches();
Chris@122 642 invalidateMagnitudes();
Chris@122 643 emit layerParametersChanged();
Chris@122 644 }
Chris@122 645 if (name == "Tuning Frequency") {
Chris@122 646 emit layerParametersChanged();
Chris@122 647 }
Chris@122 648 }
Chris@122 649
Chris@122 650 void
Chris@0 651 SpectrogramLayer::setChannel(int ch)
Chris@0 652 {
Chris@0 653 if (m_channel == ch) return;
Chris@0 654
Chris@95 655 invalidatePixmapCaches();
Chris@0 656 m_channel = ch;
Chris@130 657 invalidateFFTModels();
Chris@9 658
Chris@0 659 emit layerParametersChanged();
Chris@0 660 }
Chris@0 661
Chris@0 662 int
Chris@0 663 SpectrogramLayer::getChannel() const
Chris@0 664 {
Chris@0 665 return m_channel;
Chris@0 666 }
Chris@0 667
Chris@0 668 void
Chris@0 669 SpectrogramLayer::setWindowSize(size_t ws)
Chris@0 670 {
Chris@0 671 if (m_windowSize == ws) return;
Chris@0 672
Chris@95 673 invalidatePixmapCaches();
Chris@0 674
Chris@0 675 m_windowSize = ws;
Chris@109 676 m_fftSize = ws * (m_zeroPadLevel + 1);
Chris@0 677
Chris@130 678 invalidateFFTModels();
Chris@9 679
Chris@9 680 emit layerParametersChanged();
Chris@0 681 }
Chris@0 682
Chris@0 683 size_t
Chris@0 684 SpectrogramLayer::getWindowSize() const
Chris@0 685 {
Chris@0 686 return m_windowSize;
Chris@0 687 }
Chris@0 688
Chris@0 689 void
Chris@97 690 SpectrogramLayer::setWindowHopLevel(size_t v)
Chris@0 691 {
Chris@97 692 if (m_windowHopLevel == v) return;
Chris@0 693
Chris@95 694 invalidatePixmapCaches();
Chris@0 695
Chris@97 696 m_windowHopLevel = v;
Chris@0 697
Chris@130 698 invalidateFFTModels();
Chris@9 699
Chris@9 700 emit layerParametersChanged();
Chris@9 701
Chris@110 702 // fillCache();
Chris@0 703 }
Chris@0 704
Chris@0 705 size_t
Chris@97 706 SpectrogramLayer::getWindowHopLevel() const
Chris@0 707 {
Chris@97 708 return m_windowHopLevel;
Chris@0 709 }
Chris@0 710
Chris@0 711 void
Chris@109 712 SpectrogramLayer::setZeroPadLevel(size_t v)
Chris@109 713 {
Chris@109 714 if (m_zeroPadLevel == v) return;
Chris@109 715
Chris@109 716 invalidatePixmapCaches();
Chris@109 717
Chris@109 718 m_zeroPadLevel = v;
Chris@109 719 m_fftSize = m_windowSize * (v + 1);
Chris@110 720
Chris@130 721 invalidateFFTModels();
Chris@109 722
Chris@109 723 emit layerParametersChanged();
Chris@109 724 }
Chris@109 725
Chris@109 726 size_t
Chris@109 727 SpectrogramLayer::getZeroPadLevel() const
Chris@109 728 {
Chris@109 729 return m_zeroPadLevel;
Chris@109 730 }
Chris@109 731
Chris@109 732 void
Chris@0 733 SpectrogramLayer::setWindowType(WindowType w)
Chris@0 734 {
Chris@0 735 if (m_windowType == w) return;
Chris@0 736
Chris@95 737 invalidatePixmapCaches();
Chris@0 738
Chris@0 739 m_windowType = w;
Chris@110 740
Chris@130 741 invalidateFFTModels();
Chris@9 742
Chris@9 743 emit layerParametersChanged();
Chris@0 744 }
Chris@0 745
Chris@0 746 WindowType
Chris@0 747 SpectrogramLayer::getWindowType() const
Chris@0 748 {
Chris@0 749 return m_windowType;
Chris@0 750 }
Chris@0 751
Chris@0 752 void
Chris@0 753 SpectrogramLayer::setGain(float gain)
Chris@0 754 {
Chris@101 755 // std::cerr << "SpectrogramLayer::setGain(" << gain << ") (my gain is now "
Chris@101 756 // << m_gain << ")" << std::endl;
Chris@55 757
Chris@40 758 if (m_gain == gain) return;
Chris@0 759
Chris@95 760 invalidatePixmapCaches();
Chris@0 761
Chris@0 762 m_gain = gain;
Chris@0 763
Chris@9 764 emit layerParametersChanged();
Chris@0 765 }
Chris@0 766
Chris@0 767 float
Chris@0 768 SpectrogramLayer::getGain() const
Chris@0 769 {
Chris@0 770 return m_gain;
Chris@0 771 }
Chris@0 772
Chris@0 773 void
Chris@37 774 SpectrogramLayer::setThreshold(float threshold)
Chris@37 775 {
Chris@40 776 if (m_threshold == threshold) return;
Chris@37 777
Chris@95 778 invalidatePixmapCaches();
Chris@37 779
Chris@37 780 m_threshold = threshold;
Chris@37 781
Chris@37 782 emit layerParametersChanged();
Chris@37 783 }
Chris@37 784
Chris@37 785 float
Chris@37 786 SpectrogramLayer::getThreshold() const
Chris@37 787 {
Chris@37 788 return m_threshold;
Chris@37 789 }
Chris@37 790
Chris@37 791 void
Chris@37 792 SpectrogramLayer::setMinFrequency(size_t mf)
Chris@37 793 {
Chris@37 794 if (m_minFrequency == mf) return;
Chris@37 795
Chris@248 796 // std::cerr << "SpectrogramLayer::setMinFrequency: " << mf << std::endl;
Chris@187 797
Chris@95 798 invalidatePixmapCaches();
Chris@119 799 invalidateMagnitudes();
Chris@37 800
Chris@37 801 m_minFrequency = mf;
Chris@37 802
Chris@37 803 emit layerParametersChanged();
Chris@37 804 }
Chris@37 805
Chris@37 806 size_t
Chris@37 807 SpectrogramLayer::getMinFrequency() const
Chris@37 808 {
Chris@37 809 return m_minFrequency;
Chris@37 810 }
Chris@37 811
Chris@37 812 void
Chris@0 813 SpectrogramLayer::setMaxFrequency(size_t mf)
Chris@0 814 {
Chris@0 815 if (m_maxFrequency == mf) return;
Chris@0 816
Chris@248 817 // std::cerr << "SpectrogramLayer::setMaxFrequency: " << mf << std::endl;
Chris@187 818
Chris@95 819 invalidatePixmapCaches();
Chris@119 820 invalidateMagnitudes();
Chris@0 821
Chris@0 822 m_maxFrequency = mf;
Chris@0 823
Chris@9 824 emit layerParametersChanged();
Chris@0 825 }
Chris@0 826
Chris@0 827 size_t
Chris@0 828 SpectrogramLayer::getMaxFrequency() const
Chris@0 829 {
Chris@0 830 return m_maxFrequency;
Chris@0 831 }
Chris@0 832
Chris@0 833 void
Chris@9 834 SpectrogramLayer::setColourRotation(int r)
Chris@9 835 {
Chris@95 836 invalidatePixmapCaches();
Chris@9 837
Chris@9 838 if (r < 0) r = 0;
Chris@9 839 if (r > 256) r = 256;
Chris@9 840 int distance = r - m_colourRotation;
Chris@9 841
Chris@9 842 if (distance != 0) {
Chris@197 843 rotatePalette(-distance);
Chris@9 844 m_colourRotation = r;
Chris@9 845 }
Chris@9 846
Chris@9 847 emit layerParametersChanged();
Chris@9 848 }
Chris@9 849
Chris@9 850 void
Chris@0 851 SpectrogramLayer::setColourScale(ColourScale colourScale)
Chris@0 852 {
Chris@0 853 if (m_colourScale == colourScale) return;
Chris@0 854
Chris@95 855 invalidatePixmapCaches();
Chris@0 856
Chris@0 857 m_colourScale = colourScale;
Chris@0 858
Chris@9 859 emit layerParametersChanged();
Chris@0 860 }
Chris@0 861
Chris@0 862 SpectrogramLayer::ColourScale
Chris@0 863 SpectrogramLayer::getColourScale() const
Chris@0 864 {
Chris@0 865 return m_colourScale;
Chris@0 866 }
Chris@0 867
Chris@0 868 void
Chris@197 869 SpectrogramLayer::setColourMap(int map)
Chris@0 870 {
Chris@197 871 if (m_colourMap == map) return;
Chris@0 872
Chris@95 873 invalidatePixmapCaches();
Chris@0 874
Chris@197 875 m_colourMap = map;
Chris@197 876 initialisePalette();
Chris@9 877
Chris@0 878 emit layerParametersChanged();
Chris@0 879 }
Chris@0 880
Chris@196 881 int
Chris@197 882 SpectrogramLayer::getColourMap() const
Chris@0 883 {
Chris@197 884 return m_colourMap;
Chris@0 885 }
Chris@0 886
Chris@0 887 void
Chris@0 888 SpectrogramLayer::setFrequencyScale(FrequencyScale frequencyScale)
Chris@0 889 {
Chris@0 890 if (m_frequencyScale == frequencyScale) return;
Chris@0 891
Chris@95 892 invalidatePixmapCaches();
Chris@0 893 m_frequencyScale = frequencyScale;
Chris@9 894
Chris@9 895 emit layerParametersChanged();
Chris@0 896 }
Chris@0 897
Chris@0 898 SpectrogramLayer::FrequencyScale
Chris@0 899 SpectrogramLayer::getFrequencyScale() const
Chris@0 900 {
Chris@0 901 return m_frequencyScale;
Chris@0 902 }
Chris@0 903
Chris@0 904 void
Chris@37 905 SpectrogramLayer::setBinDisplay(BinDisplay binDisplay)
Chris@35 906 {
Chris@37 907 if (m_binDisplay == binDisplay) return;
Chris@35 908
Chris@95 909 invalidatePixmapCaches();
Chris@37 910 m_binDisplay = binDisplay;
Chris@35 911
Chris@35 912 emit layerParametersChanged();
Chris@35 913 }
Chris@35 914
Chris@37 915 SpectrogramLayer::BinDisplay
Chris@37 916 SpectrogramLayer::getBinDisplay() const
Chris@35 917 {
Chris@37 918 return m_binDisplay;
Chris@35 919 }
Chris@35 920
Chris@35 921 void
Chris@36 922 SpectrogramLayer::setNormalizeColumns(bool n)
Chris@36 923 {
Chris@36 924 if (m_normalizeColumns == n) return;
Chris@36 925
Chris@95 926 invalidatePixmapCaches();
Chris@119 927 invalidateMagnitudes();
Chris@36 928 m_normalizeColumns = n;
Chris@36 929
Chris@36 930 emit layerParametersChanged();
Chris@36 931 }
Chris@36 932
Chris@36 933 bool
Chris@36 934 SpectrogramLayer::getNormalizeColumns() const
Chris@36 935 {
Chris@36 936 return m_normalizeColumns;
Chris@36 937 }
Chris@36 938
Chris@36 939 void
Chris@120 940 SpectrogramLayer::setNormalizeVisibleArea(bool n)
Chris@120 941 {
Chris@327 942 std::cerr << "SpectrogramLayer::setNormalizeVisibleArea(" << n
Chris@327 943 << ") (from " << m_normalizeVisibleArea << ")" << std::endl;
Chris@327 944
Chris@120 945 if (m_normalizeVisibleArea == n) return;
Chris@120 946
Chris@120 947 invalidatePixmapCaches();
Chris@120 948 invalidateMagnitudes();
Chris@120 949 m_normalizeVisibleArea = n;
Chris@120 950
Chris@120 951 emit layerParametersChanged();
Chris@120 952 }
Chris@120 953
Chris@120 954 bool
Chris@120 955 SpectrogramLayer::getNormalizeVisibleArea() const
Chris@120 956 {
Chris@120 957 return m_normalizeVisibleArea;
Chris@120 958 }
Chris@120 959
Chris@120 960 void
Chris@47 961 SpectrogramLayer::setLayerDormant(const View *v, bool dormant)
Chris@29 962 {
Chris@33 963 if (dormant) {
Chris@33 964
Chris@331 965 #ifdef DEBUG_SPECTROGRAM_REPAINT
Chris@331 966 std::cerr << "SpectrogramLayer::setLayerDormant(" << dormant << ")"
Chris@331 967 << std::endl;
Chris@331 968 #endif
Chris@331 969
Chris@131 970 if (isLayerDormant(v)) {
Chris@131 971 return;
Chris@131 972 }
Chris@131 973
Chris@131 974 Layer::setLayerDormant(v, true);
Chris@33 975
Chris@95 976 invalidatePixmapCaches();
Chris@95 977 m_pixmapCaches.erase(v);
Chris@114 978
Chris@130 979 if (m_fftModels.find(v) != m_fftModels.end()) {
Chris@193 980
Chris@193 981 if (m_sliceableModel == m_fftModels[v].first) {
Chris@193 982 bool replaced = false;
Chris@193 983 for (ViewFFTMap::iterator i = m_fftModels.begin();
Chris@193 984 i != m_fftModels.end(); ++i) {
Chris@193 985 if (i->second.first != m_sliceableModel) {
Chris@193 986 emit sliceableModelReplaced(m_sliceableModel, i->second.first);
Chris@193 987 replaced = true;
Chris@193 988 break;
Chris@193 989 }
Chris@193 990 }
Chris@193 991 if (!replaced) emit sliceableModelReplaced(m_sliceableModel, 0);
Chris@193 992 }
Chris@193 993
Chris@130 994 delete m_fftModels[v].first;
Chris@130 995 m_fftModels.erase(v);
Chris@114 996 }
Chris@33 997
Chris@33 998 } else {
Chris@33 999
Chris@131 1000 Layer::setLayerDormant(v, false);
Chris@33 1001 }
Chris@29 1002 }
Chris@29 1003
Chris@29 1004 void
Chris@0 1005 SpectrogramLayer::cacheInvalid()
Chris@0 1006 {
Chris@391 1007 #ifdef DEBUG_SPECTROGRAM_REPAINT
Chris@391 1008 std::cerr << "SpectrogramLayer::cacheInvalid()" << std::endl;
Chris@391 1009 #endif
Chris@391 1010
Chris@95 1011 invalidatePixmapCaches();
Chris@119 1012 invalidateMagnitudes();
Chris@0 1013 }
Chris@0 1014
Chris@0 1015 void
Chris@391 1016 SpectrogramLayer::cacheInvalid(size_t from, size_t to)
Chris@0 1017 {
Chris@391 1018 #ifdef DEBUG_SPECTROGRAM_REPAINT
Chris@391 1019 std::cerr << "SpectrogramLayer::cacheInvalid(" << from << ", " << to << ")" << std::endl;
Chris@391 1020 #endif
Chris@391 1021
Chris@391 1022 invalidatePixmapCaches(from, to);
Chris@391 1023 invalidateMagnitudes();
Chris@0 1024 }
Chris@0 1025
Chris@0 1026 void
Chris@0 1027 SpectrogramLayer::fillTimerTimedOut()
Chris@0 1028 {
Chris@115 1029 if (!m_model) return;
Chris@115 1030
Chris@115 1031 bool allDone = true;
Chris@115 1032
Chris@184 1033 #ifdef DEBUG_SPECTROGRAM_REPAINT
Chris@184 1034 std::cerr << "SpectrogramLayer::fillTimerTimedOut: have " << m_fftModels.size() << " FFT models associated with views" << std::endl;
Chris@184 1035 #endif
Chris@184 1036
Chris@130 1037 for (ViewFFTMap::iterator i = m_fftModels.begin();
Chris@130 1038 i != m_fftModels.end(); ++i) {
Chris@115 1039
Chris@130 1040 const FFTModel *model = i->second.first;
Chris@115 1041 size_t lastFill = i->second.second;
Chris@115 1042
Chris@130 1043 if (model) {
Chris@130 1044
Chris@130 1045 size_t fill = model->getFillExtent();
Chris@115 1046
Chris@0 1047 #ifdef DEBUG_SPECTROGRAM_REPAINT
Chris@130 1048 std::cerr << "SpectrogramLayer::fillTimerTimedOut: extent for " << model << ": " << fill << ", last " << lastFill << ", total " << m_model->getEndFrame() << std::endl;
Chris@0 1049 #endif
Chris@115 1050
Chris@115 1051 if (fill >= lastFill) {
Chris@115 1052 if (fill >= m_model->getEndFrame() && lastFill > 0) {
Chris@0 1053 #ifdef DEBUG_SPECTROGRAM_REPAINT
Chris@115 1054 std::cerr << "complete!" << std::endl;
Chris@0 1055 #endif
Chris@115 1056 invalidatePixmapCaches();
Chris@184 1057 i->second.second = -1;
Chris@115 1058 emit modelChanged();
Chris@115 1059
Chris@115 1060 } else if (fill > lastFill) {
Chris@0 1061 #ifdef DEBUG_SPECTROGRAM_REPAINT
Chris@115 1062 std::cerr << "SpectrogramLayer: emitting modelChanged("
Chris@115 1063 << lastFill << "," << fill << ")" << std::endl;
Chris@0 1064 #endif
Chris@115 1065 invalidatePixmapCaches(lastFill, fill);
Chris@184 1066 i->second.second = fill;
Chris@115 1067 emit modelChanged(lastFill, fill);
Chris@115 1068 }
Chris@115 1069 } else {
Chris@0 1070 #ifdef DEBUG_SPECTROGRAM_REPAINT
Chris@115 1071 std::cerr << "SpectrogramLayer: going backwards, emitting modelChanged("
Chris@115 1072 << m_model->getStartFrame() << "," << m_model->getEndFrame() << ")" << std::endl;
Chris@0 1073 #endif
Chris@115 1074 invalidatePixmapCaches();
Chris@184 1075 i->second.second = fill;
Chris@115 1076 emit modelChanged(m_model->getStartFrame(), m_model->getEndFrame());
Chris@115 1077 }
Chris@115 1078
Chris@115 1079 if (i->second.second >= 0) {
Chris@115 1080 allDone = false;
Chris@115 1081 }
Chris@115 1082 }
Chris@0 1083 }
Chris@115 1084
Chris@115 1085 if (allDone) {
Chris@115 1086 #ifdef DEBUG_SPECTROGRAM_REPAINT
Chris@115 1087 std::cerr << "SpectrogramLayer: all complete!" << std::endl;
Chris@115 1088 #endif
Chris@115 1089 delete m_updateTimer;
Chris@115 1090 m_updateTimer = 0;
Chris@115 1091 }
Chris@0 1092 }
Chris@0 1093
Chris@224 1094 bool
Chris@224 1095 SpectrogramLayer::hasLightBackground() const
Chris@224 1096 {
Chris@287 1097 return ColourMapper(m_colourMap, 1.f, 255.f).hasLightBackground();
Chris@224 1098 }
Chris@224 1099
Chris@0 1100 void
Chris@197 1101 SpectrogramLayer::initialisePalette()
Chris@0 1102 {
Chris@10 1103 int formerRotation = m_colourRotation;
Chris@10 1104
Chris@197 1105 if (m_colourMap == (int)ColourMapper::BlackOnWhite) {
Chris@197 1106 m_palette.setColour(NO_VALUE, Qt::white);
Chris@38 1107 } else {
Chris@197 1108 m_palette.setColour(NO_VALUE, Qt::black);
Chris@38 1109 }
Chris@0 1110
Chris@197 1111 ColourMapper mapper(m_colourMap, 1.f, 255.f);
Chris@196 1112
Chris@0 1113 for (int pixel = 1; pixel < 256; ++pixel) {
Chris@197 1114 m_palette.setColour(pixel, mapper.map(pixel));
Chris@0 1115 }
Chris@9 1116
Chris@196 1117 m_crosshairColour = mapper.getContrastingColour();
Chris@196 1118
Chris@9 1119 m_colourRotation = 0;
Chris@197 1120 rotatePalette(m_colourRotation - formerRotation);
Chris@10 1121 m_colourRotation = formerRotation;
Chris@9 1122 }
Chris@9 1123
Chris@9 1124 void
Chris@197 1125 SpectrogramLayer::rotatePalette(int distance)
Chris@9 1126 {
Chris@31 1127 QColor newPixels[256];
Chris@9 1128
Chris@197 1129 newPixels[NO_VALUE] = m_palette.getColour(NO_VALUE);
Chris@9 1130
Chris@9 1131 for (int pixel = 1; pixel < 256; ++pixel) {
Chris@9 1132 int target = pixel + distance;
Chris@9 1133 while (target < 1) target += 255;
Chris@9 1134 while (target > 255) target -= 255;
Chris@197 1135 newPixels[target] = m_palette.getColour(pixel);
Chris@9 1136 }
Chris@9 1137
Chris@9 1138 for (int pixel = 0; pixel < 256; ++pixel) {
Chris@197 1139 m_palette.setColour(pixel, newPixels[pixel]);
Chris@9 1140 }
Chris@0 1141 }
Chris@0 1142
Chris@38 1143 unsigned char
Chris@119 1144 SpectrogramLayer::getDisplayValue(View *v, float input) const
Chris@38 1145 {
Chris@38 1146 int value;
Chris@37 1147
Chris@120 1148 float min = 0.f;
Chris@120 1149 float max = 1.f;
Chris@120 1150
Chris@120 1151 if (m_normalizeVisibleArea) {
Chris@120 1152 min = m_viewMags[v].getMin();
Chris@120 1153 max = m_viewMags[v].getMax();
Chris@120 1154 } else if (!m_normalizeColumns) {
Chris@224 1155 if (m_colourScale == LinearColourScale //||
Chris@224 1156 // m_colourScale == MeterColourScale) {
Chris@224 1157 ) {
Chris@224 1158 max = 0.1f;
Chris@120 1159 }
Chris@120 1160 }
Chris@120 1161
Chris@119 1162 float thresh = -80.f;
Chris@119 1163
Chris@119 1164 if (max == 0.f) max = 1.f;
Chris@119 1165 if (max == min) min = max - 0.0001f;
Chris@119 1166
Chris@40 1167 switch (m_colourScale) {
Chris@40 1168
Chris@40 1169 default:
Chris@40 1170 case LinearColourScale:
Chris@119 1171 value = int(((input - min) / (max - min)) * 255.f) + 1;
Chris@40 1172 break;
Chris@40 1173
Chris@40 1174 case MeterColourScale:
Chris@210 1175 value = AudioLevel::multiplier_to_preview
Chris@210 1176 ((input - min) / (max - min), 254) + 1;
Chris@40 1177 break;
Chris@119 1178
Chris@210 1179 case dBSquaredColourScale:
Chris@215 1180 input = ((input - min) * (input - min)) / ((max - min) * (max - min));
Chris@133 1181 if (input > 0.f) {
Chris@133 1182 input = 10.f * log10f(input);
Chris@133 1183 } else {
Chris@133 1184 input = thresh;
Chris@133 1185 }
Chris@119 1186 if (min > 0.f) {
Chris@119 1187 thresh = 10.f * log10f(min * min);
Chris@119 1188 if (thresh < -80.f) thresh = -80.f;
Chris@119 1189 }
Chris@119 1190 input = (input - thresh) / (-thresh);
Chris@119 1191 if (input < 0.f) input = 0.f;
Chris@119 1192 if (input > 1.f) input = 1.f;
Chris@119 1193 value = int(input * 255.f) + 1;
Chris@119 1194 break;
Chris@40 1195
Chris@215 1196 case dBColourScale:
Chris@215 1197 //!!! experiment with normalizing the visible area this way.
Chris@215 1198 //In any case, we need to have some indication of what the dB
Chris@215 1199 //scale is relative to.
Chris@215 1200 input = (input - min) / (max - min);
Chris@215 1201 if (input > 0.f) {
Chris@215 1202 input = 10.f * log10f(input);
Chris@215 1203 } else {
Chris@215 1204 input = thresh;
Chris@215 1205 }
Chris@215 1206 if (min > 0.f) {
Chris@215 1207 thresh = 10.f * log10f(min);
Chris@215 1208 if (thresh < -80.f) thresh = -80.f;
Chris@215 1209 }
Chris@215 1210 input = (input - thresh) / (-thresh);
Chris@215 1211 if (input < 0.f) input = 0.f;
Chris@215 1212 if (input > 1.f) input = 1.f;
Chris@215 1213 value = int(input * 255.f) + 1;
Chris@215 1214 break;
Chris@215 1215
Chris@40 1216 case PhaseColourScale:
Chris@40 1217 value = int((input * 127.0 / M_PI) + 128);
Chris@40 1218 break;
Chris@0 1219 }
Chris@210 1220
Chris@38 1221 if (value > UCHAR_MAX) value = UCHAR_MAX;
Chris@38 1222 if (value < 0) value = 0;
Chris@38 1223 return value;
Chris@0 1224 }
Chris@0 1225
Chris@40 1226 float
Chris@40 1227 SpectrogramLayer::getInputForDisplayValue(unsigned char uc) const
Chris@40 1228 {
Chris@153 1229 //!!! unused
Chris@153 1230
Chris@40 1231 int value = uc;
Chris@40 1232 float input;
Chris@40 1233
Chris@120 1234 //!!! incorrect for normalizing visible area (and also out of date)
Chris@120 1235
Chris@40 1236 switch (m_colourScale) {
Chris@40 1237
Chris@40 1238 default:
Chris@40 1239 case LinearColourScale:
Chris@40 1240 input = float(value - 1) / 255.0 / (m_normalizeColumns ? 1 : 50);
Chris@40 1241 break;
Chris@40 1242
Chris@40 1243 case MeterColourScale:
Chris@40 1244 input = AudioLevel::preview_to_multiplier(value - 1, 255)
Chris@40 1245 / (m_normalizeColumns ? 1.0 : 50.0);
Chris@40 1246 break;
Chris@40 1247
Chris@215 1248 case dBSquaredColourScale:
Chris@40 1249 input = float(value - 1) / 255.0;
Chris@40 1250 input = (input * 80.0) - 80.0;
Chris@40 1251 input = powf(10.0, input) / 20.0;
Chris@40 1252 value = int(input);
Chris@40 1253 break;
Chris@40 1254
Chris@215 1255 case dBColourScale:
Chris@119 1256 input = float(value - 1) / 255.0;
Chris@119 1257 input = (input * 80.0) - 80.0;
Chris@119 1258 input = powf(10.0, input) / 20.0;
Chris@119 1259 value = int(input);
Chris@119 1260 break;
Chris@119 1261
Chris@40 1262 case PhaseColourScale:
Chris@40 1263 input = float(value - 128) * M_PI / 127.0;
Chris@40 1264 break;
Chris@40 1265 }
Chris@40 1266
Chris@40 1267 return input;
Chris@40 1268 }
Chris@40 1269
Chris@40 1270 float
Chris@40 1271 SpectrogramLayer::getEffectiveMinFrequency() const
Chris@40 1272 {
Chris@40 1273 int sr = m_model->getSampleRate();
Chris@107 1274 float minf = float(sr) / m_fftSize;
Chris@40 1275
Chris@40 1276 if (m_minFrequency > 0.0) {
Chris@107 1277 size_t minbin = size_t((double(m_minFrequency) * m_fftSize) / sr + 0.01);
Chris@40 1278 if (minbin < 1) minbin = 1;
Chris@107 1279 minf = minbin * sr / m_fftSize;
Chris@40 1280 }
Chris@40 1281
Chris@40 1282 return minf;
Chris@40 1283 }
Chris@40 1284
Chris@40 1285 float
Chris@40 1286 SpectrogramLayer::getEffectiveMaxFrequency() const
Chris@40 1287 {
Chris@40 1288 int sr = m_model->getSampleRate();
Chris@40 1289 float maxf = float(sr) / 2;
Chris@40 1290
Chris@40 1291 if (m_maxFrequency > 0.0) {
Chris@107 1292 size_t maxbin = size_t((double(m_maxFrequency) * m_fftSize) / sr + 0.1);
Chris@107 1293 if (maxbin > m_fftSize / 2) maxbin = m_fftSize / 2;
Chris@107 1294 maxf = maxbin * sr / m_fftSize;
Chris@40 1295 }
Chris@40 1296
Chris@40 1297 return maxf;
Chris@40 1298 }
Chris@40 1299
Chris@0 1300 bool
Chris@44 1301 SpectrogramLayer::getYBinRange(View *v, int y, float &q0, float &q1) const
Chris@0 1302 {
Chris@382 1303 Profiler profiler("SpectrogramLayer::getYBinRange");
Chris@382 1304
Chris@44 1305 int h = v->height();
Chris@0 1306 if (y < 0 || y >= h) return false;
Chris@0 1307
Chris@38 1308 int sr = m_model->getSampleRate();
Chris@40 1309 float minf = getEffectiveMinFrequency();
Chris@40 1310 float maxf = getEffectiveMaxFrequency();
Chris@0 1311
Chris@38 1312 bool logarithmic = (m_frequencyScale == LogFrequencyScale);
Chris@38 1313
Chris@130 1314 //!!! wrong for smoothing -- wrong fft size for fft model
Chris@114 1315
Chris@44 1316 q0 = v->getFrequencyForY(y, minf, maxf, logarithmic);
Chris@44 1317 q1 = v->getFrequencyForY(y - 1, minf, maxf, logarithmic);
Chris@38 1318
Chris@38 1319 // Now map these on to actual bins
Chris@38 1320
Chris@107 1321 int b0 = int((q0 * m_fftSize) / sr);
Chris@107 1322 int b1 = int((q1 * m_fftSize) / sr);
Chris@0 1323
Chris@40 1324 //!!! this is supposed to return fractions-of-bins, as it were, hence the floats
Chris@38 1325 q0 = b0;
Chris@38 1326 q1 = b1;
Chris@38 1327
Chris@107 1328 // q0 = (b0 * sr) / m_fftSize;
Chris@107 1329 // q1 = (b1 * sr) / m_fftSize;
Chris@0 1330
Chris@0 1331 return true;
Chris@0 1332 }
Chris@38 1333
Chris@0 1334 bool
Chris@44 1335 SpectrogramLayer::getXBinRange(View *v, int x, float &s0, float &s1) const
Chris@0 1336 {
Chris@21 1337 size_t modelStart = m_model->getStartFrame();
Chris@21 1338 size_t modelEnd = m_model->getEndFrame();
Chris@0 1339
Chris@0 1340 // Each pixel column covers an exact range of sample frames:
Chris@44 1341 int f0 = v->getFrameForX(x) - modelStart;
Chris@44 1342 int f1 = v->getFrameForX(x + 1) - modelStart - 1;
Chris@20 1343
Chris@41 1344 if (f1 < int(modelStart) || f0 > int(modelEnd)) {
Chris@41 1345 return false;
Chris@41 1346 }
Chris@20 1347
Chris@0 1348 // And that range may be drawn from a possibly non-integral
Chris@0 1349 // range of spectrogram windows:
Chris@0 1350
Chris@0 1351 size_t windowIncrement = getWindowIncrement();
Chris@0 1352 s0 = float(f0) / windowIncrement;
Chris@0 1353 s1 = float(f1) / windowIncrement;
Chris@0 1354
Chris@0 1355 return true;
Chris@0 1356 }
Chris@0 1357
Chris@0 1358 bool
Chris@44 1359 SpectrogramLayer::getXBinSourceRange(View *v, int x, RealTime &min, RealTime &max) const
Chris@0 1360 {
Chris@0 1361 float s0 = 0, s1 = 0;
Chris@44 1362 if (!getXBinRange(v, x, s0, s1)) return false;
Chris@0 1363
Chris@0 1364 int s0i = int(s0 + 0.001);
Chris@0 1365 int s1i = int(s1);
Chris@0 1366
Chris@0 1367 int windowIncrement = getWindowIncrement();
Chris@0 1368 int w0 = s0i * windowIncrement - (m_windowSize - windowIncrement)/2;
Chris@0 1369 int w1 = s1i * windowIncrement + windowIncrement +
Chris@0 1370 (m_windowSize - windowIncrement)/2 - 1;
Chris@0 1371
Chris@0 1372 min = RealTime::frame2RealTime(w0, m_model->getSampleRate());
Chris@0 1373 max = RealTime::frame2RealTime(w1, m_model->getSampleRate());
Chris@0 1374 return true;
Chris@0 1375 }
Chris@0 1376
Chris@0 1377 bool
Chris@44 1378 SpectrogramLayer::getYBinSourceRange(View *v, int y, float &freqMin, float &freqMax)
Chris@0 1379 const
Chris@0 1380 {
Chris@0 1381 float q0 = 0, q1 = 0;
Chris@44 1382 if (!getYBinRange(v, y, q0, q1)) return false;
Chris@0 1383
Chris@0 1384 int q0i = int(q0 + 0.001);
Chris@0 1385 int q1i = int(q1);
Chris@0 1386
Chris@0 1387 int sr = m_model->getSampleRate();
Chris@0 1388
Chris@0 1389 for (int q = q0i; q <= q1i; ++q) {
Chris@121 1390 if (q == q0i) freqMin = (sr * q) / m_fftSize;
Chris@121 1391 if (q == q1i) freqMax = (sr * (q+1)) / m_fftSize;
Chris@0 1392 }
Chris@0 1393 return true;
Chris@0 1394 }
Chris@35 1395
Chris@35 1396 bool
Chris@44 1397 SpectrogramLayer::getAdjustedYBinSourceRange(View *v, int x, int y,
Chris@35 1398 float &freqMin, float &freqMax,
Chris@35 1399 float &adjFreqMin, float &adjFreqMax)
Chris@35 1400 const
Chris@35 1401 {
Chris@277 1402 if (!m_model || !m_model->isOK() || !m_model->isReady()) {
Chris@277 1403 return false;
Chris@277 1404 }
Chris@277 1405
Chris@130 1406 FFTModel *fft = getFFTModel(v);
Chris@114 1407 if (!fft) return false;
Chris@110 1408
Chris@35 1409 float s0 = 0, s1 = 0;
Chris@44 1410 if (!getXBinRange(v, x, s0, s1)) return false;
Chris@35 1411
Chris@35 1412 float q0 = 0, q1 = 0;
Chris@44 1413 if (!getYBinRange(v, y, q0, q1)) return false;
Chris@35 1414
Chris@35 1415 int s0i = int(s0 + 0.001);
Chris@35 1416 int s1i = int(s1);
Chris@35 1417
Chris@35 1418 int q0i = int(q0 + 0.001);
Chris@35 1419 int q1i = int(q1);
Chris@35 1420
Chris@35 1421 int sr = m_model->getSampleRate();
Chris@35 1422
Chris@38 1423 size_t windowSize = m_windowSize;
Chris@38 1424 size_t windowIncrement = getWindowIncrement();
Chris@38 1425
Chris@35 1426 bool haveAdj = false;
Chris@35 1427
Chris@37 1428 bool peaksOnly = (m_binDisplay == PeakBins ||
Chris@37 1429 m_binDisplay == PeakFrequencies);
Chris@37 1430
Chris@35 1431 for (int q = q0i; q <= q1i; ++q) {
Chris@35 1432
Chris@35 1433 for (int s = s0i; s <= s1i; ++s) {
Chris@35 1434
Chris@160 1435 if (!fft->isColumnAvailable(s)) continue;
Chris@117 1436
Chris@35 1437 float binfreq = (sr * q) / m_windowSize;
Chris@35 1438 if (q == q0i) freqMin = binfreq;
Chris@35 1439 if (q == q1i) freqMax = binfreq;
Chris@37 1440
Chris@114 1441 if (peaksOnly && !fft->isLocalPeak(s, q)) continue;
Chris@38 1442
Chris@253 1443 if (!fft->isOverThreshold(s, q, m_threshold * (m_fftSize/2))) continue;
Chris@38 1444
Chris@38 1445 float freq = binfreq;
Chris@38 1446 bool steady = false;
Chris@40 1447
Chris@114 1448 if (s < int(fft->getWidth()) - 1) {
Chris@38 1449
Chris@277 1450 fft->estimateStableFrequency(s, q, freq);
Chris@35 1451
Chris@38 1452 if (!haveAdj || freq < adjFreqMin) adjFreqMin = freq;
Chris@38 1453 if (!haveAdj || freq > adjFreqMax) adjFreqMax = freq;
Chris@35 1454
Chris@35 1455 haveAdj = true;
Chris@35 1456 }
Chris@35 1457 }
Chris@35 1458 }
Chris@35 1459
Chris@35 1460 if (!haveAdj) {
Chris@40 1461 adjFreqMin = adjFreqMax = 0.0;
Chris@35 1462 }
Chris@35 1463
Chris@35 1464 return haveAdj;
Chris@35 1465 }
Chris@0 1466
Chris@0 1467 bool
Chris@44 1468 SpectrogramLayer::getXYBinSourceRange(View *v, int x, int y,
Chris@38 1469 float &min, float &max,
Chris@38 1470 float &phaseMin, float &phaseMax) const
Chris@0 1471 {
Chris@277 1472 if (!m_model || !m_model->isOK() || !m_model->isReady()) {
Chris@277 1473 return false;
Chris@277 1474 }
Chris@277 1475
Chris@0 1476 float q0 = 0, q1 = 0;
Chris@44 1477 if (!getYBinRange(v, y, q0, q1)) return false;
Chris@0 1478
Chris@0 1479 float s0 = 0, s1 = 0;
Chris@44 1480 if (!getXBinRange(v, x, s0, s1)) return false;
Chris@0 1481
Chris@0 1482 int q0i = int(q0 + 0.001);
Chris@0 1483 int q1i = int(q1);
Chris@0 1484
Chris@0 1485 int s0i = int(s0 + 0.001);
Chris@0 1486 int s1i = int(s1);
Chris@0 1487
Chris@37 1488 bool rv = false;
Chris@37 1489
Chris@122 1490 size_t zp = getZeroPadLevel(v);
Chris@122 1491 q0i *= zp + 1;
Chris@122 1492 q1i *= zp + 1;
Chris@122 1493
Chris@130 1494 FFTModel *fft = getFFTModel(v);
Chris@0 1495
Chris@114 1496 if (fft) {
Chris@114 1497
Chris@114 1498 int cw = fft->getWidth();
Chris@114 1499 int ch = fft->getHeight();
Chris@0 1500
Chris@110 1501 min = 0.0;
Chris@110 1502 max = 0.0;
Chris@110 1503 phaseMin = 0.0;
Chris@110 1504 phaseMax = 0.0;
Chris@110 1505 bool have = false;
Chris@0 1506
Chris@110 1507 for (int q = q0i; q <= q1i; ++q) {
Chris@110 1508 for (int s = s0i; s <= s1i; ++s) {
Chris@110 1509 if (s >= 0 && q >= 0 && s < cw && q < ch) {
Chris@117 1510
Chris@160 1511 if (!fft->isColumnAvailable(s)) continue;
Chris@110 1512
Chris@110 1513 float value;
Chris@38 1514
Chris@114 1515 value = fft->getPhaseAt(s, q);
Chris@110 1516 if (!have || value < phaseMin) { phaseMin = value; }
Chris@110 1517 if (!have || value > phaseMax) { phaseMax = value; }
Chris@91 1518
Chris@252 1519 value = fft->getMagnitudeAt(s, q) / (m_fftSize/2);
Chris@110 1520 if (!have || value < min) { min = value; }
Chris@110 1521 if (!have || value > max) { max = value; }
Chris@110 1522
Chris@110 1523 have = true;
Chris@110 1524 }
Chris@110 1525 }
Chris@110 1526 }
Chris@110 1527
Chris@110 1528 if (have) {
Chris@110 1529 rv = true;
Chris@110 1530 }
Chris@0 1531 }
Chris@0 1532
Chris@37 1533 return rv;
Chris@0 1534 }
Chris@0 1535
Chris@114 1536 size_t
Chris@114 1537 SpectrogramLayer::getZeroPadLevel(const View *v) const
Chris@114 1538 {
Chris@114 1539 //!!! tidy all this stuff
Chris@114 1540
Chris@114 1541 if (m_binDisplay != AllBins) return 0;
Chris@221 1542
Chris@221 1543 Preferences::SpectrogramSmoothing smoothing =
Chris@221 1544 Preferences::getInstance()->getSpectrogramSmoothing();
Chris@221 1545
Chris@221 1546 if (smoothing == Preferences::NoSpectrogramSmoothing ||
Chris@221 1547 smoothing == Preferences::SpectrogramInterpolated) return 0;
Chris@221 1548
Chris@114 1549 if (m_frequencyScale == LogFrequencyScale) return 3;
Chris@114 1550
Chris@114 1551 int sr = m_model->getSampleRate();
Chris@114 1552
Chris@184 1553 size_t maxbin = m_fftSize / 2;
Chris@114 1554 if (m_maxFrequency > 0) {
Chris@184 1555 maxbin = int((double(m_maxFrequency) * m_fftSize) / sr + 0.1);
Chris@184 1556 if (maxbin > m_fftSize / 2) maxbin = m_fftSize / 2;
Chris@114 1557 }
Chris@114 1558
Chris@114 1559 size_t minbin = 1;
Chris@114 1560 if (m_minFrequency > 0) {
Chris@114 1561 minbin = int((double(m_minFrequency) * m_fftSize) / sr + 0.1);
Chris@114 1562 if (minbin < 1) minbin = 1;
Chris@184 1563 if (minbin >= maxbin) minbin = maxbin - 1;
Chris@114 1564 }
Chris@114 1565
Chris@118 1566 float perPixel =
Chris@118 1567 float(v->height()) /
Chris@184 1568 float((maxbin - minbin) / (m_zeroPadLevel + 1));
Chris@118 1569
Chris@118 1570 if (perPixel > 2.8) {
Chris@118 1571 return 3; // 4x oversampling
Chris@118 1572 } else if (perPixel > 1.5) {
Chris@118 1573 return 1; // 2x
Chris@114 1574 } else {
Chris@118 1575 return 0; // 1x
Chris@114 1576 }
Chris@114 1577 }
Chris@114 1578
Chris@114 1579 size_t
Chris@114 1580 SpectrogramLayer::getFFTSize(const View *v) const
Chris@114 1581 {
Chris@114 1582 return m_fftSize * (getZeroPadLevel(v) + 1);
Chris@114 1583 }
Chris@114 1584
Chris@130 1585 FFTModel *
Chris@130 1586 SpectrogramLayer::getFFTModel(const View *v) const
Chris@114 1587 {
Chris@114 1588 if (!m_model) return 0;
Chris@114 1589
Chris@114 1590 size_t fftSize = getFFTSize(v);
Chris@114 1591
Chris@130 1592 if (m_fftModels.find(v) != m_fftModels.end()) {
Chris@184 1593 if (m_fftModels[v].first == 0) {
Chris@184 1594 #ifdef DEBUG_SPECTROGRAM_REPAINT
Chris@184 1595 std::cerr << "SpectrogramLayer::getFFTModel(" << v << "): Found null model" << std::endl;
Chris@184 1596 #endif
Chris@184 1597 return 0;
Chris@184 1598 }
Chris@184 1599 if (m_fftModels[v].first->getHeight() != fftSize / 2 + 1) {
Chris@184 1600 #ifdef DEBUG_SPECTROGRAM_REPAINT
Chris@184 1601 std::cerr << "SpectrogramLayer::getFFTModel(" << v << "): Found a model with the wrong height (" << m_fftModels[v].first->getHeight() << ", wanted " << (fftSize / 2 + 1) << ")" << std::endl;
Chris@184 1602 #endif
Chris@130 1603 delete m_fftModels[v].first;
Chris@130 1604 m_fftModels.erase(v);
Chris@184 1605 } else {
Chris@184 1606 #ifdef DEBUG_SPECTROGRAM_REPAINT
Chris@187 1607 std::cerr << "SpectrogramLayer::getFFTModel(" << v << "): Found a good model of height " << m_fftModels[v].first->getHeight() << std::endl;
Chris@184 1608 #endif
Chris@184 1609 return m_fftModels[v].first;
Chris@114 1610 }
Chris@114 1611 }
Chris@114 1612
Chris@130 1613 if (m_fftModels.find(v) == m_fftModels.end()) {
Chris@169 1614
Chris@169 1615 FFTModel *model = new FFTModel(m_model,
Chris@169 1616 m_channel,
Chris@169 1617 m_windowType,
Chris@169 1618 m_windowSize,
Chris@169 1619 getWindowIncrement(),
Chris@169 1620 fftSize,
Chris@382 1621 true, // polar
Chris@327 1622 StorageAdviser::SpeedCritical,
Chris@169 1623 m_candidateFillStartFrame);
Chris@169 1624
Chris@178 1625 if (!model->isOK()) {
Chris@178 1626 QMessageBox::critical
Chris@178 1627 (0, tr("FFT cache failed"),
Chris@178 1628 tr("Failed to create the FFT model for this spectrogram.\n"
Chris@178 1629 "There may be insufficient memory or disc space to continue."));
Chris@178 1630 delete model;
Chris@178 1631 m_fftModels[v] = FFTFillPair(0, 0);
Chris@178 1632 return 0;
Chris@178 1633 }
Chris@178 1634
Chris@193 1635 if (!m_sliceableModel) {
Chris@248 1636 #ifdef DEBUG_SPECTROGRAM
Chris@193 1637 std::cerr << "SpectrogramLayer: emitting sliceableModelReplaced(0, " << model << ")" << std::endl;
Chris@248 1638 #endif
Chris@193 1639 ((SpectrogramLayer *)this)->sliceableModelReplaced(0, model);
Chris@193 1640 m_sliceableModel = model;
Chris@193 1641 }
Chris@193 1642
Chris@169 1643 m_fftModels[v] = FFTFillPair(model, 0);
Chris@169 1644
Chris@169 1645 model->resume();
Chris@114 1646
Chris@114 1647 delete m_updateTimer;
Chris@114 1648 m_updateTimer = new QTimer((SpectrogramLayer *)this);
Chris@114 1649 connect(m_updateTimer, SIGNAL(timeout()),
Chris@114 1650 this, SLOT(fillTimerTimedOut()));
Chris@114 1651 m_updateTimer->start(200);
Chris@114 1652 }
Chris@114 1653
Chris@130 1654 return m_fftModels[v].first;
Chris@114 1655 }
Chris@114 1656
Chris@193 1657 const Model *
Chris@193 1658 SpectrogramLayer::getSliceableModel() const
Chris@193 1659 {
Chris@193 1660 if (m_sliceableModel) return m_sliceableModel;
Chris@193 1661 if (m_fftModels.empty()) return 0;
Chris@193 1662 m_sliceableModel = m_fftModels.begin()->second.first;
Chris@193 1663 return m_sliceableModel;
Chris@193 1664 }
Chris@193 1665
Chris@114 1666 void
Chris@130 1667 SpectrogramLayer::invalidateFFTModels()
Chris@114 1668 {
Chris@130 1669 for (ViewFFTMap::iterator i = m_fftModels.begin();
Chris@130 1670 i != m_fftModels.end(); ++i) {
Chris@115 1671 delete i->second.first;
Chris@114 1672 }
Chris@114 1673
Chris@130 1674 m_fftModels.clear();
Chris@193 1675
Chris@193 1676 if (m_sliceableModel) {
Chris@193 1677 std::cerr << "SpectrogramLayer: emitting sliceableModelReplaced(" << m_sliceableModel << ", 0)" << std::endl;
Chris@193 1678 emit sliceableModelReplaced(m_sliceableModel, 0);
Chris@193 1679 m_sliceableModel = 0;
Chris@193 1680 }
Chris@114 1681 }
Chris@114 1682
Chris@0 1683 void
Chris@119 1684 SpectrogramLayer::invalidateMagnitudes()
Chris@119 1685 {
Chris@119 1686 m_viewMags.clear();
Chris@119 1687 for (std::vector<MagnitudeRange>::iterator i = m_columnMags.begin();
Chris@119 1688 i != m_columnMags.end(); ++i) {
Chris@119 1689 *i = MagnitudeRange();
Chris@119 1690 }
Chris@119 1691 }
Chris@119 1692
Chris@119 1693 bool
Chris@119 1694 SpectrogramLayer::updateViewMagnitudes(View *v) const
Chris@119 1695 {
Chris@119 1696 MagnitudeRange mag;
Chris@119 1697
Chris@119 1698 int x0 = 0, x1 = v->width();
Chris@119 1699 float s00 = 0, s01 = 0, s10 = 0, s11 = 0;
Chris@119 1700
Chris@203 1701 if (!getXBinRange(v, x0, s00, s01)) {
Chris@203 1702 s00 = s01 = m_model->getStartFrame() / getWindowIncrement();
Chris@203 1703 }
Chris@203 1704
Chris@203 1705 if (!getXBinRange(v, x1, s10, s11)) {
Chris@203 1706 s10 = s11 = m_model->getEndFrame() / getWindowIncrement();
Chris@203 1707 }
Chris@119 1708
Chris@119 1709 int s0 = int(std::min(s00, s10) + 0.0001);
Chris@203 1710 int s1 = int(std::max(s01, s11) + 0.0001);
Chris@203 1711
Chris@203 1712 // std::cerr << "SpectrogramLayer::updateViewMagnitudes: x0 = " << x0 << ", x1 = " << x1 << ", s00 = " << s00 << ", s11 = " << s11 << " s0 = " << s0 << ", s1 = " << s1 << std::endl;
Chris@119 1713
Chris@248 1714 if (int(m_columnMags.size()) <= s1) {
Chris@119 1715 m_columnMags.resize(s1 + 1);
Chris@119 1716 }
Chris@119 1717
Chris@119 1718 for (int s = s0; s <= s1; ++s) {
Chris@119 1719 if (m_columnMags[s].isSet()) {
Chris@119 1720 mag.sample(m_columnMags[s]);
Chris@119 1721 }
Chris@119 1722 }
Chris@119 1723
Chris@184 1724 #ifdef DEBUG_SPECTROGRAM_REPAINT
Chris@119 1725 std::cerr << "SpectrogramLayer::updateViewMagnitudes returning from cols "
Chris@119 1726 << s0 << " -> " << s1 << " inclusive" << std::endl;
Chris@184 1727 #endif
Chris@119 1728
Chris@119 1729 if (!mag.isSet()) return false;
Chris@119 1730 if (mag == m_viewMags[v]) return false;
Chris@119 1731 m_viewMags[v] = mag;
Chris@119 1732 return true;
Chris@119 1733 }
Chris@119 1734
Chris@119 1735 void
Chris@389 1736 SpectrogramLayer::setSynchronousPainting(bool synchronous)
Chris@389 1737 {
Chris@389 1738 m_synchronous = synchronous;
Chris@389 1739 }
Chris@389 1740
Chris@389 1741 void
Chris@44 1742 SpectrogramLayer::paint(View *v, QPainter &paint, QRect rect) const
Chris@0 1743 {
Chris@253 1744 // What a lovely, old-fashioned function this is.
Chris@253 1745 // It's practically FORTRAN 77 in its clarity and linearity.
Chris@253 1746
Chris@334 1747 Profiler profiler("SpectrogramLayer::paint", false);
Chris@334 1748
Chris@0 1749 #ifdef DEBUG_SPECTROGRAM_REPAINT
Chris@95 1750 std::cerr << "SpectrogramLayer::paint(): m_model is " << m_model << ", zoom level is " << v->getZoomLevel() << ", m_updateTimer " << m_updateTimer << std::endl;
Chris@95 1751
Chris@95 1752 std::cerr << "rect is " << rect.x() << "," << rect.y() << " " << rect.width() << "x" << rect.height() << std::endl;
Chris@0 1753 #endif
Chris@95 1754
Chris@133 1755 long startFrame = v->getStartFrame();
Chris@133 1756 if (startFrame < 0) m_candidateFillStartFrame = 0;
Chris@133 1757 else m_candidateFillStartFrame = startFrame;
Chris@44 1758
Chris@0 1759 if (!m_model || !m_model->isOK() || !m_model->isReady()) {
Chris@0 1760 return;
Chris@0 1761 }
Chris@0 1762
Chris@47 1763 if (isLayerDormant(v)) {
Chris@48 1764 std::cerr << "SpectrogramLayer::paint(): Layer is dormant, making it undormant again" << std::endl;
Chris@29 1765 }
Chris@29 1766
Chris@48 1767 // Need to do this even if !isLayerDormant, as that could mean v
Chris@48 1768 // is not in the dormancy map at all -- we need it to be present
Chris@48 1769 // and accountable for when determining whether we need the cache
Chris@48 1770 // in the cache-fill thread above.
Chris@131 1771 //!!! no longer use cache-fill thread
Chris@131 1772 const_cast<SpectrogramLayer *>(this)->Layer::setLayerDormant(v, false);
Chris@48 1773
Chris@114 1774 size_t fftSize = getFFTSize(v);
Chris@130 1775 FFTModel *fft = getFFTModel(v);
Chris@114 1776 if (!fft) {
Chris@130 1777 std::cerr << "ERROR: SpectrogramLayer::paint(): No FFT model, returning" << std::endl;
Chris@0 1778 return;
Chris@0 1779 }
Chris@0 1780
Chris@95 1781 PixmapCache &cache = m_pixmapCaches[v];
Chris@95 1782
Chris@95 1783 #ifdef DEBUG_SPECTROGRAM_REPAINT
Chris@95 1784 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 1785 #endif
Chris@95 1786
Chris@248 1787 #ifdef DEBUG_SPECTROGRAM_REPAINT
Chris@0 1788 bool stillCacheing = (m_updateTimer != 0);
Chris@0 1789 std::cerr << "SpectrogramLayer::paint(): Still cacheing = " << stillCacheing << std::endl;
Chris@0 1790 #endif
Chris@0 1791
Chris@44 1792 int zoomLevel = v->getZoomLevel();
Chris@0 1793
Chris@0 1794 int x0 = 0;
Chris@44 1795 int x1 = v->width();
Chris@0 1796
Chris@0 1797 bool recreateWholePixmapCache = true;
Chris@0 1798
Chris@95 1799 x0 = rect.left();
Chris@95 1800 x1 = rect.right() + 1;
Chris@95 1801
Chris@95 1802 if (cache.validArea.width() > 0) {
Chris@95 1803
Chris@95 1804 if (int(cache.zoomLevel) == zoomLevel &&
Chris@95 1805 cache.pixmap.width() == v->width() &&
Chris@95 1806 cache.pixmap.height() == v->height()) {
Chris@95 1807
Chris@95 1808 if (v->getXForFrame(cache.startFrame) ==
Chris@95 1809 v->getXForFrame(startFrame) &&
Chris@95 1810 cache.validArea.x() <= x0 &&
Chris@95 1811 cache.validArea.x() + cache.validArea.width() >= x1) {
Chris@0 1812
Chris@0 1813 #ifdef DEBUG_SPECTROGRAM_REPAINT
Chris@0 1814 std::cerr << "SpectrogramLayer: pixmap cache good" << std::endl;
Chris@0 1815 #endif
Chris@0 1816
Chris@95 1817 paint.drawPixmap(rect, cache.pixmap, rect);
Chris@121 1818 illuminateLocalFeatures(v, paint);
Chris@0 1819 return;
Chris@0 1820
Chris@0 1821 } else {
Chris@0 1822
Chris@0 1823 #ifdef DEBUG_SPECTROGRAM_REPAINT
Chris@0 1824 std::cerr << "SpectrogramLayer: pixmap cache partially OK" << std::endl;
Chris@0 1825 #endif
Chris@0 1826
Chris@0 1827 recreateWholePixmapCache = false;
Chris@0 1828
Chris@95 1829 int dx = v->getXForFrame(cache.startFrame) -
Chris@44 1830 v->getXForFrame(startFrame);
Chris@0 1831
Chris@0 1832 #ifdef DEBUG_SPECTROGRAM_REPAINT
Chris@95 1833 std::cerr << "SpectrogramLayer: dx = " << dx << " (pixmap cache " << cache.pixmap.width() << "x" << cache.pixmap.height() << ")" << std::endl;
Chris@0 1834 #endif
Chris@0 1835
Chris@95 1836 if (dx != 0 &&
Chris@95 1837 dx > -cache.pixmap.width() &&
Chris@95 1838 dx < cache.pixmap.width()) {
Chris@0 1839
Chris@331 1840 QPixmap tmp = cache.pixmap;
Chris@331 1841 QPainter cachePainter(&cache.pixmap);
Chris@331 1842 if (dx < 0) {
Chris@331 1843 cachePainter.drawPixmap
Chris@331 1844 (QRect(0, 0,
Chris@331 1845 cache.pixmap.width() + dx,
Chris@331 1846 cache.pixmap.height()),
Chris@331 1847 tmp,
Chris@331 1848 QRect(-dx, 0,
Chris@331 1849 cache.pixmap.width() + dx,
Chris@331 1850 cache.pixmap.height()));
Chris@331 1851 } else {
Chris@331 1852 cachePainter.drawPixmap
Chris@331 1853 (QRect(dx, 0,
Chris@331 1854 cache.pixmap.width() - dx,
Chris@331 1855 cache.pixmap.height()),
Chris@331 1856 tmp,
Chris@331 1857 QRect(0, 0,
Chris@331 1858 cache.pixmap.width() - dx,
Chris@331 1859 cache.pixmap.height()));
Chris@331 1860 }
Chris@0 1861
Chris@95 1862 int px = cache.validArea.x();
Chris@95 1863 int pw = cache.validArea.width();
Chris@0 1864
Chris@0 1865 if (dx < 0) {
Chris@95 1866 x0 = cache.pixmap.width() + dx;
Chris@95 1867 x1 = cache.pixmap.width();
Chris@95 1868 px += dx;
Chris@95 1869 if (px < 0) {
Chris@95 1870 pw += px;
Chris@95 1871 px = 0;
Chris@95 1872 if (pw < 0) pw = 0;
Chris@95 1873 }
Chris@0 1874 } else {
Chris@0 1875 x0 = 0;
Chris@0 1876 x1 = dx;
Chris@95 1877 px += dx;
Chris@95 1878 if (px + pw > cache.pixmap.width()) {
Chris@95 1879 pw = int(cache.pixmap.width()) - px;
Chris@95 1880 if (pw < 0) pw = 0;
Chris@95 1881 }
Chris@0 1882 }
Chris@95 1883
Chris@95 1884 cache.validArea =
Chris@95 1885 QRect(px, cache.validArea.y(),
Chris@95 1886 pw, cache.validArea.height());
Chris@95 1887
Chris@331 1888 #ifdef DEBUG_SPECTROGRAM_REPAINT
Chris@331 1889 std::cerr << "valid area now "
Chris@331 1890 << px << "," << cache.validArea.y()
Chris@331 1891 << " " << pw << "x" << cache.validArea.height()
Chris@331 1892 << std::endl;
Chris@331 1893 #endif
Chris@331 1894
Chris@95 1895 paint.drawPixmap(rect & cache.validArea,
Chris@95 1896 cache.pixmap,
Chris@95 1897 rect & cache.validArea);
Chris@331 1898
Chris@331 1899 } else if (dx != 0) {
Chris@331 1900
Chris@331 1901 // we scrolled too far to be of use
Chris@331 1902
Chris@391 1903 #ifdef DEBUG_SPECTROGRAM_REPAINT
Chris@391 1904 std::cerr << "dx == " << dx << ": scrolled too far for cache to be useful" << std::endl;
Chris@391 1905 #endif
Chris@391 1906
Chris@331 1907 cache.validArea = QRect();
Chris@331 1908 recreateWholePixmapCache = true;
Chris@331 1909 }
Chris@0 1910 }
Chris@0 1911 } else {
Chris@0 1912 #ifdef DEBUG_SPECTROGRAM_REPAINT
Chris@0 1913 std::cerr << "SpectrogramLayer: pixmap cache useless" << std::endl;
Chris@224 1914 if (int(cache.zoomLevel) != zoomLevel) {
Chris@224 1915 std::cerr << "(cache zoomLevel " << cache.zoomLevel
Chris@224 1916 << " != " << zoomLevel << ")" << std::endl;
Chris@224 1917 }
Chris@224 1918 if (cache.pixmap.width() != v->width()) {
Chris@224 1919 std::cerr << "(cache width " << cache.pixmap.width()
Chris@224 1920 << " != " << v->width();
Chris@224 1921 }
Chris@224 1922 if (cache.pixmap.height() != v->height()) {
Chris@224 1923 std::cerr << "(cache height " << cache.pixmap.height()
Chris@224 1924 << " != " << v->height();
Chris@224 1925 }
Chris@0 1926 #endif
Chris@95 1927 cache.validArea = QRect();
Chris@337 1928 // recreateWholePixmapCache = true;
Chris@0 1929 }
Chris@0 1930 }
Chris@95 1931
Chris@133 1932 if (updateViewMagnitudes(v)) {
Chris@184 1933 #ifdef DEBUG_SPECTROGRAM_REPAINT
Chris@133 1934 std::cerr << "SpectrogramLayer: magnitude range changed to [" << m_viewMags[v].getMin() << "->" << m_viewMags[v].getMax() << "]" << std::endl;
Chris@184 1935 #endif
Chris@331 1936 if (m_normalizeVisibleArea) {
Chris@331 1937 cache.validArea = QRect();
Chris@331 1938 recreateWholePixmapCache = true;
Chris@331 1939 }
Chris@133 1940 } else {
Chris@184 1941 #ifdef DEBUG_SPECTROGRAM_REPAINT
Chris@133 1942 std::cerr << "No change in magnitude range [" << m_viewMags[v].getMin() << "->" << m_viewMags[v].getMax() << "]" << std::endl;
Chris@184 1943 #endif
Chris@133 1944 }
Chris@133 1945
Chris@95 1946 if (recreateWholePixmapCache) {
Chris@95 1947 x0 = 0;
Chris@95 1948 x1 = v->width();
Chris@95 1949 }
Chris@95 1950
Chris@215 1951 struct timeval tv;
Chris@215 1952 (void)gettimeofday(&tv, 0);
Chris@215 1953 RealTime mainPaintStart = RealTime::fromTimeval(tv);
Chris@215 1954
Chris@215 1955 int paintBlockWidth = m_lastPaintBlockWidth;
Chris@215 1956
Chris@389 1957 if (m_synchronous) {
Chris@389 1958 if (paintBlockWidth < x1 - x0) {
Chris@389 1959 // always paint full width
Chris@389 1960 paintBlockWidth = x1 - x0;
Chris@389 1961 }
Chris@215 1962 } else {
Chris@389 1963 if (paintBlockWidth == 0) {
Chris@389 1964 paintBlockWidth = (300000 / zoomLevel);
Chris@389 1965 } else {
Chris@389 1966 RealTime lastTime = m_lastPaintTime;
Chris@389 1967 while (lastTime > RealTime::fromMilliseconds(200) &&
Chris@389 1968 paintBlockWidth > 50) {
Chris@389 1969 paintBlockWidth /= 2;
Chris@389 1970 lastTime = lastTime / 2;
Chris@389 1971 }
Chris@389 1972 while (lastTime < RealTime::fromMilliseconds(90) &&
Chris@389 1973 paintBlockWidth < 1500) {
Chris@389 1974 paintBlockWidth *= 2;
Chris@389 1975 lastTime = lastTime * 2;
Chris@389 1976 }
Chris@215 1977 }
Chris@389 1978
Chris@389 1979 if (paintBlockWidth < 20) paintBlockWidth = 20;
Chris@215 1980 }
Chris@215 1981
Chris@224 1982 #ifdef DEBUG_SPECTROGRAM_REPAINT
Chris@215 1983 std::cerr << "[" << this << "]: last paint width: " << m_lastPaintBlockWidth << ", last paint time: " << m_lastPaintTime << ", new paint width: " << paintBlockWidth << std::endl;
Chris@224 1984 #endif
Chris@224 1985
Chris@224 1986 // We always paint the full height when refreshing the cache.
Chris@224 1987 // Smaller heights can be used when painting direct from cache
Chris@224 1988 // (further up in this function), but we want to ensure the cache
Chris@224 1989 // is coherent without having to worry about vertical matching of
Chris@224 1990 // required and valid areas as well as horizontal.
Chris@224 1991
Chris@224 1992 int h = v->height();
Chris@215 1993
Chris@96 1994 if (cache.validArea.width() > 0) {
Chris@96 1995
Chris@331 1996 // If part of the cache is known to be valid, select a strip
Chris@331 1997 // immediately to left or right of the valid part
Chris@331 1998
Chris@96 1999 int vx0 = 0, vx1 = 0;
Chris@96 2000 vx0 = cache.validArea.x();
Chris@96 2001 vx1 = cache.validArea.x() + cache.validArea.width();
Chris@96 2002
Chris@96 2003 #ifdef DEBUG_SPECTROGRAM_REPAINT
Chris@96 2004 std::cerr << "x0 " << x0 << ", x1 " << x1 << ", vx0 " << vx0 << ", vx1 " << vx1 << ", paintBlockWidth " << paintBlockWidth << std::endl;
Chris@331 2005 #endif
Chris@96 2006 if (x0 < vx0) {
Chris@96 2007 if (x0 + paintBlockWidth < vx0) {
Chris@96 2008 x0 = vx0 - paintBlockWidth;
Chris@331 2009 }
Chris@331 2010 x1 = vx0;
Chris@331 2011 } else if (x0 >= vx1) {
Chris@331 2012 x0 = vx1;
Chris@331 2013 if (x1 > x0 + paintBlockWidth) {
Chris@331 2014 x1 = x0 + paintBlockWidth;
Chris@331 2015 }
Chris@331 2016 } else {
Chris@331 2017 // x0 is within the valid area
Chris@331 2018 if (x1 > vx1) {
Chris@331 2019 x0 = vx1;
Chris@331 2020 if (x0 + paintBlockWidth < x1) {
Chris@331 2021 x1 = x0 + paintBlockWidth;
Chris@331 2022 }
Chris@96 2023 } else {
Chris@331 2024 x1 = x0; // it's all valid, paint nothing
Chris@95 2025 }
Chris@96 2026 }
Chris@331 2027
Chris@96 2028 cache.validArea = QRect
Chris@96 2029 (std::min(vx0, x0), cache.validArea.y(),
Chris@96 2030 std::max(vx1 - std::min(vx0, x0),
Chris@337 2031 x1 - std::min(vx0, x0)),
Chris@96 2032 cache.validArea.height());
Chris@337 2033
Chris@337 2034 #ifdef DEBUG_SPECTROGRAM_REPAINT
Chris@337 2035 std::cerr << "Valid area becomes " << cache.validArea.x()
Chris@337 2036 << ", " << cache.validArea.y() << ", "
Chris@337 2037 << cache.validArea.width() << "x"
Chris@337 2038 << cache.validArea.height() << std::endl;
Chris@337 2039 #endif
Chris@95 2040
Chris@96 2041 } else {
Chris@96 2042 if (x1 > x0 + paintBlockWidth) {
Chris@133 2043 int sfx = x1;
Chris@133 2044 if (startFrame < 0) sfx = v->getXForFrame(0);
Chris@133 2045 if (sfx >= x0 && sfx + paintBlockWidth <= x1) {
Chris@133 2046 x0 = sfx;
Chris@133 2047 x1 = x0 + paintBlockWidth;
Chris@133 2048 } else {
Chris@133 2049 int mid = (x1 + x0) / 2;
Chris@133 2050 x0 = mid - paintBlockWidth/2;
Chris@133 2051 x1 = x0 + paintBlockWidth;
Chris@133 2052 }
Chris@95 2053 }
Chris@337 2054 #ifdef DEBUG_SPECTROGRAM_REPAINT
Chris@337 2055 std::cerr << "Valid area becomes " << x0 << ", 0, " << (x1-x0)
Chris@337 2056 << "x" << h << std::endl;
Chris@337 2057 #endif
Chris@224 2058 cache.validArea = QRect(x0, 0, x1 - x0, h);
Chris@95 2059 }
Chris@95 2060
Chris@0 2061 int w = x1 - x0;
Chris@0 2062
Chris@95 2063 #ifdef DEBUG_SPECTROGRAM_REPAINT
Chris@95 2064 std::cerr << "x0 " << x0 << ", x1 " << x1 << ", w " << w << ", h " << h << std::endl;
Chris@95 2065 #endif
Chris@95 2066
Chris@95 2067 if (m_drawBuffer.width() < w || m_drawBuffer.height() < h) {
Chris@95 2068 m_drawBuffer = QImage(w, h, QImage::Format_RGB32);
Chris@95 2069 }
Chris@95 2070
Chris@197 2071 m_drawBuffer.fill(m_palette.getColour(0).rgb());
Chris@35 2072
Chris@37 2073 int sr = m_model->getSampleRate();
Chris@122 2074
Chris@122 2075 // Set minFreq and maxFreq to the frequency extents of the possibly
Chris@122 2076 // zero-padded visible bin range, and displayMinFreq and displayMaxFreq
Chris@122 2077 // to the actual scale frequency extents (presumably not zero padded).
Chris@253 2078
Chris@253 2079 // If we are zero padding, we want to use the zero-padded
Chris@253 2080 // equivalents of the bins that we would be using if not zero
Chris@253 2081 // padded, to avoid spaces at the top and bottom of the display.
Chris@253 2082
Chris@253 2083 // Note fftSize is the actual zero-padded fft size, m_fftSize the
Chris@253 2084 // nominal fft size.
Chris@35 2085
Chris@253 2086 size_t maxbin = m_fftSize / 2;
Chris@35 2087 if (m_maxFrequency > 0) {
Chris@253 2088 maxbin = int((double(m_maxFrequency) * m_fftSize) / sr + 0.001);
Chris@253 2089 if (maxbin > m_fftSize / 2) maxbin = m_fftSize / 2;
Chris@35 2090 }
Chris@111 2091
Chris@40 2092 size_t minbin = 1;
Chris@37 2093 if (m_minFrequency > 0) {
Chris@253 2094 minbin = int((double(m_minFrequency) * m_fftSize) / sr + 0.001);
Chris@253 2095 // std::cerr << "m_minFrequency = " << m_minFrequency << " -> minbin = " << minbin << std::endl;
Chris@40 2096 if (minbin < 1) minbin = 1;
Chris@184 2097 if (minbin >= maxbin) minbin = maxbin - 1;
Chris@37 2098 }
Chris@37 2099
Chris@253 2100 int zpl = getZeroPadLevel(v) + 1;
Chris@253 2101 minbin = minbin * zpl;
Chris@253 2102 maxbin = (maxbin + 1) * zpl - 1;
Chris@253 2103
Chris@114 2104 float minFreq = (float(minbin) * sr) / fftSize;
Chris@184 2105 float maxFreq = (float(maxbin) * sr) / fftSize;
Chris@0 2106
Chris@122 2107 float displayMinFreq = minFreq;
Chris@122 2108 float displayMaxFreq = maxFreq;
Chris@122 2109
Chris@122 2110 if (fftSize != m_fftSize) {
Chris@122 2111 displayMinFreq = getEffectiveMinFrequency();
Chris@122 2112 displayMaxFreq = getEffectiveMaxFrequency();
Chris@122 2113 }
Chris@122 2114
Chris@253 2115 // std::cerr << "(giving actual minFreq " << minFreq << " and display minFreq " << displayMinFreq << ")" << std::endl;
Chris@253 2116
Chris@92 2117 float ymag[h];
Chris@92 2118 float ydiv[h];
Chris@382 2119
Chris@382 2120 float yval[maxbin - minbin + 1];
Chris@382 2121 float values[maxbin - minbin + 1];
Chris@92 2122
Chris@38 2123 size_t increment = getWindowIncrement();
Chris@40 2124
Chris@40 2125 bool logarithmic = (m_frequencyScale == LogFrequencyScale);
Chris@38 2126
Chris@184 2127 for (size_t q = minbin; q <= maxbin; ++q) {
Chris@114 2128 float f0 = (float(q) * sr) / fftSize;
Chris@382 2129 yval[q - minbin] =
Chris@382 2130 v->getYForFrequency(f0, displayMinFreq, displayMaxFreq,
Chris@382 2131 logarithmic);
Chris@92 2132 }
Chris@92 2133
Chris@119 2134 MagnitudeRange overallMag = m_viewMags[v];
Chris@119 2135 bool overallMagChanged = false;
Chris@119 2136
Chris@162 2137 bool fftSuspended = false;
Chris@131 2138
Chris@221 2139 bool interpolate = false;
Chris@221 2140 Preferences::SpectrogramSmoothing smoothing =
Chris@221 2141 Preferences::getInstance()->getSpectrogramSmoothing();
Chris@221 2142 if (smoothing == Preferences::SpectrogramInterpolated ||
Chris@221 2143 smoothing == Preferences::SpectrogramZeroPaddedAndInterpolated) {
Chris@222 2144 if (m_binDisplay != PeakBins &&
Chris@222 2145 m_binDisplay != PeakFrequencies) {
Chris@222 2146 interpolate = true;
Chris@222 2147 }
Chris@221 2148 }
Chris@221 2149
Chris@137 2150 #ifdef DEBUG_SPECTROGRAM_REPAINT
Chris@224 2151 std::cerr << ((float(v->getFrameForX(1) - v->getFrameForX(0))) / increment) << " bin(s) per pixel" << std::endl;
Chris@137 2152 #endif
Chris@137 2153
Chris@224 2154 bool runOutOfData = false;
Chris@224 2155
Chris@331 2156 if (w == 0) {
Chris@331 2157 std::cerr << "*** NOTE: w == 0" << std::endl;
Chris@331 2158 }
Chris@331 2159
Chris@331 2160 #ifdef DEBUG_SPECTROGRAM_REPAINT
Chris@331 2161 size_t pixels = 0;
Chris@331 2162 #endif
Chris@331 2163
Chris@382 2164 Profiler outerprof("SpectrogramLayer::paint: all cols");
Chris@382 2165 FFTModel::PeakSet peaks;
Chris@382 2166
Chris@35 2167 for (int x = 0; x < w; ++x) {
Chris@35 2168
Chris@382 2169 Profiler innerprof("SpectrogramLayer::paint: 1 pixel column");
Chris@382 2170
Chris@331 2171 if (runOutOfData) {
Chris@331 2172 #ifdef DEBUG_SPECTROGRAM_REPAINT
Chris@331 2173 std::cerr << "Run out of data -- dropping out of loop" << std::endl;
Chris@331 2174 #endif
Chris@331 2175 break;
Chris@331 2176 }
Chris@224 2177
Chris@35 2178 for (int y = 0; y < h; ++y) {
Chris@134 2179 ymag[y] = 0.f;
Chris@134 2180 ydiv[y] = 0.f;
Chris@35 2181 }
Chris@35 2182
Chris@35 2183 float s0 = 0, s1 = 0;
Chris@35 2184
Chris@44 2185 if (!getXBinRange(v, x0 + x, s0, s1)) {
Chris@337 2186 #ifdef DEBUG_SPECTROGRAM_REPAINT
Chris@337 2187 std::cerr << "Out of range at " << x0 + x << std::endl;
Chris@337 2188 #endif
Chris@95 2189 assert(x <= m_drawBuffer.width());
Chris@35 2190 continue;
Chris@35 2191 }
Chris@35 2192
Chris@35 2193 int s0i = int(s0 + 0.001);
Chris@35 2194 int s1i = int(s1);
Chris@35 2195
Chris@248 2196 if (s1i >= int(fft->getWidth())) {
Chris@248 2197 if (s0i >= int(fft->getWidth())) {
Chris@331 2198 #ifdef DEBUG_SPECTROGRAM_REPAINT
Chris@331 2199 std::cerr << "Column " << s0i << " out of range" << std::endl;
Chris@331 2200 #endif
Chris@45 2201 continue;
Chris@45 2202 } else {
Chris@45 2203 s1i = s0i;
Chris@45 2204 }
Chris@45 2205 }
Chris@92 2206
Chris@92 2207 for (int s = s0i; s <= s1i; ++s) {
Chris@92 2208
Chris@389 2209 if (!m_synchronous) {
Chris@389 2210 if (!fft->isColumnAvailable(s)) {
Chris@224 2211 #ifdef DEBUG_SPECTROGRAM_REPAINT
Chris@389 2212 std::cerr << "Met unavailable column at col " << s << std::endl;
Chris@224 2213 #endif
Chris@389 2214 runOutOfData = true;
Chris@389 2215 break;
Chris@389 2216 }
Chris@224 2217 }
Chris@473 2218 /*!!!
Chris@162 2219 if (!fftSuspended) {
Chris@162 2220 fft->suspendWrites();
Chris@162 2221 fftSuspended = true;
Chris@162 2222 }
Chris@473 2223 */
Chris@382 2224 Profiler innerprof2("SpectrogramLayer::paint: 1 data column");
Chris@382 2225
Chris@119 2226 MagnitudeRange mag;
Chris@92 2227
Chris@382 2228 if (m_binDisplay == PeakFrequencies) {
Chris@382 2229 if (s < int(fft->getWidth()) - 1) {
Chris@382 2230 peaks = fft->getPeakFrequencies(FFTModel::AllPeaks,
Chris@382 2231 s,
Chris@382 2232 minbin, maxbin - 1);
Chris@382 2233 } else {
Chris@382 2234 peaks.clear();
Chris@382 2235 }
Chris@280 2236 }
Chris@382 2237
Chris@382 2238 if (m_colourScale == PhaseColourScale) {
Chris@382 2239 fft->getPhasesAt(s, values, minbin, maxbin - minbin + 1);
Chris@382 2240 } else if (m_normalizeColumns) {
Chris@382 2241 fft->getNormalizedMagnitudesAt(s, values, minbin, maxbin - minbin + 1);
Chris@382 2242 } else {
Chris@382 2243 fft->getMagnitudesAt(s, values, minbin, maxbin - minbin + 1);
Chris@382 2244 }
Chris@280 2245
Chris@184 2246 for (size_t q = minbin; q < maxbin; ++q) {
Chris@92 2247
Chris@382 2248 Profiler innerprof3("SpectrogramLayer::paint: 1 bin");
Chris@382 2249
Chris@382 2250 float y0 = yval[q + 1 - minbin];
Chris@382 2251 float y1 = yval[q - minbin];
Chris@92 2252
Chris@280 2253 if (m_binDisplay == PeakBins) {
Chris@114 2254 if (!fft->isLocalPeak(s, q)) continue;
Chris@40 2255 }
Chris@280 2256 if (m_binDisplay == PeakFrequencies) {
Chris@280 2257 if (peaks.find(q) == peaks.end()) continue;
Chris@280 2258 }
Chris@114 2259
Chris@114 2260 if (m_threshold != 0.f &&
Chris@253 2261 !fft->isOverThreshold(s, q, m_threshold * (m_fftSize/2))) {
Chris@114 2262 continue;
Chris@114 2263 }
Chris@40 2264
Chris@382 2265 float sprop = 1.f;
Chris@35 2266 if (s == s0i) sprop *= (s + 1) - s0;
Chris@35 2267 if (s == s1i) sprop *= s1 - s;
Chris@35 2268
Chris@280 2269 if (m_binDisplay == PeakFrequencies) {
Chris@44 2270 y0 = y1 = v->getYForFrequency
Chris@280 2271 (peaks[q], displayMinFreq, displayMaxFreq, logarithmic);
Chris@35 2272 }
Chris@38 2273
Chris@382 2274 int y0i = int(y0 + 0.001f);
Chris@35 2275 int y1i = int(y1);
Chris@35 2276
Chris@382 2277 float value = values[q - minbin];
Chris@382 2278
Chris@382 2279 if (m_colourScale != PhaseColourScale) {
Chris@382 2280 if (!m_normalizeColumns) {
Chris@382 2281 value /= (m_fftSize/2.f);
Chris@382 2282 }
Chris@382 2283 mag.sample(value);
Chris@382 2284 value *= m_gain;
Chris@382 2285 }
Chris@382 2286
Chris@221 2287 if (interpolate) {
Chris@221 2288
Chris@221 2289 int ypi = y0i;
Chris@382 2290 if (q < maxbin - 1) ypi = int(yval[q + 2 - minbin]);
Chris@221 2291
Chris@221 2292 for (int y = ypi; y <= y1i; ++y) {
Chris@221 2293
Chris@221 2294 if (y < 0 || y >= h) continue;
Chris@221 2295
Chris@221 2296 float yprop = sprop;
Chris@221 2297 float iprop = yprop;
Chris@221 2298
Chris@221 2299 if (ypi < y0i && y <= y0i) {
Chris@221 2300
Chris@382 2301 float half = float(y0i - ypi) / 2.f;
Chris@221 2302 float dist = y - (ypi + half);
Chris@221 2303
Chris@221 2304 if (dist >= 0) {
Chris@221 2305 iprop = (iprop * dist) / half;
Chris@221 2306 ymag[y] += iprop * value;
Chris@221 2307 }
Chris@221 2308 } else {
Chris@221 2309 if (y1i > y0i) {
Chris@221 2310
Chris@382 2311 float half = float(y1i - y0i) / 2.f;
Chris@221 2312 float dist = y - (y0i + half);
Chris@221 2313
Chris@221 2314 if (dist >= 0) {
Chris@221 2315 iprop = (iprop * (half - dist)) / half;
Chris@221 2316 }
Chris@221 2317 }
Chris@221 2318
Chris@221 2319 ymag[y] += iprop * value;
Chris@221 2320 ydiv[y] += yprop;
Chris@221 2321 }
Chris@221 2322 }
Chris@221 2323
Chris@221 2324 } else {
Chris@221 2325
Chris@221 2326 for (int y = y0i; y <= y1i; ++y) {
Chris@221 2327
Chris@221 2328 if (y < 0 || y >= h) continue;
Chris@221 2329
Chris@221 2330 float yprop = sprop;
Chris@221 2331 if (y == y0i) yprop *= (y + 1) - y0;
Chris@221 2332 if (y == y1i) yprop *= y1 - y;
Chris@221 2333
Chris@221 2334 for (int y = y0i; y <= y1i; ++y) {
Chris@35 2335
Chris@221 2336 if (y < 0 || y >= h) continue;
Chris@221 2337
Chris@221 2338 float yprop = sprop;
Chris@382 2339 if (y == y0i) yprop *= (y + 1.f) - y0;
Chris@221 2340 if (y == y1i) yprop *= y1 - y;
Chris@221 2341 ymag[y] += yprop * value;
Chris@221 2342 ydiv[y] += yprop;
Chris@221 2343 }
Chris@221 2344 }
Chris@221 2345 }
Chris@35 2346 }
Chris@119 2347
Chris@119 2348 if (mag.isSet()) {
Chris@119 2349
Chris@248 2350 if (s >= int(m_columnMags.size())) {
Chris@203 2351 std::cerr << "INTERNAL ERROR: " << s << " >= "
Chris@203 2352 << m_columnMags.size() << " at SpectrogramLayer.cpp:2087" << std::endl;
Chris@203 2353 }
Chris@203 2354
Chris@119 2355 m_columnMags[s].sample(mag);
Chris@119 2356
Chris@119 2357 if (overallMag.sample(mag)) {
Chris@119 2358 //!!! scaling would change here
Chris@119 2359 overallMagChanged = true;
Chris@209 2360 #ifdef DEBUG_SPECTROGRAM_REPAINT
Chris@119 2361 std::cerr << "Overall mag changed (again?) at column " << s << ", to [" << overallMag.getMin() << "->" << overallMag.getMax() << "]" << std::endl;
Chris@209 2362 #endif
Chris@119 2363 }
Chris@119 2364 }
Chris@35 2365 }
Chris@35 2366
Chris@382 2367 Profiler drawbufferprof("SpectrogramLayer::paint: set buffer pixels");
Chris@382 2368
Chris@35 2369 for (int y = 0; y < h; ++y) {
Chris@35 2370
Chris@35 2371 if (ydiv[y] > 0.0) {
Chris@40 2372
Chris@40 2373 unsigned char pixel = 0;
Chris@40 2374
Chris@38 2375 float avg = ymag[y] / ydiv[y];
Chris@138 2376 pixel = getDisplayValue(v, avg);
Chris@40 2377
Chris@95 2378 assert(x <= m_drawBuffer.width());
Chris@197 2379 QColor c = m_palette.getColour(pixel);
Chris@95 2380 m_drawBuffer.setPixel(x, y,
Chris@95 2381 qRgb(c.red(), c.green(), c.blue()));
Chris@331 2382 #ifdef DEBUG_SPECTROGRAM_REPAINT
Chris@331 2383 ++pixels;
Chris@331 2384 #endif
Chris@35 2385 }
Chris@35 2386 }
Chris@35 2387 }
Chris@35 2388
Chris@331 2389 #ifdef DEBUG_SPECTROGRAM_REPAINT
Chris@331 2390 std::cerr << pixels << " pixels drawn" << std::endl;
Chris@331 2391 #endif
Chris@331 2392
Chris@119 2393 if (overallMagChanged) {
Chris@119 2394 m_viewMags[v] = overallMag;
Chris@209 2395 #ifdef DEBUG_SPECTROGRAM_REPAINT
Chris@119 2396 std::cerr << "Overall mag is now [" << m_viewMags[v].getMin() << "->" << m_viewMags[v].getMax() << "] - will be updating" << std::endl;
Chris@209 2397 #endif
Chris@119 2398 } else {
Chris@209 2399 #ifdef DEBUG_SPECTROGRAM_REPAINT
Chris@119 2400 std::cerr << "Overall mag unchanged at [" << m_viewMags[v].getMin() << "->" << m_viewMags[v].getMax() << "]" << std::endl;
Chris@209 2401 #endif
Chris@119 2402 }
Chris@119 2403
Chris@382 2404 outerprof.end();
Chris@382 2405
Chris@382 2406 Profiler profiler2("SpectrogramLayer::paint: draw image");
Chris@137 2407
Chris@0 2408 if (recreateWholePixmapCache) {
Chris@407 2409 #ifdef DEBUG_SPECTROGRAM_REPAINT
Chris@331 2410 std::cerr << "Recreating pixmap cache: width = " << v->width()
Chris@331 2411 << ", height = " << h << std::endl;
Chris@407 2412 #endif
Chris@224 2413 cache.pixmap = QPixmap(v->width(), h);
Chris@0 2414 }
Chris@0 2415
Chris@331 2416 if (w > 0) {
Chris@224 2417 #ifdef DEBUG_SPECTROGRAM_REPAINT
Chris@331 2418 std::cerr << "Painting " << w << "x" << h
Chris@331 2419 << " from draw buffer at " << 0 << "," << 0
Chris@331 2420 << " to cache at " << x0 << "," << 0 << std::endl;
Chris@224 2421 #endif
Chris@224 2422
Chris@331 2423 QPainter cachePainter(&cache.pixmap);
Chris@331 2424 cachePainter.drawImage(x0, 0, m_drawBuffer, 0, 0, w, h);
Chris@331 2425 cachePainter.end();
Chris@331 2426 }
Chris@331 2427
Chris@337 2428 QRect pr = rect & cache.validArea;
Chris@337 2429
Chris@337 2430 #ifdef DEBUG_SPECTROGRAM_REPAINT
Chris@337 2431 std::cerr << "Painting " << pr.width() << "x" << pr.height()
Chris@337 2432 << " from cache at " << pr.x() << "," << pr.y()
Chris@337 2433 << " to window" << std::endl;
Chris@337 2434 #endif
Chris@337 2435
Chris@337 2436 paint.drawPixmap(pr.x(), pr.y(), cache.pixmap,
Chris@337 2437 pr.x(), pr.y(), pr.width(), pr.height());
Chris@337 2438
Chris@331 2439 cache.startFrame = startFrame;
Chris@331 2440 cache.zoomLevel = zoomLevel;
Chris@119 2441
Chris@389 2442 if (!m_synchronous) {
Chris@389 2443
Chris@389 2444 if (!m_normalizeVisibleArea || !overallMagChanged) {
Chris@0 2445
Chris@389 2446 if (cache.validArea.x() > 0) {
Chris@95 2447 #ifdef DEBUG_SPECTROGRAM_REPAINT
Chris@389 2448 std::cerr << "SpectrogramLayer::paint() updating left (0, "
Chris@389 2449 << cache.validArea.x() << ")" << std::endl;
Chris@95 2450 #endif
Chris@389 2451 v->update(0, 0, cache.validArea.x(), h);
Chris@389 2452 }
Chris@389 2453
Chris@389 2454 if (cache.validArea.x() + cache.validArea.width() <
Chris@389 2455 cache.pixmap.width()) {
Chris@389 2456 #ifdef DEBUG_SPECTROGRAM_REPAINT
Chris@389 2457 std::cerr << "SpectrogramLayer::paint() updating right ("
Chris@389 2458 << cache.validArea.x() + cache.validArea.width()
Chris@389 2459 << ", "
Chris@389 2460 << cache.pixmap.width() - (cache.validArea.x() +
Chris@389 2461 cache.validArea.width())
Chris@389 2462 << ")" << std::endl;
Chris@389 2463 #endif
Chris@389 2464 v->update(cache.validArea.x() + cache.validArea.width(),
Chris@389 2465 0,
Chris@389 2466 cache.pixmap.width() - (cache.validArea.x() +
Chris@389 2467 cache.validArea.width()),
Chris@389 2468 h);
Chris@389 2469 }
Chris@389 2470 } else {
Chris@389 2471 // overallMagChanged
Chris@389 2472 std::cerr << "\noverallMagChanged - updating all\n" << std::endl;
Chris@389 2473 cache.validArea = QRect();
Chris@389 2474 v->update();
Chris@119 2475 }
Chris@95 2476 }
Chris@0 2477
Chris@121 2478 illuminateLocalFeatures(v, paint);
Chris@120 2479
Chris@0 2480 #ifdef DEBUG_SPECTROGRAM_REPAINT
Chris@0 2481 std::cerr << "SpectrogramLayer::paint() returning" << std::endl;
Chris@0 2482 #endif
Chris@131 2483
Chris@389 2484 if (!m_synchronous) {
Chris@389 2485 m_lastPaintBlockWidth = paintBlockWidth;
Chris@389 2486 (void)gettimeofday(&tv, 0);
Chris@389 2487 m_lastPaintTime = RealTime::fromTimeval(tv) - mainPaintStart;
Chris@389 2488 }
Chris@215 2489
Chris@473 2490 //!!! if (fftSuspended) fft->resume();
Chris@0 2491 }
Chris@0 2492
Chris@121 2493 void
Chris@121 2494 SpectrogramLayer::illuminateLocalFeatures(View *v, QPainter &paint) const
Chris@121 2495 {
Chris@382 2496 Profiler profiler("SpectrogramLayer::illuminateLocalFeatures");
Chris@382 2497
Chris@121 2498 QPoint localPos;
Chris@121 2499 if (!v->shouldIlluminateLocalFeatures(this, localPos) || !m_model) {
Chris@121 2500 return;
Chris@121 2501 }
Chris@121 2502
Chris@180 2503 // std::cerr << "SpectrogramLayer: illuminateLocalFeatures("
Chris@180 2504 // << localPos.x() << "," << localPos.y() << ")" << std::endl;
Chris@121 2505
Chris@121 2506 float s0, s1;
Chris@121 2507 float f0, f1;
Chris@121 2508
Chris@121 2509 if (getXBinRange(v, localPos.x(), s0, s1) &&
Chris@121 2510 getYBinSourceRange(v, localPos.y(), f0, f1)) {
Chris@121 2511
Chris@121 2512 int s0i = int(s0 + 0.001);
Chris@121 2513 int s1i = int(s1);
Chris@121 2514
Chris@121 2515 int x0 = v->getXForFrame(s0i * getWindowIncrement());
Chris@121 2516 int x1 = v->getXForFrame((s1i + 1) * getWindowIncrement());
Chris@121 2517
Chris@248 2518 int y1 = int(getYForFrequency(v, f1));
Chris@248 2519 int y0 = int(getYForFrequency(v, f0));
Chris@121 2520
Chris@180 2521 // std::cerr << "SpectrogramLayer: illuminate "
Chris@180 2522 // << x0 << "," << y1 << " -> " << x1 << "," << y0 << std::endl;
Chris@121 2523
Chris@287 2524 paint.setPen(v->getForeground());
Chris@133 2525
Chris@133 2526 //!!! should we be using paintCrosshairs for this?
Chris@133 2527
Chris@121 2528 paint.drawRect(x0, y1, x1 - x0 + 1, y0 - y1 + 1);
Chris@121 2529 }
Chris@121 2530 }
Chris@121 2531
Chris@42 2532 float
Chris@267 2533 SpectrogramLayer::getYForFrequency(const View *v, float frequency) const
Chris@42 2534 {
Chris@44 2535 return v->getYForFrequency(frequency,
Chris@44 2536 getEffectiveMinFrequency(),
Chris@44 2537 getEffectiveMaxFrequency(),
Chris@44 2538 m_frequencyScale == LogFrequencyScale);
Chris@42 2539 }
Chris@42 2540
Chris@42 2541 float
Chris@267 2542 SpectrogramLayer::getFrequencyForY(const View *v, int y) const
Chris@42 2543 {
Chris@44 2544 return v->getFrequencyForY(y,
Chris@44 2545 getEffectiveMinFrequency(),
Chris@44 2546 getEffectiveMaxFrequency(),
Chris@44 2547 m_frequencyScale == LogFrequencyScale);
Chris@42 2548 }
Chris@42 2549
Chris@0 2550 int
Chris@115 2551 SpectrogramLayer::getCompletion(View *v) const
Chris@0 2552 {
Chris@115 2553 if (m_updateTimer == 0) return 100;
Chris@130 2554 if (m_fftModels.find(v) == m_fftModels.end()) return 100;
Chris@130 2555
Chris@130 2556 size_t completion = m_fftModels[v].first->getCompletion();
Chris@224 2557 #ifdef DEBUG_SPECTROGRAM_REPAINT
Chris@115 2558 std::cerr << "SpectrogramLayer::getCompletion: completion = " << completion << std::endl;
Chris@224 2559 #endif
Chris@0 2560 return completion;
Chris@0 2561 }
Chris@0 2562
Chris@28 2563 bool
Chris@101 2564 SpectrogramLayer::getValueExtents(float &min, float &max,
Chris@101 2565 bool &logarithmic, QString &unit) const
Chris@79 2566 {
Chris@133 2567 if (!m_model) return false;
Chris@133 2568
Chris@133 2569 int sr = m_model->getSampleRate();
Chris@133 2570 min = float(sr) / m_fftSize;
Chris@133 2571 max = float(sr) / 2;
Chris@133 2572
Chris@101 2573 logarithmic = (m_frequencyScale == LogFrequencyScale);
Chris@79 2574 unit = "Hz";
Chris@79 2575 return true;
Chris@79 2576 }
Chris@79 2577
Chris@79 2578 bool
Chris@101 2579 SpectrogramLayer::getDisplayExtents(float &min, float &max) const
Chris@101 2580 {
Chris@101 2581 min = getEffectiveMinFrequency();
Chris@101 2582 max = getEffectiveMaxFrequency();
Chris@253 2583
Chris@248 2584 // std::cerr << "SpectrogramLayer::getDisplayExtents: " << min << "->" << max << std::endl;
Chris@101 2585 return true;
Chris@101 2586 }
Chris@101 2587
Chris@101 2588 bool
Chris@120 2589 SpectrogramLayer::setDisplayExtents(float min, float max)
Chris@120 2590 {
Chris@120 2591 if (!m_model) return false;
Chris@187 2592
Chris@253 2593 // std::cerr << "SpectrogramLayer::setDisplayExtents: " << min << "->" << max << std::endl;
Chris@187 2594
Chris@120 2595 if (min < 0) min = 0;
Chris@120 2596 if (max > m_model->getSampleRate()/2) max = m_model->getSampleRate()/2;
Chris@120 2597
Chris@120 2598 size_t minf = lrintf(min);
Chris@120 2599 size_t maxf = lrintf(max);
Chris@120 2600
Chris@120 2601 if (m_minFrequency == minf && m_maxFrequency == maxf) return true;
Chris@120 2602
Chris@120 2603 invalidatePixmapCaches();
Chris@120 2604 invalidateMagnitudes();
Chris@120 2605
Chris@120 2606 m_minFrequency = minf;
Chris@120 2607 m_maxFrequency = maxf;
Chris@120 2608
Chris@120 2609 emit layerParametersChanged();
Chris@120 2610
Chris@133 2611 int vs = getCurrentVerticalZoomStep();
Chris@133 2612 if (vs != m_lastEmittedZoomStep) {
Chris@133 2613 emit verticalZoomChanged();
Chris@133 2614 m_lastEmittedZoomStep = vs;
Chris@133 2615 }
Chris@133 2616
Chris@120 2617 return true;
Chris@120 2618 }
Chris@120 2619
Chris@120 2620 bool
Chris@267 2621 SpectrogramLayer::getYScaleValue(const View *v, int y,
Chris@261 2622 float &value, QString &unit) const
Chris@261 2623 {
Chris@261 2624 value = getFrequencyForY(v, y);
Chris@261 2625 unit = "Hz";
Chris@261 2626 return true;
Chris@261 2627 }
Chris@261 2628
Chris@261 2629 bool
Chris@248 2630 SpectrogramLayer::snapToFeatureFrame(View *, int &frame,
Chris@28 2631 size_t &resolution,
Chris@28 2632 SnapType snap) const
Chris@13 2633 {
Chris@13 2634 resolution = getWindowIncrement();
Chris@28 2635 int left = (frame / resolution) * resolution;
Chris@28 2636 int right = left + resolution;
Chris@28 2637
Chris@28 2638 switch (snap) {
Chris@28 2639 case SnapLeft: frame = left; break;
Chris@28 2640 case SnapRight: frame = right; break;
Chris@28 2641 case SnapNearest:
Chris@28 2642 case SnapNeighbouring:
Chris@28 2643 if (frame - left > right - frame) frame = right;
Chris@28 2644 else frame = left;
Chris@28 2645 break;
Chris@28 2646 }
Chris@28 2647
Chris@28 2648 return true;
Chris@28 2649 }
Chris@13 2650
Chris@283 2651 void
Chris@283 2652 SpectrogramLayer::measureDoubleClick(View *v, QMouseEvent *e)
Chris@283 2653 {
Chris@283 2654 PixmapCache &cache = m_pixmapCaches[v];
Chris@283 2655
Chris@283 2656 std::cerr << "cache width: " << cache.pixmap.width() << ", height: "
Chris@283 2657 << cache.pixmap.height() << std::endl;
Chris@283 2658
Chris@283 2659 QImage image = cache.pixmap.toImage();
Chris@283 2660
Chris@283 2661 ImageRegionFinder finder;
Chris@283 2662 QRect rect = finder.findRegionExtents(&image, e->pos());
Chris@283 2663 if (rect.isValid()) {
Chris@283 2664 MeasureRect mr;
Chris@283 2665 setMeasureRectFromPixrect(v, mr, rect);
Chris@283 2666 CommandHistory::getInstance()->addCommand
Chris@283 2667 (new AddMeasurementRectCommand(this, mr));
Chris@283 2668 }
Chris@283 2669 }
Chris@283 2670
Chris@77 2671 bool
Chris@264 2672 SpectrogramLayer::getCrosshairExtents(View *v, QPainter &paint,
Chris@77 2673 QPoint cursorPos,
Chris@77 2674 std::vector<QRect> &extents) const
Chris@77 2675 {
Chris@77 2676 QRect vertical(cursorPos.x() - 12, 0, 12, v->height());
Chris@77 2677 extents.push_back(vertical);
Chris@77 2678
Chris@77 2679 QRect horizontal(0, cursorPos.y(), cursorPos.x(), 1);
Chris@77 2680 extents.push_back(horizontal);
Chris@77 2681
Chris@264 2682 int sw = getVerticalScaleWidth(v, paint);
Chris@264 2683
Chris@280 2684 QRect freq(sw, cursorPos.y() - paint.fontMetrics().ascent() - 2,
Chris@280 2685 paint.fontMetrics().width("123456 Hz") + 2,
Chris@280 2686 paint.fontMetrics().height());
Chris@280 2687 extents.push_back(freq);
Chris@264 2688
Chris@279 2689 QRect pitch(sw, cursorPos.y() + 2,
Chris@279 2690 paint.fontMetrics().width("C#10+50c") + 2,
Chris@279 2691 paint.fontMetrics().height());
Chris@279 2692 extents.push_back(pitch);
Chris@279 2693
Chris@280 2694 QRect rt(cursorPos.x(),
Chris@280 2695 v->height() - paint.fontMetrics().height() - 2,
Chris@280 2696 paint.fontMetrics().width("1234.567 s"),
Chris@280 2697 paint.fontMetrics().height());
Chris@280 2698 extents.push_back(rt);
Chris@280 2699
Chris@280 2700 int w(paint.fontMetrics().width("1234567890") + 2);
Chris@280 2701 QRect frame(cursorPos.x() - w - 2,
Chris@280 2702 v->height() - paint.fontMetrics().height() - 2,
Chris@280 2703 w,
Chris@280 2704 paint.fontMetrics().height());
Chris@280 2705 extents.push_back(frame);
Chris@280 2706
Chris@77 2707 return true;
Chris@77 2708 }
Chris@77 2709
Chris@77 2710 void
Chris@77 2711 SpectrogramLayer::paintCrosshairs(View *v, QPainter &paint,
Chris@77 2712 QPoint cursorPos) const
Chris@77 2713 {
Chris@77 2714 paint.save();
Chris@283 2715
Chris@283 2716 int sw = getVerticalScaleWidth(v, paint);
Chris@283 2717
Chris@282 2718 QFont fn = paint.font();
Chris@282 2719 if (fn.pointSize() > 8) {
Chris@282 2720 fn.setPointSize(fn.pointSize() - 1);
Chris@282 2721 paint.setFont(fn);
Chris@282 2722 }
Chris@77 2723 paint.setPen(m_crosshairColour);
Chris@77 2724
Chris@77 2725 paint.drawLine(0, cursorPos.y(), cursorPos.x() - 1, cursorPos.y());
Chris@77 2726 paint.drawLine(cursorPos.x(), 0, cursorPos.x(), v->height());
Chris@77 2727
Chris@77 2728 float fundamental = getFrequencyForY(v, cursorPos.y());
Chris@77 2729
Chris@278 2730 v->drawVisibleText(paint,
Chris@278 2731 sw + 2,
Chris@278 2732 cursorPos.y() - 2,
Chris@278 2733 QString("%1 Hz").arg(fundamental),
Chris@278 2734 View::OutlinedText);
Chris@278 2735
Chris@279 2736 if (Pitch::isFrequencyInMidiRange(fundamental)) {
Chris@279 2737 QString pitchLabel = Pitch::getPitchLabelForFrequency(fundamental);
Chris@279 2738 v->drawVisibleText(paint,
Chris@279 2739 sw + 2,
Chris@279 2740 cursorPos.y() + paint.fontMetrics().ascent() + 2,
Chris@279 2741 pitchLabel,
Chris@279 2742 View::OutlinedText);
Chris@279 2743 }
Chris@279 2744
Chris@280 2745 long frame = v->getFrameForX(cursorPos.x());
Chris@279 2746 RealTime rt = RealTime::frame2RealTime(frame, m_model->getSampleRate());
Chris@280 2747 QString rtLabel = QString("%1 s").arg(rt.toText(true).c_str());
Chris@280 2748 QString frameLabel = QString("%1").arg(frame);
Chris@280 2749 v->drawVisibleText(paint,
Chris@280 2750 cursorPos.x() - paint.fontMetrics().width(frameLabel) - 2,
Chris@280 2751 v->height() - 2,
Chris@280 2752 frameLabel,
Chris@280 2753 View::OutlinedText);
Chris@280 2754 v->drawVisibleText(paint,
Chris@280 2755 cursorPos.x() + 2,
Chris@280 2756 v->height() - 2,
Chris@280 2757 rtLabel,
Chris@280 2758 View::OutlinedText);
Chris@264 2759
Chris@77 2760 int harmonic = 2;
Chris@77 2761
Chris@77 2762 while (harmonic < 100) {
Chris@77 2763
Chris@77 2764 float hy = lrintf(getYForFrequency(v, fundamental * harmonic));
Chris@77 2765 if (hy < 0 || hy > v->height()) break;
Chris@77 2766
Chris@77 2767 int len = 7;
Chris@77 2768
Chris@77 2769 if (harmonic % 2 == 0) {
Chris@77 2770 if (harmonic % 4 == 0) {
Chris@77 2771 len = 12;
Chris@77 2772 } else {
Chris@77 2773 len = 10;
Chris@77 2774 }
Chris@77 2775 }
Chris@77 2776
Chris@77 2777 paint.drawLine(cursorPos.x() - len,
Chris@248 2778 int(hy),
Chris@77 2779 cursorPos.x(),
Chris@248 2780 int(hy));
Chris@77 2781
Chris@77 2782 ++harmonic;
Chris@77 2783 }
Chris@77 2784
Chris@77 2785 paint.restore();
Chris@77 2786 }
Chris@77 2787
Chris@25 2788 QString
Chris@44 2789 SpectrogramLayer::getFeatureDescription(View *v, QPoint &pos) const
Chris@25 2790 {
Chris@25 2791 int x = pos.x();
Chris@25 2792 int y = pos.y();
Chris@0 2793
Chris@25 2794 if (!m_model || !m_model->isOK()) return "";
Chris@0 2795
Chris@38 2796 float magMin = 0, magMax = 0;
Chris@38 2797 float phaseMin = 0, phaseMax = 0;
Chris@0 2798 float freqMin = 0, freqMax = 0;
Chris@35 2799 float adjFreqMin = 0, adjFreqMax = 0;
Chris@25 2800 QString pitchMin, pitchMax;
Chris@0 2801 RealTime rtMin, rtMax;
Chris@0 2802
Chris@38 2803 bool haveValues = false;
Chris@0 2804
Chris@44 2805 if (!getXBinSourceRange(v, x, rtMin, rtMax)) {
Chris@38 2806 return "";
Chris@38 2807 }
Chris@44 2808 if (getXYBinSourceRange(v, x, y, magMin, magMax, phaseMin, phaseMax)) {
Chris@38 2809 haveValues = true;
Chris@38 2810 }
Chris@0 2811
Chris@35 2812 QString adjFreqText = "", adjPitchText = "";
Chris@35 2813
Chris@38 2814 if (m_binDisplay == PeakFrequencies) {
Chris@35 2815
Chris@44 2816 if (!getAdjustedYBinSourceRange(v, x, y, freqMin, freqMax,
Chris@38 2817 adjFreqMin, adjFreqMax)) {
Chris@38 2818 return "";
Chris@38 2819 }
Chris@35 2820
Chris@35 2821 if (adjFreqMin != adjFreqMax) {
Chris@65 2822 adjFreqText = tr("Peak Frequency:\t%1 - %2 Hz\n")
Chris@35 2823 .arg(adjFreqMin).arg(adjFreqMax);
Chris@35 2824 } else {
Chris@65 2825 adjFreqText = tr("Peak Frequency:\t%1 Hz\n")
Chris@35 2826 .arg(adjFreqMin);
Chris@38 2827 }
Chris@38 2828
Chris@38 2829 QString pmin = Pitch::getPitchLabelForFrequency(adjFreqMin);
Chris@38 2830 QString pmax = Pitch::getPitchLabelForFrequency(adjFreqMax);
Chris@38 2831
Chris@38 2832 if (pmin != pmax) {
Chris@65 2833 adjPitchText = tr("Peak Pitch:\t%3 - %4\n").arg(pmin).arg(pmax);
Chris@38 2834 } else {
Chris@65 2835 adjPitchText = tr("Peak Pitch:\t%2\n").arg(pmin);
Chris@35 2836 }
Chris@35 2837
Chris@35 2838 } else {
Chris@35 2839
Chris@44 2840 if (!getYBinSourceRange(v, y, freqMin, freqMax)) return "";
Chris@35 2841 }
Chris@35 2842
Chris@25 2843 QString text;
Chris@25 2844
Chris@25 2845 if (rtMin != rtMax) {
Chris@25 2846 text += tr("Time:\t%1 - %2\n")
Chris@25 2847 .arg(rtMin.toText(true).c_str())
Chris@25 2848 .arg(rtMax.toText(true).c_str());
Chris@25 2849 } else {
Chris@25 2850 text += tr("Time:\t%1\n")
Chris@25 2851 .arg(rtMin.toText(true).c_str());
Chris@0 2852 }
Chris@0 2853
Chris@25 2854 if (freqMin != freqMax) {
Chris@65 2855 text += tr("%1Bin Frequency:\t%2 - %3 Hz\n%4Bin Pitch:\t%5 - %6\n")
Chris@65 2856 .arg(adjFreqText)
Chris@25 2857 .arg(freqMin)
Chris@25 2858 .arg(freqMax)
Chris@65 2859 .arg(adjPitchText)
Chris@65 2860 .arg(Pitch::getPitchLabelForFrequency(freqMin))
Chris@65 2861 .arg(Pitch::getPitchLabelForFrequency(freqMax));
Chris@65 2862 } else {
Chris@65 2863 text += tr("%1Bin Frequency:\t%2 Hz\n%3Bin Pitch:\t%4\n")
Chris@35 2864 .arg(adjFreqText)
Chris@25 2865 .arg(freqMin)
Chris@65 2866 .arg(adjPitchText)
Chris@65 2867 .arg(Pitch::getPitchLabelForFrequency(freqMin));
Chris@25 2868 }
Chris@25 2869
Chris@38 2870 if (haveValues) {
Chris@38 2871 float dbMin = AudioLevel::multiplier_to_dB(magMin);
Chris@38 2872 float dbMax = AudioLevel::multiplier_to_dB(magMax);
Chris@43 2873 QString dbMinString;
Chris@43 2874 QString dbMaxString;
Chris@43 2875 if (dbMin == AudioLevel::DB_FLOOR) {
Chris@43 2876 dbMinString = tr("-Inf");
Chris@43 2877 } else {
Chris@43 2878 dbMinString = QString("%1").arg(lrintf(dbMin));
Chris@43 2879 }
Chris@43 2880 if (dbMax == AudioLevel::DB_FLOOR) {
Chris@43 2881 dbMaxString = tr("-Inf");
Chris@43 2882 } else {
Chris@43 2883 dbMaxString = QString("%1").arg(lrintf(dbMax));
Chris@43 2884 }
Chris@25 2885 if (lrintf(dbMin) != lrintf(dbMax)) {
Chris@199 2886 text += tr("dB:\t%1 - %2").arg(dbMinString).arg(dbMaxString);
Chris@25 2887 } else {
Chris@199 2888 text += tr("dB:\t%1").arg(dbMinString);
Chris@25 2889 }
Chris@38 2890 if (phaseMin != phaseMax) {
Chris@38 2891 text += tr("\nPhase:\t%1 - %2").arg(phaseMin).arg(phaseMax);
Chris@38 2892 } else {
Chris@38 2893 text += tr("\nPhase:\t%1").arg(phaseMin);
Chris@38 2894 }
Chris@25 2895 }
Chris@25 2896
Chris@25 2897 return text;
Chris@0 2898 }
Chris@25 2899
Chris@0 2900 int
Chris@40 2901 SpectrogramLayer::getColourScaleWidth(QPainter &paint) const
Chris@40 2902 {
Chris@40 2903 int cw;
Chris@40 2904
Chris@119 2905 cw = paint.fontMetrics().width("-80dB");
Chris@119 2906
Chris@40 2907 return cw;
Chris@40 2908 }
Chris@40 2909
Chris@40 2910 int
Chris@248 2911 SpectrogramLayer::getVerticalScaleWidth(View *, QPainter &paint) const
Chris@0 2912 {
Chris@0 2913 if (!m_model || !m_model->isOK()) return 0;
Chris@0 2914
Chris@40 2915 int cw = getColourScaleWidth(paint);
Chris@40 2916
Chris@0 2917 int tw = paint.fontMetrics().width(QString("%1")
Chris@0 2918 .arg(m_maxFrequency > 0 ?
Chris@0 2919 m_maxFrequency - 1 :
Chris@0 2920 m_model->getSampleRate() / 2));
Chris@0 2921
Chris@234 2922 int fw = paint.fontMetrics().width(tr("43Hz"));
Chris@0 2923 if (tw < fw) tw = fw;
Chris@40 2924
Chris@40 2925 int tickw = (m_frequencyScale == LogFrequencyScale ? 10 : 4);
Chris@0 2926
Chris@40 2927 return cw + tickw + tw + 13;
Chris@0 2928 }
Chris@0 2929
Chris@0 2930 void
Chris@44 2931 SpectrogramLayer::paintVerticalScale(View *v, QPainter &paint, QRect rect) const
Chris@0 2932 {
Chris@0 2933 if (!m_model || !m_model->isOK()) {
Chris@0 2934 return;
Chris@0 2935 }
Chris@0 2936
Chris@382 2937 Profiler profiler("SpectrogramLayer::paintVerticalScale");
Chris@122 2938
Chris@120 2939 //!!! cache this?
Chris@120 2940
Chris@0 2941 int h = rect.height(), w = rect.width();
Chris@0 2942
Chris@40 2943 int tickw = (m_frequencyScale == LogFrequencyScale ? 10 : 4);
Chris@40 2944 int pkw = (m_frequencyScale == LogFrequencyScale ? 10 : 0);
Chris@40 2945
Chris@107 2946 size_t bins = m_fftSize / 2;
Chris@0 2947 int sr = m_model->getSampleRate();
Chris@0 2948
Chris@0 2949 if (m_maxFrequency > 0) {
Chris@107 2950 bins = int((double(m_maxFrequency) * m_fftSize) / sr + 0.1);
Chris@107 2951 if (bins > m_fftSize / 2) bins = m_fftSize / 2;
Chris@0 2952 }
Chris@0 2953
Chris@40 2954 int cw = getColourScaleWidth(paint);
Chris@119 2955 int cbw = paint.fontMetrics().width("dB");
Chris@40 2956
Chris@0 2957 int py = -1;
Chris@0 2958 int textHeight = paint.fontMetrics().height();
Chris@0 2959 int toff = -textHeight + paint.fontMetrics().ascent() + 2;
Chris@0 2960
Chris@119 2961 if (h > textHeight * 3 + 10) {
Chris@119 2962
Chris@119 2963 int topLines = 2;
Chris@119 2964 if (m_colourScale == PhaseColourScale) topLines = 1;
Chris@119 2965
Chris@119 2966 int ch = h - textHeight * (topLines + 1) - 8;
Chris@119 2967 // paint.drawRect(4, textHeight + 4, cw - 1, ch + 1);
Chris@119 2968 paint.drawRect(4 + cw - cbw, textHeight * topLines + 4, cbw - 1, ch + 1);
Chris@40 2969
Chris@40 2970 QString top, bottom;
Chris@119 2971 float min = m_viewMags[v].getMin();
Chris@119 2972 float max = m_viewMags[v].getMax();
Chris@119 2973
Chris@119 2974 float dBmin = AudioLevel::multiplier_to_dB(min);
Chris@119 2975 float dBmax = AudioLevel::multiplier_to_dB(max);
Chris@119 2976
Chris@120 2977 if (dBmax < -60.f) dBmax = -60.f;
Chris@120 2978 else top = QString("%1").arg(lrintf(dBmax));
Chris@120 2979
Chris@120 2980 if (dBmin < dBmax - 60.f) dBmin = dBmax - 60.f;
Chris@119 2981 bottom = QString("%1").arg(lrintf(dBmin));
Chris@119 2982
Chris@119 2983 //!!! & phase etc
Chris@119 2984
Chris@119 2985 if (m_colourScale != PhaseColourScale) {
Chris@119 2986 paint.drawText((cw + 6 - paint.fontMetrics().width("dBFS")) / 2,
Chris@119 2987 2 + textHeight + toff, "dBFS");
Chris@119 2988 }
Chris@119 2989
Chris@119 2990 // paint.drawText((cw + 6 - paint.fontMetrics().width(top)) / 2,
Chris@119 2991 paint.drawText(3 + cw - cbw - paint.fontMetrics().width(top),
Chris@119 2992 2 + textHeight * topLines + toff + textHeight/2, top);
Chris@119 2993
Chris@119 2994 paint.drawText(3 + cw - cbw - paint.fontMetrics().width(bottom),
Chris@119 2995 h + toff - 3 - textHeight/2, bottom);
Chris@40 2996
Chris@40 2997 paint.save();
Chris@40 2998 paint.setBrush(Qt::NoBrush);
Chris@119 2999
Chris@119 3000 int lasty = 0;
Chris@119 3001 int lastdb = 0;
Chris@119 3002
Chris@40 3003 for (int i = 0; i < ch; ++i) {
Chris@119 3004
Chris@119 3005 float dBval = dBmin + (((dBmax - dBmin) * i) / (ch - 1));
Chris@119 3006 int idb = int(dBval);
Chris@119 3007
Chris@119 3008 float value = AudioLevel::dB_to_multiplier(dBval);
Chris@119 3009 int colour = getDisplayValue(v, value * m_gain);
Chris@210 3010
Chris@197 3011 paint.setPen(m_palette.getColour(colour));
Chris@119 3012
Chris@119 3013 int y = textHeight * topLines + 4 + ch - i;
Chris@119 3014
Chris@119 3015 paint.drawLine(5 + cw - cbw, y, cw + 2, y);
Chris@119 3016
Chris@119 3017 if (i == 0) {
Chris@119 3018 lasty = y;
Chris@119 3019 lastdb = idb;
Chris@119 3020 } else if (i < ch - paint.fontMetrics().ascent() &&
Chris@120 3021 idb != lastdb &&
Chris@119 3022 ((abs(y - lasty) > textHeight &&
Chris@119 3023 idb % 10 == 0) ||
Chris@119 3024 (abs(y - lasty) > paint.fontMetrics().ascent() &&
Chris@119 3025 idb % 5 == 0))) {
Chris@287 3026 paint.setPen(v->getBackground());
Chris@119 3027 QString text = QString("%1").arg(idb);
Chris@119 3028 paint.drawText(3 + cw - cbw - paint.fontMetrics().width(text),
Chris@119 3029 y + toff + textHeight/2, text);
Chris@287 3030 paint.setPen(v->getForeground());
Chris@119 3031 paint.drawLine(5 + cw - cbw, y, 8 + cw - cbw, y);
Chris@119 3032 lasty = y;
Chris@119 3033 lastdb = idb;
Chris@119 3034 }
Chris@40 3035 }
Chris@40 3036 paint.restore();
Chris@40 3037 }
Chris@40 3038
Chris@40 3039 paint.drawLine(cw + 7, 0, cw + 7, h);
Chris@40 3040
Chris@0 3041 int bin = -1;
Chris@0 3042
Chris@44 3043 for (int y = 0; y < v->height(); ++y) {
Chris@0 3044
Chris@0 3045 float q0, q1;
Chris@44 3046 if (!getYBinRange(v, v->height() - y, q0, q1)) continue;
Chris@0 3047
Chris@0 3048 int vy;
Chris@0 3049
Chris@0 3050 if (int(q0) > bin) {
Chris@0 3051 vy = y;
Chris@0 3052 bin = int(q0);
Chris@0 3053 } else {
Chris@0 3054 continue;
Chris@0 3055 }
Chris@0 3056
Chris@107 3057 int freq = (sr * bin) / m_fftSize;
Chris@0 3058
Chris@0 3059 if (py >= 0 && (vy - py) < textHeight - 1) {
Chris@40 3060 if (m_frequencyScale == LinearFrequencyScale) {
Chris@40 3061 paint.drawLine(w - tickw, h - vy, w, h - vy);
Chris@40 3062 }
Chris@0 3063 continue;
Chris@0 3064 }
Chris@0 3065
Chris@0 3066 QString text = QString("%1").arg(freq);
Chris@234 3067 if (bin == 1) text = tr("%1Hz").arg(freq); // bin 0 is DC
Chris@40 3068 paint.drawLine(cw + 7, h - vy, w - pkw - 1, h - vy);
Chris@0 3069
Chris@0 3070 if (h - vy - textHeight >= -2) {
Chris@40 3071 int tx = w - 3 - paint.fontMetrics().width(text) - std::max(tickw, pkw);
Chris@0 3072 paint.drawText(tx, h - vy + toff, text);
Chris@0 3073 }
Chris@0 3074
Chris@0 3075 py = vy;
Chris@0 3076 }
Chris@40 3077
Chris@40 3078 if (m_frequencyScale == LogFrequencyScale) {
Chris@40 3079
Chris@277 3080 // piano keyboard
Chris@277 3081
Chris@40 3082 paint.drawLine(w - pkw - 1, 0, w - pkw - 1, h);
Chris@40 3083
Chris@40 3084 float minf = getEffectiveMinFrequency();
Chris@40 3085 float maxf = getEffectiveMaxFrequency();
Chris@40 3086
Chris@122 3087 int py = h, ppy = h;
Chris@40 3088 paint.setBrush(paint.pen().color());
Chris@40 3089
Chris@40 3090 for (int i = 0; i < 128; ++i) {
Chris@40 3091
Chris@40 3092 float f = Pitch::getFrequencyForPitch(i);
Chris@44 3093 int y = lrintf(v->getYForFrequency(f, minf, maxf, true));
Chris@122 3094
Chris@122 3095 if (y < -2) break;
Chris@122 3096 if (y > h + 2) {
Chris@122 3097 continue;
Chris@122 3098 }
Chris@122 3099
Chris@40 3100 int n = (i % 12);
Chris@122 3101
Chris@122 3102 if (n == 1) {
Chris@122 3103 // C# -- fill the C from here
Chris@284 3104 QColor col = Qt::gray;
Chris@284 3105 if (i == 61) { // filling middle C
Chris@284 3106 col = Qt::blue;
Chris@284 3107 col = col.light(150);
Chris@284 3108 }
Chris@122 3109 if (ppy - y > 2) {
Chris@122 3110 paint.fillRect(w - pkw,
Chris@122 3111 y,
Chris@122 3112 pkw,
Chris@122 3113 (py + ppy) / 2 - y,
Chris@284 3114 col);
Chris@122 3115 }
Chris@122 3116 }
Chris@122 3117
Chris@40 3118 if (n == 1 || n == 3 || n == 6 || n == 8 || n == 10) {
Chris@40 3119 // black notes
Chris@40 3120 paint.drawLine(w - pkw, y, w, y);
Chris@41 3121 int rh = ((py - y) / 4) * 2;
Chris@41 3122 if (rh < 2) rh = 2;
Chris@41 3123 paint.drawRect(w - pkw, y - (py-y)/4, pkw/2, rh);
Chris@40 3124 } else if (n == 0 || n == 5) {
Chris@122 3125 // C, F
Chris@40 3126 if (py < h) {
Chris@40 3127 paint.drawLine(w - pkw, (y + py) / 2, w, (y + py) / 2);
Chris@40 3128 }
Chris@40 3129 }
Chris@40 3130
Chris@122 3131 ppy = py;
Chris@40 3132 py = y;
Chris@40 3133 }
Chris@40 3134 }
Chris@0 3135 }
Chris@0 3136
Chris@187 3137 class SpectrogramRangeMapper : public RangeMapper
Chris@187 3138 {
Chris@187 3139 public:
Chris@248 3140 SpectrogramRangeMapper(int sr, int /* fftsize */) :
Chris@187 3141 m_dist(float(sr) / 2),
Chris@187 3142 m_s2(sqrtf(sqrtf(2))) { }
Chris@187 3143 ~SpectrogramRangeMapper() { }
Chris@187 3144
Chris@187 3145 virtual int getPositionForValue(float value) const {
Chris@187 3146
Chris@187 3147 float dist = m_dist;
Chris@187 3148
Chris@187 3149 int n = 0;
Chris@187 3150
Chris@187 3151 while (dist > (value + 0.00001) && dist > 0.1f) {
Chris@187 3152 dist /= m_s2;
Chris@187 3153 ++n;
Chris@187 3154 }
Chris@187 3155
Chris@187 3156 return n;
Chris@187 3157 }
Chris@187 3158
Chris@187 3159 virtual float getValueForPosition(int position) const {
Chris@187 3160
Chris@187 3161 // Vertical zoom step 0 shows the entire range from DC ->
Chris@187 3162 // Nyquist frequency. Step 1 shows 2^(1/4) of the range of
Chris@187 3163 // step 0, and so on until the visible range is smaller than
Chris@187 3164 // the frequency step between bins at the current fft size.
Chris@187 3165
Chris@187 3166 float dist = m_dist;
Chris@187 3167
Chris@187 3168 int n = 0;
Chris@187 3169 while (n < position) {
Chris@187 3170 dist /= m_s2;
Chris@187 3171 ++n;
Chris@187 3172 }
Chris@187 3173
Chris@187 3174 return dist;
Chris@187 3175 }
Chris@187 3176
Chris@187 3177 virtual QString getUnit() const { return "Hz"; }
Chris@187 3178
Chris@187 3179 protected:
Chris@187 3180 float m_dist;
Chris@187 3181 float m_s2;
Chris@187 3182 };
Chris@187 3183
Chris@133 3184 int
Chris@133 3185 SpectrogramLayer::getVerticalZoomSteps(int &defaultStep) const
Chris@133 3186 {
Chris@135 3187 if (!m_model) return 0;
Chris@187 3188
Chris@187 3189 int sr = m_model->getSampleRate();
Chris@187 3190
Chris@187 3191 SpectrogramRangeMapper mapper(sr, m_fftSize);
Chris@187 3192
Chris@187 3193 // int maxStep = mapper.getPositionForValue((float(sr) / m_fftSize) + 0.001);
Chris@187 3194 int maxStep = mapper.getPositionForValue(0);
Chris@187 3195 int minStep = mapper.getPositionForValue(float(sr) / 2);
Chris@250 3196
Chris@250 3197 size_t initialMax = m_initialMaxFrequency;
Chris@250 3198 if (initialMax == 0) initialMax = sr / 2;
Chris@250 3199
Chris@250 3200 defaultStep = mapper.getPositionForValue(initialMax) - minStep;
Chris@250 3201
Chris@250 3202 // std::cerr << "SpectrogramLayer::getVerticalZoomSteps: " << maxStep - minStep << " (" << maxStep <<"-" << minStep << "), default is " << defaultStep << " (from initial max freq " << initialMax << ")" << std::endl;
Chris@187 3203
Chris@187 3204 return maxStep - minStep;
Chris@133 3205 }
Chris@133 3206
Chris@133 3207 int
Chris@133 3208 SpectrogramLayer::getCurrentVerticalZoomStep() const
Chris@133 3209 {
Chris@133 3210 if (!m_model) return 0;
Chris@133 3211
Chris@133 3212 float dmin, dmax;
Chris@133 3213 getDisplayExtents(dmin, dmax);
Chris@133 3214
Chris@187 3215 SpectrogramRangeMapper mapper(m_model->getSampleRate(), m_fftSize);
Chris@187 3216 int n = mapper.getPositionForValue(dmax - dmin);
Chris@248 3217 // std::cerr << "SpectrogramLayer::getCurrentVerticalZoomStep: " << n << std::endl;
Chris@133 3218 return n;
Chris@133 3219 }
Chris@133 3220
Chris@133 3221 void
Chris@133 3222 SpectrogramLayer::setVerticalZoomStep(int step)
Chris@133 3223 {
Chris@187 3224 if (!m_model) return;
Chris@187 3225
Chris@253 3226 float dmin = m_minFrequency, dmax = m_maxFrequency;
Chris@253 3227 // getDisplayExtents(dmin, dmax);
Chris@253 3228
Chris@253 3229 // std::cerr << "current range " << dmin << " -> " << dmax << ", range " << dmax-dmin << ", mid " << (dmax + dmin)/2 << std::endl;
Chris@133 3230
Chris@133 3231 int sr = m_model->getSampleRate();
Chris@187 3232 SpectrogramRangeMapper mapper(sr, m_fftSize);
Chris@253 3233 float newdist = mapper.getValueForPosition(step);
Chris@253 3234
Chris@253 3235 float newmin, newmax;
Chris@253 3236
Chris@253 3237 if (m_frequencyScale == LogFrequencyScale) {
Chris@253 3238
Chris@253 3239 // need to pick newmin and newmax such that
Chris@253 3240 //
Chris@253 3241 // (log(newmin) + log(newmax)) / 2 == logmid
Chris@253 3242 // and
Chris@253 3243 // newmax - newmin = newdist
Chris@253 3244 //
Chris@253 3245 // so log(newmax - newdist) + log(newmax) == 2logmid
Chris@253 3246 // log(newmax(newmax - newdist)) == 2logmid
Chris@253 3247 // newmax.newmax - newmax.newdist == exp(2logmid)
Chris@253 3248 // newmax^2 + (-newdist)newmax + -exp(2logmid) == 0
Chris@253 3249 // quadratic with a = 1, b = -newdist, c = -exp(2logmid), all known
Chris@253 3250 //
Chris@253 3251 // positive root
Chris@253 3252 // newmax = (newdist + sqrt(newdist^2 + 4exp(2logmid))) / 2
Chris@253 3253 //
Chris@253 3254 // but logmid = (log(dmin) + log(dmax)) / 2
Chris@253 3255 // so exp(2logmid) = exp(log(dmin) + log(dmax))
Chris@253 3256 // = exp(log(dmin.dmax))
Chris@253 3257 // = dmin.dmax
Chris@253 3258 // so newmax = (newdist + sqrtf(newdist^2 + 4dmin.dmax)) / 2
Chris@253 3259
Chris@253 3260 newmax = (newdist + sqrtf(newdist*newdist + 4*dmin*dmax)) / 2;
Chris@253 3261 newmin = newmax - newdist;
Chris@253 3262
Chris@253 3263 // std::cerr << "newmin = " << newmin << ", newmax = " << newmax << std::endl;
Chris@253 3264
Chris@253 3265 } else {
Chris@253 3266 float dmid = (dmax + dmin) / 2;
Chris@253 3267 newmin = dmid - newdist / 2;
Chris@253 3268 newmax = dmid + newdist / 2;
Chris@253 3269 }
Chris@187 3270
Chris@187 3271 float mmin, mmax;
Chris@187 3272 mmin = 0;
Chris@187 3273 mmax = float(sr) / 2;
Chris@133 3274
Chris@187 3275 if (newmin < mmin) {
Chris@187 3276 newmax += (mmin - newmin);
Chris@187 3277 newmin = mmin;
Chris@187 3278 }
Chris@187 3279 if (newmax > mmax) {
Chris@187 3280 newmax = mmax;
Chris@187 3281 }
Chris@133 3282
Chris@253 3283 // std::cerr << "SpectrogramLayer::setVerticalZoomStep: " << step << ": " << newmin << " -> " << newmax << " (range " << newdist << ")" << std::endl;
Chris@253 3284
Chris@253 3285 setMinFrequency(lrintf(newmin));
Chris@253 3286 setMaxFrequency(lrintf(newmax));
Chris@187 3287 }
Chris@187 3288
Chris@187 3289 RangeMapper *
Chris@187 3290 SpectrogramLayer::getNewVerticalZoomRangeMapper() const
Chris@187 3291 {
Chris@187 3292 if (!m_model) return 0;
Chris@187 3293 return new SpectrogramRangeMapper(m_model->getSampleRate(), m_fftSize);
Chris@133 3294 }
Chris@133 3295
Chris@273 3296 void
Chris@273 3297 SpectrogramLayer::updateMeasureRectYCoords(View *v, const MeasureRect &r) const
Chris@273 3298 {
Chris@273 3299 int y0 = 0;
Chris@273 3300 if (r.startY > 0.0) y0 = getYForFrequency(v, r.startY);
Chris@273 3301
Chris@273 3302 int y1 = y0;
Chris@273 3303 if (r.endY > 0.0) y1 = getYForFrequency(v, r.endY);
Chris@273 3304
Chris@273 3305 // std::cerr << "SpectrogramLayer::updateMeasureRectYCoords: start " << r.startY << " -> " << y0 << ", end " << r.endY << " -> " << y1 << std::endl;
Chris@273 3306
Chris@273 3307 r.pixrect = QRect(r.pixrect.x(), y0, r.pixrect.width(), y1 - y0);
Chris@273 3308 }
Chris@273 3309
Chris@273 3310 void
Chris@273 3311 SpectrogramLayer::setMeasureRectYCoord(View *v, MeasureRect &r, bool start, int y) const
Chris@273 3312 {
Chris@273 3313 if (start) {
Chris@273 3314 r.startY = getFrequencyForY(v, y);
Chris@273 3315 r.endY = r.startY;
Chris@273 3316 } else {
Chris@273 3317 r.endY = getFrequencyForY(v, y);
Chris@273 3318 }
Chris@273 3319 // std::cerr << "SpectrogramLayer::setMeasureRectYCoord: start " << r.startY << " <- " << y << ", end " << r.endY << " <- " << y << std::endl;
Chris@273 3320
Chris@273 3321 }
Chris@273 3322
Chris@316 3323 void
Chris@316 3324 SpectrogramLayer::toXml(QTextStream &stream,
Chris@316 3325 QString indent, QString extraAttributes) const
Chris@6 3326 {
Chris@6 3327 QString s;
Chris@6 3328
Chris@6 3329 s += QString("channel=\"%1\" "
Chris@6 3330 "windowSize=\"%2\" "
Chris@153 3331 "windowHopLevel=\"%3\" "
Chris@153 3332 "gain=\"%4\" "
Chris@153 3333 "threshold=\"%5\" ")
Chris@6 3334 .arg(m_channel)
Chris@6 3335 .arg(m_windowSize)
Chris@97 3336 .arg(m_windowHopLevel)
Chris@37 3337 .arg(m_gain)
Chris@37 3338 .arg(m_threshold);
Chris@37 3339
Chris@37 3340 s += QString("minFrequency=\"%1\" "
Chris@37 3341 "maxFrequency=\"%2\" "
Chris@37 3342 "colourScale=\"%3\" "
Chris@37 3343 "colourScheme=\"%4\" "
Chris@37 3344 "colourRotation=\"%5\" "
Chris@37 3345 "frequencyScale=\"%6\" "
Chris@37 3346 "binDisplay=\"%7\" "
Chris@153 3347 "normalizeColumns=\"%8\" "
Chris@153 3348 "normalizeVisibleArea=\"%9\"")
Chris@37 3349 .arg(m_minFrequency)
Chris@6 3350 .arg(m_maxFrequency)
Chris@6 3351 .arg(m_colourScale)
Chris@197 3352 .arg(m_colourMap)
Chris@37 3353 .arg(m_colourRotation)
Chris@35 3354 .arg(m_frequencyScale)
Chris@37 3355 .arg(m_binDisplay)
Chris@153 3356 .arg(m_normalizeColumns ? "true" : "false")
Chris@153 3357 .arg(m_normalizeVisibleArea ? "true" : "false");
Chris@6 3358
Chris@316 3359 Layer::toXml(stream, indent, extraAttributes + " " + s);
Chris@6 3360 }
Chris@6 3361
Chris@11 3362 void
Chris@11 3363 SpectrogramLayer::setProperties(const QXmlAttributes &attributes)
Chris@11 3364 {
Chris@11 3365 bool ok = false;
Chris@11 3366
Chris@11 3367 int channel = attributes.value("channel").toInt(&ok);
Chris@11 3368 if (ok) setChannel(channel);
Chris@11 3369
Chris@11 3370 size_t windowSize = attributes.value("windowSize").toUInt(&ok);
Chris@11 3371 if (ok) setWindowSize(windowSize);
Chris@11 3372
Chris@97 3373 size_t windowHopLevel = attributes.value("windowHopLevel").toUInt(&ok);
Chris@97 3374 if (ok) setWindowHopLevel(windowHopLevel);
Chris@97 3375 else {
Chris@97 3376 size_t windowOverlap = attributes.value("windowOverlap").toUInt(&ok);
Chris@97 3377 // a percentage value
Chris@97 3378 if (ok) {
Chris@97 3379 if (windowOverlap == 0) setWindowHopLevel(0);
Chris@97 3380 else if (windowOverlap == 25) setWindowHopLevel(1);
Chris@97 3381 else if (windowOverlap == 50) setWindowHopLevel(2);
Chris@97 3382 else if (windowOverlap == 75) setWindowHopLevel(3);
Chris@97 3383 else if (windowOverlap == 90) setWindowHopLevel(4);
Chris@97 3384 }
Chris@97 3385 }
Chris@11 3386
Chris@11 3387 float gain = attributes.value("gain").toFloat(&ok);
Chris@11 3388 if (ok) setGain(gain);
Chris@11 3389
Chris@37 3390 float threshold = attributes.value("threshold").toFloat(&ok);
Chris@37 3391 if (ok) setThreshold(threshold);
Chris@37 3392
Chris@37 3393 size_t minFrequency = attributes.value("minFrequency").toUInt(&ok);
Chris@187 3394 if (ok) {
Chris@187 3395 std::cerr << "SpectrogramLayer::setProperties: setting min freq to " << minFrequency << std::endl;
Chris@187 3396 setMinFrequency(minFrequency);
Chris@187 3397 }
Chris@37 3398
Chris@11 3399 size_t maxFrequency = attributes.value("maxFrequency").toUInt(&ok);
Chris@187 3400 if (ok) {
Chris@187 3401 std::cerr << "SpectrogramLayer::setProperties: setting max freq to " << maxFrequency << std::endl;
Chris@187 3402 setMaxFrequency(maxFrequency);
Chris@187 3403 }
Chris@11 3404
Chris@11 3405 ColourScale colourScale = (ColourScale)
Chris@11 3406 attributes.value("colourScale").toInt(&ok);
Chris@11 3407 if (ok) setColourScale(colourScale);
Chris@11 3408
Chris@197 3409 int colourMap = attributes.value("colourScheme").toInt(&ok);
Chris@197 3410 if (ok) setColourMap(colourMap);
Chris@11 3411
Chris@37 3412 int colourRotation = attributes.value("colourRotation").toInt(&ok);
Chris@37 3413 if (ok) setColourRotation(colourRotation);
Chris@37 3414
Chris@11 3415 FrequencyScale frequencyScale = (FrequencyScale)
Chris@11 3416 attributes.value("frequencyScale").toInt(&ok);
Chris@11 3417 if (ok) setFrequencyScale(frequencyScale);
Chris@35 3418
Chris@37 3419 BinDisplay binDisplay = (BinDisplay)
Chris@37 3420 attributes.value("binDisplay").toInt(&ok);
Chris@37 3421 if (ok) setBinDisplay(binDisplay);
Chris@36 3422
Chris@36 3423 bool normalizeColumns =
Chris@36 3424 (attributes.value("normalizeColumns").trimmed() == "true");
Chris@36 3425 setNormalizeColumns(normalizeColumns);
Chris@153 3426
Chris@153 3427 bool normalizeVisibleArea =
Chris@153 3428 (attributes.value("normalizeVisibleArea").trimmed() == "true");
Chris@153 3429 setNormalizeVisibleArea(normalizeVisibleArea);
Chris@11 3430 }
Chris@11 3431