annotate layer/SpectrogramLayer.cpp @ 738:d26545a2a02d tonioni

Rather than undrawing the bottom bit of the dial, don't draw it in the first place (necessary on shaded background)
author Chris Cannam
date Thu, 06 Mar 2014 13:52:33 +0000
parents 67e6d518ac27
children 5d3a6ecdf2db 6a8d922f991d
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@0 144 connect(m_model, SIGNAL(modelChanged(size_t, size_t)),
Chris@0 145 this, SLOT(cacheInvalid(size_t, size_t)));
Chris@0 146
Chris@0 147 emit modelReplaced();
Chris@110 148 }
Chris@115 149
Chris@0 150 Layer::PropertyList
Chris@0 151 SpectrogramLayer::getProperties() const
Chris@0 152 {
Chris@0 153 PropertyList list;
Chris@87 154 list.push_back("Colour");
Chris@87 155 list.push_back("Colour Scale");
Chris@87 156 list.push_back("Window Size");
Chris@97 157 list.push_back("Window Increment");
Chris@87 158 list.push_back("Normalize Columns");
Chris@120 159 list.push_back("Normalize Visible Area");
Chris@87 160 list.push_back("Bin Display");
Chris@87 161 list.push_back("Threshold");
Chris@87 162 list.push_back("Gain");
Chris@87 163 list.push_back("Colour Rotation");
Chris@153 164 // list.push_back("Min Frequency");
Chris@153 165 // list.push_back("Max Frequency");
Chris@87 166 list.push_back("Frequency Scale");
Chris@153 167 //// list.push_back("Zero Padding");
Chris@0 168 return list;
Chris@0 169 }
Chris@0 170
Chris@87 171 QString
Chris@87 172 SpectrogramLayer::getPropertyLabel(const PropertyName &name) const
Chris@87 173 {
Chris@87 174 if (name == "Colour") return tr("Colour");
Chris@87 175 if (name == "Colour Scale") return tr("Colour Scale");
Chris@87 176 if (name == "Window Size") return tr("Window Size");
Chris@112 177 if (name == "Window Increment") return tr("Window Overlap");
Chris@87 178 if (name == "Normalize Columns") return tr("Normalize Columns");
Chris@120 179 if (name == "Normalize Visible Area") return tr("Normalize Visible Area");
Chris@87 180 if (name == "Bin Display") return tr("Bin Display");
Chris@87 181 if (name == "Threshold") return tr("Threshold");
Chris@87 182 if (name == "Gain") return tr("Gain");
Chris@87 183 if (name == "Colour Rotation") return tr("Colour Rotation");
Chris@87 184 if (name == "Min Frequency") return tr("Min Frequency");
Chris@87 185 if (name == "Max Frequency") return tr("Max Frequency");
Chris@87 186 if (name == "Frequency Scale") return tr("Frequency Scale");
Chris@109 187 if (name == "Zero Padding") return tr("Smoothing");
Chris@87 188 return "";
Chris@87 189 }
Chris@87 190
Chris@335 191 QString
Chris@335 192 SpectrogramLayer::getPropertyIconName(const PropertyName &name) const
Chris@335 193 {
Chris@335 194 if (name == "Normalize Columns") return "normalise-columns";
Chris@335 195 if (name == "Normalize Visible Area") return "normalise";
Chris@335 196 return "";
Chris@335 197 }
Chris@335 198
Chris@0 199 Layer::PropertyType
Chris@0 200 SpectrogramLayer::getPropertyType(const PropertyName &name) const
Chris@0 201 {
Chris@87 202 if (name == "Gain") return RangeProperty;
Chris@87 203 if (name == "Colour Rotation") return RangeProperty;
Chris@87 204 if (name == "Normalize Columns") return ToggleProperty;
Chris@120 205 if (name == "Normalize Visible Area") return ToggleProperty;
Chris@87 206 if (name == "Threshold") return RangeProperty;
Chris@109 207 if (name == "Zero Padding") return ToggleProperty;
Chris@0 208 return ValueProperty;
Chris@0 209 }
Chris@0 210
Chris@0 211 QString
Chris@0 212 SpectrogramLayer::getPropertyGroupName(const PropertyName &name) const
Chris@0 213 {
Chris@153 214 if (name == "Bin Display" ||
Chris@153 215 name == "Frequency Scale") return tr("Bins");
Chris@87 216 if (name == "Window Size" ||
Chris@109 217 name == "Window Increment" ||
Chris@109 218 name == "Zero Padding") return tr("Window");
Chris@87 219 if (name == "Colour" ||
Chris@87 220 name == "Threshold" ||
Chris@87 221 name == "Colour Rotation") return tr("Colour");
Chris@87 222 if (name == "Normalize Columns" ||
Chris@120 223 name == "Normalize Visible Area" ||
Chris@153 224 name == "Gain" ||
Chris@87 225 name == "Colour Scale") return tr("Scale");
Chris@0 226 return QString();
Chris@0 227 }
Chris@0 228
Chris@0 229 int
Chris@0 230 SpectrogramLayer::getPropertyRangeAndValue(const PropertyName &name,
Chris@216 231 int *min, int *max, int *deflt) const
Chris@0 232 {
Chris@216 233 int val = 0;
Chris@216 234
Chris@216 235 int garbage0, garbage1, garbage2;
Chris@55 236 if (!min) min = &garbage0;
Chris@55 237 if (!max) max = &garbage1;
Chris@216 238 if (!deflt) deflt = &garbage2;
Chris@10 239
Chris@87 240 if (name == "Gain") {
Chris@0 241
Chris@0 242 *min = -50;
Chris@0 243 *max = 50;
Chris@0 244
Chris@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@478 575 SpectrogramLayer::invalidateImageCaches(size_t startFrame, size_t endFrame)
Chris@95 576 {
Chris@478 577 for (ViewImageCache::iterator i = m_imageCaches.begin();
Chris@478 578 i != m_imageCaches.end(); ++i) {
Chris@131 579
Chris@95 580 //!!! when are views removed from the map? on setLayerDormant?
Chris@95 581 const View *v = i->first;
Chris@95 582
Chris@391 583 #ifdef DEBUG_SPECTROGRAM_REPAINT
Chris@587 584 SVDEBUG << "SpectrogramLayer::invalidateImageCaches("
Chris@391 585 << startFrame << ", " << endFrame << "): view range is "
Chris@391 586 << v->getStartFrame() << ", " << v->getEndFrame()
Chris@585 587 << endl;
Chris@391 588
Chris@682 589 cerr << "Valid area was: " << i->second.validArea.x() << ", "
Chris@391 590 << i->second.validArea.y() << " "
Chris@391 591 << i->second.validArea.width() << "x"
Chris@682 592 << i->second.validArea.height() << endl;
Chris@391 593 #endif
Chris@391 594
Chris@391 595 if (long(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@391 613 if (long(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@0 684 SpectrogramLayer::setWindowSize(size_t 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@0 698 size_t
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@97 705 SpectrogramLayer::setWindowHopLevel(size_t 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@0 720 size_t
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@109 727 SpectrogramLayer::setZeroPadLevel(size_t 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@109 741 size_t
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@37 807 SpectrogramLayer::setMinFrequency(size_t 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@37 821 size_t
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@0 828 SpectrogramLayer::setMaxFrequency(size_t 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@0 842 size_t
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@391 1052 SpectrogramLayer::cacheInvalid(size_t from, size_t to)
Chris@0 1053 {
Chris@391 1054 #ifdef DEBUG_SPECTROGRAM_REPAINT
Chris@587 1055 SVDEBUG << "SpectrogramLayer::cacheInvalid(" << from << ", " << to << ")" << endl;
Chris@391 1056 #endif
Chris@391 1057
Chris@478 1058 invalidateImageCaches(from, to);
Chris@391 1059 invalidateMagnitudes();
Chris@0 1060 }
Chris@0 1061
Chris@0 1062 void
Chris@0 1063 SpectrogramLayer::fillTimerTimedOut()
Chris@0 1064 {
Chris@115 1065 if (!m_model) return;
Chris@115 1066
Chris@115 1067 bool allDone = true;
Chris@115 1068
Chris@184 1069 #ifdef DEBUG_SPECTROGRAM_REPAINT
Chris@587 1070 SVDEBUG << "SpectrogramLayer::fillTimerTimedOut: have " << m_fftModels.size() << " FFT models associated with views" << endl;
Chris@184 1071 #endif
Chris@184 1072
Chris@130 1073 for (ViewFFTMap::iterator i = m_fftModels.begin();
Chris@130 1074 i != m_fftModels.end(); ++i) {
Chris@115 1075
Chris@130 1076 const FFTModel *model = i->second.first;
Chris@115 1077 size_t lastFill = i->second.second;
Chris@115 1078
Chris@130 1079 if (model) {
Chris@130 1080
Chris@130 1081 size_t fill = model->getFillExtent();
Chris@115 1082
Chris@0 1083 #ifdef DEBUG_SPECTROGRAM_REPAINT
Chris@587 1084 SVDEBUG << "SpectrogramLayer::fillTimerTimedOut: extent for " << model << ": " << fill << ", last " << lastFill << ", total " << m_model->getEndFrame() << endl;
Chris@0 1085 #endif
Chris@115 1086
Chris@115 1087 if (fill >= lastFill) {
Chris@115 1088 if (fill >= m_model->getEndFrame() && lastFill > 0) {
Chris@0 1089 #ifdef DEBUG_SPECTROGRAM_REPAINT
Chris@682 1090 cerr << "complete!" << endl;
Chris@0 1091 #endif
Chris@478 1092 invalidateImageCaches();
Chris@184 1093 i->second.second = -1;
Chris@115 1094 emit modelChanged();
Chris@115 1095
Chris@115 1096 } else if (fill > lastFill) {
Chris@0 1097 #ifdef DEBUG_SPECTROGRAM_REPAINT
Chris@682 1098 cerr << "SpectrogramLayer: emitting modelChanged("
Chris@682 1099 << lastFill << "," << fill << ")" << endl;
Chris@0 1100 #endif
Chris@478 1101 invalidateImageCaches(lastFill, fill);
Chris@184 1102 i->second.second = fill;
Chris@115 1103 emit modelChanged(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@115 1112 emit modelChanged(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@107 1273 size_t minbin = size_t((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@107 1288 size_t maxbin = size_t((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@21 1351 size_t modelStart = m_model->getStartFrame();
Chris@21 1352 size_t 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@0 1365 size_t 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@38 1437 size_t windowSize = m_windowSize;
Chris@38 1438 size_t windowIncrement = getWindowIncrement();
Chris@38 1439
Chris@35 1440 bool haveAdj = false;
Chris@35 1441
Chris@37 1442 bool peaksOnly = (m_binDisplay == PeakBins ||
Chris@37 1443 m_binDisplay == PeakFrequencies);
Chris@37 1444
Chris@35 1445 for (int q = q0i; q <= q1i; ++q) {
Chris@35 1446
Chris@35 1447 for (int s = s0i; s <= s1i; ++s) {
Chris@35 1448
Chris@160 1449 if (!fft->isColumnAvailable(s)) continue;
Chris@117 1450
Chris@35 1451 float binfreq = (sr * q) / m_windowSize;
Chris@35 1452 if (q == q0i) freqMin = binfreq;
Chris@35 1453 if (q == q1i) freqMax = binfreq;
Chris@37 1454
Chris@114 1455 if (peaksOnly && !fft->isLocalPeak(s, q)) continue;
Chris@38 1456
Chris@253 1457 if (!fft->isOverThreshold(s, q, m_threshold * (m_fftSize/2))) continue;
Chris@38 1458
Chris@38 1459 float freq = binfreq;
Chris@38 1460 bool steady = false;
Chris@40 1461
Chris@114 1462 if (s < int(fft->getWidth()) - 1) {
Chris@38 1463
Chris@277 1464 fft->estimateStableFrequency(s, q, freq);
Chris@35 1465
Chris@38 1466 if (!haveAdj || freq < adjFreqMin) adjFreqMin = freq;
Chris@38 1467 if (!haveAdj || freq > adjFreqMax) adjFreqMax = freq;
Chris@35 1468
Chris@35 1469 haveAdj = true;
Chris@35 1470 }
Chris@35 1471 }
Chris@35 1472 }
Chris@35 1473
Chris@35 1474 if (!haveAdj) {
Chris@40 1475 adjFreqMin = adjFreqMax = 0.0;
Chris@35 1476 }
Chris@35 1477
Chris@35 1478 return haveAdj;
Chris@35 1479 }
Chris@0 1480
Chris@0 1481 bool
Chris@44 1482 SpectrogramLayer::getXYBinSourceRange(View *v, int x, int y,
Chris@38 1483 float &min, float &max,
Chris@38 1484 float &phaseMin, float &phaseMax) const
Chris@0 1485 {
Chris@277 1486 if (!m_model || !m_model->isOK() || !m_model->isReady()) {
Chris@277 1487 return false;
Chris@277 1488 }
Chris@277 1489
Chris@0 1490 float q0 = 0, q1 = 0;
Chris@44 1491 if (!getYBinRange(v, y, q0, q1)) return false;
Chris@0 1492
Chris@0 1493 float s0 = 0, s1 = 0;
Chris@44 1494 if (!getXBinRange(v, x, s0, s1)) return false;
Chris@0 1495
Chris@0 1496 int q0i = int(q0 + 0.001);
Chris@0 1497 int q1i = int(q1);
Chris@0 1498
Chris@0 1499 int s0i = int(s0 + 0.001);
Chris@0 1500 int s1i = int(s1);
Chris@0 1501
Chris@37 1502 bool rv = false;
Chris@37 1503
Chris@122 1504 size_t zp = getZeroPadLevel(v);
Chris@122 1505 q0i *= zp + 1;
Chris@122 1506 q1i *= zp + 1;
Chris@122 1507
Chris@130 1508 FFTModel *fft = getFFTModel(v);
Chris@0 1509
Chris@114 1510 if (fft) {
Chris@114 1511
Chris@114 1512 int cw = fft->getWidth();
Chris@114 1513 int ch = fft->getHeight();
Chris@0 1514
Chris@110 1515 min = 0.0;
Chris@110 1516 max = 0.0;
Chris@110 1517 phaseMin = 0.0;
Chris@110 1518 phaseMax = 0.0;
Chris@110 1519 bool have = false;
Chris@0 1520
Chris@110 1521 for (int q = q0i; q <= q1i; ++q) {
Chris@110 1522 for (int s = s0i; s <= s1i; ++s) {
Chris@110 1523 if (s >= 0 && q >= 0 && s < cw && q < ch) {
Chris@117 1524
Chris@160 1525 if (!fft->isColumnAvailable(s)) continue;
Chris@110 1526
Chris@110 1527 float value;
Chris@38 1528
Chris@114 1529 value = fft->getPhaseAt(s, q);
Chris@110 1530 if (!have || value < phaseMin) { phaseMin = value; }
Chris@110 1531 if (!have || value > phaseMax) { phaseMax = value; }
Chris@91 1532
Chris@252 1533 value = fft->getMagnitudeAt(s, q) / (m_fftSize/2);
Chris@110 1534 if (!have || value < min) { min = value; }
Chris@110 1535 if (!have || value > max) { max = value; }
Chris@110 1536
Chris@110 1537 have = true;
Chris@110 1538 }
Chris@110 1539 }
Chris@110 1540 }
Chris@110 1541
Chris@110 1542 if (have) {
Chris@110 1543 rv = true;
Chris@110 1544 }
Chris@0 1545 }
Chris@0 1546
Chris@37 1547 return rv;
Chris@0 1548 }
Chris@0 1549
Chris@114 1550 size_t
Chris@114 1551 SpectrogramLayer::getZeroPadLevel(const View *v) const
Chris@114 1552 {
Chris@114 1553 //!!! tidy all this stuff
Chris@114 1554
Chris@114 1555 if (m_binDisplay != AllBins) return 0;
Chris@221 1556
Chris@221 1557 Preferences::SpectrogramSmoothing smoothing =
Chris@221 1558 Preferences::getInstance()->getSpectrogramSmoothing();
Chris@221 1559
Chris@221 1560 if (smoothing == Preferences::NoSpectrogramSmoothing ||
Chris@221 1561 smoothing == Preferences::SpectrogramInterpolated) return 0;
Chris@221 1562
Chris@114 1563 if (m_frequencyScale == LogFrequencyScale) return 3;
Chris@114 1564
Chris@114 1565 int sr = m_model->getSampleRate();
Chris@114 1566
Chris@184 1567 size_t maxbin = m_fftSize / 2;
Chris@114 1568 if (m_maxFrequency > 0) {
Chris@184 1569 maxbin = int((double(m_maxFrequency) * m_fftSize) / sr + 0.1);
Chris@184 1570 if (maxbin > m_fftSize / 2) maxbin = m_fftSize / 2;
Chris@114 1571 }
Chris@114 1572
Chris@114 1573 size_t minbin = 1;
Chris@114 1574 if (m_minFrequency > 0) {
Chris@114 1575 minbin = int((double(m_minFrequency) * m_fftSize) / sr + 0.1);
Chris@114 1576 if (minbin < 1) minbin = 1;
Chris@184 1577 if (minbin >= maxbin) minbin = maxbin - 1;
Chris@114 1578 }
Chris@114 1579
Chris@118 1580 float perPixel =
Chris@118 1581 float(v->height()) /
Chris@184 1582 float((maxbin - minbin) / (m_zeroPadLevel + 1));
Chris@118 1583
Chris@118 1584 if (perPixel > 2.8) {
Chris@118 1585 return 3; // 4x oversampling
Chris@118 1586 } else if (perPixel > 1.5) {
Chris@118 1587 return 1; // 2x
Chris@114 1588 } else {
Chris@118 1589 return 0; // 1x
Chris@114 1590 }
Chris@114 1591 }
Chris@114 1592
Chris@114 1593 size_t
Chris@114 1594 SpectrogramLayer::getFFTSize(const View *v) const
Chris@114 1595 {
Chris@114 1596 return m_fftSize * (getZeroPadLevel(v) + 1);
Chris@114 1597 }
Chris@114 1598
Chris@130 1599 FFTModel *
Chris@130 1600 SpectrogramLayer::getFFTModel(const View *v) const
Chris@114 1601 {
Chris@114 1602 if (!m_model) return 0;
Chris@114 1603
Chris@114 1604 size_t fftSize = getFFTSize(v);
Chris@114 1605
Chris@130 1606 if (m_fftModels.find(v) != m_fftModels.end()) {
Chris@184 1607 if (m_fftModels[v].first == 0) {
Chris@184 1608 #ifdef DEBUG_SPECTROGRAM_REPAINT
Chris@587 1609 SVDEBUG << "SpectrogramLayer::getFFTModel(" << v << "): Found null model" << endl;
Chris@184 1610 #endif
Chris@184 1611 return 0;
Chris@184 1612 }
Chris@184 1613 if (m_fftModels[v].first->getHeight() != fftSize / 2 + 1) {
Chris@184 1614 #ifdef DEBUG_SPECTROGRAM_REPAINT
Chris@587 1615 SVDEBUG << "SpectrogramLayer::getFFTModel(" << v << "): Found a model with the wrong height (" << m_fftModels[v].first->getHeight() << ", wanted " << (fftSize / 2 + 1) << ")" << endl;
Chris@184 1616 #endif
Chris@130 1617 delete m_fftModels[v].first;
Chris@130 1618 m_fftModels.erase(v);
Chris@484 1619 delete m_peakCaches[v];
Chris@484 1620 m_peakCaches.erase(v);
Chris@184 1621 } else {
Chris@184 1622 #ifdef DEBUG_SPECTROGRAM_REPAINT
Chris@587 1623 SVDEBUG << "SpectrogramLayer::getFFTModel(" << v << "): Found a good model of height " << m_fftModels[v].first->getHeight() << endl;
Chris@184 1624 #endif
Chris@184 1625 return m_fftModels[v].first;
Chris@114 1626 }
Chris@114 1627 }
Chris@114 1628
Chris@130 1629 if (m_fftModels.find(v) == m_fftModels.end()) {
Chris@169 1630
Chris@169 1631 FFTModel *model = new FFTModel(m_model,
Chris@169 1632 m_channel,
Chris@169 1633 m_windowType,
Chris@169 1634 m_windowSize,
Chris@169 1635 getWindowIncrement(),
Chris@169 1636 fftSize,
Chris@382 1637 true, // polar
Chris@327 1638 StorageAdviser::SpeedCritical,
Chris@169 1639 m_candidateFillStartFrame);
Chris@169 1640
Chris@178 1641 if (!model->isOK()) {
Chris@178 1642 QMessageBox::critical
Chris@178 1643 (0, tr("FFT cache failed"),
Chris@178 1644 tr("Failed to create the FFT model for this spectrogram.\n"
Chris@178 1645 "There may be insufficient memory or disc space to continue."));
Chris@178 1646 delete model;
Chris@178 1647 m_fftModels[v] = FFTFillPair(0, 0);
Chris@178 1648 return 0;
Chris@178 1649 }
Chris@178 1650
Chris@193 1651 if (!m_sliceableModel) {
Chris@248 1652 #ifdef DEBUG_SPECTROGRAM
Chris@682 1653 cerr << "SpectrogramLayer: emitting sliceableModelReplaced(0, " << model << ")" << endl;
Chris@248 1654 #endif
Chris@193 1655 ((SpectrogramLayer *)this)->sliceableModelReplaced(0, model);
Chris@193 1656 m_sliceableModel = model;
Chris@193 1657 }
Chris@193 1658
Chris@169 1659 m_fftModels[v] = FFTFillPair(model, 0);
Chris@169 1660
Chris@169 1661 model->resume();
Chris@114 1662
Chris@114 1663 delete m_updateTimer;
Chris@114 1664 m_updateTimer = new QTimer((SpectrogramLayer *)this);
Chris@114 1665 connect(m_updateTimer, SIGNAL(timeout()),
Chris@114 1666 this, SLOT(fillTimerTimedOut()));
Chris@114 1667 m_updateTimer->start(200);
Chris@114 1668 }
Chris@114 1669
Chris@130 1670 return m_fftModels[v].first;
Chris@114 1671 }
Chris@114 1672
Chris@484 1673 Dense3DModelPeakCache *
Chris@484 1674 SpectrogramLayer::getPeakCache(const View *v) const
Chris@484 1675 {
Chris@484 1676 if (!m_peakCaches[v]) {
Chris@484 1677 FFTModel *f = getFFTModel(v);
Chris@484 1678 if (!f) return 0;
Chris@484 1679 m_peakCaches[v] = new Dense3DModelPeakCache(f, 8);
Chris@484 1680 }
Chris@484 1681 return m_peakCaches[v];
Chris@484 1682 }
Chris@484 1683
Chris@193 1684 const Model *
Chris@193 1685 SpectrogramLayer::getSliceableModel() const
Chris@193 1686 {
Chris@193 1687 if (m_sliceableModel) return m_sliceableModel;
Chris@193 1688 if (m_fftModels.empty()) return 0;
Chris@193 1689 m_sliceableModel = m_fftModels.begin()->second.first;
Chris@193 1690 return m_sliceableModel;
Chris@193 1691 }
Chris@193 1692
Chris@114 1693 void
Chris@130 1694 SpectrogramLayer::invalidateFFTModels()
Chris@114 1695 {
Chris@130 1696 for (ViewFFTMap::iterator i = m_fftModels.begin();
Chris@130 1697 i != m_fftModels.end(); ++i) {
Chris@115 1698 delete i->second.first;
Chris@114 1699 }
Chris@486 1700 for (PeakCacheMap::iterator i = m_peakCaches.begin();
Chris@486 1701 i != m_peakCaches.end(); ++i) {
Chris@486 1702 delete i->second;
Chris@486 1703 }
Chris@114 1704
Chris@130 1705 m_fftModels.clear();
Chris@486 1706 m_peakCaches.clear();
Chris@193 1707
Chris@193 1708 if (m_sliceableModel) {
Chris@682 1709 cerr << "SpectrogramLayer: emitting sliceableModelReplaced(" << m_sliceableModel << ", 0)" << endl;
Chris@193 1710 emit sliceableModelReplaced(m_sliceableModel, 0);
Chris@193 1711 m_sliceableModel = 0;
Chris@193 1712 }
Chris@114 1713 }
Chris@114 1714
Chris@0 1715 void
Chris@119 1716 SpectrogramLayer::invalidateMagnitudes()
Chris@119 1717 {
Chris@119 1718 m_viewMags.clear();
Chris@119 1719 for (std::vector<MagnitudeRange>::iterator i = m_columnMags.begin();
Chris@119 1720 i != m_columnMags.end(); ++i) {
Chris@119 1721 *i = MagnitudeRange();
Chris@119 1722 }
Chris@119 1723 }
Chris@119 1724
Chris@119 1725 bool
Chris@119 1726 SpectrogramLayer::updateViewMagnitudes(View *v) const
Chris@119 1727 {
Chris@119 1728 MagnitudeRange mag;
Chris@119 1729
Chris@119 1730 int x0 = 0, x1 = v->width();
Chris@119 1731 float s00 = 0, s01 = 0, s10 = 0, s11 = 0;
Chris@119 1732
Chris@203 1733 if (!getXBinRange(v, x0, s00, s01)) {
Chris@203 1734 s00 = s01 = m_model->getStartFrame() / getWindowIncrement();
Chris@203 1735 }
Chris@203 1736
Chris@203 1737 if (!getXBinRange(v, x1, s10, s11)) {
Chris@203 1738 s10 = s11 = m_model->getEndFrame() / getWindowIncrement();
Chris@203 1739 }
Chris@119 1740
Chris@119 1741 int s0 = int(std::min(s00, s10) + 0.0001);
Chris@203 1742 int s1 = int(std::max(s01, s11) + 0.0001);
Chris@203 1743
Chris@587 1744 // SVDEBUG << "SpectrogramLayer::updateViewMagnitudes: x0 = " << x0 << ", x1 = " << x1 << ", s00 = " << s00 << ", s11 = " << s11 << " s0 = " << s0 << ", s1 = " << s1 << endl;
Chris@119 1745
Chris@248 1746 if (int(m_columnMags.size()) <= s1) {
Chris@119 1747 m_columnMags.resize(s1 + 1);
Chris@119 1748 }
Chris@119 1749
Chris@119 1750 for (int s = s0; s <= s1; ++s) {
Chris@119 1751 if (m_columnMags[s].isSet()) {
Chris@119 1752 mag.sample(m_columnMags[s]);
Chris@119 1753 }
Chris@119 1754 }
Chris@119 1755
Chris@184 1756 #ifdef DEBUG_SPECTROGRAM_REPAINT
Chris@587 1757 SVDEBUG << "SpectrogramLayer::updateViewMagnitudes returning from cols "
Chris@585 1758 << s0 << " -> " << s1 << " inclusive" << endl;
Chris@184 1759 #endif
Chris@119 1760
Chris@119 1761 if (!mag.isSet()) return false;
Chris@119 1762 if (mag == m_viewMags[v]) return false;
Chris@119 1763 m_viewMags[v] = mag;
Chris@119 1764 return true;
Chris@119 1765 }
Chris@119 1766
Chris@119 1767 void
Chris@389 1768 SpectrogramLayer::setSynchronousPainting(bool synchronous)
Chris@389 1769 {
Chris@389 1770 m_synchronous = synchronous;
Chris@389 1771 }
Chris@389 1772
Chris@389 1773 void
Chris@44 1774 SpectrogramLayer::paint(View *v, QPainter &paint, QRect rect) const
Chris@0 1775 {
Chris@253 1776 // What a lovely, old-fashioned function this is.
Chris@253 1777 // It's practically FORTRAN 77 in its clarity and linearity.
Chris@253 1778
Chris@334 1779 Profiler profiler("SpectrogramLayer::paint", false);
Chris@334 1780
Chris@0 1781 #ifdef DEBUG_SPECTROGRAM_REPAINT
Chris@587 1782 SVDEBUG << "SpectrogramLayer::paint(): m_model is " << m_model << ", zoom level is " << v->getZoomLevel() << ", m_updateTimer " << m_updateTimer << endl;
Chris@95 1783
Chris@682 1784 cerr << "rect is " << rect.x() << "," << rect.y() << " " << rect.width() << "x" << rect.height() << endl;
Chris@0 1785 #endif
Chris@95 1786
Chris@133 1787 long startFrame = v->getStartFrame();
Chris@133 1788 if (startFrame < 0) m_candidateFillStartFrame = 0;
Chris@133 1789 else m_candidateFillStartFrame = startFrame;
Chris@44 1790
Chris@0 1791 if (!m_model || !m_model->isOK() || !m_model->isReady()) {
Chris@0 1792 return;
Chris@0 1793 }
Chris@0 1794
Chris@47 1795 if (isLayerDormant(v)) {
Chris@587 1796 SVDEBUG << "SpectrogramLayer::paint(): Layer is dormant, making it undormant again" << endl;
Chris@29 1797 }
Chris@29 1798
Chris@48 1799 // Need to do this even if !isLayerDormant, as that could mean v
Chris@48 1800 // is not in the dormancy map at all -- we need it to be present
Chris@48 1801 // and accountable for when determining whether we need the cache
Chris@48 1802 // in the cache-fill thread above.
Chris@131 1803 //!!! no longer use cache-fill thread
Chris@131 1804 const_cast<SpectrogramLayer *>(this)->Layer::setLayerDormant(v, false);
Chris@48 1805
Chris@114 1806 size_t fftSize = getFFTSize(v);
Chris@484 1807 /*
Chris@130 1808 FFTModel *fft = getFFTModel(v);
Chris@114 1809 if (!fft) {
Chris@682 1810 cerr << "ERROR: SpectrogramLayer::paint(): No FFT model, returning" << endl;
Chris@0 1811 return;
Chris@0 1812 }
Chris@484 1813 */
Chris@478 1814 ImageCache &cache = m_imageCaches[v];
Chris@95 1815
Chris@95 1816 #ifdef DEBUG_SPECTROGRAM_REPAINT
Chris@587 1817 SVDEBUG << "SpectrogramLayer::paint(): image cache valid area " << cache.
Chris@585 1818
Chris@585 1819 validArea.x() << ", " << cache.validArea.y() << ", " << cache.validArea.width() << "x" << cache.validArea.height() << endl;
Chris@95 1820 #endif
Chris@95 1821
Chris@248 1822 #ifdef DEBUG_SPECTROGRAM_REPAINT
Chris@0 1823 bool stillCacheing = (m_updateTimer != 0);
Chris@587 1824 SVDEBUG << "SpectrogramLayer::paint(): Still cacheing = " << stillCacheing << endl;
Chris@0 1825 #endif
Chris@0 1826
Chris@44 1827 int zoomLevel = v->getZoomLevel();
Chris@0 1828
Chris@0 1829 int x0 = 0;
Chris@44 1830 int x1 = v->width();
Chris@0 1831
Chris@478 1832 bool recreateWholeImageCache = true;
Chris@0 1833
Chris@95 1834 x0 = rect.left();
Chris@95 1835 x1 = rect.right() + 1;
Chris@481 1836 /*
Chris@479 1837 float xPixelRatio = float(fft->getResolution()) / float(zoomLevel);
Chris@682 1838 cerr << "xPixelRatio = " << xPixelRatio << endl;
Chris@479 1839 if (xPixelRatio < 1.f) xPixelRatio = 1.f;
Chris@481 1840 */
Chris@95 1841 if (cache.validArea.width() > 0) {
Chris@95 1842
Chris@482 1843 int cw = cache.image.width();
Chris@482 1844 int ch = cache.image.height();
Chris@482 1845
Chris@95 1846 if (int(cache.zoomLevel) == zoomLevel &&
Chris@482 1847 cw == v->width() &&
Chris@482 1848 ch == v->height()) {
Chris@95 1849
Chris@95 1850 if (v->getXForFrame(cache.startFrame) ==
Chris@95 1851 v->getXForFrame(startFrame) &&
Chris@95 1852 cache.validArea.x() <= x0 &&
Chris@95 1853 cache.validArea.x() + cache.validArea.width() >= x1) {
Chris@0 1854
Chris@0 1855 #ifdef DEBUG_SPECTROGRAM_REPAINT
Chris@682 1856 cerr << "SpectrogramLayer: image cache good" << endl;
Chris@0 1857 #endif
Chris@0 1858
Chris@478 1859 paint.drawImage(rect, cache.image, rect);
Chris@479 1860 //!!!
Chris@479 1861 // paint.drawImage(v->rect(), cache.image,
Chris@479 1862 // QRect(QPoint(0, 0), cache.image.size()));
Chris@479 1863
Chris@121 1864 illuminateLocalFeatures(v, paint);
Chris@0 1865 return;
Chris@0 1866
Chris@0 1867 } else {
Chris@0 1868
Chris@0 1869 #ifdef DEBUG_SPECTROGRAM_REPAINT
Chris@682 1870 cerr << "SpectrogramLayer: image cache partially OK" << endl;
Chris@0 1871 #endif
Chris@0 1872
Chris@478 1873 recreateWholeImageCache = false;
Chris@0 1874
Chris@95 1875 int dx = v->getXForFrame(cache.startFrame) -
Chris@44 1876 v->getXForFrame(startFrame);
Chris@0 1877
Chris@0 1878 #ifdef DEBUG_SPECTROGRAM_REPAINT
Chris@682 1879 cerr << "SpectrogramLayer: dx = " << dx << " (image cache " << cw << "x" << ch << ")" << endl;
Chris@0 1880 #endif
Chris@0 1881
Chris@95 1882 if (dx != 0 &&
Chris@482 1883 dx > -cw &&
Chris@482 1884 dx < cw) {
Chris@482 1885
Chris@482 1886 int dxp = dx;
Chris@482 1887 if (dxp < 0) dxp = -dxp;
Chris@482 1888 int copy = (cw - dxp) * sizeof(QRgb);
Chris@482 1889 for (int y = 0; y < ch; ++y) {
Chris@482 1890 QRgb *line = (QRgb *)cache.image.scanLine(y);
Chris@482 1891 if (dx < 0) {
Chris@482 1892 memmove(line, line + dxp, copy);
Chris@482 1893 } else {
Chris@482 1894 memmove(line + dxp, line, copy);
Chris@482 1895 }
Chris@331 1896 }
Chris@0 1897
Chris@95 1898 int px = cache.validArea.x();
Chris@95 1899 int pw = cache.validArea.width();
Chris@0 1900
Chris@0 1901 if (dx < 0) {
Chris@482 1902 x0 = cw + dx;
Chris@482 1903 x1 = cw;
Chris@95 1904 px += dx;
Chris@95 1905 if (px < 0) {
Chris@95 1906 pw += px;
Chris@95 1907 px = 0;
Chris@95 1908 if (pw < 0) pw = 0;
Chris@95 1909 }
Chris@0 1910 } else {
Chris@0 1911 x0 = 0;
Chris@0 1912 x1 = dx;
Chris@95 1913 px += dx;
Chris@482 1914 if (px + pw > cw) {
Chris@482 1915 pw = int(cw) - px;
Chris@95 1916 if (pw < 0) pw = 0;
Chris@95 1917 }
Chris@0 1918 }
Chris@95 1919
Chris@95 1920 cache.validArea =
Chris@95 1921 QRect(px, cache.validArea.y(),
Chris@95 1922 pw, cache.validArea.height());
Chris@95 1923
Chris@331 1924 #ifdef DEBUG_SPECTROGRAM_REPAINT
Chris@682 1925 cerr << "valid area now "
Chris@331 1926 << px << "," << cache.validArea.y()
Chris@331 1927 << " " << pw << "x" << cache.validArea.height()
Chris@682 1928 << endl;
Chris@331 1929 #endif
Chris@479 1930 /*
Chris@478 1931 paint.drawImage(rect & cache.validArea,
Chris@478 1932 cache.image,
Chris@95 1933 rect & cache.validArea);
Chris@479 1934 */
Chris@331 1935 } else if (dx != 0) {
Chris@331 1936
Chris@331 1937 // we scrolled too far to be of use
Chris@331 1938
Chris@391 1939 #ifdef DEBUG_SPECTROGRAM_REPAINT
Chris@682 1940 cerr << "dx == " << dx << ": scrolled too far for cache to be useful" << endl;
Chris@391 1941 #endif
Chris@391 1942
Chris@331 1943 cache.validArea = QRect();
Chris@478 1944 recreateWholeImageCache = true;
Chris@331 1945 }
Chris@0 1946 }
Chris@0 1947 } else {
Chris@0 1948 #ifdef DEBUG_SPECTROGRAM_REPAINT
Chris@682 1949 cerr << "SpectrogramLayer: image cache useless" << endl;
Chris@224 1950 if (int(cache.zoomLevel) != zoomLevel) {
Chris@682 1951 cerr << "(cache zoomLevel " << cache.zoomLevel
Chris@682 1952 << " != " << zoomLevel << ")" << endl;
Chris@224 1953 }
Chris@482 1954 if (cw != v->width()) {
Chris@682 1955 cerr << "(cache width " << cw
Chris@224 1956 << " != " << v->width();
Chris@224 1957 }
Chris@482 1958 if (ch != v->height()) {
Chris@682 1959 cerr << "(cache height " << ch
Chris@224 1960 << " != " << v->height();
Chris@224 1961 }
Chris@0 1962 #endif
Chris@95 1963 cache.validArea = QRect();
Chris@478 1964 // recreateWholeImageCache = true;
Chris@0 1965 }
Chris@0 1966 }
Chris@95 1967
Chris@133 1968 if (updateViewMagnitudes(v)) {
Chris@184 1969 #ifdef DEBUG_SPECTROGRAM_REPAINT
Chris@682 1970 cerr << "SpectrogramLayer: magnitude range changed to [" << m_viewMags[v].getMin() << "->" << m_viewMags[v].getMax() << "]" << endl;
Chris@184 1971 #endif
Chris@331 1972 if (m_normalizeVisibleArea) {
Chris@331 1973 cache.validArea = QRect();
Chris@478 1974 recreateWholeImageCache = true;
Chris@331 1975 }
Chris@133 1976 } else {
Chris@184 1977 #ifdef DEBUG_SPECTROGRAM_REPAINT
Chris@682 1978 cerr << "No change in magnitude range [" << m_viewMags[v].getMin() << "->" << m_viewMags[v].getMax() << "]" << endl;
Chris@184 1979 #endif
Chris@133 1980 }
Chris@133 1981
Chris@478 1982 if (recreateWholeImageCache) {
Chris@95 1983 x0 = 0;
Chris@95 1984 x1 = v->width();
Chris@95 1985 }
Chris@95 1986
Chris@215 1987 struct timeval tv;
Chris@215 1988 (void)gettimeofday(&tv, 0);
Chris@215 1989 RealTime mainPaintStart = RealTime::fromTimeval(tv);
Chris@215 1990
Chris@215 1991 int paintBlockWidth = m_lastPaintBlockWidth;
Chris@215 1992
Chris@389 1993 if (m_synchronous) {
Chris@389 1994 if (paintBlockWidth < x1 - x0) {
Chris@389 1995 // always paint full width
Chris@389 1996 paintBlockWidth = x1 - x0;
Chris@389 1997 }
Chris@215 1998 } else {
Chris@389 1999 if (paintBlockWidth == 0) {
Chris@389 2000 paintBlockWidth = (300000 / zoomLevel);
Chris@389 2001 } else {
Chris@389 2002 RealTime lastTime = m_lastPaintTime;
Chris@389 2003 while (lastTime > RealTime::fromMilliseconds(200) &&
Chris@389 2004 paintBlockWidth > 50) {
Chris@389 2005 paintBlockWidth /= 2;
Chris@389 2006 lastTime = lastTime / 2;
Chris@389 2007 }
Chris@389 2008 while (lastTime < RealTime::fromMilliseconds(90) &&
Chris@389 2009 paintBlockWidth < 1500) {
Chris@389 2010 paintBlockWidth *= 2;
Chris@389 2011 lastTime = lastTime * 2;
Chris@389 2012 }
Chris@215 2013 }
Chris@389 2014
Chris@389 2015 if (paintBlockWidth < 20) paintBlockWidth = 20;
Chris@215 2016 }
Chris@215 2017
Chris@525 2018 #ifdef DEBUG_SPECTROGRAM_REPAINT
Chris@682 2019 cerr << "[" << this << "]: last paint width: " << m_lastPaintBlockWidth << ", last paint time: " << m_lastPaintTime << ", new paint width: " << paintBlockWidth << endl;
Chris@525 2020 #endif
Chris@224 2021
Chris@224 2022 // We always paint the full height when refreshing the cache.
Chris@224 2023 // Smaller heights can be used when painting direct from cache
Chris@224 2024 // (further up in this function), but we want to ensure the cache
Chris@224 2025 // is coherent without having to worry about vertical matching of
Chris@224 2026 // required and valid areas as well as horizontal.
Chris@224 2027
Chris@224 2028 int h = v->height();
Chris@215 2029
Chris@96 2030 if (cache.validArea.width() > 0) {
Chris@96 2031
Chris@331 2032 // If part of the cache is known to be valid, select a strip
Chris@331 2033 // immediately to left or right of the valid part
Chris@331 2034
Chris@481 2035 //!!! this really needs to be coordinated with the selection
Chris@481 2036 //!!! of m_drawBuffer boundaries in the bufferBinResolution
Chris@481 2037 //!!! case below
Chris@481 2038
Chris@96 2039 int vx0 = 0, vx1 = 0;
Chris@96 2040 vx0 = cache.validArea.x();
Chris@96 2041 vx1 = cache.validArea.x() + cache.validArea.width();
Chris@96 2042
Chris@96 2043 #ifdef DEBUG_SPECTROGRAM_REPAINT
Chris@682 2044 cerr << "x0 " << x0 << ", x1 " << x1 << ", vx0 " << vx0 << ", vx1 " << vx1 << ", paintBlockWidth " << paintBlockWidth << endl;
Chris@331 2045 #endif
Chris@96 2046 if (x0 < vx0) {
Chris@96 2047 if (x0 + paintBlockWidth < vx0) {
Chris@96 2048 x0 = vx0 - paintBlockWidth;
Chris@331 2049 }
Chris@331 2050 x1 = vx0;
Chris@331 2051 } else if (x0 >= vx1) {
Chris@331 2052 x0 = vx1;
Chris@331 2053 if (x1 > x0 + paintBlockWidth) {
Chris@331 2054 x1 = x0 + paintBlockWidth;
Chris@331 2055 }
Chris@331 2056 } else {
Chris@331 2057 // x0 is within the valid area
Chris@331 2058 if (x1 > vx1) {
Chris@331 2059 x0 = vx1;
Chris@331 2060 if (x0 + paintBlockWidth < x1) {
Chris@331 2061 x1 = x0 + paintBlockWidth;
Chris@331 2062 }
Chris@96 2063 } else {
Chris@331 2064 x1 = x0; // it's all valid, paint nothing
Chris@95 2065 }
Chris@96 2066 }
Chris@331 2067
Chris@96 2068 cache.validArea = QRect
Chris@96 2069 (std::min(vx0, x0), cache.validArea.y(),
Chris@96 2070 std::max(vx1 - std::min(vx0, x0),
Chris@337 2071 x1 - std::min(vx0, x0)),
Chris@96 2072 cache.validArea.height());
Chris@337 2073
Chris@337 2074 #ifdef DEBUG_SPECTROGRAM_REPAINT
Chris@682 2075 cerr << "Valid area becomes " << cache.validArea.x()
Chris@337 2076 << ", " << cache.validArea.y() << ", "
Chris@337 2077 << cache.validArea.width() << "x"
Chris@682 2078 << cache.validArea.height() << endl;
Chris@337 2079 #endif
Chris@95 2080
Chris@96 2081 } else {
Chris@96 2082 if (x1 > x0 + paintBlockWidth) {
Chris@133 2083 int sfx = x1;
Chris@133 2084 if (startFrame < 0) sfx = v->getXForFrame(0);
Chris@133 2085 if (sfx >= x0 && sfx + paintBlockWidth <= x1) {
Chris@133 2086 x0 = sfx;
Chris@133 2087 x1 = x0 + paintBlockWidth;
Chris@133 2088 } else {
Chris@133 2089 int mid = (x1 + x0) / 2;
Chris@133 2090 x0 = mid - paintBlockWidth/2;
Chris@133 2091 x1 = x0 + paintBlockWidth;
Chris@133 2092 }
Chris@95 2093 }
Chris@337 2094 #ifdef DEBUG_SPECTROGRAM_REPAINT
Chris@682 2095 cerr << "Valid area becomes " << x0 << ", 0, " << (x1-x0)
Chris@682 2096 << "x" << h << endl;
Chris@337 2097 #endif
Chris@224 2098 cache.validArea = QRect(x0, 0, x1 - x0, h);
Chris@95 2099 }
Chris@95 2100
Chris@481 2101 /*
Chris@480 2102 if (xPixelRatio != 1.f) {
Chris@480 2103 x0 = int((int(x0 / xPixelRatio) - 4) * xPixelRatio + 0.0001);
Chris@480 2104 x1 = int((int(x1 / xPixelRatio) + 4) * xPixelRatio + 0.0001);
Chris@480 2105 }
Chris@481 2106 */
Chris@0 2107 int w = x1 - x0;
Chris@0 2108
Chris@95 2109 #ifdef DEBUG_SPECTROGRAM_REPAINT
Chris@682 2110 cerr << "x0 " << x0 << ", x1 " << x1 << ", w " << w << ", h " << h << endl;
Chris@95 2111 #endif
Chris@95 2112
Chris@37 2113 int sr = m_model->getSampleRate();
Chris@122 2114
Chris@122 2115 // Set minFreq and maxFreq to the frequency extents of the possibly
Chris@122 2116 // zero-padded visible bin range, and displayMinFreq and displayMaxFreq
Chris@122 2117 // to the actual scale frequency extents (presumably not zero padded).
Chris@253 2118
Chris@253 2119 // If we are zero padding, we want to use the zero-padded
Chris@253 2120 // equivalents of the bins that we would be using if not zero
Chris@253 2121 // padded, to avoid spaces at the top and bottom of the display.
Chris@253 2122
Chris@253 2123 // Note fftSize is the actual zero-padded fft size, m_fftSize the
Chris@253 2124 // nominal fft size.
Chris@35 2125
Chris@253 2126 size_t maxbin = m_fftSize / 2;
Chris@35 2127 if (m_maxFrequency > 0) {
Chris@253 2128 maxbin = int((double(m_maxFrequency) * m_fftSize) / sr + 0.001);
Chris@253 2129 if (maxbin > m_fftSize / 2) maxbin = m_fftSize / 2;
Chris@35 2130 }
Chris@111 2131
Chris@40 2132 size_t minbin = 1;
Chris@37 2133 if (m_minFrequency > 0) {
Chris@253 2134 minbin = int((double(m_minFrequency) * m_fftSize) / sr + 0.001);
Chris@682 2135 // cerr << "m_minFrequency = " << m_minFrequency << " -> minbin = " << minbin << endl;
Chris@40 2136 if (minbin < 1) minbin = 1;
Chris@184 2137 if (minbin >= maxbin) minbin = maxbin - 1;
Chris@37 2138 }
Chris@37 2139
Chris@253 2140 int zpl = getZeroPadLevel(v) + 1;
Chris@253 2141 minbin = minbin * zpl;
Chris@253 2142 maxbin = (maxbin + 1) * zpl - 1;
Chris@253 2143
Chris@114 2144 float minFreq = (float(minbin) * sr) / fftSize;
Chris@184 2145 float maxFreq = (float(maxbin) * sr) / fftSize;
Chris@0 2146
Chris@122 2147 float displayMinFreq = minFreq;
Chris@122 2148 float displayMaxFreq = maxFreq;
Chris@122 2149
Chris@122 2150 if (fftSize != m_fftSize) {
Chris@122 2151 displayMinFreq = getEffectiveMinFrequency();
Chris@122 2152 displayMaxFreq = getEffectiveMaxFrequency();
Chris@122 2153 }
Chris@122 2154
Chris@682 2155 // cerr << "(giving actual minFreq " << minFreq << " and display minFreq " << displayMinFreq << ")" << endl;
Chris@253 2156
Chris@518 2157 int increment = getWindowIncrement();
Chris@40 2158
Chris@40 2159 bool logarithmic = (m_frequencyScale == LogFrequencyScale);
Chris@488 2160 /*
Chris@481 2161 float yforbin[maxbin - minbin + 1];
Chris@481 2162
Chris@184 2163 for (size_t q = minbin; q <= maxbin; ++q) {
Chris@114 2164 float f0 = (float(q) * sr) / fftSize;
Chris@477 2165 yforbin[q - minbin] =
Chris@382 2166 v->getYForFrequency(f0, displayMinFreq, displayMaxFreq,
Chris@382 2167 logarithmic);
Chris@92 2168 }
Chris@488 2169 */
Chris@119 2170 MagnitudeRange overallMag = m_viewMags[v];
Chris@119 2171 bool overallMagChanged = false;
Chris@119 2172
Chris@162 2173 bool fftSuspended = false;
Chris@131 2174
Chris@137 2175 #ifdef DEBUG_SPECTROGRAM_REPAINT
Chris@682 2176 cerr << ((float(v->getFrameForX(1) - v->getFrameForX(0))) / increment) << " bin(s) per pixel" << endl;
Chris@137 2177 #endif
Chris@137 2178
Chris@224 2179 bool runOutOfData = false;
Chris@224 2180
Chris@331 2181 if (w == 0) {
Chris@587 2182 SVDEBUG << "*** NOTE: w == 0" << endl;
Chris@331 2183 }
Chris@331 2184
Chris@331 2185 #ifdef DEBUG_SPECTROGRAM_REPAINT
Chris@331 2186 size_t pixels = 0;
Chris@331 2187 #endif
Chris@331 2188
Chris@382 2189 Profiler outerprof("SpectrogramLayer::paint: all cols");
Chris@382 2190
Chris@481 2191 // The draw buffer contains a fragment at either our pixel
Chris@481 2192 // resolution (if there is more than one time-bin per pixel) or
Chris@481 2193 // time-bin resolution (if a time-bin spans more than one pixel).
Chris@481 2194 // We need to ensure that it starts and ends at points where a
Chris@481 2195 // time-bin boundary occurs at an exact pixel boundary, and with a
Chris@481 2196 // certain amount of overlap across existing pixels so that we can
Chris@481 2197 // scale and draw from it without smoothing errors at the edges.
Chris@481 2198
Chris@481 2199 // If (getFrameForX(x) / increment) * increment ==
Chris@481 2200 // getFrameForX(x), then x is a time-bin boundary. We want two
Chris@481 2201 // such boundaries at either side of the draw buffer -- one which
Chris@481 2202 // we draw up to, and one which we subsequently crop at.
Chris@481 2203
Chris@481 2204 bool bufferBinResolution = false;
Chris@481 2205 if (increment > zoomLevel) bufferBinResolution = true;
Chris@481 2206
Chris@481 2207 long leftBoundaryFrame = -1, leftCropFrame = -1;
Chris@481 2208 long rightBoundaryFrame = -1, rightCropFrame = -1;
Chris@481 2209
Chris@481 2210 int bufwid;
Chris@481 2211
Chris@481 2212 if (bufferBinResolution) {
Chris@481 2213
Chris@482 2214 for (int x = x0; ; --x) {
Chris@481 2215 long f = v->getFrameForX(x);
Chris@481 2216 if ((f / increment) * increment == f) {
Chris@481 2217 if (leftCropFrame == -1) leftCropFrame = f;
Chris@482 2218 else if (x < x0 - 2) { leftBoundaryFrame = f; break; }
Chris@481 2219 }
Chris@481 2220 }
Chris@482 2221 for (int x = x0 + w; ; ++x) {
Chris@481 2222 long f = v->getFrameForX(x);
Chris@481 2223 if ((f / increment) * increment == f) {
Chris@481 2224 if (rightCropFrame == -1) rightCropFrame = f;
Chris@482 2225 else if (x > x0 + w + 2) { rightBoundaryFrame = f; break; }
Chris@481 2226 }
Chris@481 2227 }
Chris@485 2228 #ifdef DEBUG_SPECTROGRAM_REPAINT
Chris@481 2229 cerr << "Left: crop: " << leftCropFrame << " (bin " << leftCropFrame/increment << "); boundary: " << leftBoundaryFrame << " (bin " << leftBoundaryFrame/increment << ")" << endl;
Chris@481 2230 cerr << "Right: crop: " << rightCropFrame << " (bin " << rightCropFrame/increment << "); boundary: " << rightBoundaryFrame << " (bin " << rightBoundaryFrame/increment << ")" << endl;
Chris@485 2231 #endif
Chris@481 2232
Chris@481 2233 bufwid = (rightBoundaryFrame - leftBoundaryFrame) / increment;
Chris@481 2234
Chris@481 2235 } else {
Chris@481 2236
Chris@481 2237 bufwid = w;
Chris@481 2238 }
Chris@481 2239
Chris@545 2240 #ifdef __GNUC__
Chris@481 2241 int binforx[bufwid];
Chris@490 2242 float binfory[h];
Chris@545 2243 #else
Chris@545 2244 int *binforx = (int *)alloca(bufwid * sizeof(int));
Chris@545 2245 float *binfory = (float *)alloca(h * sizeof(float));
Chris@545 2246 #endif
Chris@481 2247
Chris@484 2248 bool usePeaksCache = false;
Chris@484 2249
Chris@481 2250 if (bufferBinResolution) {
Chris@481 2251 for (int x = 0; x < bufwid; ++x) {
Chris@481 2252 binforx[x] = (leftBoundaryFrame / increment) + x;
Chris@482 2253 // cerr << "binforx[" << x << "] = " << binforx[x] << endl;
Chris@481 2254 }
Chris@481 2255 m_drawBuffer = QImage(bufwid, h, QImage::Format_Indexed8);
Chris@481 2256 } else {
Chris@481 2257 for (int x = 0; x < bufwid; ++x) {
Chris@481 2258 float s0 = 0, s1 = 0;
Chris@481 2259 if (getXBinRange(v, x + x0, s0, s1)) {
Chris@481 2260 binforx[x] = int(s0 + 0.0001);
Chris@481 2261 } else {
Chris@487 2262 binforx[x] = -1; //???
Chris@481 2263 }
Chris@481 2264 }
Chris@481 2265 if (m_drawBuffer.width() < bufwid || m_drawBuffer.height() < h) {
Chris@481 2266 m_drawBuffer = QImage(bufwid, h, QImage::Format_Indexed8);
Chris@480 2267 }
Chris@484 2268 usePeaksCache = (increment * 8) < zoomLevel;
Chris@487 2269 if (m_colourScale == PhaseColourScale) usePeaksCache = false;
Chris@480 2270 }
Chris@481 2271
Chris@616 2272 // No longer exists in Qt5: m_drawBuffer.setNumColors(256);
Chris@481 2273 for (int pixel = 0; pixel < 256; ++pixel) {
Chris@481 2274 m_drawBuffer.setColor(pixel, m_palette.getColour(pixel).rgb());
Chris@481 2275 }
Chris@481 2276
Chris@481 2277 m_drawBuffer.fill(0);
Chris@480 2278
Chris@488 2279 if (m_binDisplay != PeakFrequencies) {
Chris@488 2280
Chris@488 2281 for (int y = 0; y < h; ++y) {
Chris@488 2282 float q0 = 0, q1 = 0;
Chris@488 2283 if (!getSmoothedYBinRange(v, h-y-1, q0, q1)) {
Chris@488 2284 binfory[y] = -1;
Chris@488 2285 } else {
Chris@490 2286 binfory[y] = q0;
Chris@488 2287 // cerr << "binfory[" << y << "] = " << binfory[y] << endl;
Chris@488 2288 }
Chris@480 2289 }
Chris@488 2290
Chris@491 2291 paintDrawBuffer(v, bufwid, h, binforx, binfory, usePeaksCache,
Chris@491 2292 overallMag, overallMagChanged);
Chris@488 2293
Chris@488 2294 } else {
Chris@488 2295
Chris@488 2296 paintDrawBufferPeakFrequencies(v, bufwid, h, binforx,
Chris@488 2297 minbin, maxbin,
Chris@488 2298 displayMinFreq, displayMaxFreq,
Chris@491 2299 logarithmic,
Chris@491 2300 overallMag, overallMagChanged);
Chris@480 2301 }
Chris@481 2302
Chris@480 2303 /*
Chris@479 2304 for (int x = 0; x < w / xPixelRatio; ++x) {
Chris@35 2305
Chris@382 2306 Profiler innerprof("SpectrogramLayer::paint: 1 pixel column");
Chris@382 2307
Chris@478 2308 runOutOfData = !paintColumnValues(v, fft, x0, x,
Chris@478 2309 minbin, maxbin,
Chris@478 2310 displayMinFreq, displayMaxFreq,
Chris@479 2311 xPixelRatio,
Chris@478 2312 h, yforbin);
Chris@477 2313
Chris@331 2314 if (runOutOfData) {
Chris@331 2315 #ifdef DEBUG_SPECTROGRAM_REPAINT
Chris@682 2316 cerr << "Run out of data -- dropping out of loop" << endl;
Chris@331 2317 #endif
Chris@331 2318 break;
Chris@331 2319 }
Chris@35 2320 }
Chris@480 2321 */
Chris@331 2322 #ifdef DEBUG_SPECTROGRAM_REPAINT
Chris@682 2323 // cerr << pixels << " pixels drawn" << endl;
Chris@331 2324 #endif
Chris@331 2325
Chris@119 2326 if (overallMagChanged) {
Chris@119 2327 m_viewMags[v] = overallMag;
Chris@209 2328 #ifdef DEBUG_SPECTROGRAM_REPAINT
Chris@682 2329 cerr << "Overall mag is now [" << m_viewMags[v].getMin() << "->" << m_viewMags[v].getMax() << "] - will be updating" << endl;
Chris@209 2330 #endif
Chris@119 2331 } else {
Chris@209 2332 #ifdef DEBUG_SPECTROGRAM_REPAINT
Chris@682 2333 cerr << "Overall mag unchanged at [" << m_viewMags[v].getMin() << "->" << m_viewMags[v].getMax() << "]" << endl;
Chris@209 2334 #endif
Chris@119 2335 }
Chris@119 2336
Chris@382 2337 outerprof.end();
Chris@382 2338
Chris@382 2339 Profiler profiler2("SpectrogramLayer::paint: draw image");
Chris@137 2340
Chris@478 2341 if (recreateWholeImageCache) {
Chris@407 2342 #ifdef DEBUG_SPECTROGRAM_REPAINT
Chris@587 2343 SVDEBUG << "Recreating image cache: width = " << v->width()
Chris@585 2344 << ", height = " << h << endl;
Chris@407 2345 #endif
Chris@556 2346 cache.image = QImage(v->width(), h, QImage::Format_ARGB32_Premultiplied);
Chris@0 2347 }
Chris@0 2348
Chris@331 2349 if (w > 0) {
Chris@224 2350 #ifdef DEBUG_SPECTROGRAM_REPAINT
Chris@587 2351 SVDEBUG << "Painting " << w << "x" << h
Chris@331 2352 << " from draw buffer at " << 0 << "," << 0
Chris@480 2353 << " to " << w << "x" << h << " on cache at "
Chris@585 2354 << x0 << "," << 0 << endl;
Chris@224 2355 #endif
Chris@224 2356
Chris@478 2357 QPainter cachePainter(&cache.image);
Chris@481 2358
Chris@481 2359 if (bufferBinResolution) {
Chris@481 2360 int scaledLeft = v->getXForFrame(leftBoundaryFrame);
Chris@481 2361 int scaledRight = v->getXForFrame(rightBoundaryFrame);
Chris@485 2362 #ifdef DEBUG_SPECTROGRAM_REPAINT
Chris@587 2363 SVDEBUG << "Rescaling image from " << bufwid
Chris@481 2364 << "x" << h << " to "
Chris@481 2365 << scaledRight-scaledLeft << "x" << h << endl;
Chris@485 2366 #endif
Chris@490 2367 Preferences::SpectrogramXSmoothing xsmoothing =
Chris@490 2368 Preferences::getInstance()->getSpectrogramXSmoothing();
Chris@587 2369 // SVDEBUG << "xsmoothing == " << xsmoothing << endl;
Chris@481 2370 QImage scaled = m_drawBuffer.scaled
Chris@481 2371 (scaledRight - scaledLeft, h,
Chris@490 2372 Qt::IgnoreAspectRatio,
Chris@490 2373 ((xsmoothing == Preferences::SpectrogramXInterpolated) ?
Chris@490 2374 Qt::SmoothTransformation : Qt::FastTransformation));
Chris@481 2375 int scaledLeftCrop = v->getXForFrame(leftCropFrame);
Chris@481 2376 int scaledRightCrop = v->getXForFrame(rightCropFrame);
Chris@485 2377 #ifdef DEBUG_SPECTROGRAM_REPAINT
Chris@587 2378 SVDEBUG << "Drawing image region of width " << scaledRightCrop - scaledLeftCrop << " to "
Chris@481 2379 << scaledLeftCrop << " from " << scaledLeftCrop - scaledLeft << endl;
Chris@485 2380 #endif
Chris@481 2381 cachePainter.drawImage
Chris@481 2382 (QRect(scaledLeftCrop, 0,
Chris@481 2383 scaledRightCrop - scaledLeftCrop, h),
Chris@481 2384 scaled,
Chris@481 2385 QRect(scaledLeftCrop - scaledLeft, 0,
Chris@481 2386 scaledRightCrop - scaledLeftCrop, h));
Chris@481 2387 } else {
Chris@481 2388 cachePainter.drawImage(QRect(x0, 0, w, h),
Chris@481 2389 m_drawBuffer,
Chris@481 2390 QRect(0, 0, w, h));
Chris@481 2391 }
Chris@481 2392
Chris@331 2393 cachePainter.end();
Chris@331 2394 }
Chris@331 2395
Chris@337 2396 QRect pr = rect & cache.validArea;
Chris@337 2397
Chris@337 2398 #ifdef DEBUG_SPECTROGRAM_REPAINT
Chris@587 2399 SVDEBUG << "Painting " << pr.width() << "x" << pr.height()
Chris@337 2400 << " from cache at " << pr.x() << "," << pr.y()
Chris@585 2401 << " to window" << endl;
Chris@337 2402 #endif
Chris@337 2403
Chris@478 2404 paint.drawImage(pr.x(), pr.y(), cache.image,
Chris@479 2405 pr.x(), pr.y(), pr.width(), pr.height());
Chris@479 2406 //!!!
Chris@479 2407 // paint.drawImage(v->rect(), cache.image,
Chris@479 2408 // QRect(QPoint(0, 0), cache.image.size()));
Chris@337 2409
Chris@331 2410 cache.startFrame = startFrame;
Chris@331 2411 cache.zoomLevel = zoomLevel;
Chris@119 2412
Chris@389 2413 if (!m_synchronous) {
Chris@389 2414
Chris@389 2415 if (!m_normalizeVisibleArea || !overallMagChanged) {
Chris@0 2416
Chris@389 2417 if (cache.validArea.x() > 0) {
Chris@95 2418 #ifdef DEBUG_SPECTROGRAM_REPAINT
Chris@587 2419 SVDEBUG << "SpectrogramLayer::paint() updating left (0, "
Chris@585 2420 << cache.validArea.x() << ")" << endl;
Chris@95 2421 #endif
Chris@389 2422 v->update(0, 0, cache.validArea.x(), h);
Chris@389 2423 }
Chris@389 2424
Chris@389 2425 if (cache.validArea.x() + cache.validArea.width() <
Chris@478 2426 cache.image.width()) {
Chris@389 2427 #ifdef DEBUG_SPECTROGRAM_REPAINT
Chris@587 2428 SVDEBUG << "SpectrogramLayer::paint() updating right ("
Chris@389 2429 << cache.validArea.x() + cache.validArea.width()
Chris@389 2430 << ", "
Chris@478 2431 << cache.image.width() - (cache.validArea.x() +
Chris@389 2432 cache.validArea.width())
Chris@585 2433 << ")" << endl;
Chris@389 2434 #endif
Chris@389 2435 v->update(cache.validArea.x() + cache.validArea.width(),
Chris@389 2436 0,
Chris@478 2437 cache.image.width() - (cache.validArea.x() +
Chris@389 2438 cache.validArea.width()),
Chris@389 2439 h);
Chris@389 2440 }
Chris@389 2441 } else {
Chris@389 2442 // overallMagChanged
Chris@682 2443 cerr << "\noverallMagChanged - updating all\n" << endl;
Chris@389 2444 cache.validArea = QRect();
Chris@389 2445 v->update();
Chris@119 2446 }
Chris@95 2447 }
Chris@0 2448
Chris@121 2449 illuminateLocalFeatures(v, paint);
Chris@120 2450
Chris@0 2451 #ifdef DEBUG_SPECTROGRAM_REPAINT
Chris@587 2452 SVDEBUG << "SpectrogramLayer::paint() returning" << endl;
Chris@0 2453 #endif
Chris@131 2454
Chris@389 2455 if (!m_synchronous) {
Chris@389 2456 m_lastPaintBlockWidth = paintBlockWidth;
Chris@389 2457 (void)gettimeofday(&tv, 0);
Chris@389 2458 m_lastPaintTime = RealTime::fromTimeval(tv) - mainPaintStart;
Chris@389 2459 }
Chris@215 2460
Chris@473 2461 //!!! if (fftSuspended) fft->resume();
Chris@0 2462 }
Chris@0 2463
Chris@480 2464 bool
Chris@488 2465 SpectrogramLayer::paintDrawBufferPeakFrequencies(View *v,
Chris@488 2466 int w,
Chris@488 2467 int h,
Chris@488 2468 int *binforx,
Chris@488 2469 int minbin,
Chris@488 2470 int maxbin,
Chris@488 2471 float displayMinFreq,
Chris@488 2472 float displayMaxFreq,
Chris@491 2473 bool logarithmic,
Chris@491 2474 MagnitudeRange &overallMag,
Chris@491 2475 bool &overallMagChanged) const
Chris@488 2476 {
Chris@488 2477 Profiler profiler("SpectrogramLayer::paintDrawBufferPeakFrequencies");
Chris@488 2478
Chris@488 2479 #ifdef DEBUG_SPECTROGRAM_REPAINT
Chris@488 2480 cerr << "minbin " << minbin << ", maxbin " << maxbin << "; w " << w << ", h " << h << endl;
Chris@488 2481 #endif
Chris@488 2482 if (minbin < 0) minbin = 0;
Chris@488 2483 if (maxbin < 0) maxbin = minbin+1;
Chris@488 2484
Chris@488 2485 FFTModel *fft = getFFTModel(v);
Chris@488 2486 if (!fft) return false;
Chris@488 2487
Chris@488 2488 FFTModel::PeakSet peakfreqs;
Chris@488 2489
Chris@488 2490 int px = -1, psx = -1;
Chris@545 2491
Chris@545 2492 #ifdef __GNUC__
Chris@488 2493 float values[maxbin - minbin + 1];
Chris@545 2494 #else
Chris@545 2495 float *values = (float *)alloca((maxbin - minbin + 1) * sizeof(float));
Chris@545 2496 #endif
Chris@488 2497
Chris@488 2498 for (int x = 0; x < w; ++x) {
Chris@488 2499
Chris@488 2500 if (binforx[x] < 0) continue;
Chris@488 2501
Chris@488 2502 float columnMax = 0.f;
Chris@488 2503
Chris@488 2504 int sx0 = binforx[x];
Chris@488 2505 int sx1 = sx0;
Chris@488 2506 if (x+1 < w) sx1 = binforx[x+1];
Chris@488 2507 if (sx0 < 0) sx0 = sx1 - 1;
Chris@488 2508 if (sx0 < 0) continue;
Chris@488 2509 if (sx1 <= sx0) sx1 = sx0 + 1;
Chris@488 2510
Chris@488 2511 for (int sx = sx0; sx < sx1; ++sx) {
Chris@488 2512
Chris@488 2513 if (x == px && sx == psx) continue;
Chris@488 2514 if (sx < 0 || sx >= int(fft->getWidth())) continue;
Chris@488 2515
Chris@488 2516 if (!m_synchronous) {
Chris@488 2517 if (!fft->isColumnAvailable(sx)) {
Chris@488 2518 #ifdef DEBUG_SPECTROGRAM_REPAINT
Chris@682 2519 cerr << "Met unavailable column at col " << sx << endl;
Chris@488 2520 #endif
Chris@488 2521 return false;
Chris@488 2522 }
Chris@488 2523 }
Chris@488 2524
Chris@488 2525 MagnitudeRange mag;
Chris@488 2526
Chris@488 2527 if (sx != psx) {
Chris@488 2528 peakfreqs = fft->getPeakFrequencies(FFTModel::AllPeaks, sx,
Chris@488 2529 minbin, maxbin - 1);
Chris@488 2530 if (m_colourScale == PhaseColourScale) {
Chris@488 2531 fft->getPhasesAt(sx, values, minbin, maxbin - minbin + 1);
Chris@488 2532 } else if (m_normalizeColumns) {
Chris@488 2533 fft->getNormalizedMagnitudesAt(sx, values, minbin, maxbin - minbin + 1);
Chris@719 2534 } else if (m_normalizeHybrid) {
Chris@719 2535 fft->getNormalizedMagnitudesAt(sx, values, minbin, maxbin - minbin + 1);
Chris@719 2536 float max = fft->getMaximumMagnitudeAt(sx);
Chris@719 2537 if (max > 0.f) {
Chris@719 2538 for (int i = minbin; i <= maxbin; ++i) {
Chris@719 2539 values[i - minbin] *= log10(max);
Chris@719 2540 }
Chris@719 2541 }
Chris@488 2542 } else {
Chris@488 2543 fft->getMagnitudesAt(sx, values, minbin, maxbin - minbin + 1);
Chris@488 2544 }
Chris@488 2545 psx = sx;
Chris@488 2546 }
Chris@488 2547
Chris@488 2548 for (FFTModel::PeakSet::const_iterator pi = peakfreqs.begin();
Chris@488 2549 pi != peakfreqs.end(); ++pi) {
Chris@488 2550
Chris@488 2551 int bin = pi->first;
Chris@488 2552 int freq = pi->second;
Chris@488 2553
Chris@488 2554 if (bin < minbin) continue;
Chris@488 2555 if (bin > maxbin) break;
Chris@488 2556
Chris@488 2557 float value = values[bin - minbin];
Chris@488 2558
Chris@488 2559 if (m_colourScale != PhaseColourScale) {
Chris@719 2560 if (!m_normalizeColumns && !m_normalizeHybrid) {
Chris@488 2561 value /= (m_fftSize/2.f);
Chris@488 2562 }
Chris@488 2563 mag.sample(value);
Chris@488 2564 value *= m_gain;
Chris@488 2565 }
Chris@488 2566
Chris@488 2567 float y = v->getYForFrequency
Chris@488 2568 (freq, displayMinFreq, displayMaxFreq, logarithmic);
Chris@488 2569
Chris@558 2570 int iy = int(y + 0.5);
Chris@558 2571 if (iy < 0 || iy >= h) continue;
Chris@558 2572
Chris@558 2573 m_drawBuffer.setPixel(x, iy, getDisplayValue(v, value));
Chris@488 2574 }
Chris@488 2575
Chris@488 2576 if (mag.isSet()) {
Chris@488 2577 if (sx >= int(m_columnMags.size())) {
Chris@540 2578 #ifdef DEBUG_SPECTROGRAM
Chris@682 2579 cerr << "INTERNAL ERROR: " << sx << " >= "
Chris@488 2580 << m_columnMags.size()
Chris@488 2581 << " at SpectrogramLayer.cpp::paintDrawBuffer"
Chris@682 2582 << endl;
Chris@540 2583 #endif
Chris@490 2584 } else {
Chris@490 2585 m_columnMags[sx].sample(mag);
Chris@491 2586 if (overallMag.sample(mag)) overallMagChanged = true;
Chris@488 2587 }
Chris@488 2588 }
Chris@488 2589 }
Chris@488 2590 }
Chris@488 2591
Chris@488 2592 return true;
Chris@488 2593 }
Chris@488 2594
Chris@488 2595 bool
Chris@481 2596 SpectrogramLayer::paintDrawBuffer(View *v,
Chris@481 2597 int w,
Chris@481 2598 int h,
Chris@481 2599 int *binforx,
Chris@490 2600 float *binfory,
Chris@491 2601 bool usePeaksCache,
Chris@491 2602 MagnitudeRange &overallMag,
Chris@491 2603 bool &overallMagChanged) const
Chris@480 2604 {
Chris@481 2605 Profiler profiler("SpectrogramLayer::paintDrawBuffer");
Chris@480 2606
Chris@490 2607 int minbin = int(binfory[0] + 0.0001);
Chris@480 2608 int maxbin = binfory[h-1];
Chris@480 2609
Chris@485 2610 #ifdef DEBUG_SPECTROGRAM_REPAINT
Chris@481 2611 cerr << "minbin " << minbin << ", maxbin " << maxbin << "; w " << w << ", h " << h << endl;
Chris@485 2612 #endif
Chris@480 2613 if (minbin < 0) minbin = 0;
Chris@480 2614 if (maxbin < 0) maxbin = minbin+1;
Chris@480 2615
Chris@484 2616 DenseThreeDimensionalModel *sourceModel = 0;
Chris@484 2617 FFTModel *fft = 0;
Chris@484 2618 int divisor = 1;
Chris@485 2619 #ifdef DEBUG_SPECTROGRAM_REPAINT
Chris@484 2620 cerr << "Note: bin display = " << m_binDisplay << ", w = " << w << ", binforx[" << w-1 << "] = " << binforx[w-1] << ", binforx[0] = " << binforx[0] << endl;
Chris@485 2621 #endif
Chris@484 2622 if (usePeaksCache) { //!!!
Chris@484 2623 sourceModel = getPeakCache(v);
Chris@484 2624 divisor = 8;//!!!
Chris@484 2625 minbin = 0;
Chris@484 2626 maxbin = sourceModel->getHeight();
Chris@484 2627 } else {
Chris@484 2628 sourceModel = fft = getFFTModel(v);
Chris@484 2629 }
Chris@484 2630
Chris@484 2631 if (!sourceModel) return false;
Chris@484 2632
Chris@490 2633 bool interpolate = false;
Chris@490 2634 Preferences::SpectrogramSmoothing smoothing =
Chris@490 2635 Preferences::getInstance()->getSpectrogramSmoothing();
Chris@490 2636 if (smoothing == Preferences::SpectrogramInterpolated ||
Chris@490 2637 smoothing == Preferences::SpectrogramZeroPaddedAndInterpolated) {
Chris@490 2638 if (m_binDisplay != PeakBins &&
Chris@490 2639 m_binDisplay != PeakFrequencies) {
Chris@490 2640 interpolate = true;
Chris@490 2641 }
Chris@490 2642 }
Chris@490 2643
Chris@480 2644 int psx = -1;
Chris@545 2645
Chris@545 2646 #ifdef __GNUC__
Chris@490 2647 float autoarray[maxbin - minbin + 1];
Chris@545 2648 float peaks[h];
Chris@545 2649 #else
Chris@545 2650 float *autoarray = (float *)alloca((maxbin - minbin + 1) * sizeof(float));
Chris@545 2651 float *peaks = (float *)alloca(h * sizeof(float));
Chris@545 2652 #endif
Chris@545 2653
Chris@490 2654 const float *values = autoarray;
Chris@484 2655 DenseThreeDimensionalModel::Column c;
Chris@480 2656
Chris@480 2657 for (int x = 0; x < w; ++x) {
Chris@480 2658
Chris@482 2659 if (binforx[x] < 0) continue;
Chris@482 2660
Chris@488 2661 // float columnGain = m_gain;
Chris@487 2662 float columnMax = 0.f;
Chris@487 2663
Chris@484 2664 int sx0 = binforx[x] / divisor;
Chris@483 2665 int sx1 = sx0;
Chris@484 2666 if (x+1 < w) sx1 = binforx[x+1] / divisor;
Chris@483 2667 if (sx0 < 0) sx0 = sx1 - 1;
Chris@483 2668 if (sx0 < 0) continue;
Chris@483 2669 if (sx1 <= sx0) sx1 = sx0 + 1;
Chris@483 2670
Chris@483 2671 for (int y = 0; y < h; ++y) peaks[y] = 0.f;
Chris@480 2672
Chris@483 2673 for (int sx = sx0; sx < sx1; ++sx) {
Chris@483 2674
Chris@518 2675 #ifdef DEBUG_SPECTROGRAM_REPAINT
Chris@682 2676 // cerr << "sx = " << sx << endl;
Chris@518 2677 #endif
Chris@518 2678
Chris@484 2679 if (sx < 0 || sx >= int(sourceModel->getWidth())) continue;
Chris@483 2680
Chris@483 2681 if (!m_synchronous) {
Chris@484 2682 if (!sourceModel->isColumnAvailable(sx)) {
Chris@480 2683 #ifdef DEBUG_SPECTROGRAM_REPAINT
Chris@682 2684 cerr << "Met unavailable column at col " << sx << endl;
Chris@480 2685 #endif
Chris@483 2686 return false;
Chris@480 2687 }
Chris@483 2688 }
Chris@483 2689
Chris@488 2690 MagnitudeRange mag;
Chris@488 2691
Chris@483 2692 if (sx != psx) {
Chris@484 2693 if (fft) {
Chris@485 2694 #ifdef DEBUG_SPECTROGRAM_REPAINT
Chris@587 2695 SVDEBUG << "Retrieving column " << sx << " from fft directly" << endl;
Chris@485 2696 #endif
Chris@487 2697 if (m_colourScale == PhaseColourScale) {
Chris@490 2698 fft->getPhasesAt(sx, autoarray, minbin, maxbin - minbin + 1);
Chris@487 2699 } else if (m_normalizeColumns) {
Chris@490 2700 fft->getNormalizedMagnitudesAt(sx, autoarray, minbin, maxbin - minbin + 1);
Chris@719 2701 } else if (m_normalizeHybrid) {
Chris@719 2702 fft->getNormalizedMagnitudesAt(sx, autoarray, minbin, maxbin - minbin + 1);
Chris@719 2703 float max = fft->getMaximumMagnitudeAt(sx);
Chris@719 2704 for (int i = minbin; i <= maxbin; ++i) {
Chris@719 2705 if (max > 0.f) {
Chris@719 2706 autoarray[i - minbin] *= log10(max);
Chris@719 2707 }
Chris@719 2708 }
Chris@487 2709 } else {
Chris@490 2710 fft->getMagnitudesAt(sx, autoarray, minbin, maxbin - minbin + 1);
Chris@487 2711 }
Chris@484 2712 } else {
Chris@485 2713 #ifdef DEBUG_SPECTROGRAM_REPAINT
Chris@587 2714 SVDEBUG << "Retrieving column " << sx << " from peaks cache" << endl;
Chris@485 2715 #endif
Chris@484 2716 c = sourceModel->getColumn(sx);
Chris@719 2717 if (m_normalizeColumns || m_normalizeHybrid) {
Chris@487 2718 for (int y = 0; y < h; ++y) {
Chris@487 2719 if (c[y] > columnMax) columnMax = c[y];
Chris@487 2720 }
Chris@487 2721 }
Chris@490 2722 values = c.constData() + minbin;
Chris@484 2723 }
Chris@483 2724 psx = sx;
Chris@483 2725 }
Chris@483 2726
Chris@483 2727 for (int y = 0; y < h; ++y) {
Chris@480 2728
Chris@490 2729 float sy0 = binfory[y];
Chris@490 2730 float sy1 = sy0 + 1;
Chris@481 2731 if (y+1 < h) sy1 = binfory[y+1];
Chris@490 2732
Chris@490 2733 float value = 0.f;
Chris@490 2734
Chris@490 2735 if (interpolate && fabsf(sy1 - sy0) < 1.f) {
Chris@490 2736
Chris@490 2737 float centre = (sy0 + sy1) / 2;
Chris@490 2738 float dist = (centre - 0.5) - lrintf(centre - 0.5);
Chris@490 2739 int bin = int(centre);
Chris@490 2740 int other = (dist < 0 ? (bin-1) : (bin+1));
Chris@490 2741 if (bin < minbin) bin = minbin;
Chris@490 2742 if (bin > maxbin) bin = maxbin;
Chris@490 2743 if (other < minbin || other > maxbin) other = bin;
Chris@490 2744 float prop = 1.f - fabsf(dist);
Chris@490 2745
Chris@490 2746 float v0 = values[bin - minbin];
Chris@490 2747 float v1 = values[other - minbin];
Chris@490 2748 if (m_binDisplay == PeakBins) {
Chris@490 2749 if (bin == minbin || bin == maxbin ||
Chris@490 2750 v0 < values[bin-minbin-1] ||
Chris@490 2751 v0 < values[bin-minbin+1]) v0 = 0.f;
Chris@490 2752 if (other == minbin || other == maxbin ||
Chris@490 2753 v1 < values[other-minbin-1] ||
Chris@490 2754 v1 < values[other-minbin+1]) v1 = 0.f;
Chris@489 2755 }
Chris@490 2756 if (v0 == 0.f && v1 == 0.f) continue;
Chris@490 2757 value = prop * v0 + (1.f - prop) * v1;
Chris@484 2758
Chris@488 2759 if (m_colourScale != PhaseColourScale) {
Chris@488 2760 if (!m_normalizeColumns) {
Chris@488 2761 value /= (m_fftSize/2.f);
Chris@488 2762 }
Chris@488 2763 mag.sample(value);
Chris@488 2764 value *= m_gain;
Chris@488 2765 }
Chris@488 2766
Chris@490 2767 peaks[y] = value;
Chris@490 2768
Chris@490 2769 } else {
Chris@490 2770
Chris@490 2771 int by0 = int(sy0 + 0.0001);
Chris@490 2772 int by1 = int(sy1 + 0.0001);
Chris@490 2773 if (by1 < by0 + 1) by1 = by0 + 1;
Chris@490 2774
Chris@490 2775 for (int bin = by0; bin < by1; ++bin) {
Chris@490 2776
Chris@490 2777 value = values[bin - minbin];
Chris@490 2778 if (m_binDisplay == PeakBins) {
Chris@490 2779 if (bin == minbin || bin == maxbin ||
Chris@490 2780 value < values[bin-minbin-1] ||
Chris@490 2781 value < values[bin-minbin+1]) continue;
Chris@480 2782 }
Chris@490 2783
Chris@490 2784 if (m_colourScale != PhaseColourScale) {
Chris@490 2785 if (!m_normalizeColumns) {
Chris@490 2786 value /= (m_fftSize/2.f);
Chris@490 2787 }
Chris@490 2788 mag.sample(value);
Chris@490 2789 value *= m_gain;
Chris@490 2790 }
Chris@490 2791
Chris@490 2792 if (value > peaks[y]) peaks[y] = value; //!!! not right for phase!
Chris@480 2793 }
Chris@480 2794 }
Chris@483 2795 }
Chris@488 2796
Chris@488 2797 if (mag.isSet()) {
Chris@488 2798 if (sx >= int(m_columnMags.size())) {
Chris@540 2799 #ifdef DEBUG_SPECTROGRAM
Chris@682 2800 cerr << "INTERNAL ERROR: " << sx << " >= "
Chris@488 2801 << m_columnMags.size()
Chris@488 2802 << " at SpectrogramLayer.cpp::paintDrawBuffer"
Chris@682 2803 << endl;
Chris@540 2804 #endif
Chris@490 2805 } else {
Chris@490 2806 m_columnMags[sx].sample(mag);
Chris@491 2807 if (overallMag.sample(mag)) overallMagChanged = true;
Chris@488 2808 }
Chris@488 2809 }
Chris@483 2810 }
Chris@483 2811
Chris@483 2812 for (int y = 0; y < h; ++y) {
Chris@483 2813
Chris@483 2814 float peak = peaks[y];
Chris@483 2815
Chris@488 2816 if (m_colourScale != PhaseColourScale &&
Chris@719 2817 (m_normalizeColumns || m_normalizeHybrid) &&
Chris@488 2818 columnMax > 0.f) {
Chris@488 2819 peak /= columnMax;
Chris@719 2820 if (m_normalizeHybrid) {
Chris@719 2821 peak *= log10(columnMax);
Chris@719 2822 }
Chris@480 2823 }
Chris@483 2824
Chris@483 2825 unsigned char peakpix = getDisplayValue(v, peak);
Chris@480 2826
Chris@480 2827 m_drawBuffer.setPixel(x, h-y-1, peakpix);
Chris@480 2828 }
Chris@480 2829 }
Chris@480 2830
Chris@480 2831 return true;
Chris@480 2832 }
Chris@477 2833
Chris@121 2834 void
Chris@121 2835 SpectrogramLayer::illuminateLocalFeatures(View *v, QPainter &paint) const
Chris@121 2836 {
Chris@382 2837 Profiler profiler("SpectrogramLayer::illuminateLocalFeatures");
Chris@382 2838
Chris@121 2839 QPoint localPos;
Chris@121 2840 if (!v->shouldIlluminateLocalFeatures(this, localPos) || !m_model) {
Chris@121 2841 return;
Chris@121 2842 }
Chris@121 2843
Chris@682 2844 // cerr << "SpectrogramLayer: illuminateLocalFeatures("
Chris@682 2845 // << localPos.x() << "," << localPos.y() << ")" << endl;
Chris@121 2846
Chris@121 2847 float s0, s1;
Chris@121 2848 float f0, f1;
Chris@121 2849
Chris@121 2850 if (getXBinRange(v, localPos.x(), s0, s1) &&
Chris@121 2851 getYBinSourceRange(v, localPos.y(), f0, f1)) {
Chris@121 2852
Chris@121 2853 int s0i = int(s0 + 0.001);
Chris@121 2854 int s1i = int(s1);
Chris@121 2855
Chris@121 2856 int x0 = v->getXForFrame(s0i * getWindowIncrement());
Chris@121 2857 int x1 = v->getXForFrame((s1i + 1) * getWindowIncrement());
Chris@121 2858
Chris@248 2859 int y1 = int(getYForFrequency(v, f1));
Chris@248 2860 int y0 = int(getYForFrequency(v, f0));
Chris@121 2861
Chris@682 2862 // cerr << "SpectrogramLayer: illuminate "
Chris@682 2863 // << x0 << "," << y1 << " -> " << x1 << "," << y0 << endl;
Chris@121 2864
Chris@287 2865 paint.setPen(v->getForeground());
Chris@133 2866
Chris@133 2867 //!!! should we be using paintCrosshairs for this?
Chris@133 2868
Chris@121 2869 paint.drawRect(x0, y1, x1 - x0 + 1, y0 - y1 + 1);
Chris@121 2870 }
Chris@121 2871 }
Chris@121 2872
Chris@42 2873 float
Chris@267 2874 SpectrogramLayer::getYForFrequency(const View *v, float frequency) const
Chris@42 2875 {
Chris@44 2876 return v->getYForFrequency(frequency,
Chris@44 2877 getEffectiveMinFrequency(),
Chris@44 2878 getEffectiveMaxFrequency(),
Chris@44 2879 m_frequencyScale == LogFrequencyScale);
Chris@42 2880 }
Chris@42 2881
Chris@42 2882 float
Chris@267 2883 SpectrogramLayer::getFrequencyForY(const View *v, int y) const
Chris@42 2884 {
Chris@44 2885 return v->getFrequencyForY(y,
Chris@44 2886 getEffectiveMinFrequency(),
Chris@44 2887 getEffectiveMaxFrequency(),
Chris@44 2888 m_frequencyScale == LogFrequencyScale);
Chris@42 2889 }
Chris@42 2890
Chris@0 2891 int
Chris@115 2892 SpectrogramLayer::getCompletion(View *v) const
Chris@0 2893 {
Chris@115 2894 if (m_updateTimer == 0) return 100;
Chris@130 2895 if (m_fftModels.find(v) == m_fftModels.end()) return 100;
Chris@130 2896
Chris@130 2897 size_t completion = m_fftModels[v].first->getCompletion();
Chris@224 2898 #ifdef DEBUG_SPECTROGRAM_REPAINT
Chris@587 2899 SVDEBUG << "SpectrogramLayer::getCompletion: completion = " << completion << endl;
Chris@224 2900 #endif
Chris@0 2901 return completion;
Chris@0 2902 }
Chris@0 2903
Chris@583 2904 QString
Chris@583 2905 SpectrogramLayer::getError(View *v) const
Chris@583 2906 {
Chris@583 2907 if (m_fftModels.find(v) == m_fftModels.end()) return "";
Chris@583 2908 return m_fftModels[v].first->getError();
Chris@583 2909 }
Chris@583 2910
Chris@28 2911 bool
Chris@101 2912 SpectrogramLayer::getValueExtents(float &min, float &max,
Chris@101 2913 bool &logarithmic, QString &unit) const
Chris@79 2914 {
Chris@133 2915 if (!m_model) return false;
Chris@133 2916
Chris@133 2917 int sr = m_model->getSampleRate();
Chris@133 2918 min = float(sr) / m_fftSize;
Chris@133 2919 max = float(sr) / 2;
Chris@133 2920
Chris@101 2921 logarithmic = (m_frequencyScale == LogFrequencyScale);
Chris@79 2922 unit = "Hz";
Chris@79 2923 return true;
Chris@79 2924 }
Chris@79 2925
Chris@79 2926 bool
Chris@101 2927 SpectrogramLayer::getDisplayExtents(float &min, float &max) const
Chris@101 2928 {
Chris@101 2929 min = getEffectiveMinFrequency();
Chris@101 2930 max = getEffectiveMaxFrequency();
Chris@253 2931
Chris@587 2932 // SVDEBUG << "SpectrogramLayer::getDisplayExtents: " << min << "->" << max << endl;
Chris@101 2933 return true;
Chris@101 2934 }
Chris@101 2935
Chris@101 2936 bool
Chris@120 2937 SpectrogramLayer::setDisplayExtents(float min, float max)
Chris@120 2938 {
Chris@120 2939 if (!m_model) return false;
Chris@187 2940
Chris@587 2941 // SVDEBUG << "SpectrogramLayer::setDisplayExtents: " << min << "->" << max << endl;
Chris@187 2942
Chris@120 2943 if (min < 0) min = 0;
Chris@120 2944 if (max > m_model->getSampleRate()/2) max = m_model->getSampleRate()/2;
Chris@120 2945
Chris@120 2946 size_t minf = lrintf(min);
Chris@120 2947 size_t maxf = lrintf(max);
Chris@120 2948
Chris@120 2949 if (m_minFrequency == minf && m_maxFrequency == maxf) return true;
Chris@120 2950
Chris@478 2951 invalidateImageCaches();
Chris@120 2952 invalidateMagnitudes();
Chris@120 2953
Chris@120 2954 m_minFrequency = minf;
Chris@120 2955 m_maxFrequency = maxf;
Chris@120 2956
Chris@120 2957 emit layerParametersChanged();
Chris@120 2958
Chris@133 2959 int vs = getCurrentVerticalZoomStep();
Chris@133 2960 if (vs != m_lastEmittedZoomStep) {
Chris@133 2961 emit verticalZoomChanged();
Chris@133 2962 m_lastEmittedZoomStep = vs;
Chris@133 2963 }
Chris@133 2964
Chris@120 2965 return true;
Chris@120 2966 }
Chris@120 2967
Chris@120 2968 bool
Chris@267 2969 SpectrogramLayer::getYScaleValue(const View *v, int y,
Chris@261 2970 float &value, QString &unit) const
Chris@261 2971 {
Chris@261 2972 value = getFrequencyForY(v, y);
Chris@261 2973 unit = "Hz";
Chris@261 2974 return true;
Chris@261 2975 }
Chris@261 2976
Chris@261 2977 bool
Chris@248 2978 SpectrogramLayer::snapToFeatureFrame(View *, int &frame,
Chris@28 2979 size_t &resolution,
Chris@28 2980 SnapType snap) const
Chris@13 2981 {
Chris@13 2982 resolution = getWindowIncrement();
Chris@28 2983 int left = (frame / resolution) * resolution;
Chris@28 2984 int right = left + resolution;
Chris@28 2985
Chris@28 2986 switch (snap) {
Chris@28 2987 case SnapLeft: frame = left; break;
Chris@28 2988 case SnapRight: frame = right; break;
Chris@28 2989 case SnapNearest:
Chris@28 2990 case SnapNeighbouring:
Chris@28 2991 if (frame - left > right - frame) frame = right;
Chris@28 2992 else frame = left;
Chris@28 2993 break;
Chris@28 2994 }
Chris@28 2995
Chris@28 2996 return true;
Chris@28 2997 }
Chris@13 2998
Chris@283 2999 void
Chris@283 3000 SpectrogramLayer::measureDoubleClick(View *v, QMouseEvent *e)
Chris@283 3001 {
Chris@478 3002 ImageCache &cache = m_imageCaches[v];
Chris@478 3003
Chris@682 3004 cerr << "cache width: " << cache.image.width() << ", height: "
Chris@682 3005 << cache.image.height() << endl;
Chris@478 3006
Chris@478 3007 QImage image = cache.image;
Chris@283 3008
Chris@283 3009 ImageRegionFinder finder;
Chris@283 3010 QRect rect = finder.findRegionExtents(&image, e->pos());
Chris@283 3011 if (rect.isValid()) {
Chris@283 3012 MeasureRect mr;
Chris@283 3013 setMeasureRectFromPixrect(v, mr, rect);
Chris@283 3014 CommandHistory::getInstance()->addCommand
Chris@283 3015 (new AddMeasurementRectCommand(this, mr));
Chris@283 3016 }
Chris@283 3017 }
Chris@283 3018
Chris@77 3019 bool
Chris@264 3020 SpectrogramLayer::getCrosshairExtents(View *v, QPainter &paint,
Chris@77 3021 QPoint cursorPos,
Chris@77 3022 std::vector<QRect> &extents) const
Chris@77 3023 {
Chris@77 3024 QRect vertical(cursorPos.x() - 12, 0, 12, v->height());
Chris@77 3025 extents.push_back(vertical);
Chris@77 3026
Chris@77 3027 QRect horizontal(0, cursorPos.y(), cursorPos.x(), 1);
Chris@77 3028 extents.push_back(horizontal);
Chris@77 3029
Chris@608 3030 int sw = getVerticalScaleWidth(v, m_haveDetailedScale, paint);
Chris@264 3031
Chris@280 3032 QRect freq(sw, cursorPos.y() - paint.fontMetrics().ascent() - 2,
Chris@280 3033 paint.fontMetrics().width("123456 Hz") + 2,
Chris@280 3034 paint.fontMetrics().height());
Chris@280 3035 extents.push_back(freq);
Chris@264 3036
Chris@279 3037 QRect pitch(sw, cursorPos.y() + 2,
Chris@279 3038 paint.fontMetrics().width("C#10+50c") + 2,
Chris@279 3039 paint.fontMetrics().height());
Chris@279 3040 extents.push_back(pitch);
Chris@279 3041
Chris@280 3042 QRect rt(cursorPos.x(),
Chris@280 3043 v->height() - paint.fontMetrics().height() - 2,
Chris@280 3044 paint.fontMetrics().width("1234.567 s"),
Chris@280 3045 paint.fontMetrics().height());
Chris@280 3046 extents.push_back(rt);
Chris@280 3047
Chris@280 3048 int w(paint.fontMetrics().width("1234567890") + 2);
Chris@280 3049 QRect frame(cursorPos.x() - w - 2,
Chris@280 3050 v->height() - paint.fontMetrics().height() - 2,
Chris@280 3051 w,
Chris@280 3052 paint.fontMetrics().height());
Chris@280 3053 extents.push_back(frame);
Chris@280 3054
Chris@77 3055 return true;
Chris@77 3056 }
Chris@77 3057
Chris@77 3058 void
Chris@77 3059 SpectrogramLayer::paintCrosshairs(View *v, QPainter &paint,
Chris@77 3060 QPoint cursorPos) const
Chris@77 3061 {
Chris@77 3062 paint.save();
Chris@283 3063
Chris@608 3064 int sw = getVerticalScaleWidth(v, m_haveDetailedScale, paint);
Chris@283 3065
Chris@282 3066 QFont fn = paint.font();
Chris@282 3067 if (fn.pointSize() > 8) {
Chris@282 3068 fn.setPointSize(fn.pointSize() - 1);
Chris@282 3069 paint.setFont(fn);
Chris@282 3070 }
Chris@77 3071 paint.setPen(m_crosshairColour);
Chris@77 3072
Chris@77 3073 paint.drawLine(0, cursorPos.y(), cursorPos.x() - 1, cursorPos.y());
Chris@77 3074 paint.drawLine(cursorPos.x(), 0, cursorPos.x(), v->height());
Chris@77 3075
Chris@77 3076 float fundamental = getFrequencyForY(v, cursorPos.y());
Chris@77 3077
Chris@278 3078 v->drawVisibleText(paint,
Chris@278 3079 sw + 2,
Chris@278 3080 cursorPos.y() - 2,
Chris@278 3081 QString("%1 Hz").arg(fundamental),
Chris@278 3082 View::OutlinedText);
Chris@278 3083
Chris@279 3084 if (Pitch::isFrequencyInMidiRange(fundamental)) {
Chris@279 3085 QString pitchLabel = Pitch::getPitchLabelForFrequency(fundamental);
Chris@279 3086 v->drawVisibleText(paint,
Chris@279 3087 sw + 2,
Chris@279 3088 cursorPos.y() + paint.fontMetrics().ascent() + 2,
Chris@279 3089 pitchLabel,
Chris@279 3090 View::OutlinedText);
Chris@279 3091 }
Chris@279 3092
Chris@280 3093 long frame = v->getFrameForX(cursorPos.x());
Chris@279 3094 RealTime rt = RealTime::frame2RealTime(frame, m_model->getSampleRate());
Chris@280 3095 QString rtLabel = QString("%1 s").arg(rt.toText(true).c_str());
Chris@280 3096 QString frameLabel = QString("%1").arg(frame);
Chris@280 3097 v->drawVisibleText(paint,
Chris@280 3098 cursorPos.x() - paint.fontMetrics().width(frameLabel) - 2,
Chris@280 3099 v->height() - 2,
Chris@280 3100 frameLabel,
Chris@280 3101 View::OutlinedText);
Chris@280 3102 v->drawVisibleText(paint,
Chris@280 3103 cursorPos.x() + 2,
Chris@280 3104 v->height() - 2,
Chris@280 3105 rtLabel,
Chris@280 3106 View::OutlinedText);
Chris@264 3107
Chris@77 3108 int harmonic = 2;
Chris@77 3109
Chris@77 3110 while (harmonic < 100) {
Chris@77 3111
Chris@77 3112 float hy = lrintf(getYForFrequency(v, fundamental * harmonic));
Chris@77 3113 if (hy < 0 || hy > v->height()) break;
Chris@77 3114
Chris@77 3115 int len = 7;
Chris@77 3116
Chris@77 3117 if (harmonic % 2 == 0) {
Chris@77 3118 if (harmonic % 4 == 0) {
Chris@77 3119 len = 12;
Chris@77 3120 } else {
Chris@77 3121 len = 10;
Chris@77 3122 }
Chris@77 3123 }
Chris@77 3124
Chris@77 3125 paint.drawLine(cursorPos.x() - len,
Chris@248 3126 int(hy),
Chris@77 3127 cursorPos.x(),
Chris@248 3128 int(hy));
Chris@77 3129
Chris@77 3130 ++harmonic;
Chris@77 3131 }
Chris@77 3132
Chris@77 3133 paint.restore();
Chris@77 3134 }
Chris@77 3135
Chris@25 3136 QString
Chris@44 3137 SpectrogramLayer::getFeatureDescription(View *v, QPoint &pos) const
Chris@25 3138 {
Chris@25 3139 int x = pos.x();
Chris@25 3140 int y = pos.y();
Chris@0 3141
Chris@25 3142 if (!m_model || !m_model->isOK()) return "";
Chris@0 3143
Chris@38 3144 float magMin = 0, magMax = 0;
Chris@38 3145 float phaseMin = 0, phaseMax = 0;
Chris@0 3146 float freqMin = 0, freqMax = 0;
Chris@35 3147 float adjFreqMin = 0, adjFreqMax = 0;
Chris@25 3148 QString pitchMin, pitchMax;
Chris@0 3149 RealTime rtMin, rtMax;
Chris@0 3150
Chris@38 3151 bool haveValues = false;
Chris@0 3152
Chris@44 3153 if (!getXBinSourceRange(v, x, rtMin, rtMax)) {
Chris@38 3154 return "";
Chris@38 3155 }
Chris@44 3156 if (getXYBinSourceRange(v, x, y, magMin, magMax, phaseMin, phaseMax)) {
Chris@38 3157 haveValues = true;
Chris@38 3158 }
Chris@0 3159
Chris@35 3160 QString adjFreqText = "", adjPitchText = "";
Chris@35 3161
Chris@38 3162 if (m_binDisplay == PeakFrequencies) {
Chris@35 3163
Chris@44 3164 if (!getAdjustedYBinSourceRange(v, x, y, freqMin, freqMax,
Chris@38 3165 adjFreqMin, adjFreqMax)) {
Chris@38 3166 return "";
Chris@38 3167 }
Chris@35 3168
Chris@35 3169 if (adjFreqMin != adjFreqMax) {
Chris@65 3170 adjFreqText = tr("Peak Frequency:\t%1 - %2 Hz\n")
Chris@35 3171 .arg(adjFreqMin).arg(adjFreqMax);
Chris@35 3172 } else {
Chris@65 3173 adjFreqText = tr("Peak Frequency:\t%1 Hz\n")
Chris@35 3174 .arg(adjFreqMin);
Chris@38 3175 }
Chris@38 3176
Chris@38 3177 QString pmin = Pitch::getPitchLabelForFrequency(adjFreqMin);
Chris@38 3178 QString pmax = Pitch::getPitchLabelForFrequency(adjFreqMax);
Chris@38 3179
Chris@38 3180 if (pmin != pmax) {
Chris@65 3181 adjPitchText = tr("Peak Pitch:\t%3 - %4\n").arg(pmin).arg(pmax);
Chris@38 3182 } else {
Chris@65 3183 adjPitchText = tr("Peak Pitch:\t%2\n").arg(pmin);
Chris@35 3184 }
Chris@35 3185
Chris@35 3186 } else {
Chris@35 3187
Chris@44 3188 if (!getYBinSourceRange(v, y, freqMin, freqMax)) return "";
Chris@35 3189 }
Chris@35 3190
Chris@25 3191 QString text;
Chris@25 3192
Chris@25 3193 if (rtMin != rtMax) {
Chris@25 3194 text += tr("Time:\t%1 - %2\n")
Chris@25 3195 .arg(rtMin.toText(true).c_str())
Chris@25 3196 .arg(rtMax.toText(true).c_str());
Chris@25 3197 } else {
Chris@25 3198 text += tr("Time:\t%1\n")
Chris@25 3199 .arg(rtMin.toText(true).c_str());
Chris@0 3200 }
Chris@0 3201
Chris@25 3202 if (freqMin != freqMax) {
Chris@65 3203 text += tr("%1Bin Frequency:\t%2 - %3 Hz\n%4Bin Pitch:\t%5 - %6\n")
Chris@65 3204 .arg(adjFreqText)
Chris@25 3205 .arg(freqMin)
Chris@25 3206 .arg(freqMax)
Chris@65 3207 .arg(adjPitchText)
Chris@65 3208 .arg(Pitch::getPitchLabelForFrequency(freqMin))
Chris@65 3209 .arg(Pitch::getPitchLabelForFrequency(freqMax));
Chris@65 3210 } else {
Chris@65 3211 text += tr("%1Bin Frequency:\t%2 Hz\n%3Bin Pitch:\t%4\n")
Chris@35 3212 .arg(adjFreqText)
Chris@25 3213 .arg(freqMin)
Chris@65 3214 .arg(adjPitchText)
Chris@65 3215 .arg(Pitch::getPitchLabelForFrequency(freqMin));
Chris@25 3216 }
Chris@25 3217
Chris@38 3218 if (haveValues) {
Chris@38 3219 float dbMin = AudioLevel::multiplier_to_dB(magMin);
Chris@38 3220 float dbMax = AudioLevel::multiplier_to_dB(magMax);
Chris@43 3221 QString dbMinString;
Chris@43 3222 QString dbMaxString;
Chris@43 3223 if (dbMin == AudioLevel::DB_FLOOR) {
Chris@43 3224 dbMinString = tr("-Inf");
Chris@43 3225 } else {
Chris@43 3226 dbMinString = QString("%1").arg(lrintf(dbMin));
Chris@43 3227 }
Chris@43 3228 if (dbMax == AudioLevel::DB_FLOOR) {
Chris@43 3229 dbMaxString = tr("-Inf");
Chris@43 3230 } else {
Chris@43 3231 dbMaxString = QString("%1").arg(lrintf(dbMax));
Chris@43 3232 }
Chris@25 3233 if (lrintf(dbMin) != lrintf(dbMax)) {
Chris@199 3234 text += tr("dB:\t%1 - %2").arg(dbMinString).arg(dbMaxString);
Chris@25 3235 } else {
Chris@199 3236 text += tr("dB:\t%1").arg(dbMinString);
Chris@25 3237 }
Chris@38 3238 if (phaseMin != phaseMax) {
Chris@38 3239 text += tr("\nPhase:\t%1 - %2").arg(phaseMin).arg(phaseMax);
Chris@38 3240 } else {
Chris@38 3241 text += tr("\nPhase:\t%1").arg(phaseMin);
Chris@38 3242 }
Chris@25 3243 }
Chris@25 3244
Chris@25 3245 return text;
Chris@0 3246 }
Chris@25 3247
Chris@0 3248 int
Chris@40 3249 SpectrogramLayer::getColourScaleWidth(QPainter &paint) const
Chris@40 3250 {
Chris@40 3251 int cw;
Chris@40 3252
Chris@119 3253 cw = paint.fontMetrics().width("-80dB");
Chris@119 3254
Chris@40 3255 return cw;
Chris@40 3256 }
Chris@40 3257
Chris@40 3258 int
Chris@607 3259 SpectrogramLayer::getVerticalScaleWidth(View *, bool detailed, QPainter &paint) const
Chris@0 3260 {
Chris@0 3261 if (!m_model || !m_model->isOK()) return 0;
Chris@0 3262
Chris@607 3263 int cw = 0;
Chris@607 3264 if (detailed) cw = getColourScaleWidth(paint);
Chris@40 3265
Chris@0 3266 int tw = paint.fontMetrics().width(QString("%1")
Chris@0 3267 .arg(m_maxFrequency > 0 ?
Chris@0 3268 m_maxFrequency - 1 :
Chris@0 3269 m_model->getSampleRate() / 2));
Chris@0 3270
Chris@234 3271 int fw = paint.fontMetrics().width(tr("43Hz"));
Chris@0 3272 if (tw < fw) tw = fw;
Chris@40 3273
Chris@40 3274 int tickw = (m_frequencyScale == LogFrequencyScale ? 10 : 4);
Chris@0 3275
Chris@40 3276 return cw + tickw + tw + 13;
Chris@0 3277 }
Chris@0 3278
Chris@0 3279 void
Chris@607 3280 SpectrogramLayer::paintVerticalScale(View *v, bool detailed, QPainter &paint, QRect rect) const
Chris@0 3281 {
Chris@0 3282 if (!m_model || !m_model->isOK()) {
Chris@0 3283 return;
Chris@0 3284 }
Chris@0 3285
Chris@382 3286 Profiler profiler("SpectrogramLayer::paintVerticalScale");
Chris@122 3287
Chris@120 3288 //!!! cache this?
Chris@120 3289
Chris@0 3290 int h = rect.height(), w = rect.width();
Chris@0 3291
Chris@40 3292 int tickw = (m_frequencyScale == LogFrequencyScale ? 10 : 4);
Chris@40 3293 int pkw = (m_frequencyScale == LogFrequencyScale ? 10 : 0);
Chris@40 3294
Chris@107 3295 size_t bins = m_fftSize / 2;
Chris@0 3296 int sr = m_model->getSampleRate();
Chris@0 3297
Chris@0 3298 if (m_maxFrequency > 0) {
Chris@107 3299 bins = int((double(m_maxFrequency) * m_fftSize) / sr + 0.1);
Chris@107 3300 if (bins > m_fftSize / 2) bins = m_fftSize / 2;
Chris@0 3301 }
Chris@0 3302
Chris@607 3303 int cw = 0;
Chris@607 3304
Chris@607 3305 if (detailed) cw = getColourScaleWidth(paint);
Chris@119 3306 int cbw = paint.fontMetrics().width("dB");
Chris@40 3307
Chris@0 3308 int py = -1;
Chris@0 3309 int textHeight = paint.fontMetrics().height();
Chris@0 3310 int toff = -textHeight + paint.fontMetrics().ascent() + 2;
Chris@0 3311
Chris@607 3312 if (detailed && (h > textHeight * 3 + 10)) {
Chris@119 3313
Chris@119 3314 int topLines = 2;
Chris@119 3315 if (m_colourScale == PhaseColourScale) topLines = 1;
Chris@119 3316
Chris@119 3317 int ch = h - textHeight * (topLines + 1) - 8;
Chris@119 3318 // paint.drawRect(4, textHeight + 4, cw - 1, ch + 1);
Chris@119 3319 paint.drawRect(4 + cw - cbw, textHeight * topLines + 4, cbw - 1, ch + 1);
Chris@40 3320
Chris@40 3321 QString top, bottom;
Chris@119 3322 float min = m_viewMags[v].getMin();
Chris@119 3323 float max = m_viewMags[v].getMax();
Chris@119 3324
Chris@119 3325 float dBmin = AudioLevel::multiplier_to_dB(min);
Chris@119 3326 float dBmax = AudioLevel::multiplier_to_dB(max);
Chris@119 3327
Chris@120 3328 if (dBmax < -60.f) dBmax = -60.f;
Chris@120 3329 else top = QString("%1").arg(lrintf(dBmax));
Chris@120 3330
Chris@120 3331 if (dBmin < dBmax - 60.f) dBmin = dBmax - 60.f;
Chris@119 3332 bottom = QString("%1").arg(lrintf(dBmin));
Chris@119 3333
Chris@119 3334 //!!! & phase etc
Chris@119 3335
Chris@119 3336 if (m_colourScale != PhaseColourScale) {
Chris@119 3337 paint.drawText((cw + 6 - paint.fontMetrics().width("dBFS")) / 2,
Chris@119 3338 2 + textHeight + toff, "dBFS");
Chris@119 3339 }
Chris@119 3340
Chris@119 3341 // paint.drawText((cw + 6 - paint.fontMetrics().width(top)) / 2,
Chris@119 3342 paint.drawText(3 + cw - cbw - paint.fontMetrics().width(top),
Chris@119 3343 2 + textHeight * topLines + toff + textHeight/2, top);
Chris@119 3344
Chris@119 3345 paint.drawText(3 + cw - cbw - paint.fontMetrics().width(bottom),
Chris@119 3346 h + toff - 3 - textHeight/2, bottom);
Chris@40 3347
Chris@40 3348 paint.save();
Chris@40 3349 paint.setBrush(Qt::NoBrush);
Chris@119 3350
Chris@119 3351 int lasty = 0;
Chris@119 3352 int lastdb = 0;
Chris@119 3353
Chris@40 3354 for (int i = 0; i < ch; ++i) {
Chris@119 3355
Chris@119 3356 float dBval = dBmin + (((dBmax - dBmin) * i) / (ch - 1));
Chris@119 3357 int idb = int(dBval);
Chris@119 3358
Chris@119 3359 float value = AudioLevel::dB_to_multiplier(dBval);
Chris@119 3360 int colour = getDisplayValue(v, value * m_gain);
Chris@210 3361
Chris@197 3362 paint.setPen(m_palette.getColour(colour));
Chris@119 3363
Chris@119 3364 int y = textHeight * topLines + 4 + ch - i;
Chris@119 3365
Chris@119 3366 paint.drawLine(5 + cw - cbw, y, cw + 2, y);
Chris@119 3367
Chris@119 3368 if (i == 0) {
Chris@119 3369 lasty = y;
Chris@119 3370 lastdb = idb;
Chris@119 3371 } else if (i < ch - paint.fontMetrics().ascent() &&
Chris@120 3372 idb != lastdb &&
Chris@119 3373 ((abs(y - lasty) > textHeight &&
Chris@119 3374 idb % 10 == 0) ||
Chris@119 3375 (abs(y - lasty) > paint.fontMetrics().ascent() &&
Chris@119 3376 idb % 5 == 0))) {
Chris@287 3377 paint.setPen(v->getBackground());
Chris@119 3378 QString text = QString("%1").arg(idb);
Chris@119 3379 paint.drawText(3 + cw - cbw - paint.fontMetrics().width(text),
Chris@119 3380 y + toff + textHeight/2, text);
Chris@287 3381 paint.setPen(v->getForeground());
Chris@119 3382 paint.drawLine(5 + cw - cbw, y, 8 + cw - cbw, y);
Chris@119 3383 lasty = y;
Chris@119 3384 lastdb = idb;
Chris@119 3385 }
Chris@40 3386 }
Chris@40 3387 paint.restore();
Chris@40 3388 }
Chris@40 3389
Chris@40 3390 paint.drawLine(cw + 7, 0, cw + 7, h);
Chris@40 3391
Chris@0 3392 int bin = -1;
Chris@0 3393
Chris@44 3394 for (int y = 0; y < v->height(); ++y) {
Chris@0 3395
Chris@0 3396 float q0, q1;
Chris@44 3397 if (!getYBinRange(v, v->height() - y, q0, q1)) continue;
Chris@0 3398
Chris@0 3399 int vy;
Chris@0 3400
Chris@0 3401 if (int(q0) > bin) {
Chris@0 3402 vy = y;
Chris@0 3403 bin = int(q0);
Chris@0 3404 } else {
Chris@0 3405 continue;
Chris@0 3406 }
Chris@0 3407
Chris@107 3408 int freq = (sr * bin) / m_fftSize;
Chris@0 3409
Chris@0 3410 if (py >= 0 && (vy - py) < textHeight - 1) {
Chris@40 3411 if (m_frequencyScale == LinearFrequencyScale) {
Chris@40 3412 paint.drawLine(w - tickw, h - vy, w, h - vy);
Chris@40 3413 }
Chris@0 3414 continue;
Chris@0 3415 }
Chris@0 3416
Chris@0 3417 QString text = QString("%1").arg(freq);
Chris@234 3418 if (bin == 1) text = tr("%1Hz").arg(freq); // bin 0 is DC
Chris@40 3419 paint.drawLine(cw + 7, h - vy, w - pkw - 1, h - vy);
Chris@0 3420
Chris@0 3421 if (h - vy - textHeight >= -2) {
Chris@40 3422 int tx = w - 3 - paint.fontMetrics().width(text) - std::max(tickw, pkw);
Chris@0 3423 paint.drawText(tx, h - vy + toff, text);
Chris@0 3424 }
Chris@0 3425
Chris@0 3426 py = vy;
Chris@0 3427 }
Chris@40 3428
Chris@40 3429 if (m_frequencyScale == LogFrequencyScale) {
Chris@40 3430
Chris@277 3431 // piano keyboard
Chris@277 3432
Chris@690 3433 PianoScale().paintPianoVertical
Chris@690 3434 (v, paint, QRect(w - pkw - 1, 0, pkw, h),
Chris@690 3435 getEffectiveMinFrequency(), getEffectiveMaxFrequency());
Chris@40 3436 }
Chris@608 3437
Chris@608 3438 m_haveDetailedScale = detailed;
Chris@0 3439 }
Chris@0 3440
Chris@187 3441 class SpectrogramRangeMapper : public RangeMapper
Chris@187 3442 {
Chris@187 3443 public:
Chris@248 3444 SpectrogramRangeMapper(int sr, int /* fftsize */) :
Chris@187 3445 m_dist(float(sr) / 2),
Chris@187 3446 m_s2(sqrtf(sqrtf(2))) { }
Chris@187 3447 ~SpectrogramRangeMapper() { }
Chris@187 3448
Chris@187 3449 virtual int getPositionForValue(float value) const {
Chris@187 3450
Chris@187 3451 float dist = m_dist;
Chris@187 3452
Chris@187 3453 int n = 0;
Chris@187 3454
Chris@187 3455 while (dist > (value + 0.00001) && dist > 0.1f) {
Chris@187 3456 dist /= m_s2;
Chris@187 3457 ++n;
Chris@187 3458 }
Chris@187 3459
Chris@187 3460 return n;
Chris@187 3461 }
Chris@187 3462
Chris@187 3463 virtual float getValueForPosition(int position) const {
Chris@187 3464
Chris@187 3465 // Vertical zoom step 0 shows the entire range from DC ->
Chris@187 3466 // Nyquist frequency. Step 1 shows 2^(1/4) of the range of
Chris@187 3467 // step 0, and so on until the visible range is smaller than
Chris@187 3468 // the frequency step between bins at the current fft size.
Chris@187 3469
Chris@187 3470 float dist = m_dist;
Chris@187 3471
Chris@187 3472 int n = 0;
Chris@187 3473 while (n < position) {
Chris@187 3474 dist /= m_s2;
Chris@187 3475 ++n;
Chris@187 3476 }
Chris@187 3477
Chris@187 3478 return dist;
Chris@187 3479 }
Chris@187 3480
Chris@187 3481 virtual QString getUnit() const { return "Hz"; }
Chris@187 3482
Chris@187 3483 protected:
Chris@187 3484 float m_dist;
Chris@187 3485 float m_s2;
Chris@187 3486 };
Chris@187 3487
Chris@133 3488 int
Chris@133 3489 SpectrogramLayer::getVerticalZoomSteps(int &defaultStep) const
Chris@133 3490 {
Chris@135 3491 if (!m_model) return 0;
Chris@187 3492
Chris@187 3493 int sr = m_model->getSampleRate();
Chris@187 3494
Chris@187 3495 SpectrogramRangeMapper mapper(sr, m_fftSize);
Chris@187 3496
Chris@187 3497 // int maxStep = mapper.getPositionForValue((float(sr) / m_fftSize) + 0.001);
Chris@187 3498 int maxStep = mapper.getPositionForValue(0);
Chris@187 3499 int minStep = mapper.getPositionForValue(float(sr) / 2);
Chris@250 3500
Chris@250 3501 size_t initialMax = m_initialMaxFrequency;
Chris@250 3502 if (initialMax == 0) initialMax = sr / 2;
Chris@250 3503
Chris@250 3504 defaultStep = mapper.getPositionForValue(initialMax) - minStep;
Chris@250 3505
Chris@587 3506 // SVDEBUG << "SpectrogramLayer::getVerticalZoomSteps: " << maxStep - minStep << " (" << maxStep <<"-" << minStep << "), default is " << defaultStep << " (from initial max freq " << initialMax << ")" << endl;
Chris@187 3507
Chris@187 3508 return maxStep - minStep;
Chris@133 3509 }
Chris@133 3510
Chris@133 3511 int
Chris@133 3512 SpectrogramLayer::getCurrentVerticalZoomStep() const
Chris@133 3513 {
Chris@133 3514 if (!m_model) return 0;
Chris@133 3515
Chris@133 3516 float dmin, dmax;
Chris@133 3517 getDisplayExtents(dmin, dmax);
Chris@133 3518
Chris@187 3519 SpectrogramRangeMapper mapper(m_model->getSampleRate(), m_fftSize);
Chris@187 3520 int n = mapper.getPositionForValue(dmax - dmin);
Chris@587 3521 // SVDEBUG << "SpectrogramLayer::getCurrentVerticalZoomStep: " << n << endl;
Chris@133 3522 return n;
Chris@133 3523 }
Chris@133 3524
Chris@133 3525 void
Chris@133 3526 SpectrogramLayer::setVerticalZoomStep(int step)
Chris@133 3527 {
Chris@187 3528 if (!m_model) return;
Chris@187 3529
Chris@253 3530 float dmin = m_minFrequency, dmax = m_maxFrequency;
Chris@253 3531 // getDisplayExtents(dmin, dmax);
Chris@253 3532
Chris@682 3533 // cerr << "current range " << dmin << " -> " << dmax << ", range " << dmax-dmin << ", mid " << (dmax + dmin)/2 << endl;
Chris@133 3534
Chris@133 3535 int sr = m_model->getSampleRate();
Chris@187 3536 SpectrogramRangeMapper mapper(sr, m_fftSize);
Chris@253 3537 float newdist = mapper.getValueForPosition(step);
Chris@253 3538
Chris@253 3539 float newmin, newmax;
Chris@253 3540
Chris@253 3541 if (m_frequencyScale == LogFrequencyScale) {
Chris@253 3542
Chris@253 3543 // need to pick newmin and newmax such that
Chris@253 3544 //
Chris@253 3545 // (log(newmin) + log(newmax)) / 2 == logmid
Chris@253 3546 // and
Chris@253 3547 // newmax - newmin = newdist
Chris@253 3548 //
Chris@253 3549 // so log(newmax - newdist) + log(newmax) == 2logmid
Chris@253 3550 // log(newmax(newmax - newdist)) == 2logmid
Chris@253 3551 // newmax.newmax - newmax.newdist == exp(2logmid)
Chris@253 3552 // newmax^2 + (-newdist)newmax + -exp(2logmid) == 0
Chris@253 3553 // quadratic with a = 1, b = -newdist, c = -exp(2logmid), all known
Chris@253 3554 //
Chris@253 3555 // positive root
Chris@253 3556 // newmax = (newdist + sqrt(newdist^2 + 4exp(2logmid))) / 2
Chris@253 3557 //
Chris@253 3558 // but logmid = (log(dmin) + log(dmax)) / 2
Chris@253 3559 // so exp(2logmid) = exp(log(dmin) + log(dmax))
Chris@253 3560 // = exp(log(dmin.dmax))
Chris@253 3561 // = dmin.dmax
Chris@253 3562 // so newmax = (newdist + sqrtf(newdist^2 + 4dmin.dmax)) / 2
Chris@253 3563
Chris@253 3564 newmax = (newdist + sqrtf(newdist*newdist + 4*dmin*dmax)) / 2;
Chris@253 3565 newmin = newmax - newdist;
Chris@253 3566
Chris@682 3567 // cerr << "newmin = " << newmin << ", newmax = " << newmax << endl;
Chris@253 3568
Chris@253 3569 } else {
Chris@253 3570 float dmid = (dmax + dmin) / 2;
Chris@253 3571 newmin = dmid - newdist / 2;
Chris@253 3572 newmax = dmid + newdist / 2;
Chris@253 3573 }
Chris@187 3574
Chris@187 3575 float mmin, mmax;
Chris@187 3576 mmin = 0;
Chris@187 3577 mmax = float(sr) / 2;
Chris@133 3578
Chris@187 3579 if (newmin < mmin) {
Chris@187 3580 newmax += (mmin - newmin);
Chris@187 3581 newmin = mmin;
Chris@187 3582 }
Chris@187 3583 if (newmax > mmax) {
Chris@187 3584 newmax = mmax;
Chris@187 3585 }
Chris@133 3586
Chris@587 3587 // SVDEBUG << "SpectrogramLayer::setVerticalZoomStep: " << step << ": " << newmin << " -> " << newmax << " (range " << newdist << ")" << endl;
Chris@253 3588
Chris@253 3589 setMinFrequency(lrintf(newmin));
Chris@253 3590 setMaxFrequency(lrintf(newmax));
Chris@187 3591 }
Chris@187 3592
Chris@187 3593 RangeMapper *
Chris@187 3594 SpectrogramLayer::getNewVerticalZoomRangeMapper() const
Chris@187 3595 {
Chris@187 3596 if (!m_model) return 0;
Chris@187 3597 return new SpectrogramRangeMapper(m_model->getSampleRate(), m_fftSize);
Chris@133 3598 }
Chris@133 3599
Chris@273 3600 void
Chris@273 3601 SpectrogramLayer::updateMeasureRectYCoords(View *v, const MeasureRect &r) const
Chris@273 3602 {
Chris@273 3603 int y0 = 0;
Chris@273 3604 if (r.startY > 0.0) y0 = getYForFrequency(v, r.startY);
Chris@273 3605
Chris@273 3606 int y1 = y0;
Chris@273 3607 if (r.endY > 0.0) y1 = getYForFrequency(v, r.endY);
Chris@273 3608
Chris@587 3609 // SVDEBUG << "SpectrogramLayer::updateMeasureRectYCoords: start " << r.startY << " -> " << y0 << ", end " << r.endY << " -> " << y1 << endl;
Chris@273 3610
Chris@273 3611 r.pixrect = QRect(r.pixrect.x(), y0, r.pixrect.width(), y1 - y0);
Chris@273 3612 }
Chris@273 3613
Chris@273 3614 void
Chris@273 3615 SpectrogramLayer::setMeasureRectYCoord(View *v, MeasureRect &r, bool start, int y) const
Chris@273 3616 {
Chris@273 3617 if (start) {
Chris@273 3618 r.startY = getFrequencyForY(v, y);
Chris@273 3619 r.endY = r.startY;
Chris@273 3620 } else {
Chris@273 3621 r.endY = getFrequencyForY(v, y);
Chris@273 3622 }
Chris@587 3623 // SVDEBUG << "SpectrogramLayer::setMeasureRectYCoord: start " << r.startY << " <- " << y << ", end " << r.endY << " <- " << y << endl;
Chris@273 3624
Chris@273 3625 }
Chris@273 3626
Chris@316 3627 void
Chris@316 3628 SpectrogramLayer::toXml(QTextStream &stream,
Chris@316 3629 QString indent, QString extraAttributes) const
Chris@6 3630 {
Chris@6 3631 QString s;
Chris@6 3632
Chris@6 3633 s += QString("channel=\"%1\" "
Chris@6 3634 "windowSize=\"%2\" "
Chris@153 3635 "windowHopLevel=\"%3\" "
Chris@153 3636 "gain=\"%4\" "
Chris@153 3637 "threshold=\"%5\" ")
Chris@6 3638 .arg(m_channel)
Chris@6 3639 .arg(m_windowSize)
Chris@97 3640 .arg(m_windowHopLevel)
Chris@37 3641 .arg(m_gain)
Chris@37 3642 .arg(m_threshold);
Chris@37 3643
Chris@37 3644 s += QString("minFrequency=\"%1\" "
Chris@37 3645 "maxFrequency=\"%2\" "
Chris@37 3646 "colourScale=\"%3\" "
Chris@37 3647 "colourScheme=\"%4\" "
Chris@37 3648 "colourRotation=\"%5\" "
Chris@37 3649 "frequencyScale=\"%6\" "
Chris@37 3650 "binDisplay=\"%7\" "
Chris@153 3651 "normalizeColumns=\"%8\" "
Chris@153 3652 "normalizeVisibleArea=\"%9\"")
Chris@37 3653 .arg(m_minFrequency)
Chris@6 3654 .arg(m_maxFrequency)
Chris@6 3655 .arg(m_colourScale)
Chris@197 3656 .arg(m_colourMap)
Chris@37 3657 .arg(m_colourRotation)
Chris@35 3658 .arg(m_frequencyScale)
Chris@37 3659 .arg(m_binDisplay)
Chris@153 3660 .arg(m_normalizeColumns ? "true" : "false")
Chris@153 3661 .arg(m_normalizeVisibleArea ? "true" : "false");
Chris@6 3662
Chris@316 3663 Layer::toXml(stream, indent, extraAttributes + " " + s);
Chris@6 3664 }
Chris@6 3665
Chris@11 3666 void
Chris@11 3667 SpectrogramLayer::setProperties(const QXmlAttributes &attributes)
Chris@11 3668 {
Chris@11 3669 bool ok = false;
Chris@11 3670
Chris@11 3671 int channel = attributes.value("channel").toInt(&ok);
Chris@11 3672 if (ok) setChannel(channel);
Chris@11 3673
Chris@11 3674 size_t windowSize = attributes.value("windowSize").toUInt(&ok);
Chris@11 3675 if (ok) setWindowSize(windowSize);
Chris@11 3676
Chris@97 3677 size_t windowHopLevel = attributes.value("windowHopLevel").toUInt(&ok);
Chris@97 3678 if (ok) setWindowHopLevel(windowHopLevel);
Chris@97 3679 else {
Chris@97 3680 size_t windowOverlap = attributes.value("windowOverlap").toUInt(&ok);
Chris@97 3681 // a percentage value
Chris@97 3682 if (ok) {
Chris@97 3683 if (windowOverlap == 0) setWindowHopLevel(0);
Chris@97 3684 else if (windowOverlap == 25) setWindowHopLevel(1);
Chris@97 3685 else if (windowOverlap == 50) setWindowHopLevel(2);
Chris@97 3686 else if (windowOverlap == 75) setWindowHopLevel(3);
Chris@97 3687 else if (windowOverlap == 90) setWindowHopLevel(4);
Chris@97 3688 }
Chris@97 3689 }
Chris@11 3690
Chris@11 3691 float gain = attributes.value("gain").toFloat(&ok);
Chris@11 3692 if (ok) setGain(gain);
Chris@11 3693
Chris@37 3694 float threshold = attributes.value("threshold").toFloat(&ok);
Chris@37 3695 if (ok) setThreshold(threshold);
Chris@37 3696
Chris@37 3697 size_t minFrequency = attributes.value("minFrequency").toUInt(&ok);
Chris@187 3698 if (ok) {
Chris@587 3699 SVDEBUG << "SpectrogramLayer::setProperties: setting min freq to " << minFrequency << endl;
Chris@187 3700 setMinFrequency(minFrequency);
Chris@187 3701 }
Chris@37 3702
Chris@11 3703 size_t maxFrequency = attributes.value("maxFrequency").toUInt(&ok);
Chris@187 3704 if (ok) {
Chris@587 3705 SVDEBUG << "SpectrogramLayer::setProperties: setting max freq to " << maxFrequency << endl;
Chris@187 3706 setMaxFrequency(maxFrequency);
Chris@187 3707 }
Chris@11 3708
Chris@11 3709 ColourScale colourScale = (ColourScale)
Chris@11 3710 attributes.value("colourScale").toInt(&ok);
Chris@11 3711 if (ok) setColourScale(colourScale);
Chris@11 3712
Chris@197 3713 int colourMap = attributes.value("colourScheme").toInt(&ok);
Chris@197 3714 if (ok) setColourMap(colourMap);
Chris@11 3715
Chris@37 3716 int colourRotation = attributes.value("colourRotation").toInt(&ok);
Chris@37 3717 if (ok) setColourRotation(colourRotation);
Chris@37 3718
Chris@11 3719 FrequencyScale frequencyScale = (FrequencyScale)
Chris@11 3720 attributes.value("frequencyScale").toInt(&ok);
Chris@11 3721 if (ok) setFrequencyScale(frequencyScale);
Chris@35 3722
Chris@37 3723 BinDisplay binDisplay = (BinDisplay)
Chris@37 3724 attributes.value("binDisplay").toInt(&ok);
Chris@37 3725 if (ok) setBinDisplay(binDisplay);
Chris@36 3726
Chris@36 3727 bool normalizeColumns =
Chris@36 3728 (attributes.value("normalizeColumns").trimmed() == "true");
Chris@36 3729 setNormalizeColumns(normalizeColumns);
Chris@153 3730
Chris@153 3731 bool normalizeVisibleArea =
Chris@153 3732 (attributes.value("normalizeVisibleArea").trimmed() == "true");
Chris@153 3733 setNormalizeVisibleArea(normalizeVisibleArea);
Chris@11 3734 }
Chris@11 3735