annotate layer/SpectrogramLayer.cpp @ 1127:9fb8dfd7ce4c spectrogram-minor-refactor

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