annotate layer/SpectrogramLayer.cpp @ 561:aced8ec09bc8

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