annotate layer/SpectrogramLayer.cpp @ 1025:c02de0e34233 spectrogram-minor-refactor

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