annotate layer/SpectrogramLayer.cpp @ 947:e53a87a5efb2

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