annotate layer/SpectrogramLayer.cpp @ 640:c6d705bf1672

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