annotate layer/SpectrogramLayer.cpp @ 668:d52751e2728b

Better handling of padding around scale extents: associate them with the model extents, so as not to break auto-align (of this with other layers, and of other layers with this) and zoom
author Chris Cannam
date Tue, 16 Jul 2013 17:52:16 +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