annotate layer/SpectrogramLayer.cpp @ 193:57c2350a8c40

* Add slice layers (so you can display a slice of a colour 3d plot as if it were a spectrum) * Make spectrum layer a subclass of slice layer
author Chris Cannam
date Fri, 26 Jan 2007 16:59:57 +0000
parents dd573e090eed
children 22c99c8aa1e0
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@182 7 This file copyright 2006 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@0 25
Chris@0 26 #include <QPainter>
Chris@0 27 #include <QImage>
Chris@0 28 #include <QPixmap>
Chris@0 29 #include <QRect>
Chris@0 30 #include <QTimer>
Chris@92 31 #include <QApplication>
Chris@178 32 #include <QMessageBox>
Chris@0 33
Chris@0 34 #include <iostream>
Chris@0 35
Chris@0 36 #include <cassert>
Chris@0 37 #include <cmath>
Chris@0 38
Chris@174 39 //#define DEBUG_SPECTROGRAM_REPAINT 1
Chris@0 40
Chris@44 41 SpectrogramLayer::SpectrogramLayer(Configuration config) :
Chris@0 42 m_model(0),
Chris@0 43 m_channel(0),
Chris@0 44 m_windowSize(1024),
Chris@0 45 m_windowType(HanningWindow),
Chris@97 46 m_windowHopLevel(2),
Chris@109 47 m_zeroPadLevel(0),
Chris@107 48 m_fftSize(1024),
Chris@0 49 m_gain(1.0),
Chris@37 50 m_threshold(0.0),
Chris@9 51 m_colourRotation(0),
Chris@119 52 m_minFrequency(10),
Chris@0 53 m_maxFrequency(8000),
Chris@135 54 m_initialMaxFrequency(8000),
Chris@0 55 m_colourScale(dBColourScale),
Chris@0 56 m_colourScheme(DefaultColours),
Chris@0 57 m_frequencyScale(LinearFrequencyScale),
Chris@37 58 m_binDisplay(AllBins),
Chris@36 59 m_normalizeColumns(false),
Chris@120 60 m_normalizeVisibleArea(false),
Chris@133 61 m_lastEmittedZoomStep(-1),
Chris@0 62 m_updateTimer(0),
Chris@44 63 m_candidateFillStartFrame(0),
Chris@193 64 m_exiting(false),
Chris@193 65 m_sliceableModel(0)
Chris@0 66 {
Chris@0 67 if (config == MelodicRange) {
Chris@0 68 setWindowSize(8192);
Chris@97 69 setWindowHopLevel(4);
Chris@109 70 // setWindowType(ParzenWindow);
Chris@135 71 m_initialMaxFrequency = 1000;
Chris@0 72 setMaxFrequency(1000);
Chris@0 73 setColourScale(LinearColourScale);
Chris@37 74 } else if (config == MelodicPeaks) {
Chris@37 75 setWindowSize(4096);
Chris@97 76 setWindowHopLevel(5);
Chris@109 77 // setWindowType(BlackmanWindow);
Chris@135 78 m_initialMaxFrequency = 2000;
Chris@40 79 setMaxFrequency(2000);
Chris@37 80 setMinFrequency(40);
Chris@37 81 setFrequencyScale(LogFrequencyScale);
Chris@41 82 setColourScale(MeterColourScale);
Chris@37 83 setBinDisplay(PeakFrequencies);
Chris@37 84 setNormalizeColumns(true);
Chris@0 85 }
Chris@110 86
Chris@122 87 Preferences *prefs = Preferences::getInstance();
Chris@122 88 connect(prefs, SIGNAL(propertyChanged(PropertyContainer::PropertyName)),
Chris@122 89 this, SLOT(preferenceChanged(PropertyContainer::PropertyName)));
Chris@122 90 setWindowType(prefs->getWindowType());
Chris@122 91
Chris@110 92 setColourmap();
Chris@0 93 }
Chris@0 94
Chris@0 95 SpectrogramLayer::~SpectrogramLayer()
Chris@0 96 {
Chris@0 97 delete m_updateTimer;
Chris@0 98 m_updateTimer = 0;
Chris@0 99
Chris@130 100 invalidateFFTModels();
Chris@0 101 }
Chris@0 102
Chris@0 103 void
Chris@0 104 SpectrogramLayer::setModel(const DenseTimeValueModel *model)
Chris@0 105 {
Chris@101 106 // std::cerr << "SpectrogramLayer(" << this << "): setModel(" << model << ")" << std::endl;
Chris@34 107
Chris@110 108 if (model == m_model) return;
Chris@110 109
Chris@0 110 m_model = model;
Chris@130 111 invalidateFFTModels();
Chris@0 112
Chris@0 113 if (!m_model || !m_model->isOK()) return;
Chris@0 114
Chris@0 115 connect(m_model, SIGNAL(modelChanged()), this, SIGNAL(modelChanged()));
Chris@0 116 connect(m_model, SIGNAL(modelChanged(size_t, size_t)),
Chris@0 117 this, SIGNAL(modelChanged(size_t, size_t)));
Chris@0 118
Chris@0 119 connect(m_model, SIGNAL(completionChanged()),
Chris@0 120 this, SIGNAL(modelCompletionChanged()));
Chris@0 121
Chris@0 122 connect(m_model, SIGNAL(modelChanged()), this, SLOT(cacheInvalid()));
Chris@0 123 connect(m_model, SIGNAL(modelChanged(size_t, size_t)),
Chris@0 124 this, SLOT(cacheInvalid(size_t, size_t)));
Chris@0 125
Chris@0 126 emit modelReplaced();
Chris@110 127 }
Chris@115 128
Chris@0 129 Layer::PropertyList
Chris@0 130 SpectrogramLayer::getProperties() const
Chris@0 131 {
Chris@0 132 PropertyList list;
Chris@87 133 list.push_back("Colour");
Chris@87 134 list.push_back("Colour Scale");
Chris@87 135 list.push_back("Window Size");
Chris@97 136 list.push_back("Window Increment");
Chris@87 137 list.push_back("Normalize Columns");
Chris@120 138 list.push_back("Normalize Visible Area");
Chris@87 139 list.push_back("Bin Display");
Chris@87 140 list.push_back("Threshold");
Chris@87 141 list.push_back("Gain");
Chris@87 142 list.push_back("Colour Rotation");
Chris@153 143 // list.push_back("Min Frequency");
Chris@153 144 // list.push_back("Max Frequency");
Chris@87 145 list.push_back("Frequency Scale");
Chris@153 146 //// list.push_back("Zero Padding");
Chris@0 147 return list;
Chris@0 148 }
Chris@0 149
Chris@87 150 QString
Chris@87 151 SpectrogramLayer::getPropertyLabel(const PropertyName &name) const
Chris@87 152 {
Chris@87 153 if (name == "Colour") return tr("Colour");
Chris@87 154 if (name == "Colour Scale") return tr("Colour Scale");
Chris@87 155 if (name == "Window Size") return tr("Window Size");
Chris@112 156 if (name == "Window Increment") return tr("Window Overlap");
Chris@87 157 if (name == "Normalize Columns") return tr("Normalize Columns");
Chris@120 158 if (name == "Normalize Visible Area") return tr("Normalize Visible Area");
Chris@87 159 if (name == "Bin Display") return tr("Bin Display");
Chris@87 160 if (name == "Threshold") return tr("Threshold");
Chris@87 161 if (name == "Gain") return tr("Gain");
Chris@87 162 if (name == "Colour Rotation") return tr("Colour Rotation");
Chris@87 163 if (name == "Min Frequency") return tr("Min Frequency");
Chris@87 164 if (name == "Max Frequency") return tr("Max Frequency");
Chris@87 165 if (name == "Frequency Scale") return tr("Frequency Scale");
Chris@109 166 if (name == "Zero Padding") return tr("Smoothing");
Chris@87 167 return "";
Chris@87 168 }
Chris@87 169
Chris@0 170 Layer::PropertyType
Chris@0 171 SpectrogramLayer::getPropertyType(const PropertyName &name) const
Chris@0 172 {
Chris@87 173 if (name == "Gain") return RangeProperty;
Chris@87 174 if (name == "Colour Rotation") return RangeProperty;
Chris@87 175 if (name == "Normalize Columns") return ToggleProperty;
Chris@120 176 if (name == "Normalize Visible Area") return ToggleProperty;
Chris@87 177 if (name == "Threshold") return RangeProperty;
Chris@109 178 if (name == "Zero Padding") return ToggleProperty;
Chris@0 179 return ValueProperty;
Chris@0 180 }
Chris@0 181
Chris@0 182 QString
Chris@0 183 SpectrogramLayer::getPropertyGroupName(const PropertyName &name) const
Chris@0 184 {
Chris@153 185 if (name == "Bin Display" ||
Chris@153 186 name == "Frequency Scale") return tr("Bins");
Chris@87 187 if (name == "Window Size" ||
Chris@109 188 name == "Window Increment" ||
Chris@109 189 name == "Zero Padding") return tr("Window");
Chris@87 190 if (name == "Colour" ||
Chris@153 191 // name == "Gain" ||
Chris@87 192 name == "Threshold" ||
Chris@87 193 name == "Colour Rotation") return tr("Colour");
Chris@87 194 if (name == "Normalize Columns" ||
Chris@120 195 name == "Normalize Visible Area" ||
Chris@153 196 // name == "Bin Display" ||
Chris@153 197 name == "Gain" ||
Chris@87 198 name == "Colour Scale") return tr("Scale");
Chris@153 199 // if (name == "Max Frequency" ||
Chris@153 200 // name == "Min Frequency" ||
Chris@153 201 // name == "Frequency Scale" ||
Chris@153 202 // name == "Frequency Adjustment") return tr("Range");
Chris@0 203 return QString();
Chris@0 204 }
Chris@0 205
Chris@0 206 int
Chris@0 207 SpectrogramLayer::getPropertyRangeAndValue(const PropertyName &name,
Chris@55 208 int *min, int *max) const
Chris@0 209 {
Chris@0 210 int deft = 0;
Chris@0 211
Chris@55 212 int garbage0, garbage1;
Chris@55 213 if (!min) min = &garbage0;
Chris@55 214 if (!max) max = &garbage1;
Chris@10 215
Chris@87 216 if (name == "Gain") {
Chris@0 217
Chris@0 218 *min = -50;
Chris@0 219 *max = 50;
Chris@0 220
Chris@0 221 deft = lrint(log10(m_gain) * 20.0);
Chris@0 222 if (deft < *min) deft = *min;
Chris@0 223 if (deft > *max) deft = *max;
Chris@0 224
Chris@87 225 } else if (name == "Threshold") {
Chris@37 226
Chris@37 227 *min = -50;
Chris@37 228 *max = 0;
Chris@37 229
Chris@37 230 deft = lrintf(AudioLevel::multiplier_to_dB(m_threshold));
Chris@37 231 if (deft < *min) deft = *min;
Chris@37 232 if (deft > *max) deft = *max;
Chris@37 233
Chris@87 234 } else if (name == "Colour Rotation") {
Chris@9 235
Chris@9 236 *min = 0;
Chris@9 237 *max = 256;
Chris@9 238
Chris@9 239 deft = m_colourRotation;
Chris@9 240
Chris@87 241 } else if (name == "Colour Scale") {
Chris@0 242
Chris@0 243 *min = 0;
Chris@176 244 *max = 4;
Chris@0 245
Chris@0 246 deft = (int)m_colourScale;
Chris@0 247
Chris@87 248 } else if (name == "Colour") {
Chris@0 249
Chris@0 250 *min = 0;
Chris@71 251 *max = 6;
Chris@0 252
Chris@0 253 deft = (int)m_colourScheme;
Chris@0 254
Chris@87 255 } else if (name == "Window Size") {
Chris@0 256
Chris@0 257 *min = 0;
Chris@0 258 *max = 10;
Chris@0 259
Chris@0 260 deft = 0;
Chris@0 261 int ws = m_windowSize;
Chris@0 262 while (ws > 32) { ws >>= 1; deft ++; }
Chris@0 263
Chris@97 264 } else if (name == "Window Increment") {
Chris@0 265
Chris@0 266 *min = 0;
Chris@97 267 *max = 5;
Chris@0 268
Chris@97 269 deft = m_windowHopLevel;
Chris@0 270
Chris@109 271 } else if (name == "Zero Padding") {
Chris@109 272
Chris@109 273 *min = 0;
Chris@109 274 *max = 1;
Chris@109 275
Chris@109 276 deft = m_zeroPadLevel > 0 ? 1 : 0;
Chris@109 277
Chris@87 278 } else if (name == "Min Frequency") {
Chris@37 279
Chris@37 280 *min = 0;
Chris@37 281 *max = 9;
Chris@37 282
Chris@37 283 switch (m_minFrequency) {
Chris@37 284 case 0: default: deft = 0; break;
Chris@37 285 case 10: deft = 1; break;
Chris@37 286 case 20: deft = 2; break;
Chris@37 287 case 40: deft = 3; break;
Chris@37 288 case 100: deft = 4; break;
Chris@37 289 case 250: deft = 5; break;
Chris@37 290 case 500: deft = 6; break;
Chris@37 291 case 1000: deft = 7; break;
Chris@37 292 case 4000: deft = 8; break;
Chris@37 293 case 10000: deft = 9; break;
Chris@37 294 }
Chris@37 295
Chris@87 296 } else if (name == "Max Frequency") {
Chris@0 297
Chris@0 298 *min = 0;
Chris@0 299 *max = 9;
Chris@0 300
Chris@0 301 switch (m_maxFrequency) {
Chris@0 302 case 500: deft = 0; break;
Chris@0 303 case 1000: deft = 1; break;
Chris@0 304 case 1500: deft = 2; break;
Chris@0 305 case 2000: deft = 3; break;
Chris@0 306 case 4000: deft = 4; break;
Chris@0 307 case 6000: deft = 5; break;
Chris@0 308 case 8000: deft = 6; break;
Chris@0 309 case 12000: deft = 7; break;
Chris@0 310 case 16000: deft = 8; break;
Chris@0 311 default: deft = 9; break;
Chris@0 312 }
Chris@0 313
Chris@87 314 } else if (name == "Frequency Scale") {
Chris@0 315
Chris@0 316 *min = 0;
Chris@0 317 *max = 1;
Chris@0 318 deft = (int)m_frequencyScale;
Chris@0 319
Chris@87 320 } else if (name == "Bin Display") {
Chris@35 321
Chris@35 322 *min = 0;
Chris@35 323 *max = 2;
Chris@37 324 deft = (int)m_binDisplay;
Chris@35 325
Chris@87 326 } else if (name == "Normalize Columns") {
Chris@36 327
Chris@36 328 deft = (m_normalizeColumns ? 1 : 0);
Chris@36 329
Chris@120 330 } else if (name == "Normalize Visible Area") {
Chris@120 331
Chris@120 332 deft = (m_normalizeVisibleArea ? 1 : 0);
Chris@120 333
Chris@0 334 } else {
Chris@0 335 deft = Layer::getPropertyRangeAndValue(name, min, max);
Chris@0 336 }
Chris@0 337
Chris@0 338 return deft;
Chris@0 339 }
Chris@0 340
Chris@0 341 QString
Chris@0 342 SpectrogramLayer::getPropertyValueLabel(const PropertyName &name,
Chris@9 343 int value) const
Chris@0 344 {
Chris@87 345 if (name == "Colour") {
Chris@0 346 switch (value) {
Chris@0 347 default:
Chris@0 348 case 0: return tr("Default");
Chris@0 349 case 1: return tr("White on Black");
Chris@0 350 case 2: return tr("Black on White");
Chris@0 351 case 3: return tr("Red on Blue");
Chris@0 352 case 4: return tr("Yellow on Black");
Chris@71 353 case 5: return tr("Blue on Black");
Chris@71 354 case 6: return tr("Fruit Salad");
Chris@0 355 }
Chris@0 356 }
Chris@87 357 if (name == "Colour Scale") {
Chris@0 358 switch (value) {
Chris@0 359 default:
Chris@37 360 case 0: return tr("Linear");
Chris@37 361 case 1: return tr("Meter");
Chris@37 362 case 2: return tr("dB");
Chris@176 363 case 3: return tr("dB^2");
Chris@119 364 case 4: return tr("Phase");
Chris@0 365 }
Chris@0 366 }
Chris@87 367 if (name == "Window Size") {
Chris@0 368 return QString("%1").arg(32 << value);
Chris@0 369 }
Chris@97 370 if (name == "Window Increment") {
Chris@0 371 switch (value) {
Chris@0 372 default:
Chris@112 373 case 0: return tr("None");
Chris@112 374 case 1: return tr("25 %");
Chris@112 375 case 2: return tr("50 %");
Chris@112 376 case 3: return tr("75 %");
Chris@112 377 case 4: return tr("87.5 %");
Chris@112 378 case 5: return tr("93.75 %");
Chris@0 379 }
Chris@0 380 }
Chris@109 381 if (name == "Zero Padding") {
Chris@109 382 if (value == 0) return tr("None");
Chris@109 383 return QString("%1x").arg(value + 1);
Chris@109 384 }
Chris@87 385 if (name == "Min Frequency") {
Chris@37 386 switch (value) {
Chris@37 387 default:
Chris@38 388 case 0: return tr("No min");
Chris@37 389 case 1: return tr("10 Hz");
Chris@37 390 case 2: return tr("20 Hz");
Chris@37 391 case 3: return tr("40 Hz");
Chris@37 392 case 4: return tr("100 Hz");
Chris@37 393 case 5: return tr("250 Hz");
Chris@37 394 case 6: return tr("500 Hz");
Chris@37 395 case 7: return tr("1 KHz");
Chris@37 396 case 8: return tr("4 KHz");
Chris@37 397 case 9: return tr("10 KHz");
Chris@37 398 }
Chris@37 399 }
Chris@87 400 if (name == "Max Frequency") {
Chris@0 401 switch (value) {
Chris@0 402 default:
Chris@0 403 case 0: return tr("500 Hz");
Chris@0 404 case 1: return tr("1 KHz");
Chris@0 405 case 2: return tr("1.5 KHz");
Chris@0 406 case 3: return tr("2 KHz");
Chris@0 407 case 4: return tr("4 KHz");
Chris@0 408 case 5: return tr("6 KHz");
Chris@0 409 case 6: return tr("8 KHz");
Chris@0 410 case 7: return tr("12 KHz");
Chris@0 411 case 8: return tr("16 KHz");
Chris@38 412 case 9: return tr("No max");
Chris@0 413 }
Chris@0 414 }
Chris@87 415 if (name == "Frequency Scale") {
Chris@0 416 switch (value) {
Chris@0 417 default:
Chris@0 418 case 0: return tr("Linear");
Chris@0 419 case 1: return tr("Log");
Chris@0 420 }
Chris@0 421 }
Chris@87 422 if (name == "Bin Display") {
Chris@35 423 switch (value) {
Chris@35 424 default:
Chris@37 425 case 0: return tr("All Bins");
Chris@37 426 case 1: return tr("Peak Bins");
Chris@37 427 case 2: return tr("Frequencies");
Chris@35 428 }
Chris@35 429 }
Chris@0 430 return tr("<unknown>");
Chris@0 431 }
Chris@0 432
Chris@167 433 RangeMapper *
Chris@167 434 SpectrogramLayer::getNewPropertyRangeMapper(const PropertyName &name) const
Chris@167 435 {
Chris@167 436 if (name == "Gain") {
Chris@167 437 return new LinearRangeMapper(-50, 50, -25, 25, tr("dB"));
Chris@167 438 }
Chris@167 439 if (name == "Threshold") {
Chris@167 440 return new LinearRangeMapper(-50, 0, -50, 0, tr("dB"));
Chris@167 441 }
Chris@167 442 return 0;
Chris@167 443 }
Chris@167 444
Chris@0 445 void
Chris@0 446 SpectrogramLayer::setProperty(const PropertyName &name, int value)
Chris@0 447 {
Chris@87 448 if (name == "Gain") {
Chris@0 449 setGain(pow(10, float(value)/20.0));
Chris@87 450 } else if (name == "Threshold") {
Chris@37 451 if (value == -50) setThreshold(0.0);
Chris@37 452 else setThreshold(AudioLevel::dB_to_multiplier(value));
Chris@87 453 } else if (name == "Colour Rotation") {
Chris@9 454 setColourRotation(value);
Chris@87 455 } else if (name == "Colour") {
Chris@0 456 switch (value) {
Chris@0 457 default:
Chris@0 458 case 0: setColourScheme(DefaultColours); break;
Chris@0 459 case 1: setColourScheme(WhiteOnBlack); break;
Chris@0 460 case 2: setColourScheme(BlackOnWhite); break;
Chris@0 461 case 3: setColourScheme(RedOnBlue); break;
Chris@0 462 case 4: setColourScheme(YellowOnBlack); break;
Chris@71 463 case 5: setColourScheme(BlueOnBlack); break;
Chris@71 464 case 6: setColourScheme(Rainbow); break;
Chris@0 465 }
Chris@87 466 } else if (name == "Window Size") {
Chris@0 467 setWindowSize(32 << value);
Chris@97 468 } else if (name == "Window Increment") {
Chris@97 469 setWindowHopLevel(value);
Chris@109 470 } else if (name == "Zero Padding") {
Chris@109 471 setZeroPadLevel(value > 0.1 ? 3 : 0);
Chris@87 472 } else if (name == "Min Frequency") {
Chris@37 473 switch (value) {
Chris@37 474 default:
Chris@37 475 case 0: setMinFrequency(0); break;
Chris@37 476 case 1: setMinFrequency(10); break;
Chris@37 477 case 2: setMinFrequency(20); break;
Chris@37 478 case 3: setMinFrequency(40); break;
Chris@37 479 case 4: setMinFrequency(100); break;
Chris@37 480 case 5: setMinFrequency(250); break;
Chris@37 481 case 6: setMinFrequency(500); break;
Chris@37 482 case 7: setMinFrequency(1000); break;
Chris@37 483 case 8: setMinFrequency(4000); break;
Chris@37 484 case 9: setMinFrequency(10000); break;
Chris@37 485 }
Chris@133 486 int vs = getCurrentVerticalZoomStep();
Chris@133 487 if (vs != m_lastEmittedZoomStep) {
Chris@133 488 emit verticalZoomChanged();
Chris@133 489 m_lastEmittedZoomStep = vs;
Chris@133 490 }
Chris@87 491 } else if (name == "Max Frequency") {
Chris@0 492 switch (value) {
Chris@0 493 case 0: setMaxFrequency(500); break;
Chris@0 494 case 1: setMaxFrequency(1000); break;
Chris@0 495 case 2: setMaxFrequency(1500); break;
Chris@0 496 case 3: setMaxFrequency(2000); break;
Chris@0 497 case 4: setMaxFrequency(4000); break;
Chris@0 498 case 5: setMaxFrequency(6000); break;
Chris@0 499 case 6: setMaxFrequency(8000); break;
Chris@0 500 case 7: setMaxFrequency(12000); break;
Chris@0 501 case 8: setMaxFrequency(16000); break;
Chris@0 502 default:
Chris@0 503 case 9: setMaxFrequency(0); break;
Chris@0 504 }
Chris@133 505 int vs = getCurrentVerticalZoomStep();
Chris@133 506 if (vs != m_lastEmittedZoomStep) {
Chris@133 507 emit verticalZoomChanged();
Chris@133 508 m_lastEmittedZoomStep = vs;
Chris@133 509 }
Chris@87 510 } else if (name == "Colour Scale") {
Chris@0 511 switch (value) {
Chris@0 512 default:
Chris@0 513 case 0: setColourScale(LinearColourScale); break;
Chris@0 514 case 1: setColourScale(MeterColourScale); break;
Chris@0 515 case 2: setColourScale(dBColourScale); break;
Chris@119 516 case 3: setColourScale(OtherColourScale); break;
Chris@119 517 case 4: setColourScale(PhaseColourScale); break;
Chris@0 518 }
Chris@87 519 } else if (name == "Frequency Scale") {
Chris@0 520 switch (value) {
Chris@0 521 default:
Chris@0 522 case 0: setFrequencyScale(LinearFrequencyScale); break;
Chris@0 523 case 1: setFrequencyScale(LogFrequencyScale); break;
Chris@0 524 }
Chris@87 525 } else if (name == "Bin Display") {
Chris@35 526 switch (value) {
Chris@35 527 default:
Chris@37 528 case 0: setBinDisplay(AllBins); break;
Chris@37 529 case 1: setBinDisplay(PeakBins); break;
Chris@37 530 case 2: setBinDisplay(PeakFrequencies); break;
Chris@35 531 }
Chris@82 532 } else if (name == "Normalize Columns") {
Chris@36 533 setNormalizeColumns(value ? true : false);
Chris@120 534 } else if (name == "Normalize Visible Area") {
Chris@120 535 setNormalizeVisibleArea(value ? true : false);
Chris@0 536 }
Chris@0 537 }
Chris@0 538
Chris@0 539 void
Chris@95 540 SpectrogramLayer::invalidatePixmapCaches()
Chris@95 541 {
Chris@95 542 for (ViewPixmapCache::iterator i = m_pixmapCaches.begin();
Chris@95 543 i != m_pixmapCaches.end(); ++i) {
Chris@95 544 i->second.validArea = QRect();
Chris@95 545 }
Chris@95 546 }
Chris@95 547
Chris@95 548 void
Chris@95 549 SpectrogramLayer::invalidatePixmapCaches(size_t startFrame, size_t endFrame)
Chris@95 550 {
Chris@95 551 for (ViewPixmapCache::iterator i = m_pixmapCaches.begin();
Chris@95 552 i != m_pixmapCaches.end(); ++i) {
Chris@131 553
Chris@95 554 //!!! when are views removed from the map? on setLayerDormant?
Chris@95 555 const View *v = i->first;
Chris@95 556
Chris@115 557 if (startFrame < v->getEndFrame() && int(endFrame) >= v->getStartFrame()) {
Chris@95 558 i->second.validArea = QRect();
Chris@95 559 }
Chris@95 560 }
Chris@95 561 }
Chris@95 562
Chris@95 563 void
Chris@122 564 SpectrogramLayer::preferenceChanged(PropertyContainer::PropertyName name)
Chris@122 565 {
Chris@122 566 std::cerr << "SpectrogramLayer::preferenceChanged(" << name.toStdString() << ")" << std::endl;
Chris@122 567
Chris@122 568 if (name == "Window Type") {
Chris@122 569 setWindowType(Preferences::getInstance()->getWindowType());
Chris@122 570 return;
Chris@122 571 }
Chris@122 572 if (name == "Smooth Spectrogram") {
Chris@122 573 invalidatePixmapCaches();
Chris@122 574 invalidateMagnitudes();
Chris@122 575 emit layerParametersChanged();
Chris@122 576 }
Chris@122 577 if (name == "Tuning Frequency") {
Chris@122 578 emit layerParametersChanged();
Chris@122 579 }
Chris@122 580 }
Chris@122 581
Chris@122 582 void
Chris@0 583 SpectrogramLayer::setChannel(int ch)
Chris@0 584 {
Chris@0 585 if (m_channel == ch) return;
Chris@0 586
Chris@95 587 invalidatePixmapCaches();
Chris@0 588 m_channel = ch;
Chris@130 589 invalidateFFTModels();
Chris@9 590
Chris@0 591 emit layerParametersChanged();
Chris@0 592 }
Chris@0 593
Chris@0 594 int
Chris@0 595 SpectrogramLayer::getChannel() const
Chris@0 596 {
Chris@0 597 return m_channel;
Chris@0 598 }
Chris@0 599
Chris@0 600 void
Chris@0 601 SpectrogramLayer::setWindowSize(size_t ws)
Chris@0 602 {
Chris@0 603 if (m_windowSize == ws) return;
Chris@0 604
Chris@95 605 invalidatePixmapCaches();
Chris@0 606
Chris@0 607 m_windowSize = ws;
Chris@109 608 m_fftSize = ws * (m_zeroPadLevel + 1);
Chris@0 609
Chris@130 610 invalidateFFTModels();
Chris@9 611
Chris@9 612 emit layerParametersChanged();
Chris@0 613 }
Chris@0 614
Chris@0 615 size_t
Chris@0 616 SpectrogramLayer::getWindowSize() const
Chris@0 617 {
Chris@0 618 return m_windowSize;
Chris@0 619 }
Chris@0 620
Chris@0 621 void
Chris@97 622 SpectrogramLayer::setWindowHopLevel(size_t v)
Chris@0 623 {
Chris@97 624 if (m_windowHopLevel == v) return;
Chris@0 625
Chris@95 626 invalidatePixmapCaches();
Chris@0 627
Chris@97 628 m_windowHopLevel = v;
Chris@0 629
Chris@130 630 invalidateFFTModels();
Chris@9 631
Chris@9 632 emit layerParametersChanged();
Chris@9 633
Chris@110 634 // fillCache();
Chris@0 635 }
Chris@0 636
Chris@0 637 size_t
Chris@97 638 SpectrogramLayer::getWindowHopLevel() const
Chris@0 639 {
Chris@97 640 return m_windowHopLevel;
Chris@0 641 }
Chris@0 642
Chris@0 643 void
Chris@109 644 SpectrogramLayer::setZeroPadLevel(size_t v)
Chris@109 645 {
Chris@109 646 if (m_zeroPadLevel == v) return;
Chris@109 647
Chris@109 648 invalidatePixmapCaches();
Chris@109 649
Chris@109 650 m_zeroPadLevel = v;
Chris@109 651 m_fftSize = m_windowSize * (v + 1);
Chris@110 652
Chris@130 653 invalidateFFTModels();
Chris@109 654
Chris@109 655 emit layerParametersChanged();
Chris@109 656 }
Chris@109 657
Chris@109 658 size_t
Chris@109 659 SpectrogramLayer::getZeroPadLevel() const
Chris@109 660 {
Chris@109 661 return m_zeroPadLevel;
Chris@109 662 }
Chris@109 663
Chris@109 664 void
Chris@0 665 SpectrogramLayer::setWindowType(WindowType w)
Chris@0 666 {
Chris@0 667 if (m_windowType == w) return;
Chris@0 668
Chris@95 669 invalidatePixmapCaches();
Chris@0 670
Chris@0 671 m_windowType = w;
Chris@110 672
Chris@130 673 invalidateFFTModels();
Chris@9 674
Chris@9 675 emit layerParametersChanged();
Chris@0 676 }
Chris@0 677
Chris@0 678 WindowType
Chris@0 679 SpectrogramLayer::getWindowType() const
Chris@0 680 {
Chris@0 681 return m_windowType;
Chris@0 682 }
Chris@0 683
Chris@0 684 void
Chris@0 685 SpectrogramLayer::setGain(float gain)
Chris@0 686 {
Chris@101 687 // std::cerr << "SpectrogramLayer::setGain(" << gain << ") (my gain is now "
Chris@101 688 // << m_gain << ")" << std::endl;
Chris@55 689
Chris@40 690 if (m_gain == gain) return;
Chris@0 691
Chris@95 692 invalidatePixmapCaches();
Chris@0 693
Chris@0 694 m_gain = gain;
Chris@0 695
Chris@9 696 emit layerParametersChanged();
Chris@0 697 }
Chris@0 698
Chris@0 699 float
Chris@0 700 SpectrogramLayer::getGain() const
Chris@0 701 {
Chris@0 702 return m_gain;
Chris@0 703 }
Chris@0 704
Chris@0 705 void
Chris@37 706 SpectrogramLayer::setThreshold(float threshold)
Chris@37 707 {
Chris@40 708 if (m_threshold == threshold) return;
Chris@37 709
Chris@95 710 invalidatePixmapCaches();
Chris@37 711
Chris@37 712 m_threshold = threshold;
Chris@37 713
Chris@37 714 emit layerParametersChanged();
Chris@37 715 }
Chris@37 716
Chris@37 717 float
Chris@37 718 SpectrogramLayer::getThreshold() const
Chris@37 719 {
Chris@37 720 return m_threshold;
Chris@37 721 }
Chris@37 722
Chris@37 723 void
Chris@37 724 SpectrogramLayer::setMinFrequency(size_t mf)
Chris@37 725 {
Chris@37 726 if (m_minFrequency == mf) return;
Chris@37 727
Chris@187 728 std::cerr << "SpectrogramLayer::setMinFrequency: " << mf << std::endl;
Chris@187 729
Chris@95 730 invalidatePixmapCaches();
Chris@119 731 invalidateMagnitudes();
Chris@37 732
Chris@37 733 m_minFrequency = mf;
Chris@37 734
Chris@37 735 emit layerParametersChanged();
Chris@37 736 }
Chris@37 737
Chris@37 738 size_t
Chris@37 739 SpectrogramLayer::getMinFrequency() const
Chris@37 740 {
Chris@37 741 return m_minFrequency;
Chris@37 742 }
Chris@37 743
Chris@37 744 void
Chris@0 745 SpectrogramLayer::setMaxFrequency(size_t mf)
Chris@0 746 {
Chris@0 747 if (m_maxFrequency == mf) return;
Chris@0 748
Chris@187 749 std::cerr << "SpectrogramLayer::setMaxFrequency: " << mf << std::endl;
Chris@187 750
Chris@95 751 invalidatePixmapCaches();
Chris@119 752 invalidateMagnitudes();
Chris@0 753
Chris@0 754 m_maxFrequency = mf;
Chris@0 755
Chris@9 756 emit layerParametersChanged();
Chris@0 757 }
Chris@0 758
Chris@0 759 size_t
Chris@0 760 SpectrogramLayer::getMaxFrequency() const
Chris@0 761 {
Chris@0 762 return m_maxFrequency;
Chris@0 763 }
Chris@0 764
Chris@0 765 void
Chris@9 766 SpectrogramLayer::setColourRotation(int r)
Chris@9 767 {
Chris@95 768 invalidatePixmapCaches();
Chris@9 769
Chris@9 770 if (r < 0) r = 0;
Chris@9 771 if (r > 256) r = 256;
Chris@9 772 int distance = r - m_colourRotation;
Chris@9 773
Chris@9 774 if (distance != 0) {
Chris@90 775 rotateColourmap(-distance);
Chris@9 776 m_colourRotation = r;
Chris@9 777 }
Chris@9 778
Chris@9 779 emit layerParametersChanged();
Chris@9 780 }
Chris@9 781
Chris@9 782 void
Chris@0 783 SpectrogramLayer::setColourScale(ColourScale colourScale)
Chris@0 784 {
Chris@0 785 if (m_colourScale == colourScale) return;
Chris@0 786
Chris@95 787 invalidatePixmapCaches();
Chris@0 788
Chris@0 789 m_colourScale = colourScale;
Chris@0 790
Chris@9 791 emit layerParametersChanged();
Chris@0 792 }
Chris@0 793
Chris@0 794 SpectrogramLayer::ColourScale
Chris@0 795 SpectrogramLayer::getColourScale() const
Chris@0 796 {
Chris@0 797 return m_colourScale;
Chris@0 798 }
Chris@0 799
Chris@0 800 void
Chris@0 801 SpectrogramLayer::setColourScheme(ColourScheme scheme)
Chris@0 802 {
Chris@0 803 if (m_colourScheme == scheme) return;
Chris@0 804
Chris@95 805 invalidatePixmapCaches();
Chris@0 806
Chris@0 807 m_colourScheme = scheme;
Chris@90 808 setColourmap();
Chris@9 809
Chris@0 810 emit layerParametersChanged();
Chris@0 811 }
Chris@0 812
Chris@0 813 SpectrogramLayer::ColourScheme
Chris@0 814 SpectrogramLayer::getColourScheme() const
Chris@0 815 {
Chris@0 816 return m_colourScheme;
Chris@0 817 }
Chris@0 818
Chris@0 819 void
Chris@0 820 SpectrogramLayer::setFrequencyScale(FrequencyScale frequencyScale)
Chris@0 821 {
Chris@0 822 if (m_frequencyScale == frequencyScale) return;
Chris@0 823
Chris@95 824 invalidatePixmapCaches();
Chris@0 825 m_frequencyScale = frequencyScale;
Chris@9 826
Chris@9 827 emit layerParametersChanged();
Chris@0 828 }
Chris@0 829
Chris@0 830 SpectrogramLayer::FrequencyScale
Chris@0 831 SpectrogramLayer::getFrequencyScale() const
Chris@0 832 {
Chris@0 833 return m_frequencyScale;
Chris@0 834 }
Chris@0 835
Chris@0 836 void
Chris@37 837 SpectrogramLayer::setBinDisplay(BinDisplay binDisplay)
Chris@35 838 {
Chris@37 839 if (m_binDisplay == binDisplay) return;
Chris@35 840
Chris@95 841 invalidatePixmapCaches();
Chris@37 842 m_binDisplay = binDisplay;
Chris@35 843
Chris@35 844 emit layerParametersChanged();
Chris@35 845 }
Chris@35 846
Chris@37 847 SpectrogramLayer::BinDisplay
Chris@37 848 SpectrogramLayer::getBinDisplay() const
Chris@35 849 {
Chris@37 850 return m_binDisplay;
Chris@35 851 }
Chris@35 852
Chris@35 853 void
Chris@36 854 SpectrogramLayer::setNormalizeColumns(bool n)
Chris@36 855 {
Chris@36 856 if (m_normalizeColumns == n) return;
Chris@36 857
Chris@95 858 invalidatePixmapCaches();
Chris@119 859 invalidateMagnitudes();
Chris@36 860 m_normalizeColumns = n;
Chris@36 861
Chris@36 862 emit layerParametersChanged();
Chris@36 863 }
Chris@36 864
Chris@36 865 bool
Chris@36 866 SpectrogramLayer::getNormalizeColumns() const
Chris@36 867 {
Chris@36 868 return m_normalizeColumns;
Chris@36 869 }
Chris@36 870
Chris@36 871 void
Chris@120 872 SpectrogramLayer::setNormalizeVisibleArea(bool n)
Chris@120 873 {
Chris@120 874 if (m_normalizeVisibleArea == n) return;
Chris@120 875
Chris@120 876 invalidatePixmapCaches();
Chris@120 877 invalidateMagnitudes();
Chris@120 878 m_normalizeVisibleArea = n;
Chris@120 879
Chris@120 880 emit layerParametersChanged();
Chris@120 881 }
Chris@120 882
Chris@120 883 bool
Chris@120 884 SpectrogramLayer::getNormalizeVisibleArea() const
Chris@120 885 {
Chris@120 886 return m_normalizeVisibleArea;
Chris@120 887 }
Chris@120 888
Chris@120 889 void
Chris@47 890 SpectrogramLayer::setLayerDormant(const View *v, bool dormant)
Chris@29 891 {
Chris@33 892 if (dormant) {
Chris@33 893
Chris@131 894 if (isLayerDormant(v)) {
Chris@131 895 return;
Chris@131 896 }
Chris@131 897
Chris@131 898 Layer::setLayerDormant(v, true);
Chris@33 899
Chris@95 900 invalidatePixmapCaches();
Chris@95 901 m_pixmapCaches.erase(v);
Chris@114 902
Chris@130 903 if (m_fftModels.find(v) != m_fftModels.end()) {
Chris@193 904
Chris@193 905 if (m_sliceableModel == m_fftModels[v].first) {
Chris@193 906 bool replaced = false;
Chris@193 907 for (ViewFFTMap::iterator i = m_fftModels.begin();
Chris@193 908 i != m_fftModels.end(); ++i) {
Chris@193 909 if (i->second.first != m_sliceableModel) {
Chris@193 910 emit sliceableModelReplaced(m_sliceableModel, i->second.first);
Chris@193 911 replaced = true;
Chris@193 912 break;
Chris@193 913 }
Chris@193 914 }
Chris@193 915 if (!replaced) emit sliceableModelReplaced(m_sliceableModel, 0);
Chris@193 916 }
Chris@193 917
Chris@130 918 delete m_fftModels[v].first;
Chris@130 919 m_fftModels.erase(v);
Chris@114 920 }
Chris@33 921
Chris@33 922 } else {
Chris@33 923
Chris@131 924 Layer::setLayerDormant(v, false);
Chris@33 925 }
Chris@29 926 }
Chris@29 927
Chris@29 928 void
Chris@0 929 SpectrogramLayer::cacheInvalid()
Chris@0 930 {
Chris@95 931 invalidatePixmapCaches();
Chris@119 932 invalidateMagnitudes();
Chris@0 933 }
Chris@0 934
Chris@0 935 void
Chris@0 936 SpectrogramLayer::cacheInvalid(size_t, size_t)
Chris@0 937 {
Chris@0 938 // for now (or forever?)
Chris@0 939 cacheInvalid();
Chris@0 940 }
Chris@0 941
Chris@0 942 void
Chris@0 943 SpectrogramLayer::fillTimerTimedOut()
Chris@0 944 {
Chris@115 945 if (!m_model) return;
Chris@115 946
Chris@115 947 bool allDone = true;
Chris@115 948
Chris@184 949 #ifdef DEBUG_SPECTROGRAM_REPAINT
Chris@184 950 std::cerr << "SpectrogramLayer::fillTimerTimedOut: have " << m_fftModels.size() << " FFT models associated with views" << std::endl;
Chris@184 951 #endif
Chris@184 952
Chris@130 953 for (ViewFFTMap::iterator i = m_fftModels.begin();
Chris@130 954 i != m_fftModels.end(); ++i) {
Chris@115 955
Chris@115 956 const View *v = i->first;
Chris@130 957 const FFTModel *model = i->second.first;
Chris@115 958 size_t lastFill = i->second.second;
Chris@115 959
Chris@130 960 if (model) {
Chris@130 961
Chris@130 962 size_t fill = model->getFillExtent();
Chris@115 963
Chris@0 964 #ifdef DEBUG_SPECTROGRAM_REPAINT
Chris@130 965 std::cerr << "SpectrogramLayer::fillTimerTimedOut: extent for " << model << ": " << fill << ", last " << lastFill << ", total " << m_model->getEndFrame() << std::endl;
Chris@0 966 #endif
Chris@115 967
Chris@115 968 if (fill >= lastFill) {
Chris@115 969 if (fill >= m_model->getEndFrame() && lastFill > 0) {
Chris@0 970 #ifdef DEBUG_SPECTROGRAM_REPAINT
Chris@115 971 std::cerr << "complete!" << std::endl;
Chris@0 972 #endif
Chris@115 973 invalidatePixmapCaches();
Chris@184 974 i->second.second = -1;
Chris@115 975 emit modelChanged();
Chris@115 976
Chris@115 977 } else if (fill > lastFill) {
Chris@0 978 #ifdef DEBUG_SPECTROGRAM_REPAINT
Chris@115 979 std::cerr << "SpectrogramLayer: emitting modelChanged("
Chris@115 980 << lastFill << "," << fill << ")" << std::endl;
Chris@0 981 #endif
Chris@115 982 invalidatePixmapCaches(lastFill, fill);
Chris@184 983 i->second.second = fill;
Chris@115 984 emit modelChanged(lastFill, fill);
Chris@115 985 }
Chris@115 986 } else {
Chris@0 987 #ifdef DEBUG_SPECTROGRAM_REPAINT
Chris@115 988 std::cerr << "SpectrogramLayer: going backwards, emitting modelChanged("
Chris@115 989 << m_model->getStartFrame() << "," << m_model->getEndFrame() << ")" << std::endl;
Chris@0 990 #endif
Chris@115 991 invalidatePixmapCaches();
Chris@184 992 i->second.second = fill;
Chris@115 993 emit modelChanged(m_model->getStartFrame(), m_model->getEndFrame());
Chris@115 994 }
Chris@115 995
Chris@115 996 if (i->second.second >= 0) {
Chris@115 997 allDone = false;
Chris@115 998 }
Chris@115 999 }
Chris@0 1000 }
Chris@115 1001
Chris@115 1002 if (allDone) {
Chris@115 1003 #ifdef DEBUG_SPECTROGRAM_REPAINT
Chris@115 1004 std::cerr << "SpectrogramLayer: all complete!" << std::endl;
Chris@115 1005 #endif
Chris@115 1006 delete m_updateTimer;
Chris@115 1007 m_updateTimer = 0;
Chris@115 1008 }
Chris@0 1009 }
Chris@0 1010
Chris@0 1011 void
Chris@90 1012 SpectrogramLayer::setColourmap()
Chris@0 1013 {
Chris@10 1014 int formerRotation = m_colourRotation;
Chris@10 1015
Chris@38 1016 if (m_colourScheme == BlackOnWhite) {
Chris@86 1017 m_colourMap.setColour(NO_VALUE, Qt::white);
Chris@38 1018 } else {
Chris@86 1019 m_colourMap.setColour(NO_VALUE, Qt::black);
Chris@38 1020 }
Chris@0 1021
Chris@0 1022 for (int pixel = 1; pixel < 256; ++pixel) {
Chris@0 1023
Chris@0 1024 QColor colour;
Chris@0 1025 int hue, px;
Chris@0 1026
Chris@0 1027 switch (m_colourScheme) {
Chris@0 1028
Chris@0 1029 default:
Chris@0 1030 case DefaultColours:
Chris@0 1031 hue = 256 - pixel;
Chris@0 1032 colour = QColor::fromHsv(hue, pixel/2 + 128, pixel);
Chris@77 1033 m_crosshairColour = QColor(255, 150, 50);
Chris@77 1034 // m_crosshairColour = QColor::fromHsv(240, 160, 255);
Chris@0 1035 break;
Chris@0 1036
Chris@0 1037 case WhiteOnBlack:
Chris@0 1038 colour = QColor(pixel, pixel, pixel);
Chris@77 1039 m_crosshairColour = Qt::red;
Chris@0 1040 break;
Chris@0 1041
Chris@0 1042 case BlackOnWhite:
Chris@0 1043 colour = QColor(256-pixel, 256-pixel, 256-pixel);
Chris@77 1044 m_crosshairColour = Qt::darkGreen;
Chris@0 1045 break;
Chris@0 1046
Chris@0 1047 case RedOnBlue:
Chris@0 1048 colour = QColor(pixel > 128 ? (pixel - 128) * 2 : 0, 0,
Chris@0 1049 pixel < 128 ? pixel : (256 - pixel));
Chris@77 1050 m_crosshairColour = Qt::green;
Chris@0 1051 break;
Chris@0 1052
Chris@0 1053 case YellowOnBlack:
Chris@0 1054 px = 256 - pixel;
Chris@0 1055 colour = QColor(px < 64 ? 255 - px/2 :
Chris@0 1056 px < 128 ? 224 - (px - 64) :
Chris@0 1057 px < 192 ? 160 - (px - 128) * 3 / 2 :
Chris@0 1058 256 - px,
Chris@0 1059 pixel,
Chris@0 1060 pixel / 4);
Chris@77 1061 m_crosshairColour = QColor::fromHsv(240, 255, 255);
Chris@0 1062 break;
Chris@0 1063
Chris@71 1064 case BlueOnBlack:
Chris@71 1065 colour = QColor::fromHsv
Chris@71 1066 (240, pixel > 226 ? 256 - (pixel - 226) * 8 : 255,
Chris@71 1067 (pixel * pixel) / 255);
Chris@77 1068 m_crosshairColour = Qt::red;
Chris@71 1069 break;
Chris@71 1070
Chris@40 1071 case Rainbow:
Chris@40 1072 hue = 250 - pixel;
Chris@40 1073 if (hue < 0) hue += 256;
Chris@40 1074 colour = QColor::fromHsv(pixel, 255, 255);
Chris@77 1075 m_crosshairColour = Qt::white;
Chris@0 1076 break;
Chris@0 1077 }
Chris@0 1078
Chris@86 1079 m_colourMap.setColour(pixel, colour);
Chris@0 1080 }
Chris@9 1081
Chris@9 1082 m_colourRotation = 0;
Chris@90 1083 rotateColourmap(m_colourRotation - formerRotation);
Chris@10 1084 m_colourRotation = formerRotation;
Chris@9 1085 }
Chris@9 1086
Chris@9 1087 void
Chris@90 1088 SpectrogramLayer::rotateColourmap(int distance)
Chris@9 1089 {
Chris@31 1090 QColor newPixels[256];
Chris@9 1091
Chris@86 1092 newPixels[NO_VALUE] = m_colourMap.getColour(NO_VALUE);
Chris@9 1093
Chris@9 1094 for (int pixel = 1; pixel < 256; ++pixel) {
Chris@9 1095 int target = pixel + distance;
Chris@9 1096 while (target < 1) target += 255;
Chris@9 1097 while (target > 255) target -= 255;
Chris@86 1098 newPixels[target] = m_colourMap.getColour(pixel);
Chris@9 1099 }
Chris@9 1100
Chris@9 1101 for (int pixel = 0; pixel < 256; ++pixel) {
Chris@86 1102 m_colourMap.setColour(pixel, newPixels[pixel]);
Chris@9 1103 }
Chris@0 1104 }
Chris@0 1105
Chris@38 1106 float
Chris@38 1107 SpectrogramLayer::calculateFrequency(size_t bin,
Chris@38 1108 size_t windowSize,
Chris@38 1109 size_t windowIncrement,
Chris@38 1110 size_t sampleRate,
Chris@38 1111 float oldPhase,
Chris@38 1112 float newPhase,
Chris@38 1113 bool &steadyState)
Chris@38 1114 {
Chris@38 1115 // At frequency f, phase shift of 2pi (one cycle) happens in 1/f sec.
Chris@38 1116 // At hopsize h and sample rate sr, one hop happens in h/sr sec.
Chris@38 1117 // At window size w, for bin b, f is b*sr/w.
Chris@38 1118 // thus 2pi phase shift happens in w/(b*sr) sec.
Chris@38 1119 // We need to know what phase shift we expect from h/sr sec.
Chris@38 1120 // -> 2pi * ((h/sr) / (w/(b*sr)))
Chris@38 1121 // = 2pi * ((h * b * sr) / (w * sr))
Chris@38 1122 // = 2pi * (h * b) / w.
Chris@38 1123
Chris@38 1124 float frequency = (float(bin) * sampleRate) / windowSize;
Chris@38 1125
Chris@38 1126 float expectedPhase =
Chris@38 1127 oldPhase + (2.0 * M_PI * bin * windowIncrement) / windowSize;
Chris@38 1128
Chris@104 1129 float phaseError = princargf(newPhase - expectedPhase);
Chris@38 1130
Chris@142 1131 if (fabsf(phaseError) < (1.1f * (windowIncrement * M_PI) / windowSize)) {
Chris@38 1132
Chris@38 1133 // The new frequency estimate based on the phase error
Chris@38 1134 // resulting from assuming the "native" frequency of this bin
Chris@38 1135
Chris@38 1136 float newFrequency =
Chris@38 1137 (sampleRate * (expectedPhase + phaseError - oldPhase)) /
Chris@38 1138 (2 * M_PI * windowIncrement);
Chris@38 1139
Chris@38 1140 steadyState = true;
Chris@38 1141 return newFrequency;
Chris@38 1142 }
Chris@38 1143
Chris@38 1144 steadyState = false;
Chris@38 1145 return frequency;
Chris@38 1146 }
Chris@38 1147
Chris@38 1148 unsigned char
Chris@119 1149 SpectrogramLayer::getDisplayValue(View *v, float input) const
Chris@38 1150 {
Chris@38 1151 int value;
Chris@37 1152
Chris@120 1153 float min = 0.f;
Chris@120 1154 float max = 1.f;
Chris@120 1155
Chris@120 1156 if (m_normalizeVisibleArea) {
Chris@120 1157 min = m_viewMags[v].getMin();
Chris@120 1158 max = m_viewMags[v].getMax();
Chris@120 1159 } else if (!m_normalizeColumns) {
Chris@120 1160 if (m_colourScale == LinearColourScale ||
Chris@120 1161 m_colourScale == MeterColourScale) {
Chris@120 1162 max = 0.1f;
Chris@120 1163 }
Chris@120 1164 }
Chris@120 1165
Chris@119 1166 float thresh = -80.f;
Chris@119 1167
Chris@119 1168 if (max == 0.f) max = 1.f;
Chris@119 1169 if (max == min) min = max - 0.0001f;
Chris@119 1170
Chris@40 1171 switch (m_colourScale) {
Chris@40 1172
Chris@40 1173 default:
Chris@40 1174 case LinearColourScale:
Chris@119 1175 // value = int
Chris@119 1176 // (input * (m_normalizeColumns ? 1.0 : 50.0) * 255.0) + 1;
Chris@119 1177 value = int(((input - min) / (max - min)) * 255.f) + 1;
Chris@40 1178 break;
Chris@40 1179
Chris@40 1180 case MeterColourScale:
Chris@119 1181 // value = AudioLevel::multiplier_to_preview
Chris@119 1182 // (input * (m_normalizeColumns ? 1.0 : 50.0), 255) + 1;
Chris@119 1183 value = AudioLevel::multiplier_to_preview((input - min) / (max - min), 255) + 1;
Chris@40 1184 break;
Chris@40 1185
Chris@40 1186 case dBColourScale:
Chris@119 1187 //!!! experiment with normalizing the visible area this way.
Chris@119 1188 //In any case, we need to have some indication of what the dB
Chris@119 1189 //scale is relative to.
Chris@133 1190 input = input / max;
Chris@133 1191 if (input > 0.f) {
Chris@133 1192 input = 10.f * log10f(input);
Chris@133 1193 } else {
Chris@133 1194 input = thresh;
Chris@133 1195 }
Chris@119 1196 if (min > 0.f) {
Chris@119 1197 thresh = 10.f * log10f(min);
Chris@119 1198 if (thresh < -80.f) thresh = -80.f;
Chris@119 1199 }
Chris@119 1200 input = (input - thresh) / (-thresh);
Chris@119 1201 if (input < 0.f) input = 0.f;
Chris@119 1202 if (input > 1.f) input = 1.f;
Chris@119 1203 value = int(input * 255.f) + 1;
Chris@119 1204 break;
Chris@119 1205
Chris@119 1206 case OtherColourScale:
Chris@119 1207 //!!! the "Other" scale is just where our current experiments go
Chris@119 1208 //!!! power rather than v
Chris@133 1209 input = (input * input) / (max * max);
Chris@133 1210 if (input > 0.f) {
Chris@133 1211 input = 10.f * log10f(input);
Chris@133 1212 } else {
Chris@133 1213 input = thresh;
Chris@133 1214 }
Chris@119 1215 if (min > 0.f) {
Chris@119 1216 thresh = 10.f * log10f(min * min);
Chris@119 1217 if (thresh < -80.f) thresh = -80.f;
Chris@119 1218 }
Chris@119 1219 input = (input - thresh) / (-thresh);
Chris@119 1220 if (input < 0.f) input = 0.f;
Chris@119 1221 if (input > 1.f) input = 1.f;
Chris@119 1222 value = int(input * 255.f) + 1;
Chris@119 1223 break;
Chris@119 1224
Chris@119 1225 /*!!!
Chris@119 1226 input = 10.f * log10f(input * input);
Chris@119 1227 input = 1.f / (1.f + expf(- (input + 20.f) / 10.f));
Chris@119 1228
Chris@119 1229 if (input < 0.f) input = 0.f;
Chris@119 1230 if (input > 1.f) input = 1.f;
Chris@119 1231 value = int(input * 255.f) + 1;
Chris@119 1232 */
Chris@40 1233 break;
Chris@40 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@38 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@40 1267 case dBColourScale:
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@119 1274 case OtherColourScale:
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@44 1322 int h = v->height();
Chris@0 1323 if (y < 0 || y >= h) return false;
Chris@0 1324
Chris@38 1325 int sr = m_model->getSampleRate();
Chris@40 1326 float minf = getEffectiveMinFrequency();
Chris@40 1327 float maxf = getEffectiveMaxFrequency();
Chris@0 1328
Chris@38 1329 bool logarithmic = (m_frequencyScale == LogFrequencyScale);
Chris@38 1330
Chris@130 1331 //!!! wrong for smoothing -- wrong fft size for fft model
Chris@114 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@38 1336 // Now map these on to actual bins
Chris@38 1337
Chris@107 1338 int b0 = int((q0 * m_fftSize) / sr);
Chris@107 1339 int b1 = int((q1 * m_fftSize) / sr);
Chris@0 1340
Chris@40 1341 //!!! this is supposed to return fractions-of-bins, as it were, hence the floats
Chris@38 1342 q0 = b0;
Chris@38 1343 q1 = b1;
Chris@38 1344
Chris@107 1345 // q0 = (b0 * sr) / m_fftSize;
Chris@107 1346 // q1 = (b1 * sr) / m_fftSize;
Chris@0 1347
Chris@0 1348 return true;
Chris@0 1349 }
Chris@38 1350
Chris@0 1351 bool
Chris@44 1352 SpectrogramLayer::getXBinRange(View *v, int x, float &s0, float &s1) const
Chris@0 1353 {
Chris@21 1354 size_t modelStart = m_model->getStartFrame();
Chris@21 1355 size_t modelEnd = m_model->getEndFrame();
Chris@0 1356
Chris@0 1357 // Each pixel column covers an exact range of sample frames:
Chris@44 1358 int f0 = v->getFrameForX(x) - modelStart;
Chris@44 1359 int f1 = v->getFrameForX(x + 1) - modelStart - 1;
Chris@20 1360
Chris@41 1361 if (f1 < int(modelStart) || f0 > int(modelEnd)) {
Chris@41 1362 return false;
Chris@41 1363 }
Chris@20 1364
Chris@0 1365 // And that range may be drawn from a possibly non-integral
Chris@0 1366 // range of spectrogram windows:
Chris@0 1367
Chris@0 1368 size_t windowIncrement = getWindowIncrement();
Chris@0 1369 s0 = float(f0) / windowIncrement;
Chris@0 1370 s1 = float(f1) / windowIncrement;
Chris@0 1371
Chris@0 1372 return true;
Chris@0 1373 }
Chris@0 1374
Chris@0 1375 bool
Chris@44 1376 SpectrogramLayer::getXBinSourceRange(View *v, int x, RealTime &min, RealTime &max) const
Chris@0 1377 {
Chris@0 1378 float s0 = 0, s1 = 0;
Chris@44 1379 if (!getXBinRange(v, x, s0, s1)) return false;
Chris@0 1380
Chris@0 1381 int s0i = int(s0 + 0.001);
Chris@0 1382 int s1i = int(s1);
Chris@0 1383
Chris@0 1384 int windowIncrement = getWindowIncrement();
Chris@0 1385 int w0 = s0i * windowIncrement - (m_windowSize - windowIncrement)/2;
Chris@0 1386 int w1 = s1i * windowIncrement + windowIncrement +
Chris@0 1387 (m_windowSize - windowIncrement)/2 - 1;
Chris@0 1388
Chris@0 1389 min = RealTime::frame2RealTime(w0, m_model->getSampleRate());
Chris@0 1390 max = RealTime::frame2RealTime(w1, m_model->getSampleRate());
Chris@0 1391 return true;
Chris@0 1392 }
Chris@0 1393
Chris@0 1394 bool
Chris@44 1395 SpectrogramLayer::getYBinSourceRange(View *v, int y, float &freqMin, float &freqMax)
Chris@0 1396 const
Chris@0 1397 {
Chris@0 1398 float q0 = 0, q1 = 0;
Chris@44 1399 if (!getYBinRange(v, y, q0, q1)) return false;
Chris@0 1400
Chris@0 1401 int q0i = int(q0 + 0.001);
Chris@0 1402 int q1i = int(q1);
Chris@0 1403
Chris@0 1404 int sr = m_model->getSampleRate();
Chris@0 1405
Chris@0 1406 for (int q = q0i; q <= q1i; ++q) {
Chris@121 1407 if (q == q0i) freqMin = (sr * q) / m_fftSize;
Chris@121 1408 if (q == q1i) freqMax = (sr * (q+1)) / m_fftSize;
Chris@0 1409 }
Chris@0 1410 return true;
Chris@0 1411 }
Chris@35 1412
Chris@35 1413 bool
Chris@44 1414 SpectrogramLayer::getAdjustedYBinSourceRange(View *v, int x, int y,
Chris@35 1415 float &freqMin, float &freqMax,
Chris@35 1416 float &adjFreqMin, float &adjFreqMax)
Chris@35 1417 const
Chris@35 1418 {
Chris@130 1419 FFTModel *fft = getFFTModel(v);
Chris@114 1420 if (!fft) return false;
Chris@110 1421
Chris@35 1422 float s0 = 0, s1 = 0;
Chris@44 1423 if (!getXBinRange(v, x, s0, s1)) return false;
Chris@35 1424
Chris@35 1425 float q0 = 0, q1 = 0;
Chris@44 1426 if (!getYBinRange(v, y, q0, q1)) return false;
Chris@35 1427
Chris@35 1428 int s0i = int(s0 + 0.001);
Chris@35 1429 int s1i = int(s1);
Chris@35 1430
Chris@35 1431 int q0i = int(q0 + 0.001);
Chris@35 1432 int q1i = int(q1);
Chris@35 1433
Chris@35 1434 int sr = m_model->getSampleRate();
Chris@35 1435
Chris@38 1436 size_t windowSize = m_windowSize;
Chris@38 1437 size_t windowIncrement = getWindowIncrement();
Chris@38 1438
Chris@35 1439 bool haveAdj = false;
Chris@35 1440
Chris@37 1441 bool peaksOnly = (m_binDisplay == PeakBins ||
Chris@37 1442 m_binDisplay == PeakFrequencies);
Chris@37 1443
Chris@35 1444 for (int q = q0i; q <= q1i; ++q) {
Chris@35 1445
Chris@35 1446 for (int s = s0i; s <= s1i; ++s) {
Chris@35 1447
Chris@160 1448 if (!fft->isColumnAvailable(s)) continue;
Chris@117 1449
Chris@35 1450 float binfreq = (sr * q) / m_windowSize;
Chris@35 1451 if (q == q0i) freqMin = binfreq;
Chris@35 1452 if (q == q1i) freqMax = binfreq;
Chris@37 1453
Chris@114 1454 if (peaksOnly && !fft->isLocalPeak(s, q)) continue;
Chris@38 1455
Chris@114 1456 if (!fft->isOverThreshold(s, q, m_threshold)) continue;
Chris@38 1457
Chris@38 1458 float freq = binfreq;
Chris@38 1459 bool steady = false;
Chris@40 1460
Chris@114 1461 if (s < int(fft->getWidth()) - 1) {
Chris@38 1462
Chris@38 1463 freq = calculateFrequency(q,
Chris@38 1464 windowSize,
Chris@38 1465 windowIncrement,
Chris@38 1466 sr,
Chris@114 1467 fft->getPhaseAt(s, q),
Chris@114 1468 fft->getPhaseAt(s+1, q),
Chris@38 1469 steady);
Chris@35 1470
Chris@38 1471 if (!haveAdj || freq < adjFreqMin) adjFreqMin = freq;
Chris@38 1472 if (!haveAdj || freq > adjFreqMax) adjFreqMax = freq;
Chris@35 1473
Chris@35 1474 haveAdj = true;
Chris@35 1475 }
Chris@35 1476 }
Chris@35 1477 }
Chris@35 1478
Chris@35 1479 if (!haveAdj) {
Chris@40 1480 adjFreqMin = adjFreqMax = 0.0;
Chris@35 1481 }
Chris@35 1482
Chris@35 1483 return haveAdj;
Chris@35 1484 }
Chris@0 1485
Chris@0 1486 bool
Chris@44 1487 SpectrogramLayer::getXYBinSourceRange(View *v, int x, int y,
Chris@38 1488 float &min, float &max,
Chris@38 1489 float &phaseMin, float &phaseMax) const
Chris@0 1490 {
Chris@0 1491 float q0 = 0, q1 = 0;
Chris@44 1492 if (!getYBinRange(v, y, q0, q1)) return false;
Chris@0 1493
Chris@0 1494 float s0 = 0, s1 = 0;
Chris@44 1495 if (!getXBinRange(v, x, s0, s1)) return false;
Chris@0 1496
Chris@0 1497 int q0i = int(q0 + 0.001);
Chris@0 1498 int q1i = int(q1);
Chris@0 1499
Chris@0 1500 int s0i = int(s0 + 0.001);
Chris@0 1501 int s1i = int(s1);
Chris@0 1502
Chris@37 1503 bool rv = false;
Chris@37 1504
Chris@122 1505 size_t zp = getZeroPadLevel(v);
Chris@122 1506 q0i *= zp + 1;
Chris@122 1507 q1i *= zp + 1;
Chris@122 1508
Chris@130 1509 FFTModel *fft = getFFTModel(v);
Chris@0 1510
Chris@114 1511 if (fft) {
Chris@114 1512
Chris@114 1513 int cw = fft->getWidth();
Chris@114 1514 int ch = fft->getHeight();
Chris@0 1515
Chris@110 1516 min = 0.0;
Chris@110 1517 max = 0.0;
Chris@110 1518 phaseMin = 0.0;
Chris@110 1519 phaseMax = 0.0;
Chris@110 1520 bool have = false;
Chris@0 1521
Chris@110 1522 for (int q = q0i; q <= q1i; ++q) {
Chris@110 1523 for (int s = s0i; s <= s1i; ++s) {
Chris@110 1524 if (s >= 0 && q >= 0 && s < cw && q < ch) {
Chris@117 1525
Chris@160 1526 if (!fft->isColumnAvailable(s)) continue;
Chris@110 1527
Chris@110 1528 float value;
Chris@38 1529
Chris@114 1530 value = fft->getPhaseAt(s, q);
Chris@110 1531 if (!have || value < phaseMin) { phaseMin = value; }
Chris@110 1532 if (!have || value > phaseMax) { phaseMax = value; }
Chris@91 1533
Chris@114 1534 value = fft->getMagnitudeAt(s, q);
Chris@110 1535 if (!have || value < min) { min = value; }
Chris@110 1536 if (!have || value > max) { max = value; }
Chris@110 1537
Chris@110 1538 have = true;
Chris@110 1539 }
Chris@110 1540 }
Chris@110 1541 }
Chris@110 1542
Chris@110 1543 if (have) {
Chris@110 1544 rv = true;
Chris@110 1545 }
Chris@0 1546 }
Chris@0 1547
Chris@37 1548 return rv;
Chris@0 1549 }
Chris@0 1550
Chris@114 1551 size_t
Chris@114 1552 SpectrogramLayer::getZeroPadLevel(const View *v) const
Chris@114 1553 {
Chris@114 1554 //!!! tidy all this stuff
Chris@114 1555
Chris@114 1556 if (m_binDisplay != AllBins) return 0;
Chris@118 1557 if (!Preferences::getInstance()->getSmoothSpectrogram()) return 0;
Chris@114 1558 if (m_frequencyScale == LogFrequencyScale) return 3;
Chris@114 1559
Chris@114 1560 int sr = m_model->getSampleRate();
Chris@114 1561
Chris@184 1562 size_t maxbin = m_fftSize / 2;
Chris@114 1563 if (m_maxFrequency > 0) {
Chris@184 1564 maxbin = int((double(m_maxFrequency) * m_fftSize) / sr + 0.1);
Chris@184 1565 if (maxbin > m_fftSize / 2) maxbin = m_fftSize / 2;
Chris@114 1566 }
Chris@114 1567
Chris@114 1568 size_t minbin = 1;
Chris@114 1569 if (m_minFrequency > 0) {
Chris@114 1570 minbin = int((double(m_minFrequency) * m_fftSize) / sr + 0.1);
Chris@114 1571 if (minbin < 1) minbin = 1;
Chris@184 1572 if (minbin >= maxbin) minbin = maxbin - 1;
Chris@114 1573 }
Chris@114 1574
Chris@118 1575 float perPixel =
Chris@118 1576 float(v->height()) /
Chris@184 1577 float((maxbin - minbin) / (m_zeroPadLevel + 1));
Chris@118 1578
Chris@118 1579 if (perPixel > 2.8) {
Chris@118 1580 return 3; // 4x oversampling
Chris@118 1581 } else if (perPixel > 1.5) {
Chris@118 1582 return 1; // 2x
Chris@114 1583 } else {
Chris@118 1584 return 0; // 1x
Chris@114 1585 }
Chris@114 1586 }
Chris@114 1587
Chris@114 1588 size_t
Chris@114 1589 SpectrogramLayer::getFFTSize(const View *v) const
Chris@114 1590 {
Chris@114 1591 return m_fftSize * (getZeroPadLevel(v) + 1);
Chris@114 1592 }
Chris@114 1593
Chris@130 1594 FFTModel *
Chris@130 1595 SpectrogramLayer::getFFTModel(const View *v) const
Chris@114 1596 {
Chris@114 1597 if (!m_model) return 0;
Chris@114 1598
Chris@114 1599 size_t fftSize = getFFTSize(v);
Chris@114 1600
Chris@130 1601 if (m_fftModels.find(v) != m_fftModels.end()) {
Chris@184 1602 if (m_fftModels[v].first == 0) {
Chris@184 1603 #ifdef DEBUG_SPECTROGRAM_REPAINT
Chris@184 1604 std::cerr << "SpectrogramLayer::getFFTModel(" << v << "): Found null model" << std::endl;
Chris@184 1605 #endif
Chris@184 1606 return 0;
Chris@184 1607 }
Chris@184 1608 if (m_fftModels[v].first->getHeight() != fftSize / 2 + 1) {
Chris@184 1609 #ifdef DEBUG_SPECTROGRAM_REPAINT
Chris@184 1610 std::cerr << "SpectrogramLayer::getFFTModel(" << v << "): Found a model with the wrong height (" << m_fftModels[v].first->getHeight() << ", wanted " << (fftSize / 2 + 1) << ")" << std::endl;
Chris@184 1611 #endif
Chris@130 1612 delete m_fftModels[v].first;
Chris@130 1613 m_fftModels.erase(v);
Chris@184 1614 } else {
Chris@184 1615 #ifdef DEBUG_SPECTROGRAM_REPAINT
Chris@187 1616 std::cerr << "SpectrogramLayer::getFFTModel(" << v << "): Found a good model of height " << m_fftModels[v].first->getHeight() << std::endl;
Chris@184 1617 #endif
Chris@184 1618 return m_fftModels[v].first;
Chris@114 1619 }
Chris@114 1620 }
Chris@114 1621
Chris@130 1622 if (m_fftModels.find(v) == m_fftModels.end()) {
Chris@169 1623
Chris@169 1624 FFTModel *model = new FFTModel(m_model,
Chris@169 1625 m_channel,
Chris@169 1626 m_windowType,
Chris@169 1627 m_windowSize,
Chris@169 1628 getWindowIncrement(),
Chris@169 1629 fftSize,
Chris@169 1630 true,
Chris@169 1631 m_candidateFillStartFrame);
Chris@169 1632
Chris@178 1633 if (!model->isOK()) {
Chris@178 1634 QMessageBox::critical
Chris@178 1635 (0, tr("FFT cache failed"),
Chris@178 1636 tr("Failed to create the FFT model for this spectrogram.\n"
Chris@178 1637 "There may be insufficient memory or disc space to continue."));
Chris@178 1638 delete model;
Chris@178 1639 m_fftModels[v] = FFTFillPair(0, 0);
Chris@178 1640 return 0;
Chris@178 1641 }
Chris@178 1642
Chris@193 1643 if (!m_sliceableModel) {
Chris@193 1644 std::cerr << "SpectrogramLayer: emitting sliceableModelReplaced(0, " << model << ")" << std::endl;
Chris@193 1645 ((SpectrogramLayer *)this)->sliceableModelReplaced(0, model);
Chris@193 1646 m_sliceableModel = model;
Chris@193 1647 }
Chris@193 1648
Chris@169 1649 m_fftModels[v] = FFTFillPair(model, 0);
Chris@169 1650
Chris@169 1651 model->resume();
Chris@114 1652
Chris@114 1653 delete m_updateTimer;
Chris@114 1654 m_updateTimer = new QTimer((SpectrogramLayer *)this);
Chris@114 1655 connect(m_updateTimer, SIGNAL(timeout()),
Chris@114 1656 this, SLOT(fillTimerTimedOut()));
Chris@114 1657 m_updateTimer->start(200);
Chris@114 1658 }
Chris@114 1659
Chris@130 1660 return m_fftModels[v].first;
Chris@114 1661 }
Chris@114 1662
Chris@193 1663 const Model *
Chris@193 1664 SpectrogramLayer::getSliceableModel() const
Chris@193 1665 {
Chris@193 1666 if (m_sliceableModel) return m_sliceableModel;
Chris@193 1667 if (m_fftModels.empty()) return 0;
Chris@193 1668 m_sliceableModel = m_fftModels.begin()->second.first;
Chris@193 1669 return m_sliceableModel;
Chris@193 1670 }
Chris@193 1671
Chris@114 1672 void
Chris@130 1673 SpectrogramLayer::invalidateFFTModels()
Chris@114 1674 {
Chris@130 1675 for (ViewFFTMap::iterator i = m_fftModels.begin();
Chris@130 1676 i != m_fftModels.end(); ++i) {
Chris@115 1677 delete i->second.first;
Chris@114 1678 }
Chris@114 1679
Chris@130 1680 m_fftModels.clear();
Chris@193 1681
Chris@193 1682 if (m_sliceableModel) {
Chris@193 1683 std::cerr << "SpectrogramLayer: emitting sliceableModelReplaced(" << m_sliceableModel << ", 0)" << std::endl;
Chris@193 1684 emit sliceableModelReplaced(m_sliceableModel, 0);
Chris@193 1685 m_sliceableModel = 0;
Chris@193 1686 }
Chris@114 1687 }
Chris@114 1688
Chris@0 1689 void
Chris@119 1690 SpectrogramLayer::invalidateMagnitudes()
Chris@119 1691 {
Chris@119 1692 m_viewMags.clear();
Chris@119 1693 for (std::vector<MagnitudeRange>::iterator i = m_columnMags.begin();
Chris@119 1694 i != m_columnMags.end(); ++i) {
Chris@119 1695 *i = MagnitudeRange();
Chris@119 1696 }
Chris@119 1697 }
Chris@119 1698
Chris@119 1699 bool
Chris@119 1700 SpectrogramLayer::updateViewMagnitudes(View *v) const
Chris@119 1701 {
Chris@119 1702 MagnitudeRange mag;
Chris@119 1703
Chris@119 1704 int x0 = 0, x1 = v->width();
Chris@119 1705 float s00 = 0, s01 = 0, s10 = 0, s11 = 0;
Chris@119 1706
Chris@119 1707 getXBinRange(v, x0, s00, s01);
Chris@119 1708 getXBinRange(v, x1, s10, s11);
Chris@119 1709
Chris@119 1710 int s0 = int(std::min(s00, s10) + 0.0001);
Chris@119 1711 int s1 = int(std::max(s01, s11));
Chris@119 1712
Chris@119 1713 if (m_columnMags.size() <= s1) {
Chris@119 1714 m_columnMags.resize(s1 + 1);
Chris@119 1715 }
Chris@119 1716
Chris@119 1717 for (int s = s0; s <= s1; ++s) {
Chris@119 1718 if (m_columnMags[s].isSet()) {
Chris@119 1719 mag.sample(m_columnMags[s]);
Chris@119 1720 }
Chris@119 1721 }
Chris@119 1722
Chris@184 1723 #ifdef DEBUG_SPECTROGRAM_REPAINT
Chris@119 1724 std::cerr << "SpectrogramLayer::updateViewMagnitudes returning from cols "
Chris@119 1725 << s0 << " -> " << s1 << " inclusive" << std::endl;
Chris@184 1726 #endif
Chris@119 1727
Chris@119 1728 if (!mag.isSet()) return false;
Chris@119 1729 if (mag == m_viewMags[v]) return false;
Chris@119 1730 m_viewMags[v] = mag;
Chris@119 1731 return true;
Chris@119 1732 }
Chris@119 1733
Chris@119 1734 void
Chris@44 1735 SpectrogramLayer::paint(View *v, QPainter &paint, QRect rect) const
Chris@0 1736 {
Chris@55 1737 if (m_colourScheme == BlackOnWhite) {
Chris@55 1738 v->setLightBackground(true);
Chris@55 1739 } else {
Chris@55 1740 v->setLightBackground(false);
Chris@55 1741 }
Chris@55 1742
Chris@161 1743 Profiler profiler("SpectrogramLayer::paint", true);
Chris@0 1744 #ifdef DEBUG_SPECTROGRAM_REPAINT
Chris@95 1745 std::cerr << "SpectrogramLayer::paint(): m_model is " << m_model << ", zoom level is " << v->getZoomLevel() << ", m_updateTimer " << m_updateTimer << std::endl;
Chris@95 1746
Chris@95 1747 std::cerr << "rect is " << rect.x() << "," << rect.y() << " " << rect.width() << "x" << rect.height() << std::endl;
Chris@0 1748 #endif
Chris@95 1749
Chris@133 1750 long startFrame = v->getStartFrame();
Chris@133 1751 if (startFrame < 0) m_candidateFillStartFrame = 0;
Chris@133 1752 else m_candidateFillStartFrame = startFrame;
Chris@44 1753
Chris@0 1754 if (!m_model || !m_model->isOK() || !m_model->isReady()) {
Chris@0 1755 return;
Chris@0 1756 }
Chris@0 1757
Chris@47 1758 if (isLayerDormant(v)) {
Chris@48 1759 std::cerr << "SpectrogramLayer::paint(): Layer is dormant, making it undormant again" << std::endl;
Chris@29 1760 }
Chris@29 1761
Chris@48 1762 // Need to do this even if !isLayerDormant, as that could mean v
Chris@48 1763 // is not in the dormancy map at all -- we need it to be present
Chris@48 1764 // and accountable for when determining whether we need the cache
Chris@48 1765 // in the cache-fill thread above.
Chris@131 1766 //!!! no longer use cache-fill thread
Chris@131 1767 const_cast<SpectrogramLayer *>(this)->Layer::setLayerDormant(v, false);
Chris@48 1768
Chris@114 1769 size_t fftSize = getFFTSize(v);
Chris@130 1770 FFTModel *fft = getFFTModel(v);
Chris@114 1771 if (!fft) {
Chris@130 1772 std::cerr << "ERROR: SpectrogramLayer::paint(): No FFT model, returning" << std::endl;
Chris@0 1773 return;
Chris@0 1774 }
Chris@0 1775
Chris@95 1776 PixmapCache &cache = m_pixmapCaches[v];
Chris@95 1777
Chris@95 1778 #ifdef DEBUG_SPECTROGRAM_REPAINT
Chris@95 1779 std::cerr << "SpectrogramLayer::paint(): pixmap cache valid area " << cache.validArea.x() << ", " << cache.validArea.y() << ", " << cache.validArea.width() << "x" << cache.validArea.height() << std::endl;
Chris@95 1780 #endif
Chris@95 1781
Chris@0 1782 bool stillCacheing = (m_updateTimer != 0);
Chris@0 1783
Chris@0 1784 #ifdef DEBUG_SPECTROGRAM_REPAINT
Chris@0 1785 std::cerr << "SpectrogramLayer::paint(): Still cacheing = " << stillCacheing << std::endl;
Chris@0 1786 #endif
Chris@0 1787
Chris@44 1788 int zoomLevel = v->getZoomLevel();
Chris@0 1789
Chris@0 1790 int x0 = 0;
Chris@44 1791 int x1 = v->width();
Chris@0 1792 int y0 = 0;
Chris@44 1793 int y1 = v->height();
Chris@0 1794
Chris@0 1795 bool recreateWholePixmapCache = true;
Chris@0 1796
Chris@95 1797 x0 = rect.left();
Chris@95 1798 x1 = rect.right() + 1;
Chris@95 1799 y0 = rect.top();
Chris@95 1800 y1 = rect.bottom() + 1;
Chris@95 1801
Chris@95 1802 if (cache.validArea.width() > 0) {
Chris@95 1803
Chris@95 1804 if (int(cache.zoomLevel) == zoomLevel &&
Chris@95 1805 cache.pixmap.width() == v->width() &&
Chris@95 1806 cache.pixmap.height() == v->height()) {
Chris@95 1807
Chris@95 1808 if (v->getXForFrame(cache.startFrame) ==
Chris@95 1809 v->getXForFrame(startFrame) &&
Chris@95 1810 cache.validArea.x() <= x0 &&
Chris@95 1811 cache.validArea.x() + cache.validArea.width() >= x1) {
Chris@0 1812
Chris@0 1813 #ifdef DEBUG_SPECTROGRAM_REPAINT
Chris@0 1814 std::cerr << "SpectrogramLayer: pixmap cache good" << std::endl;
Chris@0 1815 #endif
Chris@0 1816
Chris@95 1817 paint.drawPixmap(rect, cache.pixmap, rect);
Chris@121 1818 illuminateLocalFeatures(v, paint);
Chris@0 1819 return;
Chris@0 1820
Chris@0 1821 } else {
Chris@0 1822
Chris@0 1823 #ifdef DEBUG_SPECTROGRAM_REPAINT
Chris@0 1824 std::cerr << "SpectrogramLayer: pixmap cache partially OK" << std::endl;
Chris@0 1825 #endif
Chris@0 1826
Chris@0 1827 recreateWholePixmapCache = false;
Chris@0 1828
Chris@95 1829 int dx = v->getXForFrame(cache.startFrame) -
Chris@44 1830 v->getXForFrame(startFrame);
Chris@0 1831
Chris@0 1832 #ifdef DEBUG_SPECTROGRAM_REPAINT
Chris@95 1833 std::cerr << "SpectrogramLayer: dx = " << dx << " (pixmap cache " << cache.pixmap.width() << "x" << cache.pixmap.height() << ")" << std::endl;
Chris@0 1834 #endif
Chris@0 1835
Chris@95 1836 if (dx != 0 &&
Chris@95 1837 dx > -cache.pixmap.width() &&
Chris@95 1838 dx < cache.pixmap.width()) {
Chris@0 1839
Chris@0 1840 #if defined(Q_WS_WIN32) || defined(Q_WS_MAC)
Chris@0 1841 // Copying a pixmap to itself doesn't work
Chris@0 1842 // properly on Windows or Mac (it only works when
Chris@0 1843 // moving in one direction).
Chris@0 1844
Chris@0 1845 //!!! Need a utility function for this
Chris@0 1846
Chris@0 1847 static QPixmap *tmpPixmap = 0;
Chris@0 1848 if (!tmpPixmap ||
Chris@95 1849 tmpPixmap->width() != cache.pixmap.width() ||
Chris@95 1850 tmpPixmap->height() != cache.pixmap.height()) {
Chris@0 1851 delete tmpPixmap;
Chris@95 1852 tmpPixmap = new QPixmap(cache.pixmap.width(),
Chris@95 1853 cache.pixmap.height());
Chris@0 1854 }
Chris@0 1855 QPainter cachePainter;
Chris@0 1856 cachePainter.begin(tmpPixmap);
Chris@95 1857 cachePainter.drawPixmap(0, 0, cache.pixmap);
Chris@0 1858 cachePainter.end();
Chris@95 1859 cachePainter.begin(&cache.pixmap);
Chris@0 1860 cachePainter.drawPixmap(dx, 0, *tmpPixmap);
Chris@0 1861 cachePainter.end();
Chris@0 1862 #else
Chris@95 1863 QPainter cachePainter(&cache.pixmap);
Chris@95 1864 cachePainter.drawPixmap(dx, 0, cache.pixmap);
Chris@0 1865 cachePainter.end();
Chris@0 1866 #endif
Chris@0 1867
Chris@95 1868 int px = cache.validArea.x();
Chris@95 1869 int pw = cache.validArea.width();
Chris@0 1870
Chris@0 1871 if (dx < 0) {
Chris@95 1872 x0 = cache.pixmap.width() + dx;
Chris@95 1873 x1 = cache.pixmap.width();
Chris@95 1874 px += dx;
Chris@95 1875 if (px < 0) {
Chris@95 1876 pw += px;
Chris@95 1877 px = 0;
Chris@95 1878 if (pw < 0) pw = 0;
Chris@95 1879 }
Chris@0 1880 } else {
Chris@0 1881 x0 = 0;
Chris@0 1882 x1 = dx;
Chris@95 1883 px += dx;
Chris@95 1884 if (px + pw > cache.pixmap.width()) {
Chris@95 1885 pw = int(cache.pixmap.width()) - px;
Chris@95 1886 if (pw < 0) pw = 0;
Chris@95 1887 }
Chris@0 1888 }
Chris@95 1889
Chris@95 1890 cache.validArea =
Chris@95 1891 QRect(px, cache.validArea.y(),
Chris@95 1892 pw, cache.validArea.height());
Chris@95 1893
Chris@95 1894 paint.drawPixmap(rect & cache.validArea,
Chris@95 1895 cache.pixmap,
Chris@95 1896 rect & cache.validArea);
Chris@0 1897 }
Chris@0 1898 }
Chris@0 1899 } else {
Chris@0 1900 #ifdef DEBUG_SPECTROGRAM_REPAINT
Chris@0 1901 std::cerr << "SpectrogramLayer: pixmap cache useless" << std::endl;
Chris@0 1902 #endif
Chris@95 1903 cache.validArea = QRect();
Chris@0 1904 }
Chris@0 1905 }
Chris@95 1906
Chris@92 1907 /*
Chris@0 1908 if (stillCacheing) {
Chris@0 1909 x0 = rect.left();
Chris@0 1910 x1 = rect.right() + 1;
Chris@0 1911 y0 = rect.top();
Chris@0 1912 y1 = rect.bottom() + 1;
Chris@0 1913 }
Chris@92 1914 */
Chris@95 1915
Chris@133 1916 if (updateViewMagnitudes(v)) {
Chris@184 1917 #ifdef DEBUG_SPECTROGRAM_REPAINT
Chris@133 1918 std::cerr << "SpectrogramLayer: magnitude range changed to [" << m_viewMags[v].getMin() << "->" << m_viewMags[v].getMax() << "]" << std::endl;
Chris@184 1919 #endif
Chris@133 1920 recreateWholePixmapCache = true;
Chris@133 1921 } else {
Chris@184 1922 #ifdef DEBUG_SPECTROGRAM_REPAINT
Chris@133 1923 std::cerr << "No change in magnitude range [" << m_viewMags[v].getMin() << "->" << m_viewMags[v].getMax() << "]" << std::endl;
Chris@184 1924 #endif
Chris@133 1925 }
Chris@133 1926
Chris@95 1927 if (recreateWholePixmapCache) {
Chris@95 1928 x0 = 0;
Chris@95 1929 x1 = v->width();
Chris@95 1930 }
Chris@95 1931
Chris@188 1932 //!!! This width should really depend on how fast the machine is
Chris@188 1933 //at redrawing the spectrogram. We could fairly easily time that,
Chris@188 1934 //in this function, and adjust accordingly. The following is
Chris@188 1935 //probably about as small as the block width should go.
Chris@96 1936 int paintBlockWidth = (300000 / zoomLevel);
Chris@96 1937 if (paintBlockWidth < 20) paintBlockWidth = 20;
Chris@96 1938
Chris@96 1939 if (cache.validArea.width() > 0) {
Chris@96 1940
Chris@96 1941 int vx0 = 0, vx1 = 0;
Chris@96 1942 vx0 = cache.validArea.x();
Chris@96 1943 vx1 = cache.validArea.x() + cache.validArea.width();
Chris@96 1944
Chris@96 1945 #ifdef DEBUG_SPECTROGRAM_REPAINT
Chris@96 1946 std::cerr << "x0 " << x0 << ", x1 " << x1 << ", vx0 " << vx0 << ", vx1 " << vx1 << ", paintBlockWidth " << paintBlockWidth << std::endl;
Chris@96 1947 #endif
Chris@96 1948 if (x0 < vx0) {
Chris@96 1949 if (x0 + paintBlockWidth < vx0) {
Chris@96 1950 x0 = vx0 - paintBlockWidth;
Chris@96 1951 } else {
Chris@96 1952 x0 = 0;
Chris@96 1953 }
Chris@96 1954 } else if (x0 > vx1) {
Chris@96 1955 x0 = vx1;
Chris@96 1956 }
Chris@95 1957
Chris@96 1958 if (x1 < vx0) {
Chris@96 1959 x1 = vx0;
Chris@96 1960 } else if (x1 > vx1) {
Chris@96 1961 if (vx1 + paintBlockWidth < x1) {
Chris@96 1962 x1 = vx1 + paintBlockWidth;
Chris@96 1963 } else {
Chris@96 1964 x1 = v->width();
Chris@95 1965 }
Chris@96 1966 }
Chris@95 1967
Chris@96 1968 cache.validArea = QRect
Chris@96 1969 (std::min(vx0, x0), cache.validArea.y(),
Chris@96 1970 std::max(vx1 - std::min(vx0, x0),
Chris@96 1971 x1 - std::min(vx0, x0)),
Chris@96 1972 cache.validArea.height());
Chris@95 1973
Chris@96 1974 } else {
Chris@96 1975 if (x1 > x0 + paintBlockWidth) {
Chris@133 1976 int sfx = x1;
Chris@133 1977 if (startFrame < 0) sfx = v->getXForFrame(0);
Chris@133 1978 if (sfx >= x0 && sfx + paintBlockWidth <= x1) {
Chris@133 1979 x0 = sfx;
Chris@133 1980 x1 = x0 + paintBlockWidth;
Chris@133 1981 } else {
Chris@133 1982 int mid = (x1 + x0) / 2;
Chris@133 1983 x0 = mid - paintBlockWidth/2;
Chris@133 1984 x1 = x0 + paintBlockWidth;
Chris@133 1985 }
Chris@95 1986 }
Chris@96 1987 cache.validArea = QRect(x0, 0, x1 - x0, v->height());
Chris@95 1988 }
Chris@95 1989
Chris@0 1990 int w = x1 - x0;
Chris@0 1991 int h = y1 - y0;
Chris@0 1992
Chris@95 1993 #ifdef DEBUG_SPECTROGRAM_REPAINT
Chris@95 1994 std::cerr << "x0 " << x0 << ", x1 " << x1 << ", w " << w << ", h " << h << std::endl;
Chris@95 1995 #endif
Chris@95 1996
Chris@95 1997 if (m_drawBuffer.width() < w || m_drawBuffer.height() < h) {
Chris@95 1998 m_drawBuffer = QImage(w, h, QImage::Format_RGB32);
Chris@95 1999 }
Chris@95 2000
Chris@97 2001 m_drawBuffer.fill(m_colourMap.getColour(0).rgb());
Chris@35 2002
Chris@37 2003 int sr = m_model->getSampleRate();
Chris@122 2004
Chris@122 2005 // Set minFreq and maxFreq to the frequency extents of the possibly
Chris@122 2006 // zero-padded visible bin range, and displayMinFreq and displayMaxFreq
Chris@122 2007 // to the actual scale frequency extents (presumably not zero padded).
Chris@35 2008
Chris@184 2009 size_t maxbin = fftSize / 2;
Chris@35 2010 if (m_maxFrequency > 0) {
Chris@184 2011 maxbin = int((double(m_maxFrequency) * fftSize) / sr + 0.1);
Chris@184 2012 if (maxbin > fftSize / 2) maxbin = fftSize / 2;
Chris@35 2013 }
Chris@111 2014
Chris@40 2015 size_t minbin = 1;
Chris@37 2016 if (m_minFrequency > 0) {
Chris@114 2017 minbin = int((double(m_minFrequency) * fftSize) / sr + 0.1);
Chris@40 2018 if (minbin < 1) minbin = 1;
Chris@184 2019 if (minbin >= maxbin) minbin = maxbin - 1;
Chris@37 2020 }
Chris@37 2021
Chris@114 2022 float minFreq = (float(minbin) * sr) / fftSize;
Chris@184 2023 float maxFreq = (float(maxbin) * sr) / fftSize;
Chris@0 2024
Chris@122 2025 float displayMinFreq = minFreq;
Chris@122 2026 float displayMaxFreq = maxFreq;
Chris@122 2027
Chris@122 2028 if (fftSize != m_fftSize) {
Chris@122 2029 displayMinFreq = getEffectiveMinFrequency();
Chris@122 2030 displayMaxFreq = getEffectiveMaxFrequency();
Chris@122 2031 }
Chris@122 2032
Chris@92 2033 float ymag[h];
Chris@92 2034 float ydiv[h];
Chris@184 2035 float yval[maxbin + 1]; //!!! cache this?
Chris@92 2036
Chris@38 2037 size_t increment = getWindowIncrement();
Chris@40 2038
Chris@40 2039 bool logarithmic = (m_frequencyScale == LogFrequencyScale);
Chris@38 2040
Chris@184 2041 for (size_t q = minbin; q <= maxbin; ++q) {
Chris@114 2042 float f0 = (float(q) * sr) / fftSize;
Chris@122 2043 yval[q] = v->getYForFrequency(f0, displayMinFreq, displayMaxFreq,
Chris@122 2044 logarithmic);
Chris@122 2045 // std::cerr << "min: " << minFreq << ", max: " << maxFreq << ", yval[" << q << "]: " << yval[q] << std::endl;
Chris@92 2046 }
Chris@92 2047
Chris@119 2048 MagnitudeRange overallMag = m_viewMags[v];
Chris@119 2049 bool overallMagChanged = false;
Chris@119 2050
Chris@162 2051 bool fftSuspended = false;
Chris@131 2052
Chris@137 2053 #ifdef DEBUG_SPECTROGRAM_REPAINT
Chris@137 2054 std::cerr << (float(v->getFrameForX(1) - v->getFrameForX(0)) / increment) << " bins per pixel" << std::endl;
Chris@137 2055 #endif
Chris@137 2056
Chris@35 2057 for (int x = 0; x < w; ++x) {
Chris@35 2058
Chris@35 2059 for (int y = 0; y < h; ++y) {
Chris@134 2060 ymag[y] = 0.f;
Chris@134 2061 ydiv[y] = 0.f;
Chris@35 2062 }
Chris@35 2063
Chris@35 2064 float s0 = 0, s1 = 0;
Chris@35 2065
Chris@44 2066 if (!getXBinRange(v, x0 + x, s0, s1)) {
Chris@95 2067 assert(x <= m_drawBuffer.width());
Chris@35 2068 continue;
Chris@35 2069 }
Chris@35 2070
Chris@35 2071 int s0i = int(s0 + 0.001);
Chris@35 2072 int s1i = int(s1);
Chris@35 2073
Chris@114 2074 if (s1i >= fft->getWidth()) {
Chris@114 2075 if (s0i >= fft->getWidth()) {
Chris@45 2076 continue;
Chris@45 2077 } else {
Chris@45 2078 s1i = s0i;
Chris@45 2079 }
Chris@45 2080 }
Chris@92 2081
Chris@92 2082 for (int s = s0i; s <= s1i; ++s) {
Chris@92 2083
Chris@160 2084 if (!fft->isColumnAvailable(s)) continue;
Chris@162 2085
Chris@162 2086 if (!fftSuspended) {
Chris@162 2087 fft->suspendWrites();
Chris@162 2088 fftSuspended = true;
Chris@162 2089 }
Chris@162 2090
Chris@119 2091 MagnitudeRange mag;
Chris@92 2092
Chris@184 2093 for (size_t q = minbin; q < maxbin; ++q) {
Chris@92 2094
Chris@92 2095 float y0 = yval[q + 1];
Chris@92 2096 float y1 = yval[q];
Chris@92 2097
Chris@40 2098 if (m_binDisplay == PeakBins ||
Chris@40 2099 m_binDisplay == PeakFrequencies) {
Chris@114 2100 if (!fft->isLocalPeak(s, q)) continue;
Chris@40 2101 }
Chris@114 2102
Chris@114 2103 if (m_threshold != 0.f &&
Chris@114 2104 !fft->isOverThreshold(s, q, m_threshold)) {
Chris@114 2105 continue;
Chris@114 2106 }
Chris@40 2107
Chris@35 2108 float sprop = 1.0;
Chris@35 2109 if (s == s0i) sprop *= (s + 1) - s0;
Chris@35 2110 if (s == s1i) sprop *= s1 - s;
Chris@35 2111
Chris@38 2112 if (m_binDisplay == PeakFrequencies &&
Chris@114 2113 s < int(fft->getWidth()) - 1) {
Chris@35 2114
Chris@38 2115 bool steady = false;
Chris@92 2116 float f = calculateFrequency(q,
Chris@38 2117 m_windowSize,
Chris@38 2118 increment,
Chris@38 2119 sr,
Chris@114 2120 fft->getPhaseAt(s, q),
Chris@114 2121 fft->getPhaseAt(s+1, q),
Chris@38 2122 steady);
Chris@40 2123
Chris@44 2124 y0 = y1 = v->getYForFrequency
Chris@122 2125 (f, displayMinFreq, displayMaxFreq, logarithmic);
Chris@35 2126 }
Chris@38 2127
Chris@35 2128 int y0i = int(y0 + 0.001);
Chris@35 2129 int y1i = int(y1);
Chris@35 2130
Chris@92 2131 float value;
Chris@92 2132
Chris@92 2133 if (m_colourScale == PhaseColourScale) {
Chris@114 2134 value = fft->getPhaseAt(s, q);
Chris@92 2135 } else if (m_normalizeColumns) {
Chris@119 2136 value = fft->getNormalizedMagnitudeAt(s, q);
Chris@119 2137 mag.sample(value);
Chris@119 2138 value *= m_gain;
Chris@92 2139 } else {
Chris@119 2140 value = fft->getMagnitudeAt(s, q);
Chris@119 2141 mag.sample(value);
Chris@119 2142 value *= m_gain;
Chris@92 2143 }
Chris@92 2144
Chris@35 2145 for (int y = y0i; y <= y1i; ++y) {
Chris@35 2146
Chris@35 2147 if (y < 0 || y >= h) continue;
Chris@35 2148
Chris@35 2149 float yprop = sprop;
Chris@35 2150 if (y == y0i) yprop *= (y + 1) - y0;
Chris@35 2151 if (y == y1i) yprop *= y1 - y;
Chris@37 2152 ymag[y] += yprop * value;
Chris@35 2153 ydiv[y] += yprop;
Chris@35 2154 }
Chris@35 2155 }
Chris@119 2156
Chris@119 2157 if (mag.isSet()) {
Chris@119 2158
Chris@119 2159 m_columnMags[s].sample(mag);
Chris@119 2160
Chris@119 2161 if (overallMag.sample(mag)) {
Chris@119 2162 //!!! scaling would change here
Chris@119 2163 overallMagChanged = true;
Chris@119 2164 std::cerr << "Overall mag changed (again?) at column " << s << ", to [" << overallMag.getMin() << "->" << overallMag.getMax() << "]" << std::endl;
Chris@119 2165 }
Chris@119 2166 }
Chris@35 2167 }
Chris@35 2168
Chris@35 2169 for (int y = 0; y < h; ++y) {
Chris@35 2170
Chris@35 2171 if (ydiv[y] > 0.0) {
Chris@40 2172
Chris@40 2173 unsigned char pixel = 0;
Chris@40 2174
Chris@38 2175 float avg = ymag[y] / ydiv[y];
Chris@138 2176 pixel = getDisplayValue(v, avg);
Chris@40 2177
Chris@95 2178 assert(x <= m_drawBuffer.width());
Chris@86 2179 QColor c = m_colourMap.getColour(pixel);
Chris@95 2180 m_drawBuffer.setPixel(x, y,
Chris@95 2181 qRgb(c.red(), c.green(), c.blue()));
Chris@35 2182 }
Chris@35 2183 }
Chris@35 2184 }
Chris@35 2185
Chris@119 2186 if (overallMagChanged) {
Chris@119 2187 m_viewMags[v] = overallMag;
Chris@119 2188 std::cerr << "Overall mag is now [" << m_viewMags[v].getMin() << "->" << m_viewMags[v].getMax() << "] - will be updating" << std::endl;
Chris@119 2189 } else {
Chris@119 2190 std::cerr << "Overall mag unchanged at [" << m_viewMags[v].getMin() << "->" << m_viewMags[v].getMax() << "]" << std::endl;
Chris@119 2191 }
Chris@119 2192
Chris@161 2193 Profiler profiler2("SpectrogramLayer::paint: draw image", true);
Chris@137 2194
Chris@95 2195 paint.drawImage(x0, y0, m_drawBuffer, 0, 0, w, h);
Chris@0 2196
Chris@0 2197 if (recreateWholePixmapCache) {
Chris@95 2198 cache.pixmap = QPixmap(v->width(), v->height());
Chris@0 2199 }
Chris@0 2200
Chris@95 2201 QPainter cachePainter(&cache.pixmap);
Chris@95 2202 cachePainter.drawImage(x0, y0, m_drawBuffer, 0, 0, w, h);
Chris@0 2203 cachePainter.end();
Chris@119 2204
Chris@120 2205 if (!m_normalizeVisibleArea || !overallMagChanged) {
Chris@0 2206
Chris@119 2207 cache.startFrame = startFrame;
Chris@119 2208 cache.zoomLevel = zoomLevel;
Chris@119 2209
Chris@119 2210 if (cache.validArea.x() > 0) {
Chris@95 2211 #ifdef DEBUG_SPECTROGRAM_REPAINT
Chris@158 2212 std::cerr << "SpectrogramLayer::paint() updating left (0, "
Chris@158 2213 << cache.validArea.x() << ")" << std::endl;
Chris@95 2214 #endif
Chris@119 2215 v->update(0, 0, cache.validArea.x(), v->height());
Chris@119 2216 }
Chris@119 2217
Chris@119 2218 if (cache.validArea.x() + cache.validArea.width() <
Chris@119 2219 cache.pixmap.width()) {
Chris@95 2220 #ifdef DEBUG_SPECTROGRAM_REPAINT
Chris@119 2221 std::cerr << "SpectrogramLayer::paint() updating right ("
Chris@119 2222 << cache.validArea.x() + cache.validArea.width()
Chris@119 2223 << ", "
Chris@119 2224 << cache.pixmap.width() - (cache.validArea.x() +
Chris@119 2225 cache.validArea.width())
Chris@119 2226 << ")" << std::endl;
Chris@95 2227 #endif
Chris@119 2228 v->update(cache.validArea.x() + cache.validArea.width(),
Chris@119 2229 0,
Chris@119 2230 cache.pixmap.width() - (cache.validArea.x() +
Chris@119 2231 cache.validArea.width()),
Chris@119 2232 v->height());
Chris@119 2233 }
Chris@119 2234 } else {
Chris@119 2235 // overallMagChanged
Chris@119 2236 cache.validArea = QRect();
Chris@119 2237 v->update();
Chris@95 2238 }
Chris@0 2239
Chris@121 2240 illuminateLocalFeatures(v, paint);
Chris@120 2241
Chris@0 2242 #ifdef DEBUG_SPECTROGRAM_REPAINT
Chris@0 2243 std::cerr << "SpectrogramLayer::paint() returning" << std::endl;
Chris@0 2244 #endif
Chris@131 2245
Chris@162 2246 if (fftSuspended) fft->resume();
Chris@0 2247 }
Chris@0 2248
Chris@121 2249 void
Chris@121 2250 SpectrogramLayer::illuminateLocalFeatures(View *v, QPainter &paint) const
Chris@121 2251 {
Chris@121 2252 QPoint localPos;
Chris@121 2253 if (!v->shouldIlluminateLocalFeatures(this, localPos) || !m_model) {
Chris@121 2254 return;
Chris@121 2255 }
Chris@121 2256
Chris@180 2257 // std::cerr << "SpectrogramLayer: illuminateLocalFeatures("
Chris@180 2258 // << localPos.x() << "," << localPos.y() << ")" << std::endl;
Chris@121 2259
Chris@121 2260 float s0, s1;
Chris@121 2261 float f0, f1;
Chris@121 2262
Chris@121 2263 if (getXBinRange(v, localPos.x(), s0, s1) &&
Chris@121 2264 getYBinSourceRange(v, localPos.y(), f0, f1)) {
Chris@121 2265
Chris@121 2266 int s0i = int(s0 + 0.001);
Chris@121 2267 int s1i = int(s1);
Chris@121 2268
Chris@121 2269 int x0 = v->getXForFrame(s0i * getWindowIncrement());
Chris@121 2270 int x1 = v->getXForFrame((s1i + 1) * getWindowIncrement());
Chris@121 2271
Chris@121 2272 int y1 = getYForFrequency(v, f1);
Chris@121 2273 int y0 = getYForFrequency(v, f0);
Chris@121 2274
Chris@180 2275 // std::cerr << "SpectrogramLayer: illuminate "
Chris@180 2276 // << x0 << "," << y1 << " -> " << x1 << "," << y0 << std::endl;
Chris@121 2277
Chris@121 2278 paint.setPen(Qt::white);
Chris@133 2279
Chris@133 2280 //!!! should we be using paintCrosshairs for this?
Chris@133 2281
Chris@121 2282 paint.drawRect(x0, y1, x1 - x0 + 1, y0 - y1 + 1);
Chris@121 2283 }
Chris@121 2284 }
Chris@121 2285
Chris@42 2286 float
Chris@44 2287 SpectrogramLayer::getYForFrequency(View *v, float frequency) const
Chris@42 2288 {
Chris@44 2289 return v->getYForFrequency(frequency,
Chris@44 2290 getEffectiveMinFrequency(),
Chris@44 2291 getEffectiveMaxFrequency(),
Chris@44 2292 m_frequencyScale == LogFrequencyScale);
Chris@42 2293 }
Chris@42 2294
Chris@42 2295 float
Chris@44 2296 SpectrogramLayer::getFrequencyForY(View *v, int y) const
Chris@42 2297 {
Chris@44 2298 return v->getFrequencyForY(y,
Chris@44 2299 getEffectiveMinFrequency(),
Chris@44 2300 getEffectiveMaxFrequency(),
Chris@44 2301 m_frequencyScale == LogFrequencyScale);
Chris@42 2302 }
Chris@42 2303
Chris@0 2304 int
Chris@115 2305 SpectrogramLayer::getCompletion(View *v) const
Chris@0 2306 {
Chris@115 2307 if (m_updateTimer == 0) return 100;
Chris@130 2308 if (m_fftModels.find(v) == m_fftModels.end()) return 100;
Chris@130 2309
Chris@130 2310 size_t completion = m_fftModels[v].first->getCompletion();
Chris@115 2311 std::cerr << "SpectrogramLayer::getCompletion: completion = " << completion << std::endl;
Chris@0 2312 return completion;
Chris@0 2313 }
Chris@0 2314
Chris@28 2315 bool
Chris@101 2316 SpectrogramLayer::getValueExtents(float &min, float &max,
Chris@101 2317 bool &logarithmic, QString &unit) const
Chris@79 2318 {
Chris@133 2319 //!!!
Chris@133 2320 // min = getEffectiveMinFrequency();
Chris@133 2321 // max = getEffectiveMaxFrequency();
Chris@133 2322
Chris@133 2323 if (!m_model) return false;
Chris@133 2324
Chris@133 2325 int sr = m_model->getSampleRate();
Chris@133 2326 min = float(sr) / m_fftSize;
Chris@133 2327 max = float(sr) / 2;
Chris@133 2328
Chris@101 2329 logarithmic = (m_frequencyScale == LogFrequencyScale);
Chris@79 2330 unit = "Hz";
Chris@79 2331 return true;
Chris@79 2332 }
Chris@79 2333
Chris@79 2334 bool
Chris@101 2335 SpectrogramLayer::getDisplayExtents(float &min, float &max) const
Chris@101 2336 {
Chris@101 2337 min = getEffectiveMinFrequency();
Chris@101 2338 max = getEffectiveMaxFrequency();
Chris@187 2339 std::cerr << "SpectrogramLayer::getDisplayExtents: " << min << "->" << max << std::endl;
Chris@101 2340 return true;
Chris@101 2341 }
Chris@101 2342
Chris@101 2343 bool
Chris@120 2344 SpectrogramLayer::setDisplayExtents(float min, float max)
Chris@120 2345 {
Chris@120 2346 if (!m_model) return false;
Chris@187 2347
Chris@187 2348 std::cerr << "SpectrogramLayer::setDisplayExtents: " << min << "->" << max << std::endl;
Chris@187 2349
Chris@120 2350 if (min < 0) min = 0;
Chris@120 2351 if (max > m_model->getSampleRate()/2) max = m_model->getSampleRate()/2;
Chris@120 2352
Chris@120 2353 size_t minf = lrintf(min);
Chris@120 2354 size_t maxf = lrintf(max);
Chris@120 2355
Chris@120 2356 if (m_minFrequency == minf && m_maxFrequency == maxf) return true;
Chris@120 2357
Chris@120 2358 invalidatePixmapCaches();
Chris@120 2359 invalidateMagnitudes();
Chris@120 2360
Chris@120 2361 m_minFrequency = minf;
Chris@120 2362 m_maxFrequency = maxf;
Chris@120 2363
Chris@120 2364 emit layerParametersChanged();
Chris@120 2365
Chris@133 2366 int vs = getCurrentVerticalZoomStep();
Chris@133 2367 if (vs != m_lastEmittedZoomStep) {
Chris@133 2368 emit verticalZoomChanged();
Chris@133 2369 m_lastEmittedZoomStep = vs;
Chris@133 2370 }
Chris@133 2371
Chris@120 2372 return true;
Chris@120 2373 }
Chris@120 2374
Chris@120 2375 bool
Chris@44 2376 SpectrogramLayer::snapToFeatureFrame(View *v, int &frame,
Chris@28 2377 size_t &resolution,
Chris@28 2378 SnapType snap) const
Chris@13 2379 {
Chris@13 2380 resolution = getWindowIncrement();
Chris@28 2381 int left = (frame / resolution) * resolution;
Chris@28 2382 int right = left + resolution;
Chris@28 2383
Chris@28 2384 switch (snap) {
Chris@28 2385 case SnapLeft: frame = left; break;
Chris@28 2386 case SnapRight: frame = right; break;
Chris@28 2387 case SnapNearest:
Chris@28 2388 case SnapNeighbouring:
Chris@28 2389 if (frame - left > right - frame) frame = right;
Chris@28 2390 else frame = left;
Chris@28 2391 break;
Chris@28 2392 }
Chris@28 2393
Chris@28 2394 return true;
Chris@28 2395 }
Chris@13 2396
Chris@77 2397 bool
Chris@77 2398 SpectrogramLayer::getCrosshairExtents(View *v, QPainter &paint,
Chris@77 2399 QPoint cursorPos,
Chris@77 2400 std::vector<QRect> &extents) const
Chris@77 2401 {
Chris@77 2402 QRect vertical(cursorPos.x() - 12, 0, 12, v->height());
Chris@77 2403 extents.push_back(vertical);
Chris@77 2404
Chris@77 2405 QRect horizontal(0, cursorPos.y(), cursorPos.x(), 1);
Chris@77 2406 extents.push_back(horizontal);
Chris@77 2407
Chris@77 2408 return true;
Chris@77 2409 }
Chris@77 2410
Chris@77 2411 void
Chris@77 2412 SpectrogramLayer::paintCrosshairs(View *v, QPainter &paint,
Chris@77 2413 QPoint cursorPos) const
Chris@77 2414 {
Chris@77 2415 paint.save();
Chris@77 2416 paint.setPen(m_crosshairColour);
Chris@77 2417
Chris@77 2418 paint.drawLine(0, cursorPos.y(), cursorPos.x() - 1, cursorPos.y());
Chris@77 2419 paint.drawLine(cursorPos.x(), 0, cursorPos.x(), v->height());
Chris@77 2420
Chris@77 2421 float fundamental = getFrequencyForY(v, cursorPos.y());
Chris@77 2422
Chris@77 2423 int harmonic = 2;
Chris@77 2424
Chris@77 2425 while (harmonic < 100) {
Chris@77 2426
Chris@77 2427 float hy = lrintf(getYForFrequency(v, fundamental * harmonic));
Chris@77 2428 if (hy < 0 || hy > v->height()) break;
Chris@77 2429
Chris@77 2430 int len = 7;
Chris@77 2431
Chris@77 2432 if (harmonic % 2 == 0) {
Chris@77 2433 if (harmonic % 4 == 0) {
Chris@77 2434 len = 12;
Chris@77 2435 } else {
Chris@77 2436 len = 10;
Chris@77 2437 }
Chris@77 2438 }
Chris@77 2439
Chris@77 2440 paint.drawLine(cursorPos.x() - len,
Chris@77 2441 hy,
Chris@77 2442 cursorPos.x(),
Chris@77 2443 hy);
Chris@77 2444
Chris@77 2445 ++harmonic;
Chris@77 2446 }
Chris@77 2447
Chris@77 2448 paint.restore();
Chris@77 2449 }
Chris@77 2450
Chris@25 2451 QString
Chris@44 2452 SpectrogramLayer::getFeatureDescription(View *v, QPoint &pos) const
Chris@25 2453 {
Chris@25 2454 int x = pos.x();
Chris@25 2455 int y = pos.y();
Chris@0 2456
Chris@25 2457 if (!m_model || !m_model->isOK()) return "";
Chris@0 2458
Chris@38 2459 float magMin = 0, magMax = 0;
Chris@38 2460 float phaseMin = 0, phaseMax = 0;
Chris@0 2461 float freqMin = 0, freqMax = 0;
Chris@35 2462 float adjFreqMin = 0, adjFreqMax = 0;
Chris@25 2463 QString pitchMin, pitchMax;
Chris@0 2464 RealTime rtMin, rtMax;
Chris@0 2465
Chris@38 2466 bool haveValues = false;
Chris@0 2467
Chris@44 2468 if (!getXBinSourceRange(v, x, rtMin, rtMax)) {
Chris@38 2469 return "";
Chris@38 2470 }
Chris@44 2471 if (getXYBinSourceRange(v, x, y, magMin, magMax, phaseMin, phaseMax)) {
Chris@38 2472 haveValues = true;
Chris@38 2473 }
Chris@0 2474
Chris@35 2475 QString adjFreqText = "", adjPitchText = "";
Chris@35 2476
Chris@38 2477 if (m_binDisplay == PeakFrequencies) {
Chris@35 2478
Chris@44 2479 if (!getAdjustedYBinSourceRange(v, x, y, freqMin, freqMax,
Chris@38 2480 adjFreqMin, adjFreqMax)) {
Chris@38 2481 return "";
Chris@38 2482 }
Chris@35 2483
Chris@35 2484 if (adjFreqMin != adjFreqMax) {
Chris@65 2485 adjFreqText = tr("Peak Frequency:\t%1 - %2 Hz\n")
Chris@35 2486 .arg(adjFreqMin).arg(adjFreqMax);
Chris@35 2487 } else {
Chris@65 2488 adjFreqText = tr("Peak Frequency:\t%1 Hz\n")
Chris@35 2489 .arg(adjFreqMin);
Chris@38 2490 }
Chris@38 2491
Chris@38 2492 QString pmin = Pitch::getPitchLabelForFrequency(adjFreqMin);
Chris@38 2493 QString pmax = Pitch::getPitchLabelForFrequency(adjFreqMax);
Chris@38 2494
Chris@38 2495 if (pmin != pmax) {
Chris@65 2496 adjPitchText = tr("Peak Pitch:\t%3 - %4\n").arg(pmin).arg(pmax);
Chris@38 2497 } else {
Chris@65 2498 adjPitchText = tr("Peak Pitch:\t%2\n").arg(pmin);
Chris@35 2499 }
Chris@35 2500
Chris@35 2501 } else {
Chris@35 2502
Chris@44 2503 if (!getYBinSourceRange(v, y, freqMin, freqMax)) return "";
Chris@35 2504 }
Chris@35 2505
Chris@25 2506 QString text;
Chris@25 2507
Chris@25 2508 if (rtMin != rtMax) {
Chris@25 2509 text += tr("Time:\t%1 - %2\n")
Chris@25 2510 .arg(rtMin.toText(true).c_str())
Chris@25 2511 .arg(rtMax.toText(true).c_str());
Chris@25 2512 } else {
Chris@25 2513 text += tr("Time:\t%1\n")
Chris@25 2514 .arg(rtMin.toText(true).c_str());
Chris@0 2515 }
Chris@0 2516
Chris@25 2517 if (freqMin != freqMax) {
Chris@65 2518 text += tr("%1Bin Frequency:\t%2 - %3 Hz\n%4Bin Pitch:\t%5 - %6\n")
Chris@65 2519 .arg(adjFreqText)
Chris@25 2520 .arg(freqMin)
Chris@25 2521 .arg(freqMax)
Chris@65 2522 .arg(adjPitchText)
Chris@65 2523 .arg(Pitch::getPitchLabelForFrequency(freqMin))
Chris@65 2524 .arg(Pitch::getPitchLabelForFrequency(freqMax));
Chris@65 2525 } else {
Chris@65 2526 text += tr("%1Bin Frequency:\t%2 Hz\n%3Bin Pitch:\t%4\n")
Chris@35 2527 .arg(adjFreqText)
Chris@25 2528 .arg(freqMin)
Chris@65 2529 .arg(adjPitchText)
Chris@65 2530 .arg(Pitch::getPitchLabelForFrequency(freqMin));
Chris@25 2531 }
Chris@25 2532
Chris@38 2533 if (haveValues) {
Chris@38 2534 float dbMin = AudioLevel::multiplier_to_dB(magMin);
Chris@38 2535 float dbMax = AudioLevel::multiplier_to_dB(magMax);
Chris@43 2536 QString dbMinString;
Chris@43 2537 QString dbMaxString;
Chris@43 2538 if (dbMin == AudioLevel::DB_FLOOR) {
Chris@43 2539 dbMinString = tr("-Inf");
Chris@43 2540 } else {
Chris@43 2541 dbMinString = QString("%1").arg(lrintf(dbMin));
Chris@43 2542 }
Chris@43 2543 if (dbMax == AudioLevel::DB_FLOOR) {
Chris@43 2544 dbMaxString = tr("-Inf");
Chris@43 2545 } else {
Chris@43 2546 dbMaxString = QString("%1").arg(lrintf(dbMax));
Chris@43 2547 }
Chris@25 2548 if (lrintf(dbMin) != lrintf(dbMax)) {
Chris@25 2549 text += tr("dB:\t%1 - %2").arg(lrintf(dbMin)).arg(lrintf(dbMax));
Chris@25 2550 } else {
Chris@25 2551 text += tr("dB:\t%1").arg(lrintf(dbMin));
Chris@25 2552 }
Chris@38 2553 if (phaseMin != phaseMax) {
Chris@38 2554 text += tr("\nPhase:\t%1 - %2").arg(phaseMin).arg(phaseMax);
Chris@38 2555 } else {
Chris@38 2556 text += tr("\nPhase:\t%1").arg(phaseMin);
Chris@38 2557 }
Chris@25 2558 }
Chris@25 2559
Chris@25 2560 return text;
Chris@0 2561 }
Chris@25 2562
Chris@0 2563 int
Chris@40 2564 SpectrogramLayer::getColourScaleWidth(QPainter &paint) const
Chris@40 2565 {
Chris@40 2566 int cw;
Chris@40 2567
Chris@119 2568 cw = paint.fontMetrics().width("-80dB");
Chris@119 2569
Chris@119 2570 /*!!!
Chris@40 2571 switch (m_colourScale) {
Chris@40 2572 default:
Chris@40 2573 case LinearColourScale:
Chris@40 2574 cw = paint.fontMetrics().width(QString("0.00"));
Chris@40 2575 break;
Chris@40 2576
Chris@40 2577 case MeterColourScale:
Chris@40 2578 case dBColourScale:
Chris@119 2579 case OtherColourScale:
Chris@40 2580 cw = std::max(paint.fontMetrics().width(tr("-Inf")),
Chris@40 2581 paint.fontMetrics().width(tr("-90")));
Chris@40 2582 break;
Chris@40 2583
Chris@40 2584 case PhaseColourScale:
Chris@40 2585 cw = paint.fontMetrics().width(QString("-") + QChar(0x3c0));
Chris@40 2586 break;
Chris@40 2587 }
Chris@119 2588 */
Chris@119 2589
Chris@40 2590
Chris@40 2591 return cw;
Chris@40 2592 }
Chris@40 2593
Chris@40 2594 int
Chris@44 2595 SpectrogramLayer::getVerticalScaleWidth(View *v, QPainter &paint) const
Chris@0 2596 {
Chris@0 2597 if (!m_model || !m_model->isOK()) return 0;
Chris@0 2598
Chris@40 2599 int cw = getColourScaleWidth(paint);
Chris@40 2600
Chris@0 2601 int tw = paint.fontMetrics().width(QString("%1")
Chris@0 2602 .arg(m_maxFrequency > 0 ?
Chris@0 2603 m_maxFrequency - 1 :
Chris@0 2604 m_model->getSampleRate() / 2));
Chris@0 2605
Chris@0 2606 int fw = paint.fontMetrics().width(QString("43Hz"));
Chris@0 2607 if (tw < fw) tw = fw;
Chris@40 2608
Chris@40 2609 int tickw = (m_frequencyScale == LogFrequencyScale ? 10 : 4);
Chris@0 2610
Chris@40 2611 return cw + tickw + tw + 13;
Chris@0 2612 }
Chris@0 2613
Chris@0 2614 void
Chris@44 2615 SpectrogramLayer::paintVerticalScale(View *v, QPainter &paint, QRect rect) const
Chris@0 2616 {
Chris@0 2617 if (!m_model || !m_model->isOK()) {
Chris@0 2618 return;
Chris@0 2619 }
Chris@0 2620
Chris@161 2621 Profiler profiler("SpectrogramLayer::paintVerticalScale", true);
Chris@122 2622
Chris@120 2623 //!!! cache this?
Chris@120 2624
Chris@0 2625 int h = rect.height(), w = rect.width();
Chris@0 2626
Chris@40 2627 int tickw = (m_frequencyScale == LogFrequencyScale ? 10 : 4);
Chris@40 2628 int pkw = (m_frequencyScale == LogFrequencyScale ? 10 : 0);
Chris@40 2629
Chris@107 2630 size_t bins = m_fftSize / 2;
Chris@0 2631 int sr = m_model->getSampleRate();
Chris@0 2632
Chris@0 2633 if (m_maxFrequency > 0) {
Chris@107 2634 bins = int((double(m_maxFrequency) * m_fftSize) / sr + 0.1);
Chris@107 2635 if (bins > m_fftSize / 2) bins = m_fftSize / 2;
Chris@0 2636 }
Chris@0 2637
Chris@40 2638 int cw = getColourScaleWidth(paint);
Chris@119 2639 int cbw = paint.fontMetrics().width("dB");
Chris@40 2640
Chris@0 2641 int py = -1;
Chris@0 2642 int textHeight = paint.fontMetrics().height();
Chris@0 2643 int toff = -textHeight + paint.fontMetrics().ascent() + 2;
Chris@0 2644
Chris@119 2645 if (h > textHeight * 3 + 10) {
Chris@119 2646
Chris@119 2647 int topLines = 2;
Chris@119 2648 if (m_colourScale == PhaseColourScale) topLines = 1;
Chris@119 2649
Chris@119 2650 int ch = h - textHeight * (topLines + 1) - 8;
Chris@119 2651 // paint.drawRect(4, textHeight + 4, cw - 1, ch + 1);
Chris@119 2652 paint.drawRect(4 + cw - cbw, textHeight * topLines + 4, cbw - 1, ch + 1);
Chris@40 2653
Chris@40 2654 QString top, bottom;
Chris@119 2655 /*!!!
Chris@40 2656 switch (m_colourScale) {
Chris@40 2657 default:
Chris@40 2658 case LinearColourScale:
Chris@40 2659 top = (m_normalizeColumns ? "1.0" : "0.02");
Chris@40 2660 bottom = (m_normalizeColumns ? "0.0" : "0.00");
Chris@40 2661 break;
Chris@40 2662
Chris@40 2663 case MeterColourScale:
Chris@40 2664 top = (m_normalizeColumns ? QString("0") :
Chris@40 2665 QString("%1").arg(int(AudioLevel::multiplier_to_dB(0.02))));
Chris@40 2666 bottom = QString("%1").
Chris@40 2667 arg(int(AudioLevel::multiplier_to_dB
Chris@40 2668 (AudioLevel::preview_to_multiplier(0, 255))));
Chris@40 2669 break;
Chris@40 2670
Chris@40 2671 case dBColourScale:
Chris@119 2672 case OtherColourScale:
Chris@40 2673 top = "0";
Chris@40 2674 bottom = "-80";
Chris@40 2675 break;
Chris@40 2676
Chris@40 2677 case PhaseColourScale:
Chris@40 2678 top = QChar(0x3c0);
Chris@40 2679 bottom = "-" + top;
Chris@40 2680 break;
Chris@40 2681 }
Chris@119 2682 */
Chris@119 2683 float min = m_viewMags[v].getMin();
Chris@119 2684 float max = m_viewMags[v].getMax();
Chris@119 2685
Chris@119 2686 float dBmin = AudioLevel::multiplier_to_dB(min);
Chris@119 2687 float dBmax = AudioLevel::multiplier_to_dB(max);
Chris@119 2688
Chris@120 2689 if (dBmax < -60.f) dBmax = -60.f;
Chris@120 2690 else top = QString("%1").arg(lrintf(dBmax));
Chris@120 2691
Chris@120 2692 if (dBmin < dBmax - 60.f) dBmin = dBmax - 60.f;
Chris@119 2693 bottom = QString("%1").arg(lrintf(dBmin));
Chris@119 2694
Chris@119 2695 //!!! & phase etc
Chris@119 2696
Chris@119 2697 if (m_colourScale != PhaseColourScale) {
Chris@119 2698 paint.drawText((cw + 6 - paint.fontMetrics().width("dBFS")) / 2,
Chris@119 2699 2 + textHeight + toff, "dBFS");
Chris@119 2700 }
Chris@119 2701
Chris@119 2702 // paint.drawText((cw + 6 - paint.fontMetrics().width(top)) / 2,
Chris@119 2703 paint.drawText(3 + cw - cbw - paint.fontMetrics().width(top),
Chris@119 2704 2 + textHeight * topLines + toff + textHeight/2, top);
Chris@119 2705
Chris@119 2706 paint.drawText(3 + cw - cbw - paint.fontMetrics().width(bottom),
Chris@119 2707 h + toff - 3 - textHeight/2, bottom);
Chris@40 2708
Chris@40 2709 paint.save();
Chris@40 2710 paint.setBrush(Qt::NoBrush);
Chris@119 2711
Chris@119 2712 int lasty = 0;
Chris@119 2713 int lastdb = 0;
Chris@119 2714
Chris@40 2715 for (int i = 0; i < ch; ++i) {
Chris@119 2716
Chris@119 2717 float dBval = dBmin + (((dBmax - dBmin) * i) / (ch - 1));
Chris@119 2718 int idb = int(dBval);
Chris@119 2719
Chris@119 2720 float value = AudioLevel::dB_to_multiplier(dBval);
Chris@119 2721 int colour = getDisplayValue(v, value * m_gain);
Chris@119 2722 /*
Chris@119 2723 float value = min + (((max - min) * i) / (ch - 1));
Chris@119 2724 if (value < m_threshold) value = 0.f;
Chris@119 2725 int colour = getDisplayValue(v, value * m_gain);
Chris@119 2726 */
Chris@119 2727 /*
Chris@119 2728 int colour = (i * 255) / ch + 1;
Chris@119 2729 */
Chris@119 2730 paint.setPen(m_colourMap.getColour(colour));
Chris@119 2731
Chris@119 2732 int y = textHeight * topLines + 4 + ch - i;
Chris@119 2733
Chris@119 2734 paint.drawLine(5 + cw - cbw, y, cw + 2, y);
Chris@119 2735
Chris@119 2736 // paint.drawLine(5, 4 + textHeight + ch - i,
Chris@119 2737 // cw + 2, 4 + textHeight + ch - i);
Chris@119 2738
Chris@119 2739
Chris@119 2740 if (i == 0) {
Chris@119 2741 lasty = y;
Chris@119 2742 lastdb = idb;
Chris@119 2743 } else if (i < ch - paint.fontMetrics().ascent() &&
Chris@120 2744 idb != lastdb &&
Chris@119 2745 ((abs(y - lasty) > textHeight &&
Chris@119 2746 idb % 10 == 0) ||
Chris@119 2747 (abs(y - lasty) > paint.fontMetrics().ascent() &&
Chris@119 2748 idb % 5 == 0))) {
Chris@119 2749 paint.setPen(Qt::black);
Chris@119 2750 QString text = QString("%1").arg(idb);
Chris@119 2751 paint.drawText(3 + cw - cbw - paint.fontMetrics().width(text),
Chris@119 2752 y + toff + textHeight/2, text);
Chris@119 2753 paint.setPen(Qt::white);
Chris@119 2754 paint.drawLine(5 + cw - cbw, y, 8 + cw - cbw, y);
Chris@119 2755 lasty = y;
Chris@119 2756 lastdb = idb;
Chris@119 2757 }
Chris@40 2758 }
Chris@40 2759 paint.restore();
Chris@40 2760 }
Chris@40 2761
Chris@40 2762 paint.drawLine(cw + 7, 0, cw + 7, h);
Chris@40 2763
Chris@0 2764 int bin = -1;
Chris@0 2765
Chris@44 2766 for (int y = 0; y < v->height(); ++y) {
Chris@0 2767
Chris@0 2768 float q0, q1;
Chris@44 2769 if (!getYBinRange(v, v->height() - y, q0, q1)) continue;
Chris@0 2770
Chris@0 2771 int vy;
Chris@0 2772
Chris@0 2773 if (int(q0) > bin) {
Chris@0 2774 vy = y;
Chris@0 2775 bin = int(q0);
Chris@0 2776 } else {
Chris@0 2777 continue;
Chris@0 2778 }
Chris@0 2779
Chris@107 2780 int freq = (sr * bin) / m_fftSize;
Chris@0 2781
Chris@0 2782 if (py >= 0 && (vy - py) < textHeight - 1) {
Chris@40 2783 if (m_frequencyScale == LinearFrequencyScale) {
Chris@40 2784 paint.drawLine(w - tickw, h - vy, w, h - vy);
Chris@40 2785 }
Chris@0 2786 continue;
Chris@0 2787 }
Chris@0 2788
Chris@0 2789 QString text = QString("%1").arg(freq);
Chris@40 2790 if (bin == 1) text = QString("%1Hz").arg(freq); // bin 0 is DC
Chris@40 2791 paint.drawLine(cw + 7, h - vy, w - pkw - 1, h - vy);
Chris@0 2792
Chris@0 2793 if (h - vy - textHeight >= -2) {
Chris@40 2794 int tx = w - 3 - paint.fontMetrics().width(text) - std::max(tickw, pkw);
Chris@0 2795 paint.drawText(tx, h - vy + toff, text);
Chris@0 2796 }
Chris@0 2797
Chris@0 2798 py = vy;
Chris@0 2799 }
Chris@40 2800
Chris@40 2801 if (m_frequencyScale == LogFrequencyScale) {
Chris@40 2802
Chris@40 2803 paint.drawLine(w - pkw - 1, 0, w - pkw - 1, h);
Chris@40 2804
Chris@110 2805 int sr = m_model->getSampleRate();
Chris@40 2806 float minf = getEffectiveMinFrequency();
Chris@40 2807 float maxf = getEffectiveMaxFrequency();
Chris@40 2808
Chris@122 2809 int py = h, ppy = h;
Chris@40 2810 paint.setBrush(paint.pen().color());
Chris@40 2811
Chris@40 2812 for (int i = 0; i < 128; ++i) {
Chris@40 2813
Chris@40 2814 float f = Pitch::getFrequencyForPitch(i);
Chris@44 2815 int y = lrintf(v->getYForFrequency(f, minf, maxf, true));
Chris@122 2816
Chris@122 2817 if (y < -2) break;
Chris@122 2818 if (y > h + 2) {
Chris@122 2819 continue;
Chris@122 2820 }
Chris@122 2821
Chris@40 2822 int n = (i % 12);
Chris@122 2823
Chris@122 2824 if (n == 1) {
Chris@122 2825 // C# -- fill the C from here
Chris@122 2826 if (ppy - y > 2) {
Chris@122 2827 paint.fillRect(w - pkw,
Chris@122 2828 // y - (py - y) / 2 - (py - y) / 4,
Chris@122 2829 y,
Chris@122 2830 pkw,
Chris@122 2831 (py + ppy) / 2 - y,
Chris@122 2832 // py - y + 1,
Chris@122 2833 Qt::gray);
Chris@122 2834 }
Chris@122 2835 }
Chris@122 2836
Chris@40 2837 if (n == 1 || n == 3 || n == 6 || n == 8 || n == 10) {
Chris@40 2838 // black notes
Chris@40 2839 paint.drawLine(w - pkw, y, w, y);
Chris@41 2840 int rh = ((py - y) / 4) * 2;
Chris@41 2841 if (rh < 2) rh = 2;
Chris@41 2842 paint.drawRect(w - pkw, y - (py-y)/4, pkw/2, rh);
Chris@40 2843 } else if (n == 0 || n == 5) {
Chris@122 2844 // C, F
Chris@40 2845 if (py < h) {
Chris@40 2846 paint.drawLine(w - pkw, (y + py) / 2, w, (y + py) / 2);
Chris@40 2847 }
Chris@40 2848 }
Chris@40 2849
Chris@122 2850 ppy = py;
Chris@40 2851 py = y;
Chris@40 2852 }
Chris@40 2853 }
Chris@0 2854 }
Chris@0 2855
Chris@187 2856 class SpectrogramRangeMapper : public RangeMapper
Chris@187 2857 {
Chris@187 2858 public:
Chris@187 2859 SpectrogramRangeMapper(int sr, int fftsize) :
Chris@187 2860 // m_dist((float(sr) / 2) - (float(sr) / fftsize)),
Chris@187 2861 m_dist(float(sr) / 2),
Chris@187 2862 m_s2(sqrtf(sqrtf(2))) { }
Chris@187 2863 ~SpectrogramRangeMapper() { }
Chris@187 2864
Chris@187 2865 virtual int getPositionForValue(float value) const {
Chris@187 2866
Chris@187 2867 float dist = m_dist;
Chris@187 2868
Chris@187 2869 int n = 0;
Chris@187 2870 int discard = 0;
Chris@187 2871
Chris@187 2872 while (dist > (value + 0.00001) && dist > 0.1f) {
Chris@187 2873 dist /= m_s2;
Chris@187 2874 ++n;
Chris@187 2875 }
Chris@187 2876
Chris@187 2877 return n;
Chris@187 2878 }
Chris@187 2879
Chris@187 2880 virtual float getValueForPosition(int position) const {
Chris@187 2881
Chris@187 2882 // Vertical zoom step 0 shows the entire range from DC ->
Chris@187 2883 // Nyquist frequency. Step 1 shows 2^(1/4) of the range of
Chris@187 2884 // step 0, and so on until the visible range is smaller than
Chris@187 2885 // the frequency step between bins at the current fft size.
Chris@187 2886
Chris@187 2887 float dist = m_dist;
Chris@187 2888
Chris@187 2889 int n = 0;
Chris@187 2890 while (n < position) {
Chris@187 2891 dist /= m_s2;
Chris@187 2892 ++n;
Chris@187 2893 }
Chris@187 2894
Chris@187 2895 return dist;
Chris@187 2896 }
Chris@187 2897
Chris@187 2898 virtual QString getUnit() const { return "Hz"; }
Chris@187 2899
Chris@187 2900 protected:
Chris@187 2901 float m_dist;
Chris@187 2902 float m_s2;
Chris@187 2903 };
Chris@187 2904
Chris@133 2905 int
Chris@133 2906 SpectrogramLayer::getVerticalZoomSteps(int &defaultStep) const
Chris@133 2907 {
Chris@135 2908 if (!m_model) return 0;
Chris@187 2909
Chris@187 2910 int sr = m_model->getSampleRate();
Chris@187 2911
Chris@187 2912 SpectrogramRangeMapper mapper(sr, m_fftSize);
Chris@187 2913
Chris@187 2914 // int maxStep = mapper.getPositionForValue((float(sr) / m_fftSize) + 0.001);
Chris@187 2915 int maxStep = mapper.getPositionForValue(0);
Chris@187 2916 int minStep = mapper.getPositionForValue(float(sr) / 2);
Chris@135 2917
Chris@187 2918 defaultStep = mapper.getPositionForValue(m_initialMaxFrequency) - minStep;
Chris@187 2919
Chris@187 2920 std::cerr << "SpectrogramLayer::getVerticalZoomSteps: " << maxStep - minStep << " (" << maxStep <<"-" << minStep << "), default is " << defaultStep << " (from initial max freq " << m_initialMaxFrequency << ")" << std::endl;
Chris@187 2921
Chris@187 2922 return maxStep - minStep;
Chris@133 2923 }
Chris@133 2924
Chris@133 2925 int
Chris@133 2926 SpectrogramLayer::getCurrentVerticalZoomStep() const
Chris@133 2927 {
Chris@133 2928 if (!m_model) return 0;
Chris@133 2929
Chris@133 2930 float dmin, dmax;
Chris@133 2931 getDisplayExtents(dmin, dmax);
Chris@133 2932
Chris@187 2933 SpectrogramRangeMapper mapper(m_model->getSampleRate(), m_fftSize);
Chris@187 2934 int n = mapper.getPositionForValue(dmax - dmin);
Chris@187 2935 std::cerr << "SpectrogramLayer::getCurrentVerticalZoomStep: " << n << std::endl;
Chris@133 2936 return n;
Chris@133 2937 }
Chris@133 2938
Chris@133 2939 void
Chris@133 2940 SpectrogramLayer::setVerticalZoomStep(int step)
Chris@133 2941 {
Chris@133 2942 //!!! does not do the right thing for log scale
Chris@133 2943
Chris@187 2944 if (!m_model) return;
Chris@187 2945
Chris@133 2946 float dmin, dmax;
Chris@133 2947 getDisplayExtents(dmin, dmax);
Chris@133 2948
Chris@133 2949 int sr = m_model->getSampleRate();
Chris@187 2950 SpectrogramRangeMapper mapper(sr, m_fftSize);
Chris@187 2951 float ddist = mapper.getValueForPosition(step);
Chris@133 2952
Chris@133 2953 float dmid = (dmax + dmin) / 2;
Chris@133 2954 float newmin = dmid - ddist / 2;
Chris@133 2955 float newmax = dmid + ddist / 2;
Chris@187 2956
Chris@187 2957 float mmin, mmax;
Chris@187 2958 mmin = 0;
Chris@187 2959 mmax = float(sr) / 2;
Chris@133 2960
Chris@187 2961 if (newmin < mmin) {
Chris@187 2962 newmax += (mmin - newmin);
Chris@187 2963 newmin = mmin;
Chris@187 2964 }
Chris@187 2965 if (newmax > mmax) {
Chris@187 2966 newmax = mmax;
Chris@187 2967 }
Chris@133 2968
Chris@187 2969 std::cerr << "SpectrogramLayer::setVerticalZoomStep: " << step << ": " << newmin << " -> " << newmax << " (range " << ddist << ")" << std::endl;
Chris@187 2970
Chris@187 2971 setMinFrequency(int(newmin));
Chris@187 2972 setMaxFrequency(int(newmax));
Chris@187 2973 }
Chris@187 2974
Chris@187 2975 RangeMapper *
Chris@187 2976 SpectrogramLayer::getNewVerticalZoomRangeMapper() const
Chris@187 2977 {
Chris@187 2978 if (!m_model) return 0;
Chris@187 2979 return new SpectrogramRangeMapper(m_model->getSampleRate(), m_fftSize);
Chris@133 2980 }
Chris@133 2981
Chris@6 2982 QString
Chris@6 2983 SpectrogramLayer::toXmlString(QString indent, QString extraAttributes) const
Chris@6 2984 {
Chris@6 2985 QString s;
Chris@6 2986
Chris@6 2987 s += QString("channel=\"%1\" "
Chris@6 2988 "windowSize=\"%2\" "
Chris@153 2989 "windowHopLevel=\"%3\" "
Chris@153 2990 "gain=\"%4\" "
Chris@153 2991 "threshold=\"%5\" ")
Chris@6 2992 .arg(m_channel)
Chris@6 2993 .arg(m_windowSize)
Chris@97 2994 .arg(m_windowHopLevel)
Chris@37 2995 .arg(m_gain)
Chris@37 2996 .arg(m_threshold);
Chris@37 2997
Chris@37 2998 s += QString("minFrequency=\"%1\" "
Chris@37 2999 "maxFrequency=\"%2\" "
Chris@37 3000 "colourScale=\"%3\" "
Chris@37 3001 "colourScheme=\"%4\" "
Chris@37 3002 "colourRotation=\"%5\" "
Chris@37 3003 "frequencyScale=\"%6\" "
Chris@37 3004 "binDisplay=\"%7\" "
Chris@153 3005 "normalizeColumns=\"%8\" "
Chris@153 3006 "normalizeVisibleArea=\"%9\"")
Chris@37 3007 .arg(m_minFrequency)
Chris@6 3008 .arg(m_maxFrequency)
Chris@6 3009 .arg(m_colourScale)
Chris@6 3010 .arg(m_colourScheme)
Chris@37 3011 .arg(m_colourRotation)
Chris@35 3012 .arg(m_frequencyScale)
Chris@37 3013 .arg(m_binDisplay)
Chris@153 3014 .arg(m_normalizeColumns ? "true" : "false")
Chris@153 3015 .arg(m_normalizeVisibleArea ? "true" : "false");
Chris@6 3016
Chris@6 3017 return Layer::toXmlString(indent, extraAttributes + " " + s);
Chris@6 3018 }
Chris@6 3019
Chris@11 3020 void
Chris@11 3021 SpectrogramLayer::setProperties(const QXmlAttributes &attributes)
Chris@11 3022 {
Chris@11 3023 bool ok = false;
Chris@11 3024
Chris@11 3025 int channel = attributes.value("channel").toInt(&ok);
Chris@11 3026 if (ok) setChannel(channel);
Chris@11 3027
Chris@11 3028 size_t windowSize = attributes.value("windowSize").toUInt(&ok);
Chris@11 3029 if (ok) setWindowSize(windowSize);
Chris@11 3030
Chris@97 3031 size_t windowHopLevel = attributes.value("windowHopLevel").toUInt(&ok);
Chris@97 3032 if (ok) setWindowHopLevel(windowHopLevel);
Chris@97 3033 else {
Chris@97 3034 size_t windowOverlap = attributes.value("windowOverlap").toUInt(&ok);
Chris@97 3035 // a percentage value
Chris@97 3036 if (ok) {
Chris@97 3037 if (windowOverlap == 0) setWindowHopLevel(0);
Chris@97 3038 else if (windowOverlap == 25) setWindowHopLevel(1);
Chris@97 3039 else if (windowOverlap == 50) setWindowHopLevel(2);
Chris@97 3040 else if (windowOverlap == 75) setWindowHopLevel(3);
Chris@97 3041 else if (windowOverlap == 90) setWindowHopLevel(4);
Chris@97 3042 }
Chris@97 3043 }
Chris@11 3044
Chris@11 3045 float gain = attributes.value("gain").toFloat(&ok);
Chris@11 3046 if (ok) setGain(gain);
Chris@11 3047
Chris@37 3048 float threshold = attributes.value("threshold").toFloat(&ok);
Chris@37 3049 if (ok) setThreshold(threshold);
Chris@37 3050
Chris@37 3051 size_t minFrequency = attributes.value("minFrequency").toUInt(&ok);
Chris@187 3052 if (ok) {
Chris@187 3053 std::cerr << "SpectrogramLayer::setProperties: setting min freq to " << minFrequency << std::endl;
Chris@187 3054 setMinFrequency(minFrequency);
Chris@187 3055 }
Chris@37 3056
Chris@11 3057 size_t maxFrequency = attributes.value("maxFrequency").toUInt(&ok);
Chris@187 3058 if (ok) {
Chris@187 3059 std::cerr << "SpectrogramLayer::setProperties: setting max freq to " << maxFrequency << std::endl;
Chris@187 3060 setMaxFrequency(maxFrequency);
Chris@187 3061 }
Chris@11 3062
Chris@11 3063 ColourScale colourScale = (ColourScale)
Chris@11 3064 attributes.value("colourScale").toInt(&ok);
Chris@11 3065 if (ok) setColourScale(colourScale);
Chris@11 3066
Chris@11 3067 ColourScheme colourScheme = (ColourScheme)
Chris@11 3068 attributes.value("colourScheme").toInt(&ok);
Chris@11 3069 if (ok) setColourScheme(colourScheme);
Chris@11 3070
Chris@37 3071 int colourRotation = attributes.value("colourRotation").toInt(&ok);
Chris@37 3072 if (ok) setColourRotation(colourRotation);
Chris@37 3073
Chris@11 3074 FrequencyScale frequencyScale = (FrequencyScale)
Chris@11 3075 attributes.value("frequencyScale").toInt(&ok);
Chris@11 3076 if (ok) setFrequencyScale(frequencyScale);
Chris@35 3077
Chris@37 3078 BinDisplay binDisplay = (BinDisplay)
Chris@37 3079 attributes.value("binDisplay").toInt(&ok);
Chris@37 3080 if (ok) setBinDisplay(binDisplay);
Chris@36 3081
Chris@36 3082 bool normalizeColumns =
Chris@36 3083 (attributes.value("normalizeColumns").trimmed() == "true");
Chris@36 3084 setNormalizeColumns(normalizeColumns);
Chris@153 3085
Chris@153 3086 bool normalizeVisibleArea =
Chris@153 3087 (attributes.value("normalizeVisibleArea").trimmed() == "true");
Chris@153 3088 setNormalizeVisibleArea(normalizeVisibleArea);
Chris@11 3089 }
Chris@11 3090