annotate layer/SpectrogramLayer.cpp @ 607:5b72899d692b

Give a dedicated key to toggling the centre line, and move it out of the overlay level setting -- reducing number of overlay levels to 3. Introduce two distinct vertical scale types (so that we can hide the spectrogram colour scale part easily)
author Chris Cannam
date Mon, 30 Jan 2012 16:01:59 +0000
parents 4806715f7a19
children 0dba6a391760
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@587 581 SVDEBUG << "SpectrogramLayer::invalidateImageCaches("
Chris@391 582 << startFrame << ", " << endFrame << "): view range is "
Chris@391 583 << v->getStartFrame() << ", " << v->getEndFrame()
Chris@585 584 << 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@587 601 SVDEBUG << "clipping from 0 to " << x-1 << 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@587 618 SVDEBUG << "clipping from " << x+1 << " to " << v->width()
Chris@585 619 << 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@587 641 SVDEBUG << "SpectrogramLayer::preferenceChanged(" << name << ")" << 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@587 767 // SVDEBUG << "SpectrogramLayer::setGain(" << gain << ") (my gain is now "
Chris@585 768 // << m_gain << ")" << 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@587 808 // SVDEBUG << "SpectrogramLayer::setMinFrequency: " << mf << 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@587 829 // SVDEBUG << "SpectrogramLayer::setMaxFrequency: " << mf << 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@587 954 SVDEBUG << "SpectrogramLayer::setNormalizeVisibleArea(" << n
Chris@585 955 << ") (from " << m_normalizeVisibleArea << ")" << 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@587 978 SVDEBUG << "SpectrogramLayer::setLayerDormant(" << dormant << ")"
Chris@585 979 << 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@587 1023 SVDEBUG << "SpectrogramLayer::cacheInvalid()" << 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@587 1034 SVDEBUG << "SpectrogramLayer::cacheInvalid(" << from << ", " << to << ")" << 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@587 1049 SVDEBUG << "SpectrogramLayer::fillTimerTimedOut: have " << m_fftModels.size() << " FFT models associated with views" << 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@587 1063 SVDEBUG << "SpectrogramLayer::fillTimerTimedOut: extent for " << model << ": " << fill << ", last " << lastFill << ", total " << m_model->getEndFrame() << 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@587 1632 SVDEBUG << "SpectrogramLayer::getFFTModel(" << v << "): Found null model" << 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@587 1638 SVDEBUG << "SpectrogramLayer::getFFTModel(" << v << "): Found a model with the wrong height (" << m_fftModels[v].first->getHeight() << ", wanted " << (fftSize / 2 + 1) << ")" << 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@587 1646 SVDEBUG << "SpectrogramLayer::getFFTModel(" << v << "): Found a good model of height " << m_fftModels[v].first->getHeight() << 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@587 1767 // SVDEBUG << "SpectrogramLayer::updateViewMagnitudes: x0 = " << x0 << ", x1 = " << x1 << ", s00 = " << s00 << ", s11 = " << s11 << " s0 = " << s0 << ", s1 = " << s1 << 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@587 1780 SVDEBUG << "SpectrogramLayer::updateViewMagnitudes returning from cols "
Chris@585 1781 << s0 << " -> " << s1 << " inclusive" << 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@587 1805 SVDEBUG << "SpectrogramLayer::paint(): m_model is " << m_model << ", zoom level is " << v->getZoomLevel() << ", m_updateTimer " << m_updateTimer << 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@587 1819 SVDEBUG << "SpectrogramLayer::paint(): Layer is dormant, making it undormant again" << 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@587 1840 SVDEBUG << "SpectrogramLayer::paint(): image cache valid area " << cache.
Chris@585 1841
Chris@585 1842 validArea.x() << ", " << cache.validArea.y() << ", " << cache.validArea.width() << "x" << cache.validArea.height() << endl;
Chris@95 1843 #endif
Chris@95 1844
Chris@248 1845 #ifdef DEBUG_SPECTROGRAM_REPAINT
Chris@0 1846 bool stillCacheing = (m_updateTimer != 0);
Chris@587 1847 SVDEBUG << "SpectrogramLayer::paint(): Still cacheing = " << stillCacheing << 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@587 2205 SVDEBUG << "*** NOTE: w == 0" << 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@587 2366 SVDEBUG << "Recreating image cache: width = " << v->width()
Chris@585 2367 << ", height = " << h << 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@587 2374 SVDEBUG << "Painting " << w << "x" << h
Chris@331 2375 << " from draw buffer at " << 0 << "," << 0
Chris@480 2376 << " to " << w << "x" << h << " on cache at "
Chris@585 2377 << x0 << "," << 0 << 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@587 2386 SVDEBUG << "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@587 2392 // SVDEBUG << "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@587 2401 SVDEBUG << "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@587 2422 SVDEBUG << "Painting " << pr.width() << "x" << pr.height()
Chris@337 2423 << " from cache at " << pr.x() << "," << pr.y()
Chris@585 2424 << " to window" << 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@587 2442 SVDEBUG << "SpectrogramLayer::paint() updating left (0, "
Chris@585 2443 << cache.validArea.x() << ")" << 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@587 2451 SVDEBUG << "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@585 2456 << ")" << 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@587 2475 SVDEBUG << "SpectrogramLayer::paint() returning" << 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@587 2710 SVDEBUG << "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@587 2721 SVDEBUG << "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@587 2903 SVDEBUG << "SpectrogramLayer::getCompletion: completion = " << completion << endl;
Chris@224 2904 #endif
Chris@0 2905 return completion;
Chris@0 2906 }
Chris@0 2907
Chris@583 2908 QString
Chris@583 2909 SpectrogramLayer::getError(View *v) const
Chris@583 2910 {
Chris@583 2911 if (m_fftModels.find(v) == m_fftModels.end()) return "";
Chris@583 2912 return m_fftModels[v].first->getError();
Chris@583 2913 }
Chris@583 2914
Chris@28 2915 bool
Chris@101 2916 SpectrogramLayer::getValueExtents(float &min, float &max,
Chris@101 2917 bool &logarithmic, QString &unit) const
Chris@79 2918 {
Chris@133 2919 if (!m_model) return false;
Chris@133 2920
Chris@133 2921 int sr = m_model->getSampleRate();
Chris@133 2922 min = float(sr) / m_fftSize;
Chris@133 2923 max = float(sr) / 2;
Chris@133 2924
Chris@101 2925 logarithmic = (m_frequencyScale == LogFrequencyScale);
Chris@79 2926 unit = "Hz";
Chris@79 2927 return true;
Chris@79 2928 }
Chris@79 2929
Chris@79 2930 bool
Chris@101 2931 SpectrogramLayer::getDisplayExtents(float &min, float &max) const
Chris@101 2932 {
Chris@101 2933 min = getEffectiveMinFrequency();
Chris@101 2934 max = getEffectiveMaxFrequency();
Chris@253 2935
Chris@587 2936 // SVDEBUG << "SpectrogramLayer::getDisplayExtents: " << min << "->" << max << endl;
Chris@101 2937 return true;
Chris@101 2938 }
Chris@101 2939
Chris@101 2940 bool
Chris@120 2941 SpectrogramLayer::setDisplayExtents(float min, float max)
Chris@120 2942 {
Chris@120 2943 if (!m_model) return false;
Chris@187 2944
Chris@587 2945 // SVDEBUG << "SpectrogramLayer::setDisplayExtents: " << min << "->" << max << endl;
Chris@187 2946
Chris@120 2947 if (min < 0) min = 0;
Chris@120 2948 if (max > m_model->getSampleRate()/2) max = m_model->getSampleRate()/2;
Chris@120 2949
Chris@120 2950 size_t minf = lrintf(min);
Chris@120 2951 size_t maxf = lrintf(max);
Chris@120 2952
Chris@120 2953 if (m_minFrequency == minf && m_maxFrequency == maxf) return true;
Chris@120 2954
Chris@478 2955 invalidateImageCaches();
Chris@120 2956 invalidateMagnitudes();
Chris@120 2957
Chris@120 2958 m_minFrequency = minf;
Chris@120 2959 m_maxFrequency = maxf;
Chris@120 2960
Chris@120 2961 emit layerParametersChanged();
Chris@120 2962
Chris@133 2963 int vs = getCurrentVerticalZoomStep();
Chris@133 2964 if (vs != m_lastEmittedZoomStep) {
Chris@133 2965 emit verticalZoomChanged();
Chris@133 2966 m_lastEmittedZoomStep = vs;
Chris@133 2967 }
Chris@133 2968
Chris@120 2969 return true;
Chris@120 2970 }
Chris@120 2971
Chris@120 2972 bool
Chris@267 2973 SpectrogramLayer::getYScaleValue(const View *v, int y,
Chris@261 2974 float &value, QString &unit) const
Chris@261 2975 {
Chris@261 2976 value = getFrequencyForY(v, y);
Chris@261 2977 unit = "Hz";
Chris@261 2978 return true;
Chris@261 2979 }
Chris@261 2980
Chris@261 2981 bool
Chris@248 2982 SpectrogramLayer::snapToFeatureFrame(View *, int &frame,
Chris@28 2983 size_t &resolution,
Chris@28 2984 SnapType snap) const
Chris@13 2985 {
Chris@13 2986 resolution = getWindowIncrement();
Chris@28 2987 int left = (frame / resolution) * resolution;
Chris@28 2988 int right = left + resolution;
Chris@28 2989
Chris@28 2990 switch (snap) {
Chris@28 2991 case SnapLeft: frame = left; break;
Chris@28 2992 case SnapRight: frame = right; break;
Chris@28 2993 case SnapNearest:
Chris@28 2994 case SnapNeighbouring:
Chris@28 2995 if (frame - left > right - frame) frame = right;
Chris@28 2996 else frame = left;
Chris@28 2997 break;
Chris@28 2998 }
Chris@28 2999
Chris@28 3000 return true;
Chris@28 3001 }
Chris@13 3002
Chris@283 3003 void
Chris@283 3004 SpectrogramLayer::measureDoubleClick(View *v, QMouseEvent *e)
Chris@283 3005 {
Chris@478 3006 ImageCache &cache = m_imageCaches[v];
Chris@478 3007
Chris@478 3008 std::cerr << "cache width: " << cache.image.width() << ", height: "
Chris@478 3009 << cache.image.height() << std::endl;
Chris@478 3010
Chris@478 3011 QImage image = cache.image;
Chris@283 3012
Chris@283 3013 ImageRegionFinder finder;
Chris@283 3014 QRect rect = finder.findRegionExtents(&image, e->pos());
Chris@283 3015 if (rect.isValid()) {
Chris@283 3016 MeasureRect mr;
Chris@283 3017 setMeasureRectFromPixrect(v, mr, rect);
Chris@283 3018 CommandHistory::getInstance()->addCommand
Chris@283 3019 (new AddMeasurementRectCommand(this, mr));
Chris@283 3020 }
Chris@283 3021 }
Chris@283 3022
Chris@77 3023 bool
Chris@264 3024 SpectrogramLayer::getCrosshairExtents(View *v, QPainter &paint,
Chris@77 3025 QPoint cursorPos,
Chris@77 3026 std::vector<QRect> &extents) const
Chris@77 3027 {
Chris@77 3028 QRect vertical(cursorPos.x() - 12, 0, 12, v->height());
Chris@77 3029 extents.push_back(vertical);
Chris@77 3030
Chris@77 3031 QRect horizontal(0, cursorPos.y(), cursorPos.x(), 1);
Chris@77 3032 extents.push_back(horizontal);
Chris@77 3033
Chris@607 3034 int sw = getVerticalScaleWidth(v, false, paint);//!!!
Chris@264 3035
Chris@280 3036 QRect freq(sw, cursorPos.y() - paint.fontMetrics().ascent() - 2,
Chris@280 3037 paint.fontMetrics().width("123456 Hz") + 2,
Chris@280 3038 paint.fontMetrics().height());
Chris@280 3039 extents.push_back(freq);
Chris@264 3040
Chris@279 3041 QRect pitch(sw, cursorPos.y() + 2,
Chris@279 3042 paint.fontMetrics().width("C#10+50c") + 2,
Chris@279 3043 paint.fontMetrics().height());
Chris@279 3044 extents.push_back(pitch);
Chris@279 3045
Chris@280 3046 QRect rt(cursorPos.x(),
Chris@280 3047 v->height() - paint.fontMetrics().height() - 2,
Chris@280 3048 paint.fontMetrics().width("1234.567 s"),
Chris@280 3049 paint.fontMetrics().height());
Chris@280 3050 extents.push_back(rt);
Chris@280 3051
Chris@280 3052 int w(paint.fontMetrics().width("1234567890") + 2);
Chris@280 3053 QRect frame(cursorPos.x() - w - 2,
Chris@280 3054 v->height() - paint.fontMetrics().height() - 2,
Chris@280 3055 w,
Chris@280 3056 paint.fontMetrics().height());
Chris@280 3057 extents.push_back(frame);
Chris@280 3058
Chris@77 3059 return true;
Chris@77 3060 }
Chris@77 3061
Chris@77 3062 void
Chris@77 3063 SpectrogramLayer::paintCrosshairs(View *v, QPainter &paint,
Chris@77 3064 QPoint cursorPos) const
Chris@77 3065 {
Chris@77 3066 paint.save();
Chris@283 3067
Chris@607 3068 int sw = getVerticalScaleWidth(v, false, paint); //!!!
Chris@283 3069
Chris@282 3070 QFont fn = paint.font();
Chris@282 3071 if (fn.pointSize() > 8) {
Chris@282 3072 fn.setPointSize(fn.pointSize() - 1);
Chris@282 3073 paint.setFont(fn);
Chris@282 3074 }
Chris@77 3075 paint.setPen(m_crosshairColour);
Chris@77 3076
Chris@77 3077 paint.drawLine(0, cursorPos.y(), cursorPos.x() - 1, cursorPos.y());
Chris@77 3078 paint.drawLine(cursorPos.x(), 0, cursorPos.x(), v->height());
Chris@77 3079
Chris@77 3080 float fundamental = getFrequencyForY(v, cursorPos.y());
Chris@77 3081
Chris@278 3082 v->drawVisibleText(paint,
Chris@278 3083 sw + 2,
Chris@278 3084 cursorPos.y() - 2,
Chris@278 3085 QString("%1 Hz").arg(fundamental),
Chris@278 3086 View::OutlinedText);
Chris@278 3087
Chris@279 3088 if (Pitch::isFrequencyInMidiRange(fundamental)) {
Chris@279 3089 QString pitchLabel = Pitch::getPitchLabelForFrequency(fundamental);
Chris@279 3090 v->drawVisibleText(paint,
Chris@279 3091 sw + 2,
Chris@279 3092 cursorPos.y() + paint.fontMetrics().ascent() + 2,
Chris@279 3093 pitchLabel,
Chris@279 3094 View::OutlinedText);
Chris@279 3095 }
Chris@279 3096
Chris@280 3097 long frame = v->getFrameForX(cursorPos.x());
Chris@279 3098 RealTime rt = RealTime::frame2RealTime(frame, m_model->getSampleRate());
Chris@280 3099 QString rtLabel = QString("%1 s").arg(rt.toText(true).c_str());
Chris@280 3100 QString frameLabel = QString("%1").arg(frame);
Chris@280 3101 v->drawVisibleText(paint,
Chris@280 3102 cursorPos.x() - paint.fontMetrics().width(frameLabel) - 2,
Chris@280 3103 v->height() - 2,
Chris@280 3104 frameLabel,
Chris@280 3105 View::OutlinedText);
Chris@280 3106 v->drawVisibleText(paint,
Chris@280 3107 cursorPos.x() + 2,
Chris@280 3108 v->height() - 2,
Chris@280 3109 rtLabel,
Chris@280 3110 View::OutlinedText);
Chris@264 3111
Chris@77 3112 int harmonic = 2;
Chris@77 3113
Chris@77 3114 while (harmonic < 100) {
Chris@77 3115
Chris@77 3116 float hy = lrintf(getYForFrequency(v, fundamental * harmonic));
Chris@77 3117 if (hy < 0 || hy > v->height()) break;
Chris@77 3118
Chris@77 3119 int len = 7;
Chris@77 3120
Chris@77 3121 if (harmonic % 2 == 0) {
Chris@77 3122 if (harmonic % 4 == 0) {
Chris@77 3123 len = 12;
Chris@77 3124 } else {
Chris@77 3125 len = 10;
Chris@77 3126 }
Chris@77 3127 }
Chris@77 3128
Chris@77 3129 paint.drawLine(cursorPos.x() - len,
Chris@248 3130 int(hy),
Chris@77 3131 cursorPos.x(),
Chris@248 3132 int(hy));
Chris@77 3133
Chris@77 3134 ++harmonic;
Chris@77 3135 }
Chris@77 3136
Chris@77 3137 paint.restore();
Chris@77 3138 }
Chris@77 3139
Chris@25 3140 QString
Chris@44 3141 SpectrogramLayer::getFeatureDescription(View *v, QPoint &pos) const
Chris@25 3142 {
Chris@25 3143 int x = pos.x();
Chris@25 3144 int y = pos.y();
Chris@0 3145
Chris@25 3146 if (!m_model || !m_model->isOK()) return "";
Chris@0 3147
Chris@38 3148 float magMin = 0, magMax = 0;
Chris@38 3149 float phaseMin = 0, phaseMax = 0;
Chris@0 3150 float freqMin = 0, freqMax = 0;
Chris@35 3151 float adjFreqMin = 0, adjFreqMax = 0;
Chris@25 3152 QString pitchMin, pitchMax;
Chris@0 3153 RealTime rtMin, rtMax;
Chris@0 3154
Chris@38 3155 bool haveValues = false;
Chris@0 3156
Chris@44 3157 if (!getXBinSourceRange(v, x, rtMin, rtMax)) {
Chris@38 3158 return "";
Chris@38 3159 }
Chris@44 3160 if (getXYBinSourceRange(v, x, y, magMin, magMax, phaseMin, phaseMax)) {
Chris@38 3161 haveValues = true;
Chris@38 3162 }
Chris@0 3163
Chris@35 3164 QString adjFreqText = "", adjPitchText = "";
Chris@35 3165
Chris@38 3166 if (m_binDisplay == PeakFrequencies) {
Chris@35 3167
Chris@44 3168 if (!getAdjustedYBinSourceRange(v, x, y, freqMin, freqMax,
Chris@38 3169 adjFreqMin, adjFreqMax)) {
Chris@38 3170 return "";
Chris@38 3171 }
Chris@35 3172
Chris@35 3173 if (adjFreqMin != adjFreqMax) {
Chris@65 3174 adjFreqText = tr("Peak Frequency:\t%1 - %2 Hz\n")
Chris@35 3175 .arg(adjFreqMin).arg(adjFreqMax);
Chris@35 3176 } else {
Chris@65 3177 adjFreqText = tr("Peak Frequency:\t%1 Hz\n")
Chris@35 3178 .arg(adjFreqMin);
Chris@38 3179 }
Chris@38 3180
Chris@38 3181 QString pmin = Pitch::getPitchLabelForFrequency(adjFreqMin);
Chris@38 3182 QString pmax = Pitch::getPitchLabelForFrequency(adjFreqMax);
Chris@38 3183
Chris@38 3184 if (pmin != pmax) {
Chris@65 3185 adjPitchText = tr("Peak Pitch:\t%3 - %4\n").arg(pmin).arg(pmax);
Chris@38 3186 } else {
Chris@65 3187 adjPitchText = tr("Peak Pitch:\t%2\n").arg(pmin);
Chris@35 3188 }
Chris@35 3189
Chris@35 3190 } else {
Chris@35 3191
Chris@44 3192 if (!getYBinSourceRange(v, y, freqMin, freqMax)) return "";
Chris@35 3193 }
Chris@35 3194
Chris@25 3195 QString text;
Chris@25 3196
Chris@25 3197 if (rtMin != rtMax) {
Chris@25 3198 text += tr("Time:\t%1 - %2\n")
Chris@25 3199 .arg(rtMin.toText(true).c_str())
Chris@25 3200 .arg(rtMax.toText(true).c_str());
Chris@25 3201 } else {
Chris@25 3202 text += tr("Time:\t%1\n")
Chris@25 3203 .arg(rtMin.toText(true).c_str());
Chris@0 3204 }
Chris@0 3205
Chris@25 3206 if (freqMin != freqMax) {
Chris@65 3207 text += tr("%1Bin Frequency:\t%2 - %3 Hz\n%4Bin Pitch:\t%5 - %6\n")
Chris@65 3208 .arg(adjFreqText)
Chris@25 3209 .arg(freqMin)
Chris@25 3210 .arg(freqMax)
Chris@65 3211 .arg(adjPitchText)
Chris@65 3212 .arg(Pitch::getPitchLabelForFrequency(freqMin))
Chris@65 3213 .arg(Pitch::getPitchLabelForFrequency(freqMax));
Chris@65 3214 } else {
Chris@65 3215 text += tr("%1Bin Frequency:\t%2 Hz\n%3Bin Pitch:\t%4\n")
Chris@35 3216 .arg(adjFreqText)
Chris@25 3217 .arg(freqMin)
Chris@65 3218 .arg(adjPitchText)
Chris@65 3219 .arg(Pitch::getPitchLabelForFrequency(freqMin));
Chris@25 3220 }
Chris@25 3221
Chris@38 3222 if (haveValues) {
Chris@38 3223 float dbMin = AudioLevel::multiplier_to_dB(magMin);
Chris@38 3224 float dbMax = AudioLevel::multiplier_to_dB(magMax);
Chris@43 3225 QString dbMinString;
Chris@43 3226 QString dbMaxString;
Chris@43 3227 if (dbMin == AudioLevel::DB_FLOOR) {
Chris@43 3228 dbMinString = tr("-Inf");
Chris@43 3229 } else {
Chris@43 3230 dbMinString = QString("%1").arg(lrintf(dbMin));
Chris@43 3231 }
Chris@43 3232 if (dbMax == AudioLevel::DB_FLOOR) {
Chris@43 3233 dbMaxString = tr("-Inf");
Chris@43 3234 } else {
Chris@43 3235 dbMaxString = QString("%1").arg(lrintf(dbMax));
Chris@43 3236 }
Chris@25 3237 if (lrintf(dbMin) != lrintf(dbMax)) {
Chris@199 3238 text += tr("dB:\t%1 - %2").arg(dbMinString).arg(dbMaxString);
Chris@25 3239 } else {
Chris@199 3240 text += tr("dB:\t%1").arg(dbMinString);
Chris@25 3241 }
Chris@38 3242 if (phaseMin != phaseMax) {
Chris@38 3243 text += tr("\nPhase:\t%1 - %2").arg(phaseMin).arg(phaseMax);
Chris@38 3244 } else {
Chris@38 3245 text += tr("\nPhase:\t%1").arg(phaseMin);
Chris@38 3246 }
Chris@25 3247 }
Chris@25 3248
Chris@25 3249 return text;
Chris@0 3250 }
Chris@25 3251
Chris@0 3252 int
Chris@40 3253 SpectrogramLayer::getColourScaleWidth(QPainter &paint) const
Chris@40 3254 {
Chris@40 3255 int cw;
Chris@40 3256
Chris@119 3257 cw = paint.fontMetrics().width("-80dB");
Chris@119 3258
Chris@40 3259 return cw;
Chris@40 3260 }
Chris@40 3261
Chris@40 3262 int
Chris@607 3263 SpectrogramLayer::getVerticalScaleWidth(View *, bool detailed, QPainter &paint) const
Chris@0 3264 {
Chris@0 3265 if (!m_model || !m_model->isOK()) return 0;
Chris@0 3266
Chris@607 3267 int cw = 0;
Chris@607 3268 if (detailed) cw = getColourScaleWidth(paint);
Chris@40 3269
Chris@0 3270 int tw = paint.fontMetrics().width(QString("%1")
Chris@0 3271 .arg(m_maxFrequency > 0 ?
Chris@0 3272 m_maxFrequency - 1 :
Chris@0 3273 m_model->getSampleRate() / 2));
Chris@0 3274
Chris@234 3275 int fw = paint.fontMetrics().width(tr("43Hz"));
Chris@0 3276 if (tw < fw) tw = fw;
Chris@40 3277
Chris@40 3278 int tickw = (m_frequencyScale == LogFrequencyScale ? 10 : 4);
Chris@0 3279
Chris@40 3280 return cw + tickw + tw + 13;
Chris@0 3281 }
Chris@0 3282
Chris@0 3283 void
Chris@607 3284 SpectrogramLayer::paintVerticalScale(View *v, bool detailed, QPainter &paint, QRect rect) const
Chris@0 3285 {
Chris@0 3286 if (!m_model || !m_model->isOK()) {
Chris@0 3287 return;
Chris@0 3288 }
Chris@0 3289
Chris@382 3290 Profiler profiler("SpectrogramLayer::paintVerticalScale");
Chris@122 3291
Chris@120 3292 //!!! cache this?
Chris@120 3293
Chris@0 3294 int h = rect.height(), w = rect.width();
Chris@0 3295
Chris@40 3296 int tickw = (m_frequencyScale == LogFrequencyScale ? 10 : 4);
Chris@40 3297 int pkw = (m_frequencyScale == LogFrequencyScale ? 10 : 0);
Chris@40 3298
Chris@107 3299 size_t bins = m_fftSize / 2;
Chris@0 3300 int sr = m_model->getSampleRate();
Chris@0 3301
Chris@0 3302 if (m_maxFrequency > 0) {
Chris@107 3303 bins = int((double(m_maxFrequency) * m_fftSize) / sr + 0.1);
Chris@107 3304 if (bins > m_fftSize / 2) bins = m_fftSize / 2;
Chris@0 3305 }
Chris@0 3306
Chris@607 3307 int cw = 0;
Chris@607 3308
Chris@607 3309 if (detailed) cw = getColourScaleWidth(paint);
Chris@119 3310 int cbw = paint.fontMetrics().width("dB");
Chris@40 3311
Chris@0 3312 int py = -1;
Chris@0 3313 int textHeight = paint.fontMetrics().height();
Chris@0 3314 int toff = -textHeight + paint.fontMetrics().ascent() + 2;
Chris@0 3315
Chris@607 3316 if (detailed && (h > textHeight * 3 + 10)) {
Chris@119 3317
Chris@119 3318 int topLines = 2;
Chris@119 3319 if (m_colourScale == PhaseColourScale) topLines = 1;
Chris@119 3320
Chris@119 3321 int ch = h - textHeight * (topLines + 1) - 8;
Chris@119 3322 // paint.drawRect(4, textHeight + 4, cw - 1, ch + 1);
Chris@119 3323 paint.drawRect(4 + cw - cbw, textHeight * topLines + 4, cbw - 1, ch + 1);
Chris@40 3324
Chris@40 3325 QString top, bottom;
Chris@119 3326 float min = m_viewMags[v].getMin();
Chris@119 3327 float max = m_viewMags[v].getMax();
Chris@119 3328
Chris@119 3329 float dBmin = AudioLevel::multiplier_to_dB(min);
Chris@119 3330 float dBmax = AudioLevel::multiplier_to_dB(max);
Chris@119 3331
Chris@120 3332 if (dBmax < -60.f) dBmax = -60.f;
Chris@120 3333 else top = QString("%1").arg(lrintf(dBmax));
Chris@120 3334
Chris@120 3335 if (dBmin < dBmax - 60.f) dBmin = dBmax - 60.f;
Chris@119 3336 bottom = QString("%1").arg(lrintf(dBmin));
Chris@119 3337
Chris@119 3338 //!!! & phase etc
Chris@119 3339
Chris@119 3340 if (m_colourScale != PhaseColourScale) {
Chris@119 3341 paint.drawText((cw + 6 - paint.fontMetrics().width("dBFS")) / 2,
Chris@119 3342 2 + textHeight + toff, "dBFS");
Chris@119 3343 }
Chris@119 3344
Chris@119 3345 // paint.drawText((cw + 6 - paint.fontMetrics().width(top)) / 2,
Chris@119 3346 paint.drawText(3 + cw - cbw - paint.fontMetrics().width(top),
Chris@119 3347 2 + textHeight * topLines + toff + textHeight/2, top);
Chris@119 3348
Chris@119 3349 paint.drawText(3 + cw - cbw - paint.fontMetrics().width(bottom),
Chris@119 3350 h + toff - 3 - textHeight/2, bottom);
Chris@40 3351
Chris@40 3352 paint.save();
Chris@40 3353 paint.setBrush(Qt::NoBrush);
Chris@119 3354
Chris@119 3355 int lasty = 0;
Chris@119 3356 int lastdb = 0;
Chris@119 3357
Chris@40 3358 for (int i = 0; i < ch; ++i) {
Chris@119 3359
Chris@119 3360 float dBval = dBmin + (((dBmax - dBmin) * i) / (ch - 1));
Chris@119 3361 int idb = int(dBval);
Chris@119 3362
Chris@119 3363 float value = AudioLevel::dB_to_multiplier(dBval);
Chris@119 3364 int colour = getDisplayValue(v, value * m_gain);
Chris@210 3365
Chris@197 3366 paint.setPen(m_palette.getColour(colour));
Chris@119 3367
Chris@119 3368 int y = textHeight * topLines + 4 + ch - i;
Chris@119 3369
Chris@119 3370 paint.drawLine(5 + cw - cbw, y, cw + 2, y);
Chris@119 3371
Chris@119 3372 if (i == 0) {
Chris@119 3373 lasty = y;
Chris@119 3374 lastdb = idb;
Chris@119 3375 } else if (i < ch - paint.fontMetrics().ascent() &&
Chris@120 3376 idb != lastdb &&
Chris@119 3377 ((abs(y - lasty) > textHeight &&
Chris@119 3378 idb % 10 == 0) ||
Chris@119 3379 (abs(y - lasty) > paint.fontMetrics().ascent() &&
Chris@119 3380 idb % 5 == 0))) {
Chris@287 3381 paint.setPen(v->getBackground());
Chris@119 3382 QString text = QString("%1").arg(idb);
Chris@119 3383 paint.drawText(3 + cw - cbw - paint.fontMetrics().width(text),
Chris@119 3384 y + toff + textHeight/2, text);
Chris@287 3385 paint.setPen(v->getForeground());
Chris@119 3386 paint.drawLine(5 + cw - cbw, y, 8 + cw - cbw, y);
Chris@119 3387 lasty = y;
Chris@119 3388 lastdb = idb;
Chris@119 3389 }
Chris@40 3390 }
Chris@40 3391 paint.restore();
Chris@40 3392 }
Chris@40 3393
Chris@40 3394 paint.drawLine(cw + 7, 0, cw + 7, h);
Chris@40 3395
Chris@0 3396 int bin = -1;
Chris@0 3397
Chris@44 3398 for (int y = 0; y < v->height(); ++y) {
Chris@0 3399
Chris@0 3400 float q0, q1;
Chris@44 3401 if (!getYBinRange(v, v->height() - y, q0, q1)) continue;
Chris@0 3402
Chris@0 3403 int vy;
Chris@0 3404
Chris@0 3405 if (int(q0) > bin) {
Chris@0 3406 vy = y;
Chris@0 3407 bin = int(q0);
Chris@0 3408 } else {
Chris@0 3409 continue;
Chris@0 3410 }
Chris@0 3411
Chris@107 3412 int freq = (sr * bin) / m_fftSize;
Chris@0 3413
Chris@0 3414 if (py >= 0 && (vy - py) < textHeight - 1) {
Chris@40 3415 if (m_frequencyScale == LinearFrequencyScale) {
Chris@40 3416 paint.drawLine(w - tickw, h - vy, w, h - vy);
Chris@40 3417 }
Chris@0 3418 continue;
Chris@0 3419 }
Chris@0 3420
Chris@0 3421 QString text = QString("%1").arg(freq);
Chris@234 3422 if (bin == 1) text = tr("%1Hz").arg(freq); // bin 0 is DC
Chris@40 3423 paint.drawLine(cw + 7, h - vy, w - pkw - 1, h - vy);
Chris@0 3424
Chris@0 3425 if (h - vy - textHeight >= -2) {
Chris@40 3426 int tx = w - 3 - paint.fontMetrics().width(text) - std::max(tickw, pkw);
Chris@0 3427 paint.drawText(tx, h - vy + toff, text);
Chris@0 3428 }
Chris@0 3429
Chris@0 3430 py = vy;
Chris@0 3431 }
Chris@40 3432
Chris@40 3433 if (m_frequencyScale == LogFrequencyScale) {
Chris@40 3434
Chris@277 3435 // piano keyboard
Chris@277 3436
Chris@40 3437 paint.drawLine(w - pkw - 1, 0, w - pkw - 1, h);
Chris@40 3438
Chris@40 3439 float minf = getEffectiveMinFrequency();
Chris@40 3440 float maxf = getEffectiveMaxFrequency();
Chris@40 3441
Chris@122 3442 int py = h, ppy = h;
Chris@40 3443 paint.setBrush(paint.pen().color());
Chris@40 3444
Chris@40 3445 for (int i = 0; i < 128; ++i) {
Chris@40 3446
Chris@40 3447 float f = Pitch::getFrequencyForPitch(i);
Chris@44 3448 int y = lrintf(v->getYForFrequency(f, minf, maxf, true));
Chris@122 3449
Chris@122 3450 if (y < -2) break;
Chris@122 3451 if (y > h + 2) {
Chris@122 3452 continue;
Chris@122 3453 }
Chris@122 3454
Chris@40 3455 int n = (i % 12);
Chris@122 3456
Chris@122 3457 if (n == 1) {
Chris@122 3458 // C# -- fill the C from here
Chris@284 3459 QColor col = Qt::gray;
Chris@284 3460 if (i == 61) { // filling middle C
Chris@284 3461 col = Qt::blue;
Chris@284 3462 col = col.light(150);
Chris@284 3463 }
Chris@122 3464 if (ppy - y > 2) {
Chris@122 3465 paint.fillRect(w - pkw,
Chris@122 3466 y,
Chris@122 3467 pkw,
Chris@122 3468 (py + ppy) / 2 - y,
Chris@284 3469 col);
Chris@122 3470 }
Chris@122 3471 }
Chris@122 3472
Chris@40 3473 if (n == 1 || n == 3 || n == 6 || n == 8 || n == 10) {
Chris@40 3474 // black notes
Chris@40 3475 paint.drawLine(w - pkw, y, w, y);
Chris@41 3476 int rh = ((py - y) / 4) * 2;
Chris@41 3477 if (rh < 2) rh = 2;
Chris@41 3478 paint.drawRect(w - pkw, y - (py-y)/4, pkw/2, rh);
Chris@40 3479 } else if (n == 0 || n == 5) {
Chris@122 3480 // C, F
Chris@40 3481 if (py < h) {
Chris@40 3482 paint.drawLine(w - pkw, (y + py) / 2, w, (y + py) / 2);
Chris@40 3483 }
Chris@40 3484 }
Chris@40 3485
Chris@122 3486 ppy = py;
Chris@40 3487 py = y;
Chris@40 3488 }
Chris@40 3489 }
Chris@0 3490 }
Chris@0 3491
Chris@187 3492 class SpectrogramRangeMapper : public RangeMapper
Chris@187 3493 {
Chris@187 3494 public:
Chris@248 3495 SpectrogramRangeMapper(int sr, int /* fftsize */) :
Chris@187 3496 m_dist(float(sr) / 2),
Chris@187 3497 m_s2(sqrtf(sqrtf(2))) { }
Chris@187 3498 ~SpectrogramRangeMapper() { }
Chris@187 3499
Chris@187 3500 virtual int getPositionForValue(float value) const {
Chris@187 3501
Chris@187 3502 float dist = m_dist;
Chris@187 3503
Chris@187 3504 int n = 0;
Chris@187 3505
Chris@187 3506 while (dist > (value + 0.00001) && dist > 0.1f) {
Chris@187 3507 dist /= m_s2;
Chris@187 3508 ++n;
Chris@187 3509 }
Chris@187 3510
Chris@187 3511 return n;
Chris@187 3512 }
Chris@187 3513
Chris@187 3514 virtual float getValueForPosition(int position) const {
Chris@187 3515
Chris@187 3516 // Vertical zoom step 0 shows the entire range from DC ->
Chris@187 3517 // Nyquist frequency. Step 1 shows 2^(1/4) of the range of
Chris@187 3518 // step 0, and so on until the visible range is smaller than
Chris@187 3519 // the frequency step between bins at the current fft size.
Chris@187 3520
Chris@187 3521 float dist = m_dist;
Chris@187 3522
Chris@187 3523 int n = 0;
Chris@187 3524 while (n < position) {
Chris@187 3525 dist /= m_s2;
Chris@187 3526 ++n;
Chris@187 3527 }
Chris@187 3528
Chris@187 3529 return dist;
Chris@187 3530 }
Chris@187 3531
Chris@187 3532 virtual QString getUnit() const { return "Hz"; }
Chris@187 3533
Chris@187 3534 protected:
Chris@187 3535 float m_dist;
Chris@187 3536 float m_s2;
Chris@187 3537 };
Chris@187 3538
Chris@133 3539 int
Chris@133 3540 SpectrogramLayer::getVerticalZoomSteps(int &defaultStep) const
Chris@133 3541 {
Chris@135 3542 if (!m_model) return 0;
Chris@187 3543
Chris@187 3544 int sr = m_model->getSampleRate();
Chris@187 3545
Chris@187 3546 SpectrogramRangeMapper mapper(sr, m_fftSize);
Chris@187 3547
Chris@187 3548 // int maxStep = mapper.getPositionForValue((float(sr) / m_fftSize) + 0.001);
Chris@187 3549 int maxStep = mapper.getPositionForValue(0);
Chris@187 3550 int minStep = mapper.getPositionForValue(float(sr) / 2);
Chris@250 3551
Chris@250 3552 size_t initialMax = m_initialMaxFrequency;
Chris@250 3553 if (initialMax == 0) initialMax = sr / 2;
Chris@250 3554
Chris@250 3555 defaultStep = mapper.getPositionForValue(initialMax) - minStep;
Chris@250 3556
Chris@587 3557 // SVDEBUG << "SpectrogramLayer::getVerticalZoomSteps: " << maxStep - minStep << " (" << maxStep <<"-" << minStep << "), default is " << defaultStep << " (from initial max freq " << initialMax << ")" << endl;
Chris@187 3558
Chris@187 3559 return maxStep - minStep;
Chris@133 3560 }
Chris@133 3561
Chris@133 3562 int
Chris@133 3563 SpectrogramLayer::getCurrentVerticalZoomStep() const
Chris@133 3564 {
Chris@133 3565 if (!m_model) return 0;
Chris@133 3566
Chris@133 3567 float dmin, dmax;
Chris@133 3568 getDisplayExtents(dmin, dmax);
Chris@133 3569
Chris@187 3570 SpectrogramRangeMapper mapper(m_model->getSampleRate(), m_fftSize);
Chris@187 3571 int n = mapper.getPositionForValue(dmax - dmin);
Chris@587 3572 // SVDEBUG << "SpectrogramLayer::getCurrentVerticalZoomStep: " << n << endl;
Chris@133 3573 return n;
Chris@133 3574 }
Chris@133 3575
Chris@133 3576 void
Chris@133 3577 SpectrogramLayer::setVerticalZoomStep(int step)
Chris@133 3578 {
Chris@187 3579 if (!m_model) return;
Chris@187 3580
Chris@253 3581 float dmin = m_minFrequency, dmax = m_maxFrequency;
Chris@253 3582 // getDisplayExtents(dmin, dmax);
Chris@253 3583
Chris@253 3584 // std::cerr << "current range " << dmin << " -> " << dmax << ", range " << dmax-dmin << ", mid " << (dmax + dmin)/2 << std::endl;
Chris@133 3585
Chris@133 3586 int sr = m_model->getSampleRate();
Chris@187 3587 SpectrogramRangeMapper mapper(sr, m_fftSize);
Chris@253 3588 float newdist = mapper.getValueForPosition(step);
Chris@253 3589
Chris@253 3590 float newmin, newmax;
Chris@253 3591
Chris@253 3592 if (m_frequencyScale == LogFrequencyScale) {
Chris@253 3593
Chris@253 3594 // need to pick newmin and newmax such that
Chris@253 3595 //
Chris@253 3596 // (log(newmin) + log(newmax)) / 2 == logmid
Chris@253 3597 // and
Chris@253 3598 // newmax - newmin = newdist
Chris@253 3599 //
Chris@253 3600 // so log(newmax - newdist) + log(newmax) == 2logmid
Chris@253 3601 // log(newmax(newmax - newdist)) == 2logmid
Chris@253 3602 // newmax.newmax - newmax.newdist == exp(2logmid)
Chris@253 3603 // newmax^2 + (-newdist)newmax + -exp(2logmid) == 0
Chris@253 3604 // quadratic with a = 1, b = -newdist, c = -exp(2logmid), all known
Chris@253 3605 //
Chris@253 3606 // positive root
Chris@253 3607 // newmax = (newdist + sqrt(newdist^2 + 4exp(2logmid))) / 2
Chris@253 3608 //
Chris@253 3609 // but logmid = (log(dmin) + log(dmax)) / 2
Chris@253 3610 // so exp(2logmid) = exp(log(dmin) + log(dmax))
Chris@253 3611 // = exp(log(dmin.dmax))
Chris@253 3612 // = dmin.dmax
Chris@253 3613 // so newmax = (newdist + sqrtf(newdist^2 + 4dmin.dmax)) / 2
Chris@253 3614
Chris@253 3615 newmax = (newdist + sqrtf(newdist*newdist + 4*dmin*dmax)) / 2;
Chris@253 3616 newmin = newmax - newdist;
Chris@253 3617
Chris@253 3618 // std::cerr << "newmin = " << newmin << ", newmax = " << newmax << std::endl;
Chris@253 3619
Chris@253 3620 } else {
Chris@253 3621 float dmid = (dmax + dmin) / 2;
Chris@253 3622 newmin = dmid - newdist / 2;
Chris@253 3623 newmax = dmid + newdist / 2;
Chris@253 3624 }
Chris@187 3625
Chris@187 3626 float mmin, mmax;
Chris@187 3627 mmin = 0;
Chris@187 3628 mmax = float(sr) / 2;
Chris@133 3629
Chris@187 3630 if (newmin < mmin) {
Chris@187 3631 newmax += (mmin - newmin);
Chris@187 3632 newmin = mmin;
Chris@187 3633 }
Chris@187 3634 if (newmax > mmax) {
Chris@187 3635 newmax = mmax;
Chris@187 3636 }
Chris@133 3637
Chris@587 3638 // SVDEBUG << "SpectrogramLayer::setVerticalZoomStep: " << step << ": " << newmin << " -> " << newmax << " (range " << newdist << ")" << endl;
Chris@253 3639
Chris@253 3640 setMinFrequency(lrintf(newmin));
Chris@253 3641 setMaxFrequency(lrintf(newmax));
Chris@187 3642 }
Chris@187 3643
Chris@187 3644 RangeMapper *
Chris@187 3645 SpectrogramLayer::getNewVerticalZoomRangeMapper() const
Chris@187 3646 {
Chris@187 3647 if (!m_model) return 0;
Chris@187 3648 return new SpectrogramRangeMapper(m_model->getSampleRate(), m_fftSize);
Chris@133 3649 }
Chris@133 3650
Chris@273 3651 void
Chris@273 3652 SpectrogramLayer::updateMeasureRectYCoords(View *v, const MeasureRect &r) const
Chris@273 3653 {
Chris@273 3654 int y0 = 0;
Chris@273 3655 if (r.startY > 0.0) y0 = getYForFrequency(v, r.startY);
Chris@273 3656
Chris@273 3657 int y1 = y0;
Chris@273 3658 if (r.endY > 0.0) y1 = getYForFrequency(v, r.endY);
Chris@273 3659
Chris@587 3660 // SVDEBUG << "SpectrogramLayer::updateMeasureRectYCoords: start " << r.startY << " -> " << y0 << ", end " << r.endY << " -> " << y1 << endl;
Chris@273 3661
Chris@273 3662 r.pixrect = QRect(r.pixrect.x(), y0, r.pixrect.width(), y1 - y0);
Chris@273 3663 }
Chris@273 3664
Chris@273 3665 void
Chris@273 3666 SpectrogramLayer::setMeasureRectYCoord(View *v, MeasureRect &r, bool start, int y) const
Chris@273 3667 {
Chris@273 3668 if (start) {
Chris@273 3669 r.startY = getFrequencyForY(v, y);
Chris@273 3670 r.endY = r.startY;
Chris@273 3671 } else {
Chris@273 3672 r.endY = getFrequencyForY(v, y);
Chris@273 3673 }
Chris@587 3674 // SVDEBUG << "SpectrogramLayer::setMeasureRectYCoord: start " << r.startY << " <- " << y << ", end " << r.endY << " <- " << y << endl;
Chris@273 3675
Chris@273 3676 }
Chris@273 3677
Chris@316 3678 void
Chris@316 3679 SpectrogramLayer::toXml(QTextStream &stream,
Chris@316 3680 QString indent, QString extraAttributes) const
Chris@6 3681 {
Chris@6 3682 QString s;
Chris@6 3683
Chris@6 3684 s += QString("channel=\"%1\" "
Chris@6 3685 "windowSize=\"%2\" "
Chris@153 3686 "windowHopLevel=\"%3\" "
Chris@153 3687 "gain=\"%4\" "
Chris@153 3688 "threshold=\"%5\" ")
Chris@6 3689 .arg(m_channel)
Chris@6 3690 .arg(m_windowSize)
Chris@97 3691 .arg(m_windowHopLevel)
Chris@37 3692 .arg(m_gain)
Chris@37 3693 .arg(m_threshold);
Chris@37 3694
Chris@37 3695 s += QString("minFrequency=\"%1\" "
Chris@37 3696 "maxFrequency=\"%2\" "
Chris@37 3697 "colourScale=\"%3\" "
Chris@37 3698 "colourScheme=\"%4\" "
Chris@37 3699 "colourRotation=\"%5\" "
Chris@37 3700 "frequencyScale=\"%6\" "
Chris@37 3701 "binDisplay=\"%7\" "
Chris@153 3702 "normalizeColumns=\"%8\" "
Chris@153 3703 "normalizeVisibleArea=\"%9\"")
Chris@37 3704 .arg(m_minFrequency)
Chris@6 3705 .arg(m_maxFrequency)
Chris@6 3706 .arg(m_colourScale)
Chris@197 3707 .arg(m_colourMap)
Chris@37 3708 .arg(m_colourRotation)
Chris@35 3709 .arg(m_frequencyScale)
Chris@37 3710 .arg(m_binDisplay)
Chris@153 3711 .arg(m_normalizeColumns ? "true" : "false")
Chris@153 3712 .arg(m_normalizeVisibleArea ? "true" : "false");
Chris@6 3713
Chris@316 3714 Layer::toXml(stream, indent, extraAttributes + " " + s);
Chris@6 3715 }
Chris@6 3716
Chris@11 3717 void
Chris@11 3718 SpectrogramLayer::setProperties(const QXmlAttributes &attributes)
Chris@11 3719 {
Chris@11 3720 bool ok = false;
Chris@11 3721
Chris@11 3722 int channel = attributes.value("channel").toInt(&ok);
Chris@11 3723 if (ok) setChannel(channel);
Chris@11 3724
Chris@11 3725 size_t windowSize = attributes.value("windowSize").toUInt(&ok);
Chris@11 3726 if (ok) setWindowSize(windowSize);
Chris@11 3727
Chris@97 3728 size_t windowHopLevel = attributes.value("windowHopLevel").toUInt(&ok);
Chris@97 3729 if (ok) setWindowHopLevel(windowHopLevel);
Chris@97 3730 else {
Chris@97 3731 size_t windowOverlap = attributes.value("windowOverlap").toUInt(&ok);
Chris@97 3732 // a percentage value
Chris@97 3733 if (ok) {
Chris@97 3734 if (windowOverlap == 0) setWindowHopLevel(0);
Chris@97 3735 else if (windowOverlap == 25) setWindowHopLevel(1);
Chris@97 3736 else if (windowOverlap == 50) setWindowHopLevel(2);
Chris@97 3737 else if (windowOverlap == 75) setWindowHopLevel(3);
Chris@97 3738 else if (windowOverlap == 90) setWindowHopLevel(4);
Chris@97 3739 }
Chris@97 3740 }
Chris@11 3741
Chris@11 3742 float gain = attributes.value("gain").toFloat(&ok);
Chris@11 3743 if (ok) setGain(gain);
Chris@11 3744
Chris@37 3745 float threshold = attributes.value("threshold").toFloat(&ok);
Chris@37 3746 if (ok) setThreshold(threshold);
Chris@37 3747
Chris@37 3748 size_t minFrequency = attributes.value("minFrequency").toUInt(&ok);
Chris@187 3749 if (ok) {
Chris@587 3750 SVDEBUG << "SpectrogramLayer::setProperties: setting min freq to " << minFrequency << endl;
Chris@187 3751 setMinFrequency(minFrequency);
Chris@187 3752 }
Chris@37 3753
Chris@11 3754 size_t maxFrequency = attributes.value("maxFrequency").toUInt(&ok);
Chris@187 3755 if (ok) {
Chris@587 3756 SVDEBUG << "SpectrogramLayer::setProperties: setting max freq to " << maxFrequency << endl;
Chris@187 3757 setMaxFrequency(maxFrequency);
Chris@187 3758 }
Chris@11 3759
Chris@11 3760 ColourScale colourScale = (ColourScale)
Chris@11 3761 attributes.value("colourScale").toInt(&ok);
Chris@11 3762 if (ok) setColourScale(colourScale);
Chris@11 3763
Chris@197 3764 int colourMap = attributes.value("colourScheme").toInt(&ok);
Chris@197 3765 if (ok) setColourMap(colourMap);
Chris@11 3766
Chris@37 3767 int colourRotation = attributes.value("colourRotation").toInt(&ok);
Chris@37 3768 if (ok) setColourRotation(colourRotation);
Chris@37 3769
Chris@11 3770 FrequencyScale frequencyScale = (FrequencyScale)
Chris@11 3771 attributes.value("frequencyScale").toInt(&ok);
Chris@11 3772 if (ok) setFrequencyScale(frequencyScale);
Chris@35 3773
Chris@37 3774 BinDisplay binDisplay = (BinDisplay)
Chris@37 3775 attributes.value("binDisplay").toInt(&ok);
Chris@37 3776 if (ok) setBinDisplay(binDisplay);
Chris@36 3777
Chris@36 3778 bool normalizeColumns =
Chris@36 3779 (attributes.value("normalizeColumns").trimmed() == "true");
Chris@36 3780 setNormalizeColumns(normalizeColumns);
Chris@153 3781
Chris@153 3782 bool normalizeVisibleArea =
Chris@153 3783 (attributes.value("normalizeVisibleArea").trimmed() == "true");
Chris@153 3784 setNormalizeVisibleArea(normalizeVisibleArea);
Chris@11 3785 }
Chris@11 3786