annotate layer/SpectrogramLayer.cpp @ 1160:a429b2acb45d 3.0-integration

Make SVDEBUG always write to a log file -- formerly this was disabled in NDEBUG builds. I think there's little use to that, it just means that we keep adding more cerr debug output because we aren't getting the log we need. And SVDEBUG logging is not usually used in tight loops, I don't think the performance overhead is too serious. Also update the About box.
author Chris Cannam
date Thu, 03 Nov 2016 14:57:00 +0000
parents 0edfed2c8482
children ab169938832a
rev   line source
Chris@58 1 /* -*- c-basic-offset: 4 indent-tabs-mode: nil -*- vi:set ts=8 sts=4 sw=4: */
Chris@0 2
Chris@0 3 /*
Chris@59 4 Sonic Visualiser
Chris@59 5 An audio file viewer and annotation editor.
Chris@59 6 Centre for Digital Music, Queen Mary, University of London.
Chris@484 7 This file copyright 2006-2009 Chris Cannam and QMUL.
Chris@0 8
Chris@59 9 This program is free software; you can redistribute it and/or
Chris@59 10 modify it under the terms of the GNU General Public License as
Chris@59 11 published by the Free Software Foundation; either version 2 of the
Chris@59 12 License, or (at your option) any later version. See the file
Chris@59 13 COPYING included with this distribution for more information.
Chris@0 14 */
Chris@0 15
Chris@0 16 #include "SpectrogramLayer.h"
Chris@0 17
Chris@128 18 #include "view/View.h"
Chris@0 19 #include "base/Profiler.h"
Chris@0 20 #include "base/AudioLevel.h"
Chris@0 21 #include "base/Window.h"
Chris@24 22 #include "base/Pitch.h"
Chris@118 23 #include "base/Preferences.h"
Chris@167 24 #include "base/RangeMapper.h"
Chris@253 25 #include "base/LogRange.h"
Chris@1063 26 #include "base/ColumnOp.h"
Chris@1147 27 #include "base/Strings.h"
Chris@376 28 #include "widgets/CommandHistory.h"
Chris@1078 29 #include "data/model/Dense3DModelPeakCache.h"
Chris@1078 30
Chris@376 31 #include "ColourMapper.h"
Chris@690 32 #include "PianoScale.h"
Chris@1078 33 #include "PaintAssistant.h"
Chris@1089 34 #include "Colour3DPlotRenderer.h"
Chris@0 35
Chris@0 36 #include <QPainter>
Chris@0 37 #include <QImage>
Chris@0 38 #include <QPixmap>
Chris@0 39 #include <QRect>
Chris@92 40 #include <QApplication>
Chris@178 41 #include <QMessageBox>
Chris@283 42 #include <QMouseEvent>
Chris@316 43 #include <QTextStream>
Chris@1017 44 #include <QSettings>
Chris@0 45
Chris@0 46 #include <iostream>
Chris@0 47
Chris@0 48 #include <cassert>
Chris@0 49 #include <cmath>
Chris@0 50
Chris@1143 51 //#define DEBUG_SPECTROGRAM 1
Chris@1143 52 //#define DEBUG_SPECTROGRAM_REPAINT 1
Chris@1025 53
Chris@1025 54 using namespace std;
Chris@907 55
Chris@44 56 SpectrogramLayer::SpectrogramLayer(Configuration config) :
Chris@0 57 m_model(0),
Chris@0 58 m_channel(0),
Chris@0 59 m_windowSize(1024),
Chris@0 60 m_windowType(HanningWindow),
Chris@97 61 m_windowHopLevel(2),
Chris@0 62 m_gain(1.0),
Chris@215 63 m_initialGain(1.0),
Chris@1128 64 m_threshold(1.0e-8f),
Chris@1128 65 m_initialThreshold(1.0e-8f),
Chris@9 66 m_colourRotation(0),
Chris@215 67 m_initialRotation(0),
Chris@119 68 m_minFrequency(10),
Chris@0 69 m_maxFrequency(8000),
Chris@135 70 m_initialMaxFrequency(8000),
Chris@1105 71 m_colourScale(ColourScaleType::Log),
Chris@1137 72 m_colourScaleMultiple(1.0),
Chris@197 73 m_colourMap(0),
Chris@1103 74 m_binScale(BinScale::Linear),
Chris@1103 75 m_binDisplay(BinDisplay::AllBins),
Chris@1104 76 m_normalization(ColumnNormalization::None),
Chris@1104 77 m_normalizeVisibleArea(false),
Chris@133 78 m_lastEmittedZoomStep(-1),
Chris@390 79 m_synchronous(false),
Chris@608 80 m_haveDetailedScale(false),
Chris@193 81 m_exiting(false),
Chris@1088 82 m_fftModel(0),
Chris@1088 83 m_peakCache(0),
Chris@1088 84 m_peakCacheDivisor(8)
Chris@0 85 {
Chris@1017 86 QString colourConfigName = "spectrogram-colour";
Chris@1017 87 int colourConfigDefault = int(ColourMapper::Green);
Chris@1017 88
Chris@215 89 if (config == FullRangeDb) {
Chris@215 90 m_initialMaxFrequency = 0;
Chris@215 91 setMaxFrequency(0);
Chris@215 92 } else if (config == MelodicRange) {
Chris@0 93 setWindowSize(8192);
Chris@97 94 setWindowHopLevel(4);
Chris@215 95 m_initialMaxFrequency = 1500;
Chris@215 96 setMaxFrequency(1500);
Chris@215 97 setMinFrequency(40);
Chris@1105 98 setColourScale(ColourScaleType::Linear);
Chris@215 99 setColourMap(ColourMapper::Sunset);
Chris@1103 100 setBinScale(BinScale::Log);
Chris@1017 101 colourConfigName = "spectrogram-melodic-colour";
Chris@1017 102 colourConfigDefault = int(ColourMapper::Sunset);
Chris@224 103 // setGain(20);
Chris@37 104 } else if (config == MelodicPeaks) {
Chris@37 105 setWindowSize(4096);
Chris@97 106 setWindowHopLevel(5);
Chris@135 107 m_initialMaxFrequency = 2000;
Chris@40 108 setMaxFrequency(2000);
Chris@37 109 setMinFrequency(40);
Chris@1103 110 setBinScale(BinScale::Log);
Chris@1105 111 setColourScale(ColourScaleType::Linear);
Chris@1103 112 setBinDisplay(BinDisplay::PeakFrequencies);
Chris@1104 113 setNormalization(ColumnNormalization::Max1);
Chris@1017 114 colourConfigName = "spectrogram-melodic-colour";
Chris@1017 115 colourConfigDefault = int(ColourMapper::Sunset);
Chris@0 116 }
Chris@110 117
Chris@1017 118 QSettings settings;
Chris@1017 119 settings.beginGroup("Preferences");
Chris@1017 120 setColourMap(settings.value(colourConfigName, colourConfigDefault).toInt());
Chris@1017 121 settings.endGroup();
Chris@1017 122
Chris@122 123 Preferences *prefs = Preferences::getInstance();
Chris@122 124 connect(prefs, SIGNAL(propertyChanged(PropertyContainer::PropertyName)),
Chris@122 125 this, SLOT(preferenceChanged(PropertyContainer::PropertyName)));
Chris@122 126 setWindowType(prefs->getWindowType());
Chris@0 127 }
Chris@0 128
Chris@0 129 SpectrogramLayer::~SpectrogramLayer()
Chris@0 130 {
Chris@1106 131 invalidateRenderers();
Chris@1088 132 invalidateFFTModel();
Chris@0 133 }
Chris@0 134
Chris@1137 135 pair<ColourScaleType, double>
Chris@1104 136 SpectrogramLayer::convertToColourScale(int value)
Chris@1104 137 {
Chris@1104 138 switch (value) {
Chris@1137 139 case 0: return { ColourScaleType::Linear, 1.0 };
Chris@1137 140 case 1: return { ColourScaleType::Meter, 1.0 };
Chris@1137 141 case 2: return { ColourScaleType::Log, 2.0 }; // dB^2 (i.e. log of power)
Chris@1137 142 case 3: return { ColourScaleType::Log, 1.0 }; // dB (of magnitude)
Chris@1137 143 case 4: return { ColourScaleType::Phase, 1.0 };
Chris@1137 144 default: return { ColourScaleType::Linear, 1.0 };
Chris@1104 145 }
Chris@1104 146 }
Chris@1104 147
Chris@1104 148 int
Chris@1137 149 SpectrogramLayer::convertFromColourScale(ColourScaleType scale, double multiple)
Chris@1104 150 {
Chris@1104 151 switch (scale) {
Chris@1105 152 case ColourScaleType::Linear: return 0;
Chris@1105 153 case ColourScaleType::Meter: return 1;
Chris@1137 154 case ColourScaleType::Log: return (multiple > 1.5 ? 2 : 3);
Chris@1105 155 case ColourScaleType::Phase: return 4;
Chris@1105 156 case ColourScaleType::PlusMinusOne:
Chris@1105 157 case ColourScaleType::Absolute:
Chris@1104 158 default: return 0;
Chris@1104 159 }
Chris@1104 160 }
Chris@1104 161
Chris@1104 162 std::pair<ColumnNormalization, bool>
Chris@1104 163 SpectrogramLayer::convertToColumnNorm(int value)
Chris@1104 164 {
Chris@1104 165 switch (value) {
Chris@1104 166 default:
Chris@1104 167 case 0: return { ColumnNormalization::None, false };
Chris@1104 168 case 1: return { ColumnNormalization::Max1, false };
Chris@1104 169 case 2: return { ColumnNormalization::None, true }; // visible area
Chris@1104 170 case 3: return { ColumnNormalization::Hybrid, false };
Chris@1104 171 }
Chris@1104 172 }
Chris@1104 173
Chris@1104 174 int
Chris@1104 175 SpectrogramLayer::convertFromColumnNorm(ColumnNormalization norm, bool visible)
Chris@1104 176 {
Chris@1104 177 if (visible) return 2;
Chris@1104 178 switch (norm) {
Chris@1104 179 case ColumnNormalization::None: return 0;
Chris@1104 180 case ColumnNormalization::Max1: return 1;
Chris@1104 181 case ColumnNormalization::Hybrid: return 3;
Chris@1104 182
Chris@1104 183 case ColumnNormalization::Sum1:
Chris@1104 184 default: return 0;
Chris@1104 185 }
Chris@1104 186 }
Chris@1104 187
Chris@0 188 void
Chris@0 189 SpectrogramLayer::setModel(const DenseTimeValueModel *model)
Chris@0 190 {
Chris@682 191 // cerr << "SpectrogramLayer(" << this << "): setModel(" << model << ")" << endl;
Chris@34 192
Chris@110 193 if (model == m_model) return;
Chris@110 194
Chris@0 195 m_model = model;
Chris@1088 196 invalidateFFTModel();
Chris@0 197
Chris@0 198 if (!m_model || !m_model->isOK()) return;
Chris@0 199
Chris@320 200 connectSignals(m_model);
Chris@0 201
Chris@0 202 connect(m_model, SIGNAL(modelChanged()), this, SLOT(cacheInvalid()));
Chris@906 203 connect(m_model, SIGNAL(modelChangedWithin(sv_frame_t, sv_frame_t)),
Chris@906 204 this, SLOT(cacheInvalid(sv_frame_t, sv_frame_t)));
Chris@0 205
Chris@0 206 emit modelReplaced();
Chris@110 207 }
Chris@115 208
Chris@0 209 Layer::PropertyList
Chris@0 210 SpectrogramLayer::getProperties() const
Chris@0 211 {
Chris@0 212 PropertyList list;
Chris@87 213 list.push_back("Colour");
Chris@87 214 list.push_back("Colour Scale");
Chris@87 215 list.push_back("Window Size");
Chris@97 216 list.push_back("Window Increment");
Chris@862 217 list.push_back("Normalization");
Chris@87 218 list.push_back("Bin Display");
Chris@87 219 list.push_back("Threshold");
Chris@87 220 list.push_back("Gain");
Chris@87 221 list.push_back("Colour Rotation");
Chris@153 222 // list.push_back("Min Frequency");
Chris@153 223 // list.push_back("Max Frequency");
Chris@87 224 list.push_back("Frequency Scale");
Chris@0 225 return list;
Chris@0 226 }
Chris@0 227
Chris@87 228 QString
Chris@87 229 SpectrogramLayer::getPropertyLabel(const PropertyName &name) const
Chris@87 230 {
Chris@87 231 if (name == "Colour") return tr("Colour");
Chris@87 232 if (name == "Colour Scale") return tr("Colour Scale");
Chris@87 233 if (name == "Window Size") return tr("Window Size");
Chris@112 234 if (name == "Window Increment") return tr("Window Overlap");
Chris@862 235 if (name == "Normalization") return tr("Normalization");
Chris@87 236 if (name == "Bin Display") return tr("Bin Display");
Chris@87 237 if (name == "Threshold") return tr("Threshold");
Chris@87 238 if (name == "Gain") return tr("Gain");
Chris@87 239 if (name == "Colour Rotation") return tr("Colour Rotation");
Chris@87 240 if (name == "Min Frequency") return tr("Min Frequency");
Chris@87 241 if (name == "Max Frequency") return tr("Max Frequency");
Chris@87 242 if (name == "Frequency Scale") return tr("Frequency Scale");
Chris@87 243 return "";
Chris@87 244 }
Chris@87 245
Chris@335 246 QString
Chris@862 247 SpectrogramLayer::getPropertyIconName(const PropertyName &) const
Chris@335 248 {
Chris@335 249 return "";
Chris@335 250 }
Chris@335 251
Chris@0 252 Layer::PropertyType
Chris@0 253 SpectrogramLayer::getPropertyType(const PropertyName &name) const
Chris@0 254 {
Chris@87 255 if (name == "Gain") return RangeProperty;
Chris@87 256 if (name == "Colour Rotation") return RangeProperty;
Chris@87 257 if (name == "Threshold") return RangeProperty;
Chris@0 258 return ValueProperty;
Chris@0 259 }
Chris@0 260
Chris@0 261 QString
Chris@0 262 SpectrogramLayer::getPropertyGroupName(const PropertyName &name) const
Chris@0 263 {
Chris@153 264 if (name == "Bin Display" ||
Chris@153 265 name == "Frequency Scale") return tr("Bins");
Chris@87 266 if (name == "Window Size" ||
Chris@1086 267 name == "Window Increment") return tr("Window");
Chris@87 268 if (name == "Colour" ||
Chris@87 269 name == "Threshold" ||
Chris@87 270 name == "Colour Rotation") return tr("Colour");
Chris@862 271 if (name == "Normalization" ||
Chris@153 272 name == "Gain" ||
Chris@87 273 name == "Colour Scale") return tr("Scale");
Chris@0 274 return QString();
Chris@0 275 }
Chris@0 276
Chris@0 277 int
Chris@0 278 SpectrogramLayer::getPropertyRangeAndValue(const PropertyName &name,
Chris@216 279 int *min, int *max, int *deflt) const
Chris@0 280 {
Chris@216 281 int val = 0;
Chris@216 282
Chris@216 283 int garbage0, garbage1, garbage2;
Chris@55 284 if (!min) min = &garbage0;
Chris@55 285 if (!max) max = &garbage1;
Chris@216 286 if (!deflt) deflt = &garbage2;
Chris@10 287
Chris@87 288 if (name == "Gain") {
Chris@0 289
Chris@0 290 *min = -50;
Chris@0 291 *max = 50;
Chris@0 292
Chris@906 293 *deflt = int(lrint(log10(m_initialGain) * 20.0));
Chris@216 294 if (*deflt < *min) *deflt = *min;
Chris@216 295 if (*deflt > *max) *deflt = *max;
Chris@216 296
Chris@906 297 val = int(lrint(log10(m_gain) * 20.0));
Chris@216 298 if (val < *min) val = *min;
Chris@216 299 if (val > *max) val = *max;
Chris@0 300
Chris@87 301 } else if (name == "Threshold") {
Chris@37 302
Chris@1127 303 *min = -81;
Chris@1127 304 *max = -1;
Chris@37 305
Chris@906 306 *deflt = int(lrint(AudioLevel::multiplier_to_dB(m_initialThreshold)));
Chris@216 307 if (*deflt < *min) *deflt = *min;
Chris@216 308 if (*deflt > *max) *deflt = *max;
Chris@216 309
Chris@906 310 val = int(lrint(AudioLevel::multiplier_to_dB(m_threshold)));
Chris@216 311 if (val < *min) val = *min;
Chris@216 312 if (val > *max) val = *max;
Chris@37 313
Chris@87 314 } else if (name == "Colour Rotation") {
Chris@9 315
Chris@9 316 *min = 0;
Chris@9 317 *max = 256;
Chris@216 318 *deflt = m_initialRotation;
Chris@216 319
Chris@216 320 val = m_colourRotation;
Chris@9 321
Chris@87 322 } else if (name == "Colour Scale") {
Chris@0 323
Chris@1099 324 // linear, meter, db^2, db, phase
Chris@0 325 *min = 0;
Chris@176 326 *max = 4;
Chris@1092 327 *deflt = 2;
Chris@216 328
Chris@1137 329 val = convertFromColourScale(m_colourScale, m_colourScaleMultiple);
Chris@0 330
Chris@87 331 } else if (name == "Colour") {
Chris@0 332
Chris@0 333 *min = 0;
Chris@196 334 *max = ColourMapper::getColourMapCount() - 1;
Chris@216 335 *deflt = 0;
Chris@216 336
Chris@216 337 val = m_colourMap;
Chris@0 338
Chris@87 339 } else if (name == "Window Size") {
Chris@0 340
Chris@0 341 *min = 0;
Chris@0 342 *max = 10;
Chris@216 343 *deflt = 5;
Chris@0 344
Chris@216 345 val = 0;
Chris@0 346 int ws = m_windowSize;
Chris@216 347 while (ws > 32) { ws >>= 1; val ++; }
Chris@0 348
Chris@97 349 } else if (name == "Window Increment") {
Chris@0 350
Chris@0 351 *min = 0;
Chris@97 352 *max = 5;
Chris@216 353 *deflt = 2;
Chris@216 354
Chris@216 355 val = m_windowHopLevel;
Chris@0 356
Chris@87 357 } else if (name == "Min Frequency") {
Chris@37 358
Chris@37 359 *min = 0;
Chris@37 360 *max = 9;
Chris@216 361 *deflt = 1;
Chris@37 362
Chris@37 363 switch (m_minFrequency) {
Chris@216 364 case 0: default: val = 0; break;
Chris@216 365 case 10: val = 1; break;
Chris@216 366 case 20: val = 2; break;
Chris@216 367 case 40: val = 3; break;
Chris@216 368 case 100: val = 4; break;
Chris@216 369 case 250: val = 5; break;
Chris@216 370 case 500: val = 6; break;
Chris@216 371 case 1000: val = 7; break;
Chris@216 372 case 4000: val = 8; break;
Chris@216 373 case 10000: val = 9; break;
Chris@37 374 }
Chris@37 375
Chris@87 376 } else if (name == "Max Frequency") {
Chris@0 377
Chris@0 378 *min = 0;
Chris@0 379 *max = 9;
Chris@216 380 *deflt = 6;
Chris@0 381
Chris@0 382 switch (m_maxFrequency) {
Chris@216 383 case 500: val = 0; break;
Chris@216 384 case 1000: val = 1; break;
Chris@216 385 case 1500: val = 2; break;
Chris@216 386 case 2000: val = 3; break;
Chris@216 387 case 4000: val = 4; break;
Chris@216 388 case 6000: val = 5; break;
Chris@216 389 case 8000: val = 6; break;
Chris@216 390 case 12000: val = 7; break;
Chris@216 391 case 16000: val = 8; break;
Chris@216 392 default: val = 9; break;
Chris@0 393 }
Chris@0 394
Chris@87 395 } else if (name == "Frequency Scale") {
Chris@0 396
Chris@0 397 *min = 0;
Chris@0 398 *max = 1;
Chris@1103 399 *deflt = int(BinScale::Linear);
Chris@1093 400 val = (int)m_binScale;
Chris@0 401
Chris@87 402 } else if (name == "Bin Display") {
Chris@35 403
Chris@35 404 *min = 0;
Chris@35 405 *max = 2;
Chris@1103 406 *deflt = int(BinDisplay::AllBins);
Chris@216 407 val = (int)m_binDisplay;
Chris@35 408
Chris@862 409 } else if (name == "Normalization") {
Chris@36 410
Chris@862 411 *min = 0;
Chris@862 412 *max = 3;
Chris@1104 413 *deflt = 0;
Chris@1104 414
Chris@1104 415 val = convertFromColumnNorm(m_normalization, m_normalizeVisibleArea);
Chris@120 416
Chris@0 417 } else {
Chris@216 418 val = Layer::getPropertyRangeAndValue(name, min, max, deflt);
Chris@0 419 }
Chris@0 420
Chris@216 421 return val;
Chris@0 422 }
Chris@0 423
Chris@0 424 QString
Chris@0 425 SpectrogramLayer::getPropertyValueLabel(const PropertyName &name,
Chris@9 426 int value) const
Chris@0 427 {
Chris@87 428 if (name == "Colour") {
Chris@196 429 return ColourMapper::getColourMapName(value);
Chris@0 430 }
Chris@87 431 if (name == "Colour Scale") {
Chris@0 432 switch (value) {
Chris@0 433 default:
Chris@37 434 case 0: return tr("Linear");
Chris@37 435 case 1: return tr("Meter");
Chris@215 436 case 2: return tr("dBV^2");
Chris@215 437 case 3: return tr("dBV");
Chris@119 438 case 4: return tr("Phase");
Chris@0 439 }
Chris@0 440 }
Chris@862 441 if (name == "Normalization") {
Chris@862 442 return ""; // icon only
Chris@862 443 }
Chris@87 444 if (name == "Window Size") {
Chris@0 445 return QString("%1").arg(32 << value);
Chris@0 446 }
Chris@97 447 if (name == "Window Increment") {
Chris@0 448 switch (value) {
Chris@0 449 default:
Chris@112 450 case 0: return tr("None");
Chris@112 451 case 1: return tr("25 %");
Chris@112 452 case 2: return tr("50 %");
Chris@112 453 case 3: return tr("75 %");
Chris@112 454 case 4: return tr("87.5 %");
Chris@112 455 case 5: return tr("93.75 %");
Chris@0 456 }
Chris@0 457 }
Chris@87 458 if (name == "Min Frequency") {
Chris@37 459 switch (value) {
Chris@37 460 default:
Chris@38 461 case 0: return tr("No min");
Chris@37 462 case 1: return tr("10 Hz");
Chris@37 463 case 2: return tr("20 Hz");
Chris@37 464 case 3: return tr("40 Hz");
Chris@37 465 case 4: return tr("100 Hz");
Chris@37 466 case 5: return tr("250 Hz");
Chris@37 467 case 6: return tr("500 Hz");
Chris@37 468 case 7: return tr("1 KHz");
Chris@37 469 case 8: return tr("4 KHz");
Chris@37 470 case 9: return tr("10 KHz");
Chris@37 471 }
Chris@37 472 }
Chris@87 473 if (name == "Max Frequency") {
Chris@0 474 switch (value) {
Chris@0 475 default:
Chris@0 476 case 0: return tr("500 Hz");
Chris@0 477 case 1: return tr("1 KHz");
Chris@0 478 case 2: return tr("1.5 KHz");
Chris@0 479 case 3: return tr("2 KHz");
Chris@0 480 case 4: return tr("4 KHz");
Chris@0 481 case 5: return tr("6 KHz");
Chris@0 482 case 6: return tr("8 KHz");
Chris@0 483 case 7: return tr("12 KHz");
Chris@0 484 case 8: return tr("16 KHz");
Chris@38 485 case 9: return tr("No max");
Chris@0 486 }
Chris@0 487 }
Chris@87 488 if (name == "Frequency Scale") {
Chris@0 489 switch (value) {
Chris@0 490 default:
Chris@0 491 case 0: return tr("Linear");
Chris@0 492 case 1: return tr("Log");
Chris@0 493 }
Chris@0 494 }
Chris@87 495 if (name == "Bin Display") {
Chris@35 496 switch (value) {
Chris@35 497 default:
Chris@37 498 case 0: return tr("All Bins");
Chris@37 499 case 1: return tr("Peak Bins");
Chris@37 500 case 2: return tr("Frequencies");
Chris@35 501 }
Chris@35 502 }
Chris@0 503 return tr("<unknown>");
Chris@0 504 }
Chris@0 505
Chris@862 506 QString
Chris@862 507 SpectrogramLayer::getPropertyValueIconName(const PropertyName &name,
Chris@862 508 int value) const
Chris@862 509 {
Chris@862 510 if (name == "Normalization") {
Chris@862 511 switch(value) {
Chris@862 512 default:
Chris@862 513 case 0: return "normalise-none";
Chris@862 514 case 1: return "normalise-columns";
Chris@862 515 case 2: return "normalise";
Chris@862 516 case 3: return "normalise-hybrid";
Chris@862 517 }
Chris@862 518 }
Chris@862 519 return "";
Chris@862 520 }
Chris@862 521
Chris@167 522 RangeMapper *
Chris@167 523 SpectrogramLayer::getNewPropertyRangeMapper(const PropertyName &name) const
Chris@167 524 {
Chris@167 525 if (name == "Gain") {
Chris@167 526 return new LinearRangeMapper(-50, 50, -25, 25, tr("dB"));
Chris@167 527 }
Chris@167 528 if (name == "Threshold") {
Chris@1147 529 return new LinearRangeMapper(-81, -1, -81, -1, tr("dB"), false,
Chris@1147 530 { { -81, Strings::minus_infinity } });
Chris@167 531 }
Chris@167 532 return 0;
Chris@167 533 }
Chris@167 534
Chris@0 535 void
Chris@0 536 SpectrogramLayer::setProperty(const PropertyName &name, int value)
Chris@0 537 {
Chris@87 538 if (name == "Gain") {
Chris@906 539 setGain(float(pow(10, float(value)/20.0)));
Chris@87 540 } else if (name == "Threshold") {
Chris@1127 541 if (value == -81) setThreshold(0.0);
Chris@906 542 else setThreshold(float(AudioLevel::dB_to_multiplier(value)));
Chris@87 543 } else if (name == "Colour Rotation") {
Chris@9 544 setColourRotation(value);
Chris@87 545 } else if (name == "Colour") {
Chris@197 546 setColourMap(value);
Chris@87 547 } else if (name == "Window Size") {
Chris@0 548 setWindowSize(32 << value);
Chris@97 549 } else if (name == "Window Increment") {
Chris@97 550 setWindowHopLevel(value);
Chris@87 551 } else if (name == "Min Frequency") {
Chris@37 552 switch (value) {
Chris@37 553 default:
Chris@37 554 case 0: setMinFrequency(0); break;
Chris@37 555 case 1: setMinFrequency(10); break;
Chris@37 556 case 2: setMinFrequency(20); break;
Chris@37 557 case 3: setMinFrequency(40); break;
Chris@37 558 case 4: setMinFrequency(100); break;
Chris@37 559 case 5: setMinFrequency(250); break;
Chris@37 560 case 6: setMinFrequency(500); break;
Chris@37 561 case 7: setMinFrequency(1000); break;
Chris@37 562 case 8: setMinFrequency(4000); break;
Chris@37 563 case 9: setMinFrequency(10000); break;
Chris@37 564 }
Chris@133 565 int vs = getCurrentVerticalZoomStep();
Chris@133 566 if (vs != m_lastEmittedZoomStep) {
Chris@133 567 emit verticalZoomChanged();
Chris@133 568 m_lastEmittedZoomStep = vs;
Chris@133 569 }
Chris@87 570 } else if (name == "Max Frequency") {
Chris@0 571 switch (value) {
Chris@0 572 case 0: setMaxFrequency(500); break;
Chris@0 573 case 1: setMaxFrequency(1000); break;
Chris@0 574 case 2: setMaxFrequency(1500); break;
Chris@0 575 case 3: setMaxFrequency(2000); break;
Chris@0 576 case 4: setMaxFrequency(4000); break;
Chris@0 577 case 5: setMaxFrequency(6000); break;
Chris@0 578 case 6: setMaxFrequency(8000); break;
Chris@0 579 case 7: setMaxFrequency(12000); break;
Chris@0 580 case 8: setMaxFrequency(16000); break;
Chris@0 581 default:
Chris@0 582 case 9: setMaxFrequency(0); break;
Chris@0 583 }
Chris@133 584 int vs = getCurrentVerticalZoomStep();
Chris@133 585 if (vs != m_lastEmittedZoomStep) {
Chris@133 586 emit verticalZoomChanged();
Chris@133 587 m_lastEmittedZoomStep = vs;
Chris@133 588 }
Chris@87 589 } else if (name == "Colour Scale") {
Chris@1137 590 setColourScaleMultiple(1.0);
Chris@0 591 switch (value) {
Chris@0 592 default:
Chris@1105 593 case 0: setColourScale(ColourScaleType::Linear); break;
Chris@1105 594 case 1: setColourScale(ColourScaleType::Meter); break;
Chris@1137 595 case 2:
Chris@1137 596 setColourScale(ColourScaleType::Log);
Chris@1137 597 setColourScaleMultiple(2.0);
Chris@1137 598 break;
Chris@1105 599 case 3: setColourScale(ColourScaleType::Log); break;
Chris@1105 600 case 4: setColourScale(ColourScaleType::Phase); break;
Chris@0 601 }
Chris@87 602 } else if (name == "Frequency Scale") {
Chris@0 603 switch (value) {
Chris@0 604 default:
Chris@1103 605 case 0: setBinScale(BinScale::Linear); break;
Chris@1103 606 case 1: setBinScale(BinScale::Log); break;
Chris@0 607 }
Chris@87 608 } else if (name == "Bin Display") {
Chris@35 609 switch (value) {
Chris@35 610 default:
Chris@1103 611 case 0: setBinDisplay(BinDisplay::AllBins); break;
Chris@1103 612 case 1: setBinDisplay(BinDisplay::PeakBins); break;
Chris@1103 613 case 2: setBinDisplay(BinDisplay::PeakFrequencies); break;
Chris@35 614 }
Chris@862 615 } else if (name == "Normalization") {
Chris@1104 616 auto n = convertToColumnNorm(value);
Chris@1104 617 setNormalization(n.first);
Chris@1104 618 setNormalizeVisibleArea(n.second);
Chris@0 619 }
Chris@0 620 }
Chris@0 621
Chris@0 622 void
Chris@1106 623 SpectrogramLayer::invalidateRenderers()
Chris@95 624 {
Chris@1044 625 #ifdef DEBUG_SPECTROGRAM
Chris@1106 626 cerr << "SpectrogramLayer::invalidateRenderers called" << endl;
Chris@1044 627 #endif
Chris@1106 628
Chris@1089 629 for (ViewRendererMap::iterator i = m_renderers.begin();
Chris@1089 630 i != m_renderers.end(); ++i) {
Chris@1089 631 delete i->second;
Chris@1089 632 }
Chris@1089 633 m_renderers.clear();
Chris@95 634 }
Chris@95 635
Chris@95 636 void
Chris@122 637 SpectrogramLayer::preferenceChanged(PropertyContainer::PropertyName name)
Chris@122 638 {
Chris@587 639 SVDEBUG << "SpectrogramLayer::preferenceChanged(" << name << ")" << endl;
Chris@122 640
Chris@122 641 if (name == "Window Type") {
Chris@122 642 setWindowType(Preferences::getInstance()->getWindowType());
Chris@122 643 return;
Chris@122 644 }
Chris@490 645 if (name == "Spectrogram Y Smoothing") {
Chris@1086 646 setWindowSize(m_windowSize);
Chris@1106 647 invalidateRenderers();
Chris@490 648 invalidateMagnitudes();
Chris@490 649 emit layerParametersChanged();
Chris@490 650 }
Chris@490 651 if (name == "Spectrogram X Smoothing") {
Chris@1106 652 invalidateRenderers();
Chris@122 653 invalidateMagnitudes();
Chris@122 654 emit layerParametersChanged();
Chris@122 655 }
Chris@122 656 if (name == "Tuning Frequency") {
Chris@122 657 emit layerParametersChanged();
Chris@122 658 }
Chris@122 659 }
Chris@122 660
Chris@122 661 void
Chris@0 662 SpectrogramLayer::setChannel(int ch)
Chris@0 663 {
Chris@0 664 if (m_channel == ch) return;
Chris@0 665
Chris@1106 666 invalidateRenderers();
Chris@0 667 m_channel = ch;
Chris@1088 668 invalidateFFTModel();
Chris@9 669
Chris@0 670 emit layerParametersChanged();
Chris@0 671 }
Chris@0 672
Chris@0 673 int
Chris@0 674 SpectrogramLayer::getChannel() const
Chris@0 675 {
Chris@0 676 return m_channel;
Chris@0 677 }
Chris@0 678
Chris@1086 679 int
Chris@1086 680 SpectrogramLayer::getFFTOversampling() const
Chris@1086 681 {
Chris@1103 682 if (m_binDisplay != BinDisplay::AllBins) {
Chris@1086 683 return 1;
Chris@1086 684 }
Chris@1086 685
Chris@1086 686 Preferences::SpectrogramSmoothing smoothing =
Chris@1086 687 Preferences::getInstance()->getSpectrogramSmoothing();
Chris@1086 688
Chris@1086 689 if (smoothing == Preferences::NoSpectrogramSmoothing ||
Chris@1086 690 smoothing == Preferences::SpectrogramInterpolated) {
Chris@1086 691 return 1;
Chris@1086 692 }
Chris@1086 693
Chris@1086 694 return 4;
Chris@1086 695 }
Chris@1086 696
Chris@1087 697 int
Chris@1087 698 SpectrogramLayer::getFFTSize() const
Chris@1087 699 {
Chris@1087 700 return m_windowSize * getFFTOversampling();
Chris@1087 701 }
Chris@1087 702
Chris@0 703 void
Chris@805 704 SpectrogramLayer::setWindowSize(int ws)
Chris@0 705 {
Chris@1087 706 if (m_windowSize == ws) return;
Chris@0 707
Chris@1106 708 invalidateRenderers();
Chris@0 709
Chris@0 710 m_windowSize = ws;
Chris@0 711
Chris@1088 712 invalidateFFTModel();
Chris@9 713
Chris@9 714 emit layerParametersChanged();
Chris@0 715 }
Chris@0 716
Chris@805 717 int
Chris@0 718 SpectrogramLayer::getWindowSize() const
Chris@0 719 {
Chris@0 720 return m_windowSize;
Chris@0 721 }
Chris@0 722
Chris@0 723 void
Chris@805 724 SpectrogramLayer::setWindowHopLevel(int v)
Chris@0 725 {
Chris@97 726 if (m_windowHopLevel == v) return;
Chris@0 727
Chris@1106 728 invalidateRenderers();
Chris@0 729
Chris@97 730 m_windowHopLevel = v;
Chris@0 731
Chris@1088 732 invalidateFFTModel();
Chris@9 733
Chris@9 734 emit layerParametersChanged();
Chris@9 735
Chris@110 736 // fillCache();
Chris@0 737 }
Chris@0 738
Chris@805 739 int
Chris@97 740 SpectrogramLayer::getWindowHopLevel() const
Chris@0 741 {
Chris@97 742 return m_windowHopLevel;
Chris@0 743 }
Chris@0 744
Chris@0 745 void
Chris@0 746 SpectrogramLayer::setWindowType(WindowType w)
Chris@0 747 {
Chris@0 748 if (m_windowType == w) return;
Chris@0 749
Chris@1106 750 invalidateRenderers();
Chris@0 751
Chris@0 752 m_windowType = w;
Chris@110 753
Chris@1088 754 invalidateFFTModel();
Chris@9 755
Chris@9 756 emit layerParametersChanged();
Chris@0 757 }
Chris@0 758
Chris@0 759 WindowType
Chris@0 760 SpectrogramLayer::getWindowType() const
Chris@0 761 {
Chris@0 762 return m_windowType;
Chris@0 763 }
Chris@0 764
Chris@0 765 void
Chris@0 766 SpectrogramLayer::setGain(float gain)
Chris@0 767 {
Chris@587 768 // SVDEBUG << "SpectrogramLayer::setGain(" << gain << ") (my gain is now "
Chris@585 769 // << m_gain << ")" << endl;
Chris@55 770
Chris@40 771 if (m_gain == gain) return;
Chris@0 772
Chris@1106 773 invalidateRenderers();
Chris@0 774
Chris@0 775 m_gain = gain;
Chris@0 776
Chris@9 777 emit layerParametersChanged();
Chris@0 778 }
Chris@0 779
Chris@0 780 float
Chris@0 781 SpectrogramLayer::getGain() const
Chris@0 782 {
Chris@0 783 return m_gain;
Chris@0 784 }
Chris@0 785
Chris@0 786 void
Chris@37 787 SpectrogramLayer::setThreshold(float threshold)
Chris@37 788 {
Chris@40 789 if (m_threshold == threshold) return;
Chris@37 790
Chris@1106 791 invalidateRenderers();
Chris@37 792
Chris@37 793 m_threshold = threshold;
Chris@37 794
Chris@37 795 emit layerParametersChanged();
Chris@37 796 }
Chris@37 797
Chris@37 798 float
Chris@37 799 SpectrogramLayer::getThreshold() const
Chris@37 800 {
Chris@37 801 return m_threshold;
Chris@37 802 }
Chris@37 803
Chris@37 804 void
Chris@805 805 SpectrogramLayer::setMinFrequency(int mf)
Chris@37 806 {
Chris@37 807 if (m_minFrequency == mf) return;
Chris@37 808
Chris@587 809 // SVDEBUG << "SpectrogramLayer::setMinFrequency: " << mf << endl;
Chris@187 810
Chris@1106 811 invalidateRenderers();
Chris@119 812 invalidateMagnitudes();
Chris@37 813
Chris@37 814 m_minFrequency = mf;
Chris@37 815
Chris@37 816 emit layerParametersChanged();
Chris@37 817 }
Chris@37 818
Chris@805 819 int
Chris@37 820 SpectrogramLayer::getMinFrequency() const
Chris@37 821 {
Chris@37 822 return m_minFrequency;
Chris@37 823 }
Chris@37 824
Chris@37 825 void
Chris@805 826 SpectrogramLayer::setMaxFrequency(int mf)
Chris@0 827 {
Chris@0 828 if (m_maxFrequency == mf) return;
Chris@0 829
Chris@587 830 // SVDEBUG << "SpectrogramLayer::setMaxFrequency: " << mf << endl;
Chris@187 831
Chris@1106 832 invalidateRenderers();
Chris@119 833 invalidateMagnitudes();
Chris@0 834
Chris@0 835 m_maxFrequency = mf;
Chris@0 836
Chris@9 837 emit layerParametersChanged();
Chris@0 838 }
Chris@0 839
Chris@805 840 int
Chris@0 841 SpectrogramLayer::getMaxFrequency() const
Chris@0 842 {
Chris@0 843 return m_maxFrequency;
Chris@0 844 }
Chris@0 845
Chris@0 846 void
Chris@9 847 SpectrogramLayer::setColourRotation(int r)
Chris@9 848 {
Chris@9 849 if (r < 0) r = 0;
Chris@9 850 if (r > 256) r = 256;
Chris@9 851 int distance = r - m_colourRotation;
Chris@9 852
Chris@9 853 if (distance != 0) {
Chris@9 854 m_colourRotation = r;
Chris@9 855 }
Chris@1141 856
Chris@1141 857 // Initially the idea with colour rotation was that we would just
Chris@1141 858 // rotate the palette of an already-generated cache. That's not
Chris@1141 859 // really practical now that cacheing is handled in a separate
Chris@1141 860 // class in which the main cache no longer has a palette.
Chris@1141 861 invalidateRenderers();
Chris@1112 862
Chris@9 863 emit layerParametersChanged();
Chris@9 864 }
Chris@9 865
Chris@9 866 void
Chris@1105 867 SpectrogramLayer::setColourScale(ColourScaleType colourScale)
Chris@0 868 {
Chris@0 869 if (m_colourScale == colourScale) return;
Chris@0 870
Chris@1106 871 invalidateRenderers();
Chris@0 872
Chris@0 873 m_colourScale = colourScale;
Chris@0 874
Chris@9 875 emit layerParametersChanged();
Chris@0 876 }
Chris@0 877
Chris@1105 878 ColourScaleType
Chris@0 879 SpectrogramLayer::getColourScale() const
Chris@0 880 {
Chris@0 881 return m_colourScale;
Chris@0 882 }
Chris@0 883
Chris@0 884 void
Chris@1137 885 SpectrogramLayer::setColourScaleMultiple(double multiple)
Chris@1137 886 {
Chris@1137 887 if (m_colourScaleMultiple == multiple) return;
Chris@1137 888
Chris@1137 889 invalidateRenderers();
Chris@1137 890
Chris@1137 891 m_colourScaleMultiple = multiple;
Chris@1137 892
Chris@1137 893 emit layerParametersChanged();
Chris@1137 894 }
Chris@1137 895
Chris@1137 896 double
Chris@1137 897 SpectrogramLayer::getColourScaleMultiple() const
Chris@1137 898 {
Chris@1137 899 return m_colourScaleMultiple;
Chris@1137 900 }
Chris@1137 901
Chris@1137 902 void
Chris@197 903 SpectrogramLayer::setColourMap(int map)
Chris@0 904 {
Chris@197 905 if (m_colourMap == map) return;
Chris@0 906
Chris@1106 907 invalidateRenderers();
Chris@0 908
Chris@197 909 m_colourMap = map;
Chris@9 910
Chris@0 911 emit layerParametersChanged();
Chris@0 912 }
Chris@0 913
Chris@196 914 int
Chris@197 915 SpectrogramLayer::getColourMap() const
Chris@0 916 {
Chris@197 917 return m_colourMap;
Chris@0 918 }
Chris@0 919
Chris@0 920 void
Chris@1103 921 SpectrogramLayer::setBinScale(BinScale binScale)
Chris@0 922 {
Chris@1093 923 if (m_binScale == binScale) return;
Chris@0 924
Chris@1106 925 invalidateRenderers();
Chris@1093 926 m_binScale = binScale;
Chris@9 927
Chris@9 928 emit layerParametersChanged();
Chris@0 929 }
Chris@0 930
Chris@1103 931 BinScale
Chris@1093 932 SpectrogramLayer::getBinScale() const
Chris@0 933 {
Chris@1093 934 return m_binScale;
Chris@0 935 }
Chris@0 936
Chris@0 937 void
Chris@1103 938 SpectrogramLayer::setBinDisplay(BinDisplay binDisplay)
Chris@35 939 {
Chris@37 940 if (m_binDisplay == binDisplay) return;
Chris@35 941
Chris@1106 942 invalidateRenderers();
Chris@37 943 m_binDisplay = binDisplay;
Chris@35 944
Chris@35 945 emit layerParametersChanged();
Chris@35 946 }
Chris@35 947
Chris@1103 948 BinDisplay
Chris@37 949 SpectrogramLayer::getBinDisplay() const
Chris@35 950 {
Chris@37 951 return m_binDisplay;
Chris@35 952 }
Chris@35 953
Chris@35 954 void
Chris@1104 955 SpectrogramLayer::setNormalization(ColumnNormalization n)
Chris@36 956 {
Chris@862 957 if (m_normalization == n) return;
Chris@36 958
Chris@1106 959 invalidateRenderers();
Chris@119 960 invalidateMagnitudes();
Chris@862 961 m_normalization = n;
Chris@36 962
Chris@36 963 emit layerParametersChanged();
Chris@36 964 }
Chris@36 965
Chris@1104 966 ColumnNormalization
Chris@862 967 SpectrogramLayer::getNormalization() const
Chris@36 968 {
Chris@862 969 return m_normalization;
Chris@36 970 }
Chris@36 971
Chris@36 972 void
Chris@1104 973 SpectrogramLayer::setNormalizeVisibleArea(bool n)
Chris@1104 974 {
Chris@1104 975 if (m_normalizeVisibleArea == n) return;
Chris@1104 976
Chris@1106 977 invalidateRenderers();
Chris@1104 978 invalidateMagnitudes();
Chris@1104 979 m_normalizeVisibleArea = n;
Chris@1104 980
Chris@1104 981 emit layerParametersChanged();
Chris@1104 982 }
Chris@1104 983
Chris@1104 984 bool
Chris@1104 985 SpectrogramLayer::getNormalizeVisibleArea() const
Chris@1104 986 {
Chris@1104 987 return m_normalizeVisibleArea;
Chris@1104 988 }
Chris@1104 989
Chris@1104 990 void
Chris@918 991 SpectrogramLayer::setLayerDormant(const LayerGeometryProvider *v, bool dormant)
Chris@29 992 {
Chris@33 993 if (dormant) {
Chris@33 994
Chris@331 995 #ifdef DEBUG_SPECTROGRAM_REPAINT
Chris@985 996 cerr << "SpectrogramLayer::setLayerDormant(" << dormant << ")"
Chris@585 997 << endl;
Chris@331 998 #endif
Chris@331 999
Chris@131 1000 if (isLayerDormant(v)) {
Chris@131 1001 return;
Chris@131 1002 }
Chris@131 1003
Chris@131 1004 Layer::setLayerDormant(v, true);
Chris@33 1005
Chris@1106 1006 invalidateRenderers();
Chris@33 1007
Chris@33 1008 } else {
Chris@33 1009
Chris@131 1010 Layer::setLayerDormant(v, false);
Chris@33 1011 }
Chris@29 1012 }
Chris@29 1013
Chris@29 1014 void
Chris@0 1015 SpectrogramLayer::cacheInvalid()
Chris@0 1016 {
Chris@391 1017 #ifdef DEBUG_SPECTROGRAM_REPAINT
Chris@985 1018 cerr << "SpectrogramLayer::cacheInvalid()" << endl;
Chris@391 1019 #endif
Chris@391 1020
Chris@1106 1021 invalidateRenderers();
Chris@119 1022 invalidateMagnitudes();
Chris@0 1023 }
Chris@0 1024
Chris@0 1025 void
Chris@1037 1026 SpectrogramLayer::cacheInvalid(
Chris@1037 1027 #ifdef DEBUG_SPECTROGRAM_REPAINT
Chris@1037 1028 sv_frame_t from, sv_frame_t to
Chris@1037 1029 #else
Chris@1037 1030 sv_frame_t , sv_frame_t
Chris@1037 1031 #endif
Chris@1037 1032 )
Chris@0 1033 {
Chris@391 1034 #ifdef DEBUG_SPECTROGRAM_REPAINT
Chris@985 1035 cerr << "SpectrogramLayer::cacheInvalid(" << from << ", " << to << ")" << endl;
Chris@391 1036 #endif
Chris@391 1037
Chris@1030 1038 // We used to call invalidateMagnitudes(from, to) to invalidate
Chris@1030 1039 // only those caches whose views contained some of the (from, to)
Chris@1030 1040 // range. That's the right thing to do; it has been lost in
Chris@1030 1041 // pulling out the image cache code, but it might not matter very
Chris@1030 1042 // much, since the underlying models for spectrogram layers don't
Chris@1030 1043 // change very often. Let's see.
Chris@1106 1044 invalidateRenderers();
Chris@391 1045 invalidateMagnitudes();
Chris@0 1046 }
Chris@0 1047
Chris@224 1048 bool
Chris@224 1049 SpectrogramLayer::hasLightBackground() const
Chris@224 1050 {
Chris@287 1051 return ColourMapper(m_colourMap, 1.f, 255.f).hasLightBackground();
Chris@224 1052 }
Chris@224 1053
Chris@905 1054 double
Chris@40 1055 SpectrogramLayer::getEffectiveMinFrequency() const
Chris@40 1056 {
Chris@907 1057 sv_samplerate_t sr = m_model->getSampleRate();
Chris@1087 1058 double minf = double(sr) / getFFTSize();
Chris@40 1059
Chris@40 1060 if (m_minFrequency > 0.0) {
Chris@1087 1061 int minbin = int((double(m_minFrequency) * getFFTSize()) / sr + 0.01);
Chris@40 1062 if (minbin < 1) minbin = 1;
Chris@1087 1063 minf = minbin * sr / getFFTSize();
Chris@40 1064 }
Chris@40 1065
Chris@40 1066 return minf;
Chris@40 1067 }
Chris@40 1068
Chris@905 1069 double
Chris@40 1070 SpectrogramLayer::getEffectiveMaxFrequency() const
Chris@40 1071 {
Chris@907 1072 sv_samplerate_t sr = m_model->getSampleRate();
Chris@905 1073 double maxf = double(sr) / 2;
Chris@40 1074
Chris@40 1075 if (m_maxFrequency > 0.0) {
Chris@1087 1076 int maxbin = int((double(m_maxFrequency) * getFFTSize()) / sr + 0.1);
Chris@1087 1077 if (maxbin > getFFTSize() / 2) maxbin = getFFTSize() / 2;
Chris@1087 1078 maxf = maxbin * sr / getFFTSize();
Chris@40 1079 }
Chris@40 1080
Chris@40 1081 return maxf;
Chris@40 1082 }
Chris@40 1083
Chris@0 1084 bool
Chris@918 1085 SpectrogramLayer::getYBinRange(LayerGeometryProvider *v, int y, double &q0, double &q1) const
Chris@0 1086 {
Chris@382 1087 Profiler profiler("SpectrogramLayer::getYBinRange");
Chris@918 1088 int h = v->getPaintHeight();
Chris@0 1089 if (y < 0 || y >= h) return false;
Chris@1117 1090 q0 = getBinForY(v, y);
Chris@1117 1091 q1 = getBinForY(v, y-1);
Chris@1117 1092 return true;
Chris@1117 1093 }
Chris@1117 1094
Chris@1117 1095 double
Chris@1117 1096 SpectrogramLayer::getYForBin(const LayerGeometryProvider *v, double bin) const
Chris@1117 1097 {
Chris@1117 1098 double minf = getEffectiveMinFrequency();
Chris@1117 1099 double maxf = getEffectiveMaxFrequency();
Chris@1117 1100 bool logarithmic = (m_binScale == BinScale::Log);
Chris@1117 1101 sv_samplerate_t sr = m_model->getSampleRate();
Chris@1117 1102
Chris@1117 1103 double freq = (bin * sr) / getFFTSize();
Chris@1117 1104
Chris@1117 1105 double y = v->getYForFrequency(freq, minf, maxf, logarithmic);
Chris@1117 1106
Chris@1117 1107 return y;
Chris@1117 1108 }
Chris@1117 1109
Chris@1117 1110 double
Chris@1117 1111 SpectrogramLayer::getBinForY(const LayerGeometryProvider *v, double y) const
Chris@1117 1112 {
Chris@907 1113 sv_samplerate_t sr = m_model->getSampleRate();
Chris@905 1114 double minf = getEffectiveMinFrequency();
Chris@905 1115 double maxf = getEffectiveMaxFrequency();
Chris@0 1116
Chris@1103 1117 bool logarithmic = (m_binScale == BinScale::Log);
Chris@38 1118
Chris@1117 1119 double freq = v->getFrequencyForY(y, minf, maxf, logarithmic);
Chris@1117 1120
Chris@1117 1121 // Now map on to ("proportion of") actual bins
Chris@1117 1122 double bin = (freq * getFFTSize()) / sr;
Chris@1117 1123
Chris@1117 1124 return bin;
Chris@1085 1125 }
Chris@1085 1126
Chris@0 1127 bool
Chris@918 1128 SpectrogramLayer::getXBinRange(LayerGeometryProvider *v, int x, double &s0, double &s1) const
Chris@0 1129 {
Chris@907 1130 sv_frame_t modelStart = m_model->getStartFrame();
Chris@907 1131 sv_frame_t modelEnd = m_model->getEndFrame();
Chris@0 1132
Chris@0 1133 // Each pixel column covers an exact range of sample frames:
Chris@907 1134 sv_frame_t f0 = v->getFrameForX(x) - modelStart;
Chris@907 1135 sv_frame_t f1 = v->getFrameForX(x + 1) - modelStart - 1;
Chris@20 1136
Chris@41 1137 if (f1 < int(modelStart) || f0 > int(modelEnd)) {
Chris@41 1138 return false;
Chris@41 1139 }
Chris@20 1140
Chris@0 1141 // And that range may be drawn from a possibly non-integral
Chris@0 1142 // range of spectrogram windows:
Chris@0 1143
Chris@805 1144 int windowIncrement = getWindowIncrement();
Chris@905 1145 s0 = double(f0) / windowIncrement;
Chris@905 1146 s1 = double(f1) / windowIncrement;
Chris@0 1147
Chris@0 1148 return true;
Chris@0 1149 }
Chris@0 1150
Chris@0 1151 bool
Chris@918 1152 SpectrogramLayer::getXBinSourceRange(LayerGeometryProvider *v, int x, RealTime &min, RealTime &max) const
Chris@0 1153 {
Chris@905 1154 double s0 = 0, s1 = 0;
Chris@44 1155 if (!getXBinRange(v, x, s0, s1)) return false;
Chris@0 1156
Chris@0 1157 int s0i = int(s0 + 0.001);
Chris@0 1158 int s1i = int(s1);
Chris@0 1159
Chris@0 1160 int windowIncrement = getWindowIncrement();
Chris@0 1161 int w0 = s0i * windowIncrement - (m_windowSize - windowIncrement)/2;
Chris@0 1162 int w1 = s1i * windowIncrement + windowIncrement +
Chris@0 1163 (m_windowSize - windowIncrement)/2 - 1;
Chris@0 1164
Chris@0 1165 min = RealTime::frame2RealTime(w0, m_model->getSampleRate());
Chris@0 1166 max = RealTime::frame2RealTime(w1, m_model->getSampleRate());
Chris@0 1167 return true;
Chris@0 1168 }
Chris@0 1169
Chris@0 1170 bool
Chris@918 1171 SpectrogramLayer::getYBinSourceRange(LayerGeometryProvider *v, int y, double &freqMin, double &freqMax)
Chris@0 1172 const
Chris@0 1173 {
Chris@905 1174 double q0 = 0, q1 = 0;
Chris@44 1175 if (!getYBinRange(v, y, q0, q1)) return false;
Chris@0 1176
Chris@0 1177 int q0i = int(q0 + 0.001);
Chris@0 1178 int q1i = int(q1);
Chris@0 1179
Chris@907 1180 sv_samplerate_t sr = m_model->getSampleRate();
Chris@0 1181
Chris@0 1182 for (int q = q0i; q <= q1i; ++q) {
Chris@1087 1183 if (q == q0i) freqMin = (sr * q) / getFFTSize();
Chris@1087 1184 if (q == q1i) freqMax = (sr * (q+1)) / getFFTSize();
Chris@0 1185 }
Chris@0 1186 return true;
Chris@0 1187 }
Chris@35 1188
Chris@35 1189 bool
Chris@918 1190 SpectrogramLayer::getAdjustedYBinSourceRange(LayerGeometryProvider *v, int x, int y,
Chris@905 1191 double &freqMin, double &freqMax,
Chris@905 1192 double &adjFreqMin, double &adjFreqMax)
Chris@35 1193 const
Chris@35 1194 {
Chris@277 1195 if (!m_model || !m_model->isOK() || !m_model->isReady()) {
Chris@277 1196 return false;
Chris@277 1197 }
Chris@277 1198
Chris@1088 1199 FFTModel *fft = getFFTModel();
Chris@114 1200 if (!fft) return false;
Chris@110 1201
Chris@905 1202 double s0 = 0, s1 = 0;
Chris@44 1203 if (!getXBinRange(v, x, s0, s1)) return false;
Chris@35 1204
Chris@905 1205 double q0 = 0, q1 = 0;
Chris@44 1206 if (!getYBinRange(v, y, q0, q1)) return false;
Chris@35 1207
Chris@35 1208 int s0i = int(s0 + 0.001);
Chris@35 1209 int s1i = int(s1);
Chris@35 1210
Chris@35 1211 int q0i = int(q0 + 0.001);
Chris@35 1212 int q1i = int(q1);
Chris@35 1213
Chris@907 1214 sv_samplerate_t sr = m_model->getSampleRate();
Chris@35 1215
Chris@35 1216 bool haveAdj = false;
Chris@35 1217
Chris@1103 1218 bool peaksOnly = (m_binDisplay == BinDisplay::PeakBins ||
Chris@1103 1219 m_binDisplay == BinDisplay::PeakFrequencies);
Chris@37 1220
Chris@35 1221 for (int q = q0i; q <= q1i; ++q) {
Chris@35 1222
Chris@35 1223 for (int s = s0i; s <= s1i; ++s) {
Chris@35 1224
Chris@905 1225 double binfreq = (double(sr) * q) / m_windowSize;
Chris@35 1226 if (q == q0i) freqMin = binfreq;
Chris@35 1227 if (q == q1i) freqMax = binfreq;
Chris@37 1228
Chris@114 1229 if (peaksOnly && !fft->isLocalPeak(s, q)) continue;
Chris@38 1230
Chris@1086 1231 if (!fft->isOverThreshold
Chris@1087 1232 (s, q, float(m_threshold * double(getFFTSize())/2.0))) {
Chris@1086 1233 continue;
Chris@1086 1234 }
Chris@907 1235
Chris@907 1236 double freq = binfreq;
Chris@40 1237
Chris@114 1238 if (s < int(fft->getWidth()) - 1) {
Chris@38 1239
Chris@277 1240 fft->estimateStableFrequency(s, q, freq);
Chris@35 1241
Chris@38 1242 if (!haveAdj || freq < adjFreqMin) adjFreqMin = freq;
Chris@38 1243 if (!haveAdj || freq > adjFreqMax) adjFreqMax = freq;
Chris@35 1244
Chris@35 1245 haveAdj = true;
Chris@35 1246 }
Chris@35 1247 }
Chris@35 1248 }
Chris@35 1249
Chris@35 1250 if (!haveAdj) {
Chris@40 1251 adjFreqMin = adjFreqMax = 0.0;
Chris@35 1252 }
Chris@35 1253
Chris@35 1254 return haveAdj;
Chris@35 1255 }
Chris@0 1256
Chris@0 1257 bool
Chris@918 1258 SpectrogramLayer::getXYBinSourceRange(LayerGeometryProvider *v, int x, int y,
Chris@905 1259 double &min, double &max,
Chris@905 1260 double &phaseMin, double &phaseMax) const
Chris@0 1261 {
Chris@277 1262 if (!m_model || !m_model->isOK() || !m_model->isReady()) {
Chris@277 1263 return false;
Chris@277 1264 }
Chris@277 1265
Chris@905 1266 double q0 = 0, q1 = 0;
Chris@44 1267 if (!getYBinRange(v, y, q0, q1)) return false;
Chris@0 1268
Chris@905 1269 double s0 = 0, s1 = 0;
Chris@44 1270 if (!getXBinRange(v, x, s0, s1)) return false;
Chris@0 1271
Chris@0 1272 int q0i = int(q0 + 0.001);
Chris@0 1273 int q1i = int(q1);
Chris@0 1274
Chris@0 1275 int s0i = int(s0 + 0.001);
Chris@0 1276 int s1i = int(s1);
Chris@0 1277
Chris@37 1278 bool rv = false;
Chris@37 1279
Chris@1088 1280 FFTModel *fft = getFFTModel();
Chris@0 1281
Chris@114 1282 if (fft) {
Chris@114 1283
Chris@114 1284 int cw = fft->getWidth();
Chris@114 1285 int ch = fft->getHeight();
Chris@0 1286
Chris@110 1287 min = 0.0;
Chris@110 1288 max = 0.0;
Chris@110 1289 phaseMin = 0.0;
Chris@110 1290 phaseMax = 0.0;
Chris@110 1291 bool have = false;
Chris@0 1292
Chris@110 1293 for (int q = q0i; q <= q1i; ++q) {
Chris@110 1294 for (int s = s0i; s <= s1i; ++s) {
Chris@110 1295 if (s >= 0 && q >= 0 && s < cw && q < ch) {
Chris@117 1296
Chris@905 1297 double value;
Chris@38 1298
Chris@114 1299 value = fft->getPhaseAt(s, q);
Chris@110 1300 if (!have || value < phaseMin) { phaseMin = value; }
Chris@110 1301 if (!have || value > phaseMax) { phaseMax = value; }
Chris@91 1302
Chris@1087 1303 value = fft->getMagnitudeAt(s, q) / (getFFTSize()/2.0);
Chris@110 1304 if (!have || value < min) { min = value; }
Chris@110 1305 if (!have || value > max) { max = value; }
Chris@110 1306
Chris@110 1307 have = true;
Chris@110 1308 }
Chris@110 1309 }
Chris@110 1310 }
Chris@110 1311
Chris@110 1312 if (have) {
Chris@110 1313 rv = true;
Chris@110 1314 }
Chris@0 1315 }
Chris@0 1316
Chris@37 1317 return rv;
Chris@0 1318 }
Chris@114 1319
Chris@130 1320 FFTModel *
Chris@1088 1321 SpectrogramLayer::getFFTModel() const
Chris@114 1322 {
Chris@114 1323 if (!m_model) return 0;
Chris@114 1324
Chris@1087 1325 int fftSize = getFFTSize();
Chris@114 1326
Chris@1088 1327 //!!! it is now surely slower to do this on every getFFTModel()
Chris@1088 1328 //!!! request than it would be to recreate the model immediately
Chris@1088 1329 //!!! when something changes instead of just invalidating it
Chris@920 1330
Chris@1088 1331 if (m_fftModel &&
Chris@1088 1332 m_fftModel->getHeight() == fftSize / 2 + 1 &&
Chris@1088 1333 m_fftModel->getWindowIncrement() == getWindowIncrement()) {
Chris@1088 1334 return m_fftModel;
Chris@114 1335 }
Chris@1088 1336
Chris@1088 1337 delete m_peakCache;
Chris@1088 1338 m_peakCache = 0;
Chris@1088 1339
Chris@1088 1340 delete m_fftModel;
Chris@1088 1341 m_fftModel = new FFTModel(m_model,
Chris@1088 1342 m_channel,
Chris@1088 1343 m_windowType,
Chris@1088 1344 m_windowSize,
Chris@1088 1345 getWindowIncrement(),
Chris@1088 1346 fftSize);
Chris@1088 1347
Chris@1088 1348 if (!m_fftModel->isOK()) {
Chris@1088 1349 QMessageBox::critical
Chris@1088 1350 (0, tr("FFT cache failed"),
Chris@1088 1351 tr("Failed to create the FFT model for this spectrogram.\n"
Chris@1088 1352 "There may be insufficient memory or disc space to continue."));
Chris@1088 1353 delete m_fftModel;
Chris@1088 1354 m_fftModel = 0;
Chris@1088 1355 return 0;
Chris@114 1356 }
Chris@114 1357
Chris@1088 1358 ((SpectrogramLayer *)this)->sliceableModelReplaced(0, m_fftModel);
Chris@1088 1359
Chris@1088 1360 return m_fftModel;
Chris@114 1361 }
Chris@114 1362
Chris@484 1363 Dense3DModelPeakCache *
Chris@1088 1364 SpectrogramLayer::getPeakCache() const
Chris@484 1365 {
Chris@1088 1366 //!!! see comment in getFFTModel
Chris@1088 1367
Chris@1088 1368 if (!m_peakCache) {
Chris@1088 1369 FFTModel *f = getFFTModel();
Chris@484 1370 if (!f) return 0;
Chris@1088 1371 m_peakCache = new Dense3DModelPeakCache(f, m_peakCacheDivisor);
Chris@484 1372 }
Chris@1088 1373 return m_peakCache;
Chris@484 1374 }
Chris@484 1375
Chris@193 1376 const Model *
Chris@193 1377 SpectrogramLayer::getSliceableModel() const
Chris@193 1378 {
Chris@1088 1379 return m_fftModel;
Chris@193 1380 }
Chris@193 1381
Chris@114 1382 void
Chris@1088 1383 SpectrogramLayer::invalidateFFTModel()
Chris@114 1384 {
Chris@1044 1385 #ifdef DEBUG_SPECTROGRAM
Chris@1088 1386 cerr << "SpectrogramLayer::invalidateFFTModel called" << endl;
Chris@1044 1387 #endif
Chris@1088 1388
Chris@1088 1389 emit sliceableModelReplaced(m_fftModel, 0);
Chris@1088 1390
Chris@1088 1391 delete m_fftModel;
Chris@1088 1392 delete m_peakCache;
Chris@1088 1393
Chris@1088 1394 m_fftModel = 0;
Chris@1088 1395 m_peakCache = 0;
Chris@114 1396 }
Chris@114 1397
Chris@0 1398 void
Chris@119 1399 SpectrogramLayer::invalidateMagnitudes()
Chris@119 1400 {
Chris@1044 1401 #ifdef DEBUG_SPECTROGRAM
Chris@1044 1402 cerr << "SpectrogramLayer::invalidateMagnitudes called" << endl;
Chris@1044 1403 #endif
Chris@119 1404 m_viewMags.clear();
Chris@119 1405 }
Chris@1134 1406
Chris@119 1407 void
Chris@389 1408 SpectrogramLayer::setSynchronousPainting(bool synchronous)
Chris@389 1409 {
Chris@389 1410 m_synchronous = synchronous;
Chris@389 1411 }
Chris@389 1412
Chris@1089 1413 Colour3DPlotRenderer *
Chris@1089 1414 SpectrogramLayer::getRenderer(LayerGeometryProvider *v) const
Chris@1089 1415 {
Chris@1136 1416 int viewId = v->getId();
Chris@1136 1417
Chris@1136 1418 if (m_renderers.find(viewId) == m_renderers.end()) {
Chris@1089 1419
Chris@1089 1420 Colour3DPlotRenderer::Sources sources;
Chris@1089 1421 sources.verticalBinLayer = this;
Chris@1090 1422 sources.fft = getFFTModel();
Chris@1090 1423 sources.source = sources.fft;
Chris@1090 1424 sources.peaks = getPeakCache();
Chris@1089 1425
Chris@1092 1426 ColourScale::Parameters cparams;
Chris@1092 1427 cparams.colourMap = m_colourMap;
Chris@1137 1428 cparams.scaleType = m_colourScale;
Chris@1137 1429 cparams.multiple = m_colourScaleMultiple;
Chris@1129 1430
Chris@1129 1431 if (m_colourScale != ColourScaleType::Phase) {
Chris@1129 1432 cparams.gain = m_gain;
Chris@1129 1433 cparams.threshold = m_threshold;
Chris@1129 1434 }
Chris@1093 1435
Chris@1136 1436 float minValue = 0.0f;
Chris@1136 1437 float maxValue = 1.0f;
Chris@1136 1438
Chris@1136 1439 if (m_normalizeVisibleArea && m_viewMags[viewId].isSet()) {
Chris@1136 1440 minValue = m_viewMags[viewId].getMin();
Chris@1136 1441 maxValue = m_viewMags[viewId].getMax();
Chris@1136 1442 } else if (m_colourScale == ColourScaleType::Linear &&
Chris@1136 1443 m_normalization == ColumnNormalization::None) {
Chris@1136 1444 maxValue = 0.1f;
Chris@1093 1445 }
Chris@1129 1446
Chris@1136 1447 if (maxValue <= minValue) {
Chris@1136 1448 maxValue = minValue + 0.1f;
Chris@1136 1449 }
Chris@1136 1450 if (maxValue <= m_threshold) {
Chris@1136 1451 maxValue = m_threshold + 0.1f;
Chris@1136 1452 }
Chris@1136 1453
Chris@1136 1454 cparams.minValue = minValue;
Chris@1136 1455 cparams.maxValue = maxValue;
Chris@1136 1456
Chris@1136 1457 m_lastRenderedMags[viewId] = MagnitudeRange(minValue, maxValue);
Chris@1136 1458
Chris@1089 1459 Colour3DPlotRenderer::Parameters params;
Chris@1092 1460 params.colourScale = ColourScale(cparams);
Chris@1089 1461 params.normalization = m_normalization;
Chris@1093 1462 params.binDisplay = m_binDisplay;
Chris@1093 1463 params.binScale = m_binScale;
Chris@1141 1464 params.alwaysOpaque = true;
Chris@1093 1465 params.invertVertical = false;
Chris@1125 1466 params.scaleFactor = 1.0;
Chris@1112 1467 params.colourRotation = m_colourRotation;
Chris@1093 1468
Chris@1145 1469 if (m_colourScale != ColourScaleType::Phase &&
Chris@1145 1470 m_normalization != ColumnNormalization::Hybrid) {
Chris@1125 1471 params.scaleFactor *= 2.f / float(getFFTSize());
Chris@1125 1472 }
Chris@1125 1473
Chris@1093 1474 Preferences::SpectrogramSmoothing smoothing =
Chris@1093 1475 Preferences::getInstance()->getSpectrogramSmoothing();
Chris@1093 1476 params.interpolate =
Chris@1093 1477 (smoothing == Preferences::SpectrogramInterpolated ||
Chris@1093 1478 smoothing == Preferences::SpectrogramZeroPaddedAndInterpolated);
Chris@1089 1479
Chris@1089 1480 m_renderers[v->getId()] = new Colour3DPlotRenderer(sources, params);
Chris@1089 1481 }
Chris@1089 1482
Chris@1089 1483 return m_renderers[v->getId()];
Chris@1089 1484 }
Chris@1089 1485
Chris@1089 1486 void
Chris@1106 1487 SpectrogramLayer::paintWithRenderer(LayerGeometryProvider *v, QPainter &paint, QRect rect) const
Chris@1106 1488 {
Chris@1121 1489 Colour3DPlotRenderer *renderer = getRenderer(v);
Chris@1121 1490
Chris@1121 1491 Colour3DPlotRenderer::RenderResult result;
Chris@1122 1492 MagnitudeRange magRange;
Chris@1122 1493 int viewId = v->getId();
Chris@1122 1494
Chris@1136 1495 bool continuingPaint = !renderer->geometryChanged(v);
Chris@1136 1496
Chris@1136 1497 if (continuingPaint) {
Chris@1122 1498 magRange = m_viewMags[viewId];
Chris@1122 1499 }
Chris@1106 1500
Chris@1106 1501 if (m_synchronous) {
Chris@1121 1502
Chris@1121 1503 result = renderer->render(v, paint, rect);
Chris@1121 1504
Chris@1121 1505 } else {
Chris@1121 1506
Chris@1121 1507 result = renderer->renderTimeConstrained(v, paint, rect);
Chris@1121 1508
Chris@1143 1509 #ifdef DEBUG_SPECTROGRAM_REPAINT
Chris@1122 1510 cerr << "rect width from this paint: " << result.rendered.width()
Chris@1122 1511 << ", mag range in this paint: " << result.range.getMin() << " -> "
Chris@1121 1512 << result.range.getMax() << endl;
Chris@1143 1513 #endif
Chris@1121 1514
Chris@1121 1515 QRect uncached = renderer->getLargestUncachedRect(v);
Chris@1121 1516 if (uncached.width() > 0) {
Chris@1121 1517 v->updatePaintRect(uncached);
Chris@1121 1518 }
Chris@1106 1519 }
Chris@1106 1520
Chris@1122 1521 magRange.sample(result.range);
Chris@1122 1522
Chris@1122 1523 if (magRange.isSet()) {
Chris@1136 1524 if (m_viewMags[viewId] != magRange) {
Chris@1122 1525 m_viewMags[viewId] = magRange;
Chris@1143 1526 #ifdef DEBUG_SPECTROGRAM_REPAINT
Chris@1136 1527 cerr << "mag range in this view has changed: "
Chris@1136 1528 << magRange.getMin() << " -> " << magRange.getMax() << endl;
Chris@1143 1529 #endif
Chris@1122 1530 }
Chris@1122 1531 }
Chris@1136 1532
Chris@1136 1533 if (!continuingPaint && m_normalizeVisibleArea &&
Chris@1136 1534 m_viewMags[viewId] != m_lastRenderedMags[viewId]) {
Chris@1143 1535 #ifdef DEBUG_SPECTROGRAM_REPAINT
Chris@1136 1536 cerr << "mag range has changed from last rendered range: re-rendering"
Chris@1136 1537 << endl;
Chris@1143 1538 #endif
Chris@1136 1539 delete m_renderers[viewId];
Chris@1136 1540 m_renderers.erase(viewId);
Chris@1136 1541 v->updatePaintRect(v->getPaintRect());
Chris@1136 1542 }
Chris@1106 1543 }
Chris@1106 1544
Chris@1106 1545 void
Chris@916 1546 SpectrogramLayer::paint(LayerGeometryProvider *v, QPainter &paint, QRect rect) const
Chris@0 1547 {
Chris@334 1548 Profiler profiler("SpectrogramLayer::paint", false);
Chris@334 1549
Chris@0 1550 #ifdef DEBUG_SPECTROGRAM_REPAINT
Chris@1026 1551 cerr << "SpectrogramLayer::paint() entering: m_model is " << m_model << ", zoom level is " << v->getZoomLevel() << endl;
Chris@95 1552
Chris@1026 1553 cerr << "SpectrogramLayer::paint(): rect is " << rect.x() << "," << rect.y() << " " << rect.width() << "x" << rect.height() << endl;
Chris@0 1554 #endif
Chris@95 1555
Chris@0 1556 if (!m_model || !m_model->isOK() || !m_model->isReady()) {
Chris@0 1557 return;
Chris@0 1558 }
Chris@0 1559
Chris@47 1560 if (isLayerDormant(v)) {
Chris@587 1561 SVDEBUG << "SpectrogramLayer::paint(): Layer is dormant, making it undormant again" << endl;
Chris@29 1562 }
Chris@29 1563
Chris@1106 1564 paintWithRenderer(v, paint, rect);
Chris@1140 1565
Chris@1140 1566 illuminateLocalFeatures(v, paint);
Chris@480 1567 }
Chris@477 1568
Chris@121 1569 void
Chris@918 1570 SpectrogramLayer::illuminateLocalFeatures(LayerGeometryProvider *v, QPainter &paint) const
Chris@121 1571 {
Chris@382 1572 Profiler profiler("SpectrogramLayer::illuminateLocalFeatures");
Chris@382 1573
Chris@121 1574 QPoint localPos;
Chris@121 1575 if (!v->shouldIlluminateLocalFeatures(this, localPos) || !m_model) {
Chris@121 1576 return;
Chris@121 1577 }
Chris@121 1578
Chris@1143 1579 #ifdef DEBUG_SPECTROGRAM_REPAINT
Chris@1134 1580 cerr << "SpectrogramLayer: illuminateLocalFeatures("
Chris@1134 1581 << localPos.x() << "," << localPos.y() << ")" << endl;
Chris@1143 1582 #endif
Chris@121 1583
Chris@905 1584 double s0, s1;
Chris@905 1585 double f0, f1;
Chris@121 1586
Chris@121 1587 if (getXBinRange(v, localPos.x(), s0, s1) &&
Chris@121 1588 getYBinSourceRange(v, localPos.y(), f0, f1)) {
Chris@121 1589
Chris@121 1590 int s0i = int(s0 + 0.001);
Chris@121 1591 int s1i = int(s1);
Chris@121 1592
Chris@121 1593 int x0 = v->getXForFrame(s0i * getWindowIncrement());
Chris@121 1594 int x1 = v->getXForFrame((s1i + 1) * getWindowIncrement());
Chris@121 1595
Chris@248 1596 int y1 = int(getYForFrequency(v, f1));
Chris@248 1597 int y0 = int(getYForFrequency(v, f0));
Chris@121 1598
Chris@1143 1599 #ifdef DEBUG_SPECTROGRAM_REPAINT
Chris@1134 1600 cerr << "SpectrogramLayer: illuminate "
Chris@1134 1601 << x0 << "," << y1 << " -> " << x1 << "," << y0 << endl;
Chris@1143 1602 #endif
Chris@121 1603
Chris@287 1604 paint.setPen(v->getForeground());
Chris@133 1605
Chris@133 1606 //!!! should we be using paintCrosshairs for this?
Chris@133 1607
Chris@121 1608 paint.drawRect(x0, y1, x1 - x0 + 1, y0 - y1 + 1);
Chris@121 1609 }
Chris@121 1610 }
Chris@121 1611
Chris@905 1612 double
Chris@918 1613 SpectrogramLayer::getYForFrequency(const LayerGeometryProvider *v, double frequency) const
Chris@42 1614 {
Chris@44 1615 return v->getYForFrequency(frequency,
Chris@44 1616 getEffectiveMinFrequency(),
Chris@44 1617 getEffectiveMaxFrequency(),
Chris@1103 1618 m_binScale == BinScale::Log);
Chris@42 1619 }
Chris@42 1620
Chris@905 1621 double
Chris@918 1622 SpectrogramLayer::getFrequencyForY(const LayerGeometryProvider *v, int y) const
Chris@42 1623 {
Chris@44 1624 return v->getFrequencyForY(y,
Chris@44 1625 getEffectiveMinFrequency(),
Chris@44 1626 getEffectiveMaxFrequency(),
Chris@1103 1627 m_binScale == BinScale::Log);
Chris@42 1628 }
Chris@42 1629
Chris@0 1630 int
Chris@1090 1631 SpectrogramLayer::getCompletion(LayerGeometryProvider *) const
Chris@0 1632 {
Chris@1088 1633 if (!m_fftModel) return 100;
Chris@1088 1634 int completion = m_fftModel->getCompletion();
Chris@224 1635 #ifdef DEBUG_SPECTROGRAM_REPAINT
Chris@985 1636 cerr << "SpectrogramLayer::getCompletion: completion = " << completion << endl;
Chris@224 1637 #endif
Chris@0 1638 return completion;
Chris@0 1639 }
Chris@0 1640
Chris@583 1641 QString
Chris@1090 1642 SpectrogramLayer::getError(LayerGeometryProvider *) const
Chris@583 1643 {
Chris@1088 1644 if (!m_fftModel) return "";
Chris@1088 1645 return m_fftModel->getError();
Chris@583 1646 }
Chris@583 1647
Chris@28 1648 bool
Chris@905 1649 SpectrogramLayer::getValueExtents(double &min, double &max,
Chris@101 1650 bool &logarithmic, QString &unit) const
Chris@79 1651 {
Chris@133 1652 if (!m_model) return false;
Chris@133 1653
Chris@907 1654 sv_samplerate_t sr = m_model->getSampleRate();
Chris@1087 1655 min = double(sr) / getFFTSize();
Chris@905 1656 max = double(sr) / 2;
Chris@133 1657
Chris@1103 1658 logarithmic = (m_binScale == BinScale::Log);
Chris@79 1659 unit = "Hz";
Chris@79 1660 return true;
Chris@79 1661 }
Chris@79 1662
Chris@79 1663 bool
Chris@905 1664 SpectrogramLayer::getDisplayExtents(double &min, double &max) const
Chris@101 1665 {
Chris@101 1666 min = getEffectiveMinFrequency();
Chris@101 1667 max = getEffectiveMaxFrequency();
Chris@253 1668
Chris@587 1669 // SVDEBUG << "SpectrogramLayer::getDisplayExtents: " << min << "->" << max << endl;
Chris@101 1670 return true;
Chris@101 1671 }
Chris@101 1672
Chris@101 1673 bool
Chris@905 1674 SpectrogramLayer::setDisplayExtents(double min, double max)
Chris@120 1675 {
Chris@120 1676 if (!m_model) return false;
Chris@187 1677
Chris@587 1678 // SVDEBUG << "SpectrogramLayer::setDisplayExtents: " << min << "->" << max << endl;
Chris@187 1679
Chris@120 1680 if (min < 0) min = 0;
Chris@907 1681 if (max > m_model->getSampleRate()/2.0) max = m_model->getSampleRate()/2.0;
Chris@120 1682
Chris@907 1683 int minf = int(lrint(min));
Chris@907 1684 int maxf = int(lrint(max));
Chris@120 1685
Chris@120 1686 if (m_minFrequency == minf && m_maxFrequency == maxf) return true;
Chris@120 1687
Chris@1106 1688 invalidateRenderers();
Chris@120 1689 invalidateMagnitudes();
Chris@120 1690
Chris@120 1691 m_minFrequency = minf;
Chris@120 1692 m_maxFrequency = maxf;
Chris@120 1693
Chris@120 1694 emit layerParametersChanged();
Chris@120 1695
Chris@133 1696 int vs = getCurrentVerticalZoomStep();
Chris@133 1697 if (vs != m_lastEmittedZoomStep) {
Chris@133 1698 emit verticalZoomChanged();
Chris@133 1699 m_lastEmittedZoomStep = vs;
Chris@133 1700 }
Chris@133 1701
Chris@120 1702 return true;
Chris@120 1703 }
Chris@120 1704
Chris@120 1705 bool
Chris@918 1706 SpectrogramLayer::getYScaleValue(const LayerGeometryProvider *v, int y,
Chris@905 1707 double &value, QString &unit) const
Chris@261 1708 {
Chris@261 1709 value = getFrequencyForY(v, y);
Chris@261 1710 unit = "Hz";
Chris@261 1711 return true;
Chris@261 1712 }
Chris@261 1713
Chris@261 1714 bool
Chris@918 1715 SpectrogramLayer::snapToFeatureFrame(LayerGeometryProvider *,
Chris@907 1716 sv_frame_t &frame,
Chris@805 1717 int &resolution,
Chris@28 1718 SnapType snap) const
Chris@13 1719 {
Chris@13 1720 resolution = getWindowIncrement();
Chris@907 1721 sv_frame_t left = (frame / resolution) * resolution;
Chris@907 1722 sv_frame_t right = left + resolution;
Chris@28 1723
Chris@28 1724 switch (snap) {
Chris@28 1725 case SnapLeft: frame = left; break;
Chris@28 1726 case SnapRight: frame = right; break;
Chris@28 1727 case SnapNearest:
Chris@28 1728 case SnapNeighbouring:
Chris@28 1729 if (frame - left > right - frame) frame = right;
Chris@28 1730 else frame = left;
Chris@28 1731 break;
Chris@28 1732 }
Chris@28 1733
Chris@28 1734 return true;
Chris@28 1735 }
Chris@13 1736
Chris@283 1737 void
Chris@1139 1738 SpectrogramLayer::measureDoubleClick(LayerGeometryProvider *v, QMouseEvent *e)
Chris@283 1739 {
Chris@1139 1740 const Colour3DPlotRenderer *renderer = getRenderer(v);
Chris@1139 1741 if (!renderer) return;
Chris@1139 1742
Chris@1139 1743 QRect rect = renderer->findSimilarRegionExtents(e->pos());
Chris@283 1744 if (rect.isValid()) {
Chris@283 1745 MeasureRect mr;
Chris@283 1746 setMeasureRectFromPixrect(v, mr, rect);
Chris@283 1747 CommandHistory::getInstance()->addCommand
Chris@283 1748 (new AddMeasurementRectCommand(this, mr));
Chris@283 1749 }
Chris@283 1750 }
Chris@283 1751
Chris@77 1752 bool
Chris@918 1753 SpectrogramLayer::getCrosshairExtents(LayerGeometryProvider *v, QPainter &paint,
Chris@77 1754 QPoint cursorPos,
Chris@1025 1755 vector<QRect> &extents) const
Chris@77 1756 {
Chris@918 1757 QRect vertical(cursorPos.x() - 12, 0, 12, v->getPaintHeight());
Chris@77 1758 extents.push_back(vertical);
Chris@77 1759
Chris@77 1760 QRect horizontal(0, cursorPos.y(), cursorPos.x(), 1);
Chris@77 1761 extents.push_back(horizontal);
Chris@77 1762
Chris@608 1763 int sw = getVerticalScaleWidth(v, m_haveDetailedScale, paint);
Chris@264 1764
Chris@280 1765 QRect freq(sw, cursorPos.y() - paint.fontMetrics().ascent() - 2,
Chris@280 1766 paint.fontMetrics().width("123456 Hz") + 2,
Chris@280 1767 paint.fontMetrics().height());
Chris@280 1768 extents.push_back(freq);
Chris@264 1769
Chris@279 1770 QRect pitch(sw, cursorPos.y() + 2,
Chris@279 1771 paint.fontMetrics().width("C#10+50c") + 2,
Chris@279 1772 paint.fontMetrics().height());
Chris@279 1773 extents.push_back(pitch);
Chris@279 1774
Chris@280 1775 QRect rt(cursorPos.x(),
Chris@918 1776 v->getPaintHeight() - paint.fontMetrics().height() - 2,
Chris@280 1777 paint.fontMetrics().width("1234.567 s"),
Chris@280 1778 paint.fontMetrics().height());
Chris@280 1779 extents.push_back(rt);
Chris@280 1780
Chris@280 1781 int w(paint.fontMetrics().width("1234567890") + 2);
Chris@280 1782 QRect frame(cursorPos.x() - w - 2,
Chris@918 1783 v->getPaintHeight() - paint.fontMetrics().height() - 2,
Chris@280 1784 w,
Chris@280 1785 paint.fontMetrics().height());
Chris@280 1786 extents.push_back(frame);
Chris@280 1787
Chris@77 1788 return true;
Chris@77 1789 }
Chris@77 1790
Chris@77 1791 void
Chris@918 1792 SpectrogramLayer::paintCrosshairs(LayerGeometryProvider *v, QPainter &paint,
Chris@77 1793 QPoint cursorPos) const
Chris@77 1794 {
Chris@77 1795 paint.save();
Chris@283 1796
Chris@608 1797 int sw = getVerticalScaleWidth(v, m_haveDetailedScale, paint);
Chris@283 1798
Chris@282 1799 QFont fn = paint.font();
Chris@282 1800 if (fn.pointSize() > 8) {
Chris@282 1801 fn.setPointSize(fn.pointSize() - 1);
Chris@282 1802 paint.setFont(fn);
Chris@282 1803 }
Chris@77 1804 paint.setPen(m_crosshairColour);
Chris@77 1805
Chris@77 1806 paint.drawLine(0, cursorPos.y(), cursorPos.x() - 1, cursorPos.y());
Chris@918 1807 paint.drawLine(cursorPos.x(), 0, cursorPos.x(), v->getPaintHeight());
Chris@77 1808
Chris@905 1809 double fundamental = getFrequencyForY(v, cursorPos.y());
Chris@77 1810
Chris@1078 1811 PaintAssistant::drawVisibleText(v, paint,
Chris@278 1812 sw + 2,
Chris@278 1813 cursorPos.y() - 2,
Chris@278 1814 QString("%1 Hz").arg(fundamental),
Chris@1078 1815 PaintAssistant::OutlinedText);
Chris@278 1816
Chris@279 1817 if (Pitch::isFrequencyInMidiRange(fundamental)) {
Chris@279 1818 QString pitchLabel = Pitch::getPitchLabelForFrequency(fundamental);
Chris@1078 1819 PaintAssistant::drawVisibleText(v, paint,
Chris@279 1820 sw + 2,
Chris@279 1821 cursorPos.y() + paint.fontMetrics().ascent() + 2,
Chris@279 1822 pitchLabel,
Chris@1078 1823 PaintAssistant::OutlinedText);
Chris@279 1824 }
Chris@279 1825
Chris@907 1826 sv_frame_t frame = v->getFrameForX(cursorPos.x());
Chris@279 1827 RealTime rt = RealTime::frame2RealTime(frame, m_model->getSampleRate());
Chris@280 1828 QString rtLabel = QString("%1 s").arg(rt.toText(true).c_str());
Chris@280 1829 QString frameLabel = QString("%1").arg(frame);
Chris@1078 1830 PaintAssistant::drawVisibleText(v, paint,
Chris@280 1831 cursorPos.x() - paint.fontMetrics().width(frameLabel) - 2,
Chris@918 1832 v->getPaintHeight() - 2,
Chris@280 1833 frameLabel,
Chris@1078 1834 PaintAssistant::OutlinedText);
Chris@1078 1835 PaintAssistant::drawVisibleText(v, paint,
Chris@280 1836 cursorPos.x() + 2,
Chris@918 1837 v->getPaintHeight() - 2,
Chris@280 1838 rtLabel,
Chris@1078 1839 PaintAssistant::OutlinedText);
Chris@264 1840
Chris@77 1841 int harmonic = 2;
Chris@77 1842
Chris@77 1843 while (harmonic < 100) {
Chris@77 1844
Chris@907 1845 int hy = int(lrint(getYForFrequency(v, fundamental * harmonic)));
Chris@918 1846 if (hy < 0 || hy > v->getPaintHeight()) break;
Chris@77 1847
Chris@77 1848 int len = 7;
Chris@77 1849
Chris@77 1850 if (harmonic % 2 == 0) {
Chris@77 1851 if (harmonic % 4 == 0) {
Chris@77 1852 len = 12;
Chris@77 1853 } else {
Chris@77 1854 len = 10;
Chris@77 1855 }
Chris@77 1856 }
Chris@77 1857
Chris@77 1858 paint.drawLine(cursorPos.x() - len,
Chris@907 1859 hy,
Chris@77 1860 cursorPos.x(),
Chris@907 1861 hy);
Chris@77 1862
Chris@77 1863 ++harmonic;
Chris@77 1864 }
Chris@77 1865
Chris@77 1866 paint.restore();
Chris@77 1867 }
Chris@77 1868
Chris@25 1869 QString
Chris@918 1870 SpectrogramLayer::getFeatureDescription(LayerGeometryProvider *v, QPoint &pos) const
Chris@25 1871 {
Chris@25 1872 int x = pos.x();
Chris@25 1873 int y = pos.y();
Chris@0 1874
Chris@25 1875 if (!m_model || !m_model->isOK()) return "";
Chris@0 1876
Chris@905 1877 double magMin = 0, magMax = 0;
Chris@905 1878 double phaseMin = 0, phaseMax = 0;
Chris@905 1879 double freqMin = 0, freqMax = 0;
Chris@905 1880 double adjFreqMin = 0, adjFreqMax = 0;
Chris@25 1881 QString pitchMin, pitchMax;
Chris@0 1882 RealTime rtMin, rtMax;
Chris@0 1883
Chris@38 1884 bool haveValues = false;
Chris@0 1885
Chris@44 1886 if (!getXBinSourceRange(v, x, rtMin, rtMax)) {
Chris@38 1887 return "";
Chris@38 1888 }
Chris@44 1889 if (getXYBinSourceRange(v, x, y, magMin, magMax, phaseMin, phaseMax)) {
Chris@38 1890 haveValues = true;
Chris@38 1891 }
Chris@0 1892
Chris@35 1893 QString adjFreqText = "", adjPitchText = "";
Chris@35 1894
Chris@1103 1895 if (m_binDisplay == BinDisplay::PeakFrequencies) {
Chris@35 1896
Chris@44 1897 if (!getAdjustedYBinSourceRange(v, x, y, freqMin, freqMax,
Chris@38 1898 adjFreqMin, adjFreqMax)) {
Chris@38 1899 return "";
Chris@38 1900 }
Chris@35 1901
Chris@35 1902 if (adjFreqMin != adjFreqMax) {
Chris@65 1903 adjFreqText = tr("Peak Frequency:\t%1 - %2 Hz\n")
Chris@35 1904 .arg(adjFreqMin).arg(adjFreqMax);
Chris@35 1905 } else {
Chris@65 1906 adjFreqText = tr("Peak Frequency:\t%1 Hz\n")
Chris@35 1907 .arg(adjFreqMin);
Chris@38 1908 }
Chris@38 1909
Chris@38 1910 QString pmin = Pitch::getPitchLabelForFrequency(adjFreqMin);
Chris@38 1911 QString pmax = Pitch::getPitchLabelForFrequency(adjFreqMax);
Chris@38 1912
Chris@38 1913 if (pmin != pmax) {
Chris@65 1914 adjPitchText = tr("Peak Pitch:\t%3 - %4\n").arg(pmin).arg(pmax);
Chris@38 1915 } else {
Chris@65 1916 adjPitchText = tr("Peak Pitch:\t%2\n").arg(pmin);
Chris@35 1917 }
Chris@35 1918
Chris@35 1919 } else {
Chris@35 1920
Chris@44 1921 if (!getYBinSourceRange(v, y, freqMin, freqMax)) return "";
Chris@35 1922 }
Chris@35 1923
Chris@25 1924 QString text;
Chris@25 1925
Chris@25 1926 if (rtMin != rtMax) {
Chris@25 1927 text += tr("Time:\t%1 - %2\n")
Chris@25 1928 .arg(rtMin.toText(true).c_str())
Chris@25 1929 .arg(rtMax.toText(true).c_str());
Chris@25 1930 } else {
Chris@25 1931 text += tr("Time:\t%1\n")
Chris@25 1932 .arg(rtMin.toText(true).c_str());
Chris@0 1933 }
Chris@0 1934
Chris@25 1935 if (freqMin != freqMax) {
Chris@65 1936 text += tr("%1Bin Frequency:\t%2 - %3 Hz\n%4Bin Pitch:\t%5 - %6\n")
Chris@65 1937 .arg(adjFreqText)
Chris@25 1938 .arg(freqMin)
Chris@25 1939 .arg(freqMax)
Chris@65 1940 .arg(adjPitchText)
Chris@65 1941 .arg(Pitch::getPitchLabelForFrequency(freqMin))
Chris@65 1942 .arg(Pitch::getPitchLabelForFrequency(freqMax));
Chris@65 1943 } else {
Chris@65 1944 text += tr("%1Bin Frequency:\t%2 Hz\n%3Bin Pitch:\t%4\n")
Chris@35 1945 .arg(adjFreqText)
Chris@25 1946 .arg(freqMin)
Chris@65 1947 .arg(adjPitchText)
Chris@65 1948 .arg(Pitch::getPitchLabelForFrequency(freqMin));
Chris@25 1949 }
Chris@25 1950
Chris@38 1951 if (haveValues) {
Chris@905 1952 double dbMin = AudioLevel::multiplier_to_dB(magMin);
Chris@905 1953 double dbMax = AudioLevel::multiplier_to_dB(magMax);
Chris@43 1954 QString dbMinString;
Chris@43 1955 QString dbMaxString;
Chris@43 1956 if (dbMin == AudioLevel::DB_FLOOR) {
Chris@1147 1957 dbMinString = Strings::minus_infinity;
Chris@43 1958 } else {
Chris@907 1959 dbMinString = QString("%1").arg(lrint(dbMin));
Chris@43 1960 }
Chris@43 1961 if (dbMax == AudioLevel::DB_FLOOR) {
Chris@1147 1962 dbMaxString = Strings::minus_infinity;
Chris@43 1963 } else {
Chris@907 1964 dbMaxString = QString("%1").arg(lrint(dbMax));
Chris@43 1965 }
Chris@907 1966 if (lrint(dbMin) != lrint(dbMax)) {
Chris@199 1967 text += tr("dB:\t%1 - %2").arg(dbMinString).arg(dbMaxString);
Chris@25 1968 } else {
Chris@199 1969 text += tr("dB:\t%1").arg(dbMinString);
Chris@25 1970 }
Chris@38 1971 if (phaseMin != phaseMax) {
Chris@38 1972 text += tr("\nPhase:\t%1 - %2").arg(phaseMin).arg(phaseMax);
Chris@38 1973 } else {
Chris@38 1974 text += tr("\nPhase:\t%1").arg(phaseMin);
Chris@38 1975 }
Chris@25 1976 }
Chris@25 1977
Chris@25 1978 return text;
Chris@0 1979 }
Chris@25 1980
Chris@0 1981 int
Chris@40 1982 SpectrogramLayer::getColourScaleWidth(QPainter &paint) const
Chris@40 1983 {
Chris@40 1984 int cw;
Chris@40 1985
Chris@119 1986 cw = paint.fontMetrics().width("-80dB");
Chris@119 1987
Chris@40 1988 return cw;
Chris@40 1989 }
Chris@40 1990
Chris@40 1991 int
Chris@918 1992 SpectrogramLayer::getVerticalScaleWidth(LayerGeometryProvider *, bool detailed, QPainter &paint) const
Chris@0 1993 {
Chris@0 1994 if (!m_model || !m_model->isOK()) return 0;
Chris@0 1995
Chris@607 1996 int cw = 0;
Chris@607 1997 if (detailed) cw = getColourScaleWidth(paint);
Chris@40 1998
Chris@0 1999 int tw = paint.fontMetrics().width(QString("%1")
Chris@0 2000 .arg(m_maxFrequency > 0 ?
Chris@0 2001 m_maxFrequency - 1 :
Chris@0 2002 m_model->getSampleRate() / 2));
Chris@0 2003
Chris@234 2004 int fw = paint.fontMetrics().width(tr("43Hz"));
Chris@0 2005 if (tw < fw) tw = fw;
Chris@40 2006
Chris@1103 2007 int tickw = (m_binScale == BinScale::Log ? 10 : 4);
Chris@0 2008
Chris@40 2009 return cw + tickw + tw + 13;
Chris@0 2010 }
Chris@0 2011
Chris@0 2012 void
Chris@1142 2013 SpectrogramLayer::paintVerticalScale(LayerGeometryProvider *v, bool detailed,
Chris@1142 2014 QPainter &paint, QRect rect) const
Chris@0 2015 {
Chris@0 2016 if (!m_model || !m_model->isOK()) {
Chris@0 2017 return;
Chris@0 2018 }
Chris@0 2019
Chris@382 2020 Profiler profiler("SpectrogramLayer::paintVerticalScale");
Chris@122 2021
Chris@120 2022 //!!! cache this?
Chris@1142 2023
Chris@0 2024 int h = rect.height(), w = rect.width();
Chris@1142 2025 int textHeight = paint.fontMetrics().height();
Chris@1142 2026
Chris@1142 2027 if (detailed && (h > textHeight * 3 + 10)) {
Chris@1142 2028 paintDetailedScale(v, paint, rect);
Chris@1142 2029 }
Chris@1142 2030 m_haveDetailedScale = detailed;
Chris@0 2031
Chris@1103 2032 int tickw = (m_binScale == BinScale::Log ? 10 : 4);
Chris@1103 2033 int pkw = (m_binScale == BinScale::Log ? 10 : 0);
Chris@40 2034
Chris@1087 2035 int bins = getFFTSize() / 2;
Chris@907 2036 sv_samplerate_t sr = m_model->getSampleRate();
Chris@0 2037
Chris@0 2038 if (m_maxFrequency > 0) {
Chris@1087 2039 bins = int((double(m_maxFrequency) * getFFTSize()) / sr + 0.1);
Chris@1087 2040 if (bins > getFFTSize() / 2) bins = getFFTSize() / 2;
Chris@0 2041 }
Chris@0 2042
Chris@607 2043 int cw = 0;
Chris@607 2044 if (detailed) cw = getColourScaleWidth(paint);
Chris@40 2045
Chris@0 2046 int py = -1;
Chris@0 2047 int toff = -textHeight + paint.fontMetrics().ascent() + 2;
Chris@0 2048
Chris@40 2049 paint.drawLine(cw + 7, 0, cw + 7, h);
Chris@40 2050
Chris@0 2051 int bin = -1;
Chris@0 2052
Chris@918 2053 for (int y = 0; y < v->getPaintHeight(); ++y) {
Chris@0 2054
Chris@905 2055 double q0, q1;
Chris@918 2056 if (!getYBinRange(v, v->getPaintHeight() - y, q0, q1)) continue;
Chris@0 2057
Chris@0 2058 int vy;
Chris@0 2059
Chris@0 2060 if (int(q0) > bin) {
Chris@0 2061 vy = y;
Chris@0 2062 bin = int(q0);
Chris@0 2063 } else {
Chris@0 2064 continue;
Chris@0 2065 }
Chris@0 2066
Chris@1087 2067 int freq = int((sr * bin) / getFFTSize());
Chris@0 2068
Chris@0 2069 if (py >= 0 && (vy - py) < textHeight - 1) {
Chris@1103 2070 if (m_binScale == BinScale::Linear) {
Chris@40 2071 paint.drawLine(w - tickw, h - vy, w, h - vy);
Chris@40 2072 }
Chris@0 2073 continue;
Chris@0 2074 }
Chris@0 2075
Chris@0 2076 QString text = QString("%1").arg(freq);
Chris@234 2077 if (bin == 1) text = tr("%1Hz").arg(freq); // bin 0 is DC
Chris@40 2078 paint.drawLine(cw + 7, h - vy, w - pkw - 1, h - vy);
Chris@0 2079
Chris@0 2080 if (h - vy - textHeight >= -2) {
Chris@1025 2081 int tx = w - 3 - paint.fontMetrics().width(text) - max(tickw, pkw);
Chris@0 2082 paint.drawText(tx, h - vy + toff, text);
Chris@0 2083 }
Chris@0 2084
Chris@0 2085 py = vy;
Chris@0 2086 }
Chris@40 2087
Chris@1103 2088 if (m_binScale == BinScale::Log) {
Chris@40 2089
Chris@277 2090 // piano keyboard
Chris@277 2091
Chris@690 2092 PianoScale().paintPianoVertical
Chris@690 2093 (v, paint, QRect(w - pkw - 1, 0, pkw, h),
Chris@690 2094 getEffectiveMinFrequency(), getEffectiveMaxFrequency());
Chris@40 2095 }
Chris@608 2096
Chris@608 2097 m_haveDetailedScale = detailed;
Chris@0 2098 }
Chris@0 2099
Chris@1142 2100 void
Chris@1142 2101 SpectrogramLayer::paintDetailedScale(LayerGeometryProvider *v,
Chris@1142 2102 QPainter &paint, QRect rect) const
Chris@1142 2103 {
Chris@1142 2104 // The colour scale
Chris@1143 2105
Chris@1143 2106 if (m_colourScale == ColourScaleType::Phase) {
Chris@1143 2107 paintDetailedScalePhase(v, paint, rect);
Chris@1143 2108 return;
Chris@1143 2109 }
Chris@1142 2110
Chris@1142 2111 int h = rect.height();
Chris@1142 2112 int textHeight = paint.fontMetrics().height();
Chris@1142 2113 int toff = -textHeight + paint.fontMetrics().ascent() + 2;
Chris@1142 2114
Chris@1142 2115 int cw = getColourScaleWidth(paint);
Chris@1142 2116 int cbw = paint.fontMetrics().width("dB");
Chris@1142 2117
Chris@1142 2118 int topLines = 2;
Chris@1142 2119
Chris@1142 2120 int ch = h - textHeight * (topLines + 1) - 8;
Chris@1142 2121 // paint.drawRect(4, textHeight + 4, cw - 1, ch + 1);
Chris@1142 2122 paint.drawRect(4 + cw - cbw, textHeight * topLines + 4, cbw - 1, ch + 1);
Chris@1142 2123
Chris@1142 2124 QString top, bottom;
Chris@1142 2125 double min = m_viewMags[v->getId()].getMin();
Chris@1142 2126 double max = m_viewMags[v->getId()].getMax();
Chris@1142 2127
Chris@1142 2128 if (min < m_threshold) min = m_threshold;
Chris@1142 2129 if (max <= min) max = min + 0.1;
Chris@1142 2130
Chris@1142 2131 double dBmin = AudioLevel::multiplier_to_dB(min);
Chris@1142 2132 double dBmax = AudioLevel::multiplier_to_dB(max);
Chris@1142 2133
Chris@1142 2134 #ifdef DEBUG_SPECTROGRAM_REPAINT
Chris@1142 2135 cerr << "paintVerticalScale: for view id " << v->getId()
Chris@1142 2136 << ": min = " << min << ", max = " << max
Chris@1142 2137 << ", dBmin = " << dBmin << ", dBmax = " << dBmax << endl;
Chris@1142 2138 #endif
Chris@1142 2139
Chris@1142 2140 if (dBmax < -60.f) dBmax = -60.f;
Chris@1142 2141 else top = QString("%1").arg(lrint(dBmax));
Chris@1142 2142
Chris@1142 2143 if (dBmin < dBmax - 60.f) dBmin = dBmax - 60.f;
Chris@1142 2144 bottom = QString("%1").arg(lrint(dBmin));
Chris@1142 2145
Chris@1142 2146 #ifdef DEBUG_SPECTROGRAM_REPAINT
Chris@1142 2147 cerr << "adjusted dB range to min = " << dBmin << ", max = " << dBmax
Chris@1142 2148 << endl;
Chris@1142 2149 #endif
Chris@1142 2150
Chris@1143 2151 paint.drawText((cw + 6 - paint.fontMetrics().width("dBFS")) / 2,
Chris@1143 2152 2 + textHeight + toff, "dBFS");
Chris@1142 2153
Chris@1142 2154 paint.drawText(3 + cw - cbw - paint.fontMetrics().width(top),
Chris@1142 2155 2 + textHeight * topLines + toff + textHeight/2, top);
Chris@1142 2156
Chris@1142 2157 paint.drawText(3 + cw - cbw - paint.fontMetrics().width(bottom),
Chris@1142 2158 h + toff - 3 - textHeight/2, bottom);
Chris@1142 2159
Chris@1142 2160 paint.save();
Chris@1142 2161 paint.setBrush(Qt::NoBrush);
Chris@1142 2162
Chris@1142 2163 int lasty = 0;
Chris@1142 2164 int lastdb = 0;
Chris@1142 2165
Chris@1142 2166 for (int i = 0; i < ch; ++i) {
Chris@1142 2167
Chris@1142 2168 double dBval = dBmin + (((dBmax - dBmin) * i) / (ch - 1));
Chris@1142 2169 int idb = int(dBval);
Chris@1142 2170
Chris@1142 2171 double value = AudioLevel::dB_to_multiplier(dBval);
Chris@1142 2172 paint.setPen(getRenderer(v)->getColour(value));
Chris@1142 2173
Chris@1142 2174 int y = textHeight * topLines + 4 + ch - i;
Chris@1142 2175
Chris@1142 2176 paint.drawLine(5 + cw - cbw, y, cw + 2, y);
Chris@1144 2177
Chris@1142 2178 if (i == 0) {
Chris@1142 2179 lasty = y;
Chris@1142 2180 lastdb = idb;
Chris@1142 2181 } else if (i < ch - paint.fontMetrics().ascent() &&
Chris@1142 2182 idb != lastdb &&
Chris@1142 2183 ((abs(y - lasty) > textHeight &&
Chris@1142 2184 idb % 10 == 0) ||
Chris@1142 2185 (abs(y - lasty) > paint.fontMetrics().ascent() &&
Chris@1142 2186 idb % 5 == 0))) {
Chris@1144 2187 paint.setPen(v->getForeground());
Chris@1142 2188 QString text = QString("%1").arg(idb);
Chris@1142 2189 paint.drawText(3 + cw - cbw - paint.fontMetrics().width(text),
Chris@1142 2190 y + toff + textHeight/2, text);
Chris@1142 2191 paint.drawLine(5 + cw - cbw, y, 8 + cw - cbw, y);
Chris@1142 2192 lasty = y;
Chris@1142 2193 lastdb = idb;
Chris@1142 2194 }
Chris@1142 2195 }
Chris@1142 2196 paint.restore();
Chris@1142 2197 }
Chris@1142 2198
Chris@1143 2199 void
Chris@1143 2200 SpectrogramLayer::paintDetailedScalePhase(LayerGeometryProvider *v,
Chris@1143 2201 QPainter &paint, QRect rect) const
Chris@1143 2202 {
Chris@1143 2203 // The colour scale in phase mode
Chris@1143 2204
Chris@1143 2205 int h = rect.height();
Chris@1143 2206 int textHeight = paint.fontMetrics().height();
Chris@1143 2207 int toff = -textHeight + paint.fontMetrics().ascent() + 2;
Chris@1143 2208
Chris@1143 2209 int cw = getColourScaleWidth(paint);
Chris@1143 2210
Chris@1143 2211 // Phase is not measured in dB of course, but this places the
Chris@1143 2212 // scale at the same position as in the magnitude spectrogram
Chris@1143 2213 int cbw = paint.fontMetrics().width("dB");
Chris@1143 2214
Chris@1143 2215 int topLines = 1;
Chris@1143 2216
Chris@1143 2217 int ch = h - textHeight * (topLines + 1) - 8;
Chris@1143 2218 paint.drawRect(4 + cw - cbw, textHeight * topLines + 4, cbw - 1, ch + 1);
Chris@1143 2219
Chris@1147 2220 QString top = Strings::pi, bottom = Strings::minus_pi, middle = "0";
Chris@1143 2221
Chris@1143 2222 double min = -M_PI;
Chris@1143 2223 double max = M_PI;
Chris@1143 2224
Chris@1143 2225 paint.drawText(3 + cw - cbw - paint.fontMetrics().width(top),
Chris@1143 2226 2 + textHeight * topLines + toff + textHeight/2, top);
Chris@1143 2227
Chris@1143 2228 paint.drawText(3 + cw - cbw - paint.fontMetrics().width(middle),
Chris@1143 2229 2 + textHeight * topLines + ch/2 + toff + textHeight/2, middle);
Chris@1143 2230
Chris@1143 2231 paint.drawText(3 + cw - cbw - paint.fontMetrics().width(bottom),
Chris@1143 2232 h + toff - 3 - textHeight/2, bottom);
Chris@1143 2233
Chris@1143 2234 paint.save();
Chris@1143 2235 paint.setBrush(Qt::NoBrush);
Chris@1143 2236
Chris@1143 2237 for (int i = 0; i < ch; ++i) {
Chris@1143 2238 double val = min + (((max - min) * i) / (ch - 1));
Chris@1143 2239 paint.setPen(getRenderer(v)->getColour(val));
Chris@1143 2240 int y = textHeight * topLines + 4 + ch - i;
Chris@1143 2241 paint.drawLine(5 + cw - cbw, y, cw + 2, y);
Chris@1143 2242 }
Chris@1143 2243 paint.restore();
Chris@1143 2244 }
Chris@1143 2245
Chris@187 2246 class SpectrogramRangeMapper : public RangeMapper
Chris@187 2247 {
Chris@187 2248 public:
Chris@901 2249 SpectrogramRangeMapper(sv_samplerate_t sr, int /* fftsize */) :
Chris@901 2250 m_dist(sr / 2),
Chris@901 2251 m_s2(sqrt(sqrt(2))) { }
Chris@187 2252 ~SpectrogramRangeMapper() { }
Chris@187 2253
Chris@901 2254 virtual int getPositionForValue(double value) const {
Chris@901 2255
Chris@901 2256 double dist = m_dist;
Chris@187 2257
Chris@187 2258 int n = 0;
Chris@187 2259
Chris@901 2260 while (dist > (value + 0.00001) && dist > 0.1) {
Chris@187 2261 dist /= m_s2;
Chris@187 2262 ++n;
Chris@187 2263 }
Chris@187 2264
Chris@187 2265 return n;
Chris@187 2266 }
Chris@724 2267
Chris@901 2268 virtual int getPositionForValueUnclamped(double value) const {
Chris@724 2269 // We don't really support this
Chris@724 2270 return getPositionForValue(value);
Chris@724 2271 }
Chris@187 2272
Chris@901 2273 virtual double getValueForPosition(int position) const {
Chris@187 2274
Chris@187 2275 // Vertical zoom step 0 shows the entire range from DC ->
Chris@187 2276 // Nyquist frequency. Step 1 shows 2^(1/4) of the range of
Chris@187 2277 // step 0, and so on until the visible range is smaller than
Chris@187 2278 // the frequency step between bins at the current fft size.
Chris@187 2279
Chris@901 2280 double dist = m_dist;
Chris@187 2281
Chris@187 2282 int n = 0;
Chris@187 2283 while (n < position) {
Chris@187 2284 dist /= m_s2;
Chris@187 2285 ++n;
Chris@187 2286 }
Chris@187 2287
Chris@187 2288 return dist;
Chris@187 2289 }
Chris@187 2290
Chris@901 2291 virtual double getValueForPositionUnclamped(int position) const {
Chris@724 2292 // We don't really support this
Chris@724 2293 return getValueForPosition(position);
Chris@724 2294 }
Chris@724 2295
Chris@187 2296 virtual QString getUnit() const { return "Hz"; }
Chris@187 2297
Chris@187 2298 protected:
Chris@901 2299 double m_dist;
Chris@901 2300 double m_s2;
Chris@187 2301 };
Chris@187 2302
Chris@133 2303 int
Chris@133 2304 SpectrogramLayer::getVerticalZoomSteps(int &defaultStep) const
Chris@133 2305 {
Chris@135 2306 if (!m_model) return 0;
Chris@187 2307
Chris@907 2308 sv_samplerate_t sr = m_model->getSampleRate();
Chris@187 2309
Chris@1087 2310 SpectrogramRangeMapper mapper(sr, getFFTSize());
Chris@1087 2311
Chris@1087 2312 // int maxStep = mapper.getPositionForValue((double(sr) / getFFTSize()) + 0.001);
Chris@187 2313 int maxStep = mapper.getPositionForValue(0);
Chris@905 2314 int minStep = mapper.getPositionForValue(double(sr) / 2);
Chris@250 2315
Chris@805 2316 int initialMax = m_initialMaxFrequency;
Chris@907 2317 if (initialMax == 0) initialMax = int(sr / 2);
Chris@250 2318
Chris@250 2319 defaultStep = mapper.getPositionForValue(initialMax) - minStep;
Chris@250 2320
Chris@587 2321 // SVDEBUG << "SpectrogramLayer::getVerticalZoomSteps: " << maxStep - minStep << " (" << maxStep <<"-" << minStep << "), default is " << defaultStep << " (from initial max freq " << initialMax << ")" << endl;
Chris@187 2322
Chris@187 2323 return maxStep - minStep;
Chris@133 2324 }
Chris@133 2325
Chris@133 2326 int
Chris@133 2327 SpectrogramLayer::getCurrentVerticalZoomStep() const
Chris@133 2328 {
Chris@133 2329 if (!m_model) return 0;
Chris@133 2330
Chris@905 2331 double dmin, dmax;
Chris@133 2332 getDisplayExtents(dmin, dmax);
Chris@133 2333
Chris@1087 2334 SpectrogramRangeMapper mapper(m_model->getSampleRate(), getFFTSize());
Chris@187 2335 int n = mapper.getPositionForValue(dmax - dmin);
Chris@587 2336 // SVDEBUG << "SpectrogramLayer::getCurrentVerticalZoomStep: " << n << endl;
Chris@133 2337 return n;
Chris@133 2338 }
Chris@133 2339
Chris@133 2340 void
Chris@133 2341 SpectrogramLayer::setVerticalZoomStep(int step)
Chris@133 2342 {
Chris@187 2343 if (!m_model) return;
Chris@187 2344
Chris@905 2345 double dmin = m_minFrequency, dmax = m_maxFrequency;
Chris@253 2346 // getDisplayExtents(dmin, dmax);
Chris@253 2347
Chris@682 2348 // cerr << "current range " << dmin << " -> " << dmax << ", range " << dmax-dmin << ", mid " << (dmax + dmin)/2 << endl;
Chris@133 2349
Chris@907 2350 sv_samplerate_t sr = m_model->getSampleRate();
Chris@1087 2351 SpectrogramRangeMapper mapper(sr, getFFTSize());
Chris@905 2352 double newdist = mapper.getValueForPosition(step);
Chris@905 2353
Chris@905 2354 double newmin, newmax;
Chris@253 2355
Chris@1103 2356 if (m_binScale == BinScale::Log) {
Chris@253 2357
Chris@253 2358 // need to pick newmin and newmax such that
Chris@253 2359 //
Chris@253 2360 // (log(newmin) + log(newmax)) / 2 == logmid
Chris@253 2361 // and
Chris@253 2362 // newmax - newmin = newdist
Chris@253 2363 //
Chris@253 2364 // so log(newmax - newdist) + log(newmax) == 2logmid
Chris@253 2365 // log(newmax(newmax - newdist)) == 2logmid
Chris@253 2366 // newmax.newmax - newmax.newdist == exp(2logmid)
Chris@253 2367 // newmax^2 + (-newdist)newmax + -exp(2logmid) == 0
Chris@253 2368 // quadratic with a = 1, b = -newdist, c = -exp(2logmid), all known
Chris@253 2369 //
Chris@253 2370 // positive root
Chris@253 2371 // newmax = (newdist + sqrt(newdist^2 + 4exp(2logmid))) / 2
Chris@253 2372 //
Chris@253 2373 // but logmid = (log(dmin) + log(dmax)) / 2
Chris@253 2374 // so exp(2logmid) = exp(log(dmin) + log(dmax))
Chris@253 2375 // = exp(log(dmin.dmax))
Chris@253 2376 // = dmin.dmax
Chris@253 2377 // so newmax = (newdist + sqrtf(newdist^2 + 4dmin.dmax)) / 2
Chris@253 2378
Chris@907 2379 newmax = (newdist + sqrt(newdist*newdist + 4*dmin*dmax)) / 2;
Chris@253 2380 newmin = newmax - newdist;
Chris@253 2381
Chris@682 2382 // cerr << "newmin = " << newmin << ", newmax = " << newmax << endl;
Chris@253 2383
Chris@253 2384 } else {
Chris@905 2385 double dmid = (dmax + dmin) / 2;
Chris@253 2386 newmin = dmid - newdist / 2;
Chris@253 2387 newmax = dmid + newdist / 2;
Chris@253 2388 }
Chris@187 2389
Chris@905 2390 double mmin, mmax;
Chris@187 2391 mmin = 0;
Chris@905 2392 mmax = double(sr) / 2;
Chris@133 2393
Chris@187 2394 if (newmin < mmin) {
Chris@187 2395 newmax += (mmin - newmin);
Chris@187 2396 newmin = mmin;
Chris@187 2397 }
Chris@187 2398 if (newmax > mmax) {
Chris@187 2399 newmax = mmax;
Chris@187 2400 }
Chris@133 2401
Chris@587 2402 // SVDEBUG << "SpectrogramLayer::setVerticalZoomStep: " << step << ": " << newmin << " -> " << newmax << " (range " << newdist << ")" << endl;
Chris@253 2403
Chris@907 2404 setMinFrequency(int(lrint(newmin)));
Chris@907 2405 setMaxFrequency(int(lrint(newmax)));
Chris@187 2406 }
Chris@187 2407
Chris@187 2408 RangeMapper *
Chris@187 2409 SpectrogramLayer::getNewVerticalZoomRangeMapper() const
Chris@187 2410 {
Chris@187 2411 if (!m_model) return 0;
Chris@1087 2412 return new SpectrogramRangeMapper(m_model->getSampleRate(), getFFTSize());
Chris@133 2413 }
Chris@133 2414
Chris@273 2415 void
Chris@918 2416 SpectrogramLayer::updateMeasureRectYCoords(LayerGeometryProvider *v, const MeasureRect &r) const
Chris@273 2417 {
Chris@273 2418 int y0 = 0;
Chris@907 2419 if (r.startY > 0.0) y0 = int(getYForFrequency(v, r.startY));
Chris@273 2420
Chris@273 2421 int y1 = y0;
Chris@907 2422 if (r.endY > 0.0) y1 = int(getYForFrequency(v, r.endY));
Chris@273 2423
Chris@587 2424 // SVDEBUG << "SpectrogramLayer::updateMeasureRectYCoords: start " << r.startY << " -> " << y0 << ", end " << r.endY << " -> " << y1 << endl;
Chris@273 2425
Chris@273 2426 r.pixrect = QRect(r.pixrect.x(), y0, r.pixrect.width(), y1 - y0);
Chris@273 2427 }
Chris@273 2428
Chris@273 2429 void
Chris@918 2430 SpectrogramLayer::setMeasureRectYCoord(LayerGeometryProvider *v, MeasureRect &r, bool start, int y) const
Chris@273 2431 {
Chris@273 2432 if (start) {
Chris@273 2433 r.startY = getFrequencyForY(v, y);
Chris@273 2434 r.endY = r.startY;
Chris@273 2435 } else {
Chris@273 2436 r.endY = getFrequencyForY(v, y);
Chris@273 2437 }
Chris@587 2438 // SVDEBUG << "SpectrogramLayer::setMeasureRectYCoord: start " << r.startY << " <- " << y << ", end " << r.endY << " <- " << y << endl;
Chris@273 2439
Chris@273 2440 }
Chris@273 2441
Chris@316 2442 void
Chris@316 2443 SpectrogramLayer::toXml(QTextStream &stream,
Chris@316 2444 QString indent, QString extraAttributes) const
Chris@6 2445 {
Chris@6 2446 QString s;
Chris@6 2447
Chris@6 2448 s += QString("channel=\"%1\" "
Chris@6 2449 "windowSize=\"%2\" "
Chris@153 2450 "windowHopLevel=\"%3\" "
Chris@153 2451 "gain=\"%4\" "
Chris@153 2452 "threshold=\"%5\" ")
Chris@6 2453 .arg(m_channel)
Chris@6 2454 .arg(m_windowSize)
Chris@97 2455 .arg(m_windowHopLevel)
Chris@37 2456 .arg(m_gain)
Chris@37 2457 .arg(m_threshold);
Chris@37 2458
Chris@37 2459 s += QString("minFrequency=\"%1\" "
Chris@37 2460 "maxFrequency=\"%2\" "
Chris@37 2461 "colourScale=\"%3\" "
Chris@37 2462 "colourScheme=\"%4\" "
Chris@37 2463 "colourRotation=\"%5\" "
Chris@37 2464 "frequencyScale=\"%6\" "
Chris@761 2465 "binDisplay=\"%7\" ")
Chris@37 2466 .arg(m_minFrequency)
Chris@6 2467 .arg(m_maxFrequency)
Chris@1137 2468 .arg(convertFromColourScale(m_colourScale, m_colourScaleMultiple))
Chris@197 2469 .arg(m_colourMap)
Chris@37 2470 .arg(m_colourRotation)
Chris@1103 2471 .arg(int(m_binScale))
Chris@1103 2472 .arg(int(m_binDisplay));
Chris@761 2473
Chris@1009 2474 // New-style normalization attributes, allowing for more types of
Chris@1009 2475 // normalization in future: write out the column normalization
Chris@1009 2476 // type separately, and then whether we are normalizing visible
Chris@1009 2477 // area as well afterwards
Chris@1009 2478
Chris@1009 2479 s += QString("columnNormalization=\"%1\" ")
Chris@1104 2480 .arg(m_normalization == ColumnNormalization::Max1 ? "peak" :
Chris@1104 2481 m_normalization == ColumnNormalization::Hybrid ? "hybrid" : "none");
Chris@1009 2482
Chris@1009 2483 // Old-style normalization attribute. We *don't* write out
Chris@1009 2484 // normalizeHybrid here because the only release that would accept
Chris@1009 2485 // it (Tony v1.0) has a totally different scale factor for
Chris@1009 2486 // it. We'll just have to accept that session files from Tony
Chris@1009 2487 // v2.0+ will look odd in Tony v1.0
Chris@1009 2488
Chris@1009 2489 s += QString("normalizeColumns=\"%1\" ")
Chris@1104 2490 .arg(m_normalization == ColumnNormalization::Max1 ? "true" : "false");
Chris@1009 2491
Chris@1009 2492 // And this applies to both old- and new-style attributes
Chris@1009 2493
Chris@1009 2494 s += QString("normalizeVisibleArea=\"%1\" ")
Chris@1104 2495 .arg(m_normalizeVisibleArea ? "true" : "false");
Chris@1009 2496
Chris@316 2497 Layer::toXml(stream, indent, extraAttributes + " " + s);
Chris@6 2498 }
Chris@6 2499
Chris@11 2500 void
Chris@11 2501 SpectrogramLayer::setProperties(const QXmlAttributes &attributes)
Chris@11 2502 {
Chris@11 2503 bool ok = false;
Chris@11 2504
Chris@11 2505 int channel = attributes.value("channel").toInt(&ok);
Chris@11 2506 if (ok) setChannel(channel);
Chris@11 2507
Chris@805 2508 int windowSize = attributes.value("windowSize").toUInt(&ok);
Chris@11 2509 if (ok) setWindowSize(windowSize);
Chris@11 2510
Chris@805 2511 int windowHopLevel = attributes.value("windowHopLevel").toUInt(&ok);
Chris@97 2512 if (ok) setWindowHopLevel(windowHopLevel);
Chris@97 2513 else {
Chris@805 2514 int windowOverlap = attributes.value("windowOverlap").toUInt(&ok);
Chris@97 2515 // a percentage value
Chris@97 2516 if (ok) {
Chris@97 2517 if (windowOverlap == 0) setWindowHopLevel(0);
Chris@97 2518 else if (windowOverlap == 25) setWindowHopLevel(1);
Chris@97 2519 else if (windowOverlap == 50) setWindowHopLevel(2);
Chris@97 2520 else if (windowOverlap == 75) setWindowHopLevel(3);
Chris@97 2521 else if (windowOverlap == 90) setWindowHopLevel(4);
Chris@97 2522 }
Chris@97 2523 }
Chris@11 2524
Chris@11 2525 float gain = attributes.value("gain").toFloat(&ok);
Chris@11 2526 if (ok) setGain(gain);
Chris@11 2527
Chris@37 2528 float threshold = attributes.value("threshold").toFloat(&ok);
Chris@37 2529 if (ok) setThreshold(threshold);
Chris@37 2530
Chris@805 2531 int minFrequency = attributes.value("minFrequency").toUInt(&ok);
Chris@187 2532 if (ok) {
Chris@587 2533 SVDEBUG << "SpectrogramLayer::setProperties: setting min freq to " << minFrequency << endl;
Chris@187 2534 setMinFrequency(minFrequency);
Chris@187 2535 }
Chris@37 2536
Chris@805 2537 int maxFrequency = attributes.value("maxFrequency").toUInt(&ok);
Chris@187 2538 if (ok) {
Chris@587 2539 SVDEBUG << "SpectrogramLayer::setProperties: setting max freq to " << maxFrequency << endl;
Chris@187 2540 setMaxFrequency(maxFrequency);
Chris@187 2541 }
Chris@11 2542
Chris@1137 2543 auto colourScale = convertToColourScale
Chris@1092 2544 (attributes.value("colourScale").toInt(&ok));
Chris@1137 2545 if (ok) {
Chris@1137 2546 setColourScale(colourScale.first);
Chris@1137 2547 setColourScaleMultiple(colourScale.second);
Chris@1137 2548 }
Chris@11 2549
Chris@197 2550 int colourMap = attributes.value("colourScheme").toInt(&ok);
Chris@197 2551 if (ok) setColourMap(colourMap);
Chris@11 2552
Chris@37 2553 int colourRotation = attributes.value("colourRotation").toInt(&ok);
Chris@37 2554 if (ok) setColourRotation(colourRotation);
Chris@37 2555
Chris@1103 2556 BinScale binScale = (BinScale)
Chris@11 2557 attributes.value("frequencyScale").toInt(&ok);
Chris@1093 2558 if (ok) setBinScale(binScale);
Chris@1093 2559
Chris@1103 2560 BinDisplay binDisplay = (BinDisplay)
Chris@37 2561 attributes.value("binDisplay").toInt(&ok);
Chris@37 2562 if (ok) setBinDisplay(binDisplay);
Chris@36 2563
Chris@1009 2564 bool haveNewStyleNormalization = false;
Chris@1009 2565
Chris@1009 2566 QString columnNormalization = attributes.value("columnNormalization");
Chris@1009 2567
Chris@1009 2568 if (columnNormalization != "") {
Chris@1009 2569
Chris@1009 2570 haveNewStyleNormalization = true;
Chris@1009 2571
Chris@1009 2572 if (columnNormalization == "peak") {
Chris@1104 2573 setNormalization(ColumnNormalization::Max1);
Chris@1009 2574 } else if (columnNormalization == "hybrid") {
Chris@1104 2575 setNormalization(ColumnNormalization::Hybrid);
Chris@1009 2576 } else if (columnNormalization == "none") {
Chris@1104 2577 setNormalization(ColumnNormalization::None);
Chris@1009 2578 } else {
Chris@1009 2579 cerr << "NOTE: Unknown or unsupported columnNormalization attribute \""
Chris@1009 2580 << columnNormalization << "\"" << endl;
Chris@1009 2581 }
Chris@1009 2582 }
Chris@1009 2583
Chris@1009 2584 if (!haveNewStyleNormalization) {
Chris@1009 2585
Chris@1009 2586 bool normalizeColumns =
Chris@1009 2587 (attributes.value("normalizeColumns").trimmed() == "true");
Chris@1009 2588 if (normalizeColumns) {
Chris@1104 2589 setNormalization(ColumnNormalization::Max1);
Chris@1009 2590 }
Chris@1009 2591
Chris@1009 2592 bool normalizeHybrid =
Chris@1009 2593 (attributes.value("normalizeHybrid").trimmed() == "true");
Chris@1009 2594 if (normalizeHybrid) {
Chris@1104 2595 setNormalization(ColumnNormalization::Hybrid);
Chris@1009 2596 }
Chris@862 2597 }
Chris@153 2598
Chris@153 2599 bool normalizeVisibleArea =
Chris@1099 2600 (attributes.value("normalizeVisibleArea").trimmed() == "true");
Chris@1104 2601 setNormalizeVisibleArea(normalizeVisibleArea);
Chris@1104 2602
Chris@1104 2603 if (!haveNewStyleNormalization && m_normalization == ColumnNormalization::Hybrid) {
Chris@1009 2604 // Tony v1.0 is (and hopefully will remain!) the only released
Chris@1009 2605 // SV-a-like to use old-style attributes when saving sessions
Chris@1009 2606 // that ask for hybrid normalization. It saves them with the
Chris@1009 2607 // wrong gain factor, so hack in a fix for that here -- this
Chris@1009 2608 // gives us backward but not forward compatibility.
Chris@1087 2609 setGain(m_gain / float(getFFTSize() / 2));
Chris@862 2610 }
Chris@11 2611 }
Chris@11 2612