annotate layer/SpectrogramLayer.cpp @ 855:57efeb75880d

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