annotate layer/SpectrogramLayer.cpp @ 101:0f36cdf407a6 sv1-v0.9rc1

* Make vertical scale alignment modes work in note layer as well as time-value layer, and several significant fixes to it * Make it possible to draw notes properly on the note layer * Show units (and frequencies etc in note layer's case) in the time-value and note layer description boxes * Minor fix to item edit dialog layout * Some minor menu rearrangement * Comment out a lot of debug output * Add SV website and reference URLs to Help menu, and add code to (attempt to) open them in the user's preferred browser
author Chris Cannam
date Fri, 12 May 2006 14:40:43 +0000
parents a0e7edf9703a
children 8ce53683d0d7
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@59 7 This file copyright 2006 Chris Cannam.
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@0 18 #include "base/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@89 23 #include "fileio/FFTFileCache.h"
Chris@0 24
Chris@0 25 #include <QPainter>
Chris@0 26 #include <QImage>
Chris@0 27 #include <QPixmap>
Chris@0 28 #include <QRect>
Chris@0 29 #include <QTimer>
Chris@92 30 #include <QApplication>
Chris@0 31
Chris@0 32 #include <iostream>
Chris@0 33
Chris@0 34 #include <cassert>
Chris@0 35 #include <cmath>
Chris@0 36
Chris@101 37 //#define DEBUG_SPECTROGRAM_REPAINT 1
Chris@0 38
Chris@75 39 static double mod(double x, double y)
Chris@75 40 {
Chris@75 41 double a = floor(x / y);
Chris@75 42 double b = x - (y * a);
Chris@75 43 return b;
Chris@75 44 }
Chris@75 45
Chris@75 46 static double princarg(double ang)
Chris@75 47 {
Chris@75 48 return mod(ang + M_PI, -2 * M_PI) + M_PI;
Chris@75 49 }
Chris@75 50
Chris@0 51
Chris@44 52 SpectrogramLayer::SpectrogramLayer(Configuration config) :
Chris@44 53 Layer(),
Chris@0 54 m_model(0),
Chris@0 55 m_channel(0),
Chris@0 56 m_windowSize(1024),
Chris@0 57 m_windowType(HanningWindow),
Chris@97 58 m_windowHopLevel(2),
Chris@0 59 m_gain(1.0),
Chris@37 60 m_threshold(0.0),
Chris@9 61 m_colourRotation(0),
Chris@37 62 m_minFrequency(0),
Chris@0 63 m_maxFrequency(8000),
Chris@0 64 m_colourScale(dBColourScale),
Chris@0 65 m_colourScheme(DefaultColours),
Chris@0 66 m_frequencyScale(LinearFrequencyScale),
Chris@37 67 m_binDisplay(AllBins),
Chris@36 68 m_normalizeColumns(false),
Chris@0 69 m_cache(0),
Chris@86 70 m_writeCache(0),
Chris@0 71 m_cacheInvalid(true),
Chris@0 72 m_fillThread(0),
Chris@0 73 m_updateTimer(0),
Chris@44 74 m_candidateFillStartFrame(0),
Chris@0 75 m_lastFillExtent(0),
Chris@0 76 m_exiting(false)
Chris@0 77 {
Chris@0 78 if (config == MelodicRange) {
Chris@0 79 setWindowSize(8192);
Chris@97 80 setWindowHopLevel(4);
Chris@0 81 setWindowType(ParzenWindow);
Chris@0 82 setMaxFrequency(1000);
Chris@0 83 setColourScale(LinearColourScale);
Chris@37 84 } else if (config == MelodicPeaks) {
Chris@37 85 setWindowSize(4096);
Chris@97 86 setWindowHopLevel(5);
Chris@37 87 setWindowType(BlackmanWindow);
Chris@40 88 setMaxFrequency(2000);
Chris@37 89 setMinFrequency(40);
Chris@37 90 setFrequencyScale(LogFrequencyScale);
Chris@41 91 setColourScale(MeterColourScale);
Chris@37 92 setBinDisplay(PeakFrequencies);
Chris@37 93 setNormalizeColumns(true);
Chris@0 94 }
Chris@0 95 }
Chris@0 96
Chris@0 97 SpectrogramLayer::~SpectrogramLayer()
Chris@0 98 {
Chris@0 99 delete m_updateTimer;
Chris@0 100 m_updateTimer = 0;
Chris@0 101
Chris@0 102 m_exiting = true;
Chris@0 103 m_condition.wakeAll();
Chris@0 104 if (m_fillThread) m_fillThread->wait();
Chris@0 105 delete m_fillThread;
Chris@0 106
Chris@86 107 delete m_writeCache;
Chris@0 108 delete m_cache;
Chris@0 109 }
Chris@0 110
Chris@0 111 void
Chris@0 112 SpectrogramLayer::setModel(const DenseTimeValueModel *model)
Chris@0 113 {
Chris@101 114 // std::cerr << "SpectrogramLayer(" << this << "): setModel(" << model << ")" << std::endl;
Chris@34 115
Chris@0 116 m_mutex.lock();
Chris@35 117 m_cacheInvalid = true;
Chris@0 118 m_model = model;
Chris@0 119 m_mutex.unlock();
Chris@0 120
Chris@0 121 if (!m_model || !m_model->isOK()) return;
Chris@0 122
Chris@0 123 connect(m_model, SIGNAL(modelChanged()), this, SIGNAL(modelChanged()));
Chris@0 124 connect(m_model, SIGNAL(modelChanged(size_t, size_t)),
Chris@0 125 this, SIGNAL(modelChanged(size_t, size_t)));
Chris@0 126
Chris@0 127 connect(m_model, SIGNAL(completionChanged()),
Chris@0 128 this, SIGNAL(modelCompletionChanged()));
Chris@0 129
Chris@0 130 connect(m_model, SIGNAL(modelChanged()), this, SLOT(cacheInvalid()));
Chris@0 131 connect(m_model, SIGNAL(modelChanged(size_t, size_t)),
Chris@0 132 this, SLOT(cacheInvalid(size_t, size_t)));
Chris@0 133
Chris@0 134 emit modelReplaced();
Chris@0 135 fillCache();
Chris@0 136 }
Chris@0 137
Chris@0 138 Layer::PropertyList
Chris@0 139 SpectrogramLayer::getProperties() const
Chris@0 140 {
Chris@0 141 PropertyList list;
Chris@87 142 list.push_back("Colour");
Chris@87 143 list.push_back("Colour Scale");
Chris@87 144 list.push_back("Window Type");
Chris@87 145 list.push_back("Window Size");
Chris@97 146 list.push_back("Window Increment");
Chris@87 147 list.push_back("Normalize Columns");
Chris@87 148 list.push_back("Bin Display");
Chris@87 149 list.push_back("Threshold");
Chris@87 150 list.push_back("Gain");
Chris@87 151 list.push_back("Colour Rotation");
Chris@87 152 list.push_back("Min Frequency");
Chris@87 153 list.push_back("Max Frequency");
Chris@87 154 list.push_back("Frequency Scale");
Chris@0 155 return list;
Chris@0 156 }
Chris@0 157
Chris@87 158 QString
Chris@87 159 SpectrogramLayer::getPropertyLabel(const PropertyName &name) const
Chris@87 160 {
Chris@87 161 if (name == "Colour") return tr("Colour");
Chris@87 162 if (name == "Colour Scale") return tr("Colour Scale");
Chris@87 163 if (name == "Window Type") return tr("Window Type");
Chris@87 164 if (name == "Window Size") return tr("Window Size");
Chris@97 165 if (name == "Window Increment") return tr("Window Increment");
Chris@87 166 if (name == "Normalize Columns") return tr("Normalize Columns");
Chris@87 167 if (name == "Bin Display") return tr("Bin Display");
Chris@87 168 if (name == "Threshold") return tr("Threshold");
Chris@87 169 if (name == "Gain") return tr("Gain");
Chris@87 170 if (name == "Colour Rotation") return tr("Colour Rotation");
Chris@87 171 if (name == "Min Frequency") return tr("Min Frequency");
Chris@87 172 if (name == "Max Frequency") return tr("Max Frequency");
Chris@87 173 if (name == "Frequency Scale") return tr("Frequency Scale");
Chris@87 174 return "";
Chris@87 175 }
Chris@87 176
Chris@0 177 Layer::PropertyType
Chris@0 178 SpectrogramLayer::getPropertyType(const PropertyName &name) const
Chris@0 179 {
Chris@87 180 if (name == "Gain") return RangeProperty;
Chris@87 181 if (name == "Colour Rotation") return RangeProperty;
Chris@87 182 if (name == "Normalize Columns") return ToggleProperty;
Chris@87 183 if (name == "Threshold") return RangeProperty;
Chris@0 184 return ValueProperty;
Chris@0 185 }
Chris@0 186
Chris@0 187 QString
Chris@0 188 SpectrogramLayer::getPropertyGroupName(const PropertyName &name) const
Chris@0 189 {
Chris@87 190 if (name == "Window Size" ||
Chris@87 191 name == "Window Type" ||
Chris@97 192 name == "Window Increment") return tr("Window");
Chris@87 193 if (name == "Colour" ||
Chris@87 194 name == "Gain" ||
Chris@87 195 name == "Threshold" ||
Chris@87 196 name == "Colour Rotation") return tr("Colour");
Chris@87 197 if (name == "Normalize Columns" ||
Chris@87 198 name == "Bin Display" ||
Chris@87 199 name == "Colour Scale") return tr("Scale");
Chris@87 200 if (name == "Max Frequency" ||
Chris@87 201 name == "Min Frequency" ||
Chris@87 202 name == "Frequency Scale" ||
Chris@87 203 name == "Frequency Adjustment") return tr("Range");
Chris@0 204 return QString();
Chris@0 205 }
Chris@0 206
Chris@0 207 int
Chris@0 208 SpectrogramLayer::getPropertyRangeAndValue(const PropertyName &name,
Chris@55 209 int *min, int *max) const
Chris@0 210 {
Chris@0 211 int deft = 0;
Chris@0 212
Chris@55 213 int garbage0, garbage1;
Chris@55 214 if (!min) min = &garbage0;
Chris@55 215 if (!max) max = &garbage1;
Chris@10 216
Chris@87 217 if (name == "Gain") {
Chris@0 218
Chris@0 219 *min = -50;
Chris@0 220 *max = 50;
Chris@0 221
Chris@0 222 deft = lrint(log10(m_gain) * 20.0);
Chris@0 223 if (deft < *min) deft = *min;
Chris@0 224 if (deft > *max) deft = *max;
Chris@0 225
Chris@87 226 } else if (name == "Threshold") {
Chris@37 227
Chris@37 228 *min = -50;
Chris@37 229 *max = 0;
Chris@37 230
Chris@37 231 deft = lrintf(AudioLevel::multiplier_to_dB(m_threshold));
Chris@37 232 if (deft < *min) deft = *min;
Chris@37 233 if (deft > *max) deft = *max;
Chris@37 234
Chris@87 235 } else if (name == "Colour Rotation") {
Chris@9 236
Chris@9 237 *min = 0;
Chris@9 238 *max = 256;
Chris@9 239
Chris@9 240 deft = m_colourRotation;
Chris@9 241
Chris@87 242 } else if (name == "Colour Scale") {
Chris@0 243
Chris@0 244 *min = 0;
Chris@0 245 *max = 3;
Chris@0 246
Chris@0 247 deft = (int)m_colourScale;
Chris@0 248
Chris@87 249 } else if (name == "Colour") {
Chris@0 250
Chris@0 251 *min = 0;
Chris@71 252 *max = 6;
Chris@0 253
Chris@0 254 deft = (int)m_colourScheme;
Chris@0 255
Chris@87 256 } else if (name == "Window Type") {
Chris@0 257
Chris@0 258 *min = 0;
Chris@0 259 *max = 6;
Chris@0 260
Chris@0 261 deft = (int)m_windowType;
Chris@0 262
Chris@87 263 } else if (name == "Window Size") {
Chris@0 264
Chris@0 265 *min = 0;
Chris@0 266 *max = 10;
Chris@0 267
Chris@0 268 deft = 0;
Chris@0 269 int ws = m_windowSize;
Chris@0 270 while (ws > 32) { ws >>= 1; deft ++; }
Chris@0 271
Chris@97 272 } else if (name == "Window Increment") {
Chris@0 273
Chris@0 274 *min = 0;
Chris@97 275 *max = 5;
Chris@0 276
Chris@97 277 deft = m_windowHopLevel;
Chris@0 278
Chris@87 279 } else if (name == "Min Frequency") {
Chris@37 280
Chris@37 281 *min = 0;
Chris@37 282 *max = 9;
Chris@37 283
Chris@37 284 switch (m_minFrequency) {
Chris@37 285 case 0: default: deft = 0; break;
Chris@37 286 case 10: deft = 1; break;
Chris@37 287 case 20: deft = 2; break;
Chris@37 288 case 40: deft = 3; break;
Chris@37 289 case 100: deft = 4; break;
Chris@37 290 case 250: deft = 5; break;
Chris@37 291 case 500: deft = 6; break;
Chris@37 292 case 1000: deft = 7; break;
Chris@37 293 case 4000: deft = 8; break;
Chris@37 294 case 10000: deft = 9; break;
Chris@37 295 }
Chris@37 296
Chris@87 297 } else if (name == "Max Frequency") {
Chris@0 298
Chris@0 299 *min = 0;
Chris@0 300 *max = 9;
Chris@0 301
Chris@0 302 switch (m_maxFrequency) {
Chris@0 303 case 500: deft = 0; break;
Chris@0 304 case 1000: deft = 1; break;
Chris@0 305 case 1500: deft = 2; break;
Chris@0 306 case 2000: deft = 3; break;
Chris@0 307 case 4000: deft = 4; break;
Chris@0 308 case 6000: deft = 5; break;
Chris@0 309 case 8000: deft = 6; break;
Chris@0 310 case 12000: deft = 7; break;
Chris@0 311 case 16000: deft = 8; break;
Chris@0 312 default: deft = 9; break;
Chris@0 313 }
Chris@0 314
Chris@87 315 } else if (name == "Frequency Scale") {
Chris@0 316
Chris@0 317 *min = 0;
Chris@0 318 *max = 1;
Chris@0 319 deft = (int)m_frequencyScale;
Chris@0 320
Chris@87 321 } else if (name == "Bin Display") {
Chris@35 322
Chris@35 323 *min = 0;
Chris@35 324 *max = 2;
Chris@37 325 deft = (int)m_binDisplay;
Chris@35 326
Chris@87 327 } else if (name == "Normalize Columns") {
Chris@36 328
Chris@36 329 deft = (m_normalizeColumns ? 1 : 0);
Chris@36 330
Chris@0 331 } else {
Chris@0 332 deft = Layer::getPropertyRangeAndValue(name, min, max);
Chris@0 333 }
Chris@0 334
Chris@0 335 return deft;
Chris@0 336 }
Chris@0 337
Chris@0 338 QString
Chris@0 339 SpectrogramLayer::getPropertyValueLabel(const PropertyName &name,
Chris@9 340 int value) const
Chris@0 341 {
Chris@87 342 if (name == "Colour") {
Chris@0 343 switch (value) {
Chris@0 344 default:
Chris@0 345 case 0: return tr("Default");
Chris@0 346 case 1: return tr("White on Black");
Chris@0 347 case 2: return tr("Black on White");
Chris@0 348 case 3: return tr("Red on Blue");
Chris@0 349 case 4: return tr("Yellow on Black");
Chris@71 350 case 5: return tr("Blue on Black");
Chris@71 351 case 6: return tr("Fruit Salad");
Chris@0 352 }
Chris@0 353 }
Chris@87 354 if (name == "Colour Scale") {
Chris@0 355 switch (value) {
Chris@0 356 default:
Chris@37 357 case 0: return tr("Linear");
Chris@37 358 case 1: return tr("Meter");
Chris@37 359 case 2: return tr("dB");
Chris@0 360 case 3: return tr("Phase");
Chris@0 361 }
Chris@0 362 }
Chris@87 363 if (name == "Window Type") {
Chris@0 364 switch ((WindowType)value) {
Chris@0 365 default:
Chris@35 366 case RectangularWindow: return tr("Rectangle");
Chris@0 367 case BartlettWindow: return tr("Bartlett");
Chris@0 368 case HammingWindow: return tr("Hamming");
Chris@0 369 case HanningWindow: return tr("Hanning");
Chris@0 370 case BlackmanWindow: return tr("Blackman");
Chris@0 371 case GaussianWindow: return tr("Gaussian");
Chris@0 372 case ParzenWindow: return tr("Parzen");
Chris@0 373 }
Chris@0 374 }
Chris@87 375 if (name == "Window Size") {
Chris@0 376 return QString("%1").arg(32 << value);
Chris@0 377 }
Chris@97 378 if (name == "Window Increment") {
Chris@0 379 switch (value) {
Chris@0 380 default:
Chris@97 381 case 0: return tr("1/1");
Chris@97 382 case 1: return tr("3/4");
Chris@97 383 case 2: return tr("1/2");
Chris@97 384 case 3: return tr("1/4");
Chris@97 385 case 4: return tr("1/8");
Chris@97 386 case 5: return tr("1/16");
Chris@0 387 }
Chris@0 388 }
Chris@87 389 if (name == "Min Frequency") {
Chris@37 390 switch (value) {
Chris@37 391 default:
Chris@38 392 case 0: return tr("No min");
Chris@37 393 case 1: return tr("10 Hz");
Chris@37 394 case 2: return tr("20 Hz");
Chris@37 395 case 3: return tr("40 Hz");
Chris@37 396 case 4: return tr("100 Hz");
Chris@37 397 case 5: return tr("250 Hz");
Chris@37 398 case 6: return tr("500 Hz");
Chris@37 399 case 7: return tr("1 KHz");
Chris@37 400 case 8: return tr("4 KHz");
Chris@37 401 case 9: return tr("10 KHz");
Chris@37 402 }
Chris@37 403 }
Chris@87 404 if (name == "Max Frequency") {
Chris@0 405 switch (value) {
Chris@0 406 default:
Chris@0 407 case 0: return tr("500 Hz");
Chris@0 408 case 1: return tr("1 KHz");
Chris@0 409 case 2: return tr("1.5 KHz");
Chris@0 410 case 3: return tr("2 KHz");
Chris@0 411 case 4: return tr("4 KHz");
Chris@0 412 case 5: return tr("6 KHz");
Chris@0 413 case 6: return tr("8 KHz");
Chris@0 414 case 7: return tr("12 KHz");
Chris@0 415 case 8: return tr("16 KHz");
Chris@38 416 case 9: return tr("No max");
Chris@0 417 }
Chris@0 418 }
Chris@87 419 if (name == "Frequency Scale") {
Chris@0 420 switch (value) {
Chris@0 421 default:
Chris@0 422 case 0: return tr("Linear");
Chris@0 423 case 1: return tr("Log");
Chris@0 424 }
Chris@0 425 }
Chris@87 426 if (name == "Bin Display") {
Chris@35 427 switch (value) {
Chris@35 428 default:
Chris@37 429 case 0: return tr("All Bins");
Chris@37 430 case 1: return tr("Peak Bins");
Chris@37 431 case 2: return tr("Frequencies");
Chris@35 432 }
Chris@35 433 }
Chris@0 434 return tr("<unknown>");
Chris@0 435 }
Chris@0 436
Chris@0 437 void
Chris@0 438 SpectrogramLayer::setProperty(const PropertyName &name, int value)
Chris@0 439 {
Chris@87 440 if (name == "Gain") {
Chris@0 441 setGain(pow(10, float(value)/20.0));
Chris@87 442 } else if (name == "Threshold") {
Chris@37 443 if (value == -50) setThreshold(0.0);
Chris@37 444 else setThreshold(AudioLevel::dB_to_multiplier(value));
Chris@87 445 } else if (name == "Colour Rotation") {
Chris@9 446 setColourRotation(value);
Chris@87 447 } else if (name == "Colour") {
Chris@0 448 switch (value) {
Chris@0 449 default:
Chris@0 450 case 0: setColourScheme(DefaultColours); break;
Chris@0 451 case 1: setColourScheme(WhiteOnBlack); break;
Chris@0 452 case 2: setColourScheme(BlackOnWhite); break;
Chris@0 453 case 3: setColourScheme(RedOnBlue); break;
Chris@0 454 case 4: setColourScheme(YellowOnBlack); break;
Chris@71 455 case 5: setColourScheme(BlueOnBlack); break;
Chris@71 456 case 6: setColourScheme(Rainbow); break;
Chris@0 457 }
Chris@87 458 } else if (name == "Window Type") {
Chris@0 459 setWindowType(WindowType(value));
Chris@87 460 } else if (name == "Window Size") {
Chris@0 461 setWindowSize(32 << value);
Chris@97 462 } else if (name == "Window Increment") {
Chris@97 463 setWindowHopLevel(value);
Chris@87 464 } else if (name == "Min Frequency") {
Chris@37 465 switch (value) {
Chris@37 466 default:
Chris@37 467 case 0: setMinFrequency(0); break;
Chris@37 468 case 1: setMinFrequency(10); break;
Chris@37 469 case 2: setMinFrequency(20); break;
Chris@37 470 case 3: setMinFrequency(40); break;
Chris@37 471 case 4: setMinFrequency(100); break;
Chris@37 472 case 5: setMinFrequency(250); break;
Chris@37 473 case 6: setMinFrequency(500); break;
Chris@37 474 case 7: setMinFrequency(1000); break;
Chris@37 475 case 8: setMinFrequency(4000); break;
Chris@37 476 case 9: setMinFrequency(10000); break;
Chris@37 477 }
Chris@87 478 } else if (name == "Max Frequency") {
Chris@0 479 switch (value) {
Chris@0 480 case 0: setMaxFrequency(500); break;
Chris@0 481 case 1: setMaxFrequency(1000); break;
Chris@0 482 case 2: setMaxFrequency(1500); break;
Chris@0 483 case 3: setMaxFrequency(2000); break;
Chris@0 484 case 4: setMaxFrequency(4000); break;
Chris@0 485 case 5: setMaxFrequency(6000); break;
Chris@0 486 case 6: setMaxFrequency(8000); break;
Chris@0 487 case 7: setMaxFrequency(12000); break;
Chris@0 488 case 8: setMaxFrequency(16000); break;
Chris@0 489 default:
Chris@0 490 case 9: setMaxFrequency(0); break;
Chris@0 491 }
Chris@87 492 } else if (name == "Colour Scale") {
Chris@0 493 switch (value) {
Chris@0 494 default:
Chris@0 495 case 0: setColourScale(LinearColourScale); break;
Chris@0 496 case 1: setColourScale(MeterColourScale); break;
Chris@0 497 case 2: setColourScale(dBColourScale); break;
Chris@0 498 case 3: setColourScale(PhaseColourScale); break;
Chris@0 499 }
Chris@87 500 } else if (name == "Frequency Scale") {
Chris@0 501 switch (value) {
Chris@0 502 default:
Chris@0 503 case 0: setFrequencyScale(LinearFrequencyScale); break;
Chris@0 504 case 1: setFrequencyScale(LogFrequencyScale); break;
Chris@0 505 }
Chris@87 506 } else if (name == "Bin Display") {
Chris@35 507 switch (value) {
Chris@35 508 default:
Chris@37 509 case 0: setBinDisplay(AllBins); break;
Chris@37 510 case 1: setBinDisplay(PeakBins); break;
Chris@37 511 case 2: setBinDisplay(PeakFrequencies); break;
Chris@35 512 }
Chris@82 513 } else if (name == "Normalize Columns") {
Chris@36 514 setNormalizeColumns(value ? true : false);
Chris@0 515 }
Chris@0 516 }
Chris@0 517
Chris@0 518 void
Chris@95 519 SpectrogramLayer::invalidatePixmapCaches()
Chris@95 520 {
Chris@95 521 for (ViewPixmapCache::iterator i = m_pixmapCaches.begin();
Chris@95 522 i != m_pixmapCaches.end(); ++i) {
Chris@95 523 i->second.validArea = QRect();
Chris@95 524 }
Chris@95 525 }
Chris@95 526
Chris@95 527 void
Chris@95 528 SpectrogramLayer::invalidatePixmapCaches(size_t startFrame, size_t endFrame)
Chris@95 529 {
Chris@95 530 for (ViewPixmapCache::iterator i = m_pixmapCaches.begin();
Chris@95 531 i != m_pixmapCaches.end(); ++i) {
Chris@95 532 //!!! when are views removed from the map? on setLayerDormant?
Chris@95 533 const View *v = i->first;
Chris@95 534
Chris@95 535 if (startFrame < v->getEndFrame() && endFrame >= v->getStartFrame()) {
Chris@95 536 i->second.validArea = QRect();
Chris@95 537 }
Chris@95 538 }
Chris@95 539 }
Chris@95 540
Chris@95 541 void
Chris@0 542 SpectrogramLayer::setChannel(int ch)
Chris@0 543 {
Chris@0 544 if (m_channel == ch) return;
Chris@0 545
Chris@0 546 m_mutex.lock();
Chris@0 547 m_cacheInvalid = true;
Chris@95 548 invalidatePixmapCaches();
Chris@0 549
Chris@0 550 m_channel = ch;
Chris@9 551
Chris@9 552 m_mutex.unlock();
Chris@9 553
Chris@0 554 emit layerParametersChanged();
Chris@9 555
Chris@0 556 fillCache();
Chris@0 557 }
Chris@0 558
Chris@0 559 int
Chris@0 560 SpectrogramLayer::getChannel() const
Chris@0 561 {
Chris@0 562 return m_channel;
Chris@0 563 }
Chris@0 564
Chris@0 565 void
Chris@0 566 SpectrogramLayer::setWindowSize(size_t ws)
Chris@0 567 {
Chris@0 568 if (m_windowSize == ws) return;
Chris@0 569
Chris@0 570 m_mutex.lock();
Chris@0 571 m_cacheInvalid = true;
Chris@95 572 invalidatePixmapCaches();
Chris@0 573
Chris@0 574 m_windowSize = ws;
Chris@0 575
Chris@0 576 m_mutex.unlock();
Chris@9 577
Chris@9 578 emit layerParametersChanged();
Chris@9 579
Chris@0 580 fillCache();
Chris@0 581 }
Chris@0 582
Chris@0 583 size_t
Chris@0 584 SpectrogramLayer::getWindowSize() const
Chris@0 585 {
Chris@0 586 return m_windowSize;
Chris@0 587 }
Chris@0 588
Chris@0 589 void
Chris@97 590 SpectrogramLayer::setWindowHopLevel(size_t v)
Chris@0 591 {
Chris@97 592 if (m_windowHopLevel == v) return;
Chris@0 593
Chris@0 594 m_mutex.lock();
Chris@0 595 m_cacheInvalid = true;
Chris@95 596 invalidatePixmapCaches();
Chris@0 597
Chris@97 598 m_windowHopLevel = v;
Chris@0 599
Chris@0 600 m_mutex.unlock();
Chris@9 601
Chris@9 602 emit layerParametersChanged();
Chris@9 603
Chris@0 604 fillCache();
Chris@0 605 }
Chris@0 606
Chris@0 607 size_t
Chris@97 608 SpectrogramLayer::getWindowHopLevel() const
Chris@0 609 {
Chris@97 610 return m_windowHopLevel;
Chris@0 611 }
Chris@0 612
Chris@0 613 void
Chris@0 614 SpectrogramLayer::setWindowType(WindowType w)
Chris@0 615 {
Chris@0 616 if (m_windowType == w) return;
Chris@0 617
Chris@0 618 m_mutex.lock();
Chris@0 619 m_cacheInvalid = true;
Chris@95 620 invalidatePixmapCaches();
Chris@0 621
Chris@0 622 m_windowType = w;
Chris@0 623
Chris@0 624 m_mutex.unlock();
Chris@9 625
Chris@9 626 emit layerParametersChanged();
Chris@9 627
Chris@0 628 fillCache();
Chris@0 629 }
Chris@0 630
Chris@0 631 WindowType
Chris@0 632 SpectrogramLayer::getWindowType() const
Chris@0 633 {
Chris@0 634 return m_windowType;
Chris@0 635 }
Chris@0 636
Chris@0 637 void
Chris@0 638 SpectrogramLayer::setGain(float gain)
Chris@0 639 {
Chris@101 640 // std::cerr << "SpectrogramLayer::setGain(" << gain << ") (my gain is now "
Chris@101 641 // << m_gain << ")" << std::endl;
Chris@55 642
Chris@40 643 if (m_gain == gain) return;
Chris@0 644
Chris@0 645 m_mutex.lock();
Chris@95 646 invalidatePixmapCaches();
Chris@0 647
Chris@0 648 m_gain = gain;
Chris@0 649
Chris@0 650 m_mutex.unlock();
Chris@9 651
Chris@9 652 emit layerParametersChanged();
Chris@9 653
Chris@0 654 fillCache();
Chris@0 655 }
Chris@0 656
Chris@0 657 float
Chris@0 658 SpectrogramLayer::getGain() const
Chris@0 659 {
Chris@0 660 return m_gain;
Chris@0 661 }
Chris@0 662
Chris@0 663 void
Chris@37 664 SpectrogramLayer::setThreshold(float threshold)
Chris@37 665 {
Chris@40 666 if (m_threshold == threshold) return;
Chris@37 667
Chris@37 668 m_mutex.lock();
Chris@95 669 invalidatePixmapCaches();
Chris@37 670
Chris@37 671 m_threshold = threshold;
Chris@37 672
Chris@37 673 m_mutex.unlock();
Chris@37 674
Chris@37 675 emit layerParametersChanged();
Chris@37 676
Chris@37 677 fillCache();
Chris@37 678 }
Chris@37 679
Chris@37 680 float
Chris@37 681 SpectrogramLayer::getThreshold() const
Chris@37 682 {
Chris@37 683 return m_threshold;
Chris@37 684 }
Chris@37 685
Chris@37 686 void
Chris@37 687 SpectrogramLayer::setMinFrequency(size_t mf)
Chris@37 688 {
Chris@37 689 if (m_minFrequency == mf) return;
Chris@37 690
Chris@37 691 m_mutex.lock();
Chris@95 692 invalidatePixmapCaches();
Chris@37 693
Chris@37 694 m_minFrequency = mf;
Chris@37 695
Chris@37 696 m_mutex.unlock();
Chris@37 697
Chris@37 698 emit layerParametersChanged();
Chris@37 699 }
Chris@37 700
Chris@37 701 size_t
Chris@37 702 SpectrogramLayer::getMinFrequency() const
Chris@37 703 {
Chris@37 704 return m_minFrequency;
Chris@37 705 }
Chris@37 706
Chris@37 707 void
Chris@0 708 SpectrogramLayer::setMaxFrequency(size_t mf)
Chris@0 709 {
Chris@0 710 if (m_maxFrequency == mf) return;
Chris@0 711
Chris@0 712 m_mutex.lock();
Chris@95 713 invalidatePixmapCaches();
Chris@0 714
Chris@0 715 m_maxFrequency = mf;
Chris@0 716
Chris@0 717 m_mutex.unlock();
Chris@9 718
Chris@9 719 emit layerParametersChanged();
Chris@0 720 }
Chris@0 721
Chris@0 722 size_t
Chris@0 723 SpectrogramLayer::getMaxFrequency() const
Chris@0 724 {
Chris@0 725 return m_maxFrequency;
Chris@0 726 }
Chris@0 727
Chris@0 728 void
Chris@9 729 SpectrogramLayer::setColourRotation(int r)
Chris@9 730 {
Chris@9 731 m_mutex.lock();
Chris@95 732 invalidatePixmapCaches();
Chris@9 733
Chris@9 734 if (r < 0) r = 0;
Chris@9 735 if (r > 256) r = 256;
Chris@9 736 int distance = r - m_colourRotation;
Chris@9 737
Chris@9 738 if (distance != 0) {
Chris@90 739 rotateColourmap(-distance);
Chris@9 740 m_colourRotation = r;
Chris@9 741 }
Chris@9 742
Chris@9 743 m_mutex.unlock();
Chris@9 744
Chris@9 745 emit layerParametersChanged();
Chris@9 746 }
Chris@9 747
Chris@9 748 void
Chris@0 749 SpectrogramLayer::setColourScale(ColourScale colourScale)
Chris@0 750 {
Chris@0 751 if (m_colourScale == colourScale) return;
Chris@0 752
Chris@0 753 m_mutex.lock();
Chris@95 754 invalidatePixmapCaches();
Chris@0 755
Chris@0 756 m_colourScale = colourScale;
Chris@0 757
Chris@0 758 m_mutex.unlock();
Chris@0 759 fillCache();
Chris@9 760
Chris@9 761 emit layerParametersChanged();
Chris@0 762 }
Chris@0 763
Chris@0 764 SpectrogramLayer::ColourScale
Chris@0 765 SpectrogramLayer::getColourScale() const
Chris@0 766 {
Chris@0 767 return m_colourScale;
Chris@0 768 }
Chris@0 769
Chris@0 770 void
Chris@0 771 SpectrogramLayer::setColourScheme(ColourScheme scheme)
Chris@0 772 {
Chris@0 773 if (m_colourScheme == scheme) return;
Chris@0 774
Chris@0 775 m_mutex.lock();
Chris@95 776 invalidatePixmapCaches();
Chris@0 777
Chris@0 778 m_colourScheme = scheme;
Chris@90 779 setColourmap();
Chris@9 780
Chris@9 781 m_mutex.unlock();
Chris@9 782
Chris@0 783 emit layerParametersChanged();
Chris@0 784 }
Chris@0 785
Chris@0 786 SpectrogramLayer::ColourScheme
Chris@0 787 SpectrogramLayer::getColourScheme() const
Chris@0 788 {
Chris@0 789 return m_colourScheme;
Chris@0 790 }
Chris@0 791
Chris@0 792 void
Chris@0 793 SpectrogramLayer::setFrequencyScale(FrequencyScale frequencyScale)
Chris@0 794 {
Chris@0 795 if (m_frequencyScale == frequencyScale) return;
Chris@0 796
Chris@0 797 m_mutex.lock();
Chris@35 798
Chris@95 799 invalidatePixmapCaches();
Chris@0 800
Chris@0 801 m_frequencyScale = frequencyScale;
Chris@0 802
Chris@0 803 m_mutex.unlock();
Chris@9 804
Chris@9 805 emit layerParametersChanged();
Chris@0 806 }
Chris@0 807
Chris@0 808 SpectrogramLayer::FrequencyScale
Chris@0 809 SpectrogramLayer::getFrequencyScale() const
Chris@0 810 {
Chris@0 811 return m_frequencyScale;
Chris@0 812 }
Chris@0 813
Chris@0 814 void
Chris@37 815 SpectrogramLayer::setBinDisplay(BinDisplay binDisplay)
Chris@35 816 {
Chris@37 817 if (m_binDisplay == binDisplay) return;
Chris@35 818
Chris@35 819 m_mutex.lock();
Chris@35 820
Chris@95 821 invalidatePixmapCaches();
Chris@35 822
Chris@37 823 m_binDisplay = binDisplay;
Chris@35 824
Chris@35 825 m_mutex.unlock();
Chris@35 826
Chris@35 827 fillCache();
Chris@35 828
Chris@35 829 emit layerParametersChanged();
Chris@35 830 }
Chris@35 831
Chris@37 832 SpectrogramLayer::BinDisplay
Chris@37 833 SpectrogramLayer::getBinDisplay() const
Chris@35 834 {
Chris@37 835 return m_binDisplay;
Chris@35 836 }
Chris@35 837
Chris@35 838 void
Chris@36 839 SpectrogramLayer::setNormalizeColumns(bool n)
Chris@36 840 {
Chris@36 841 if (m_normalizeColumns == n) return;
Chris@36 842 m_mutex.lock();
Chris@36 843
Chris@95 844 invalidatePixmapCaches();
Chris@36 845 m_normalizeColumns = n;
Chris@36 846 m_mutex.unlock();
Chris@36 847
Chris@36 848 fillCache();
Chris@36 849 emit layerParametersChanged();
Chris@36 850 }
Chris@36 851
Chris@36 852 bool
Chris@36 853 SpectrogramLayer::getNormalizeColumns() const
Chris@36 854 {
Chris@36 855 return m_normalizeColumns;
Chris@36 856 }
Chris@36 857
Chris@36 858 void
Chris@47 859 SpectrogramLayer::setLayerDormant(const View *v, bool dormant)
Chris@29 860 {
Chris@47 861 QMutexLocker locker(&m_mutex);
Chris@47 862
Chris@47 863 if (dormant == m_dormancy[v]) return;
Chris@33 864
Chris@33 865 if (dormant) {
Chris@33 866
Chris@47 867 m_dormancy[v] = true;
Chris@33 868
Chris@34 869 // delete m_cache;
Chris@34 870 // m_cache = 0;
Chris@33 871
Chris@34 872 m_cacheInvalid = true;
Chris@95 873 invalidatePixmapCaches();
Chris@95 874 m_pixmapCaches.erase(v);
Chris@33 875
Chris@33 876 } else {
Chris@33 877
Chris@47 878 m_dormancy[v] = false;
Chris@33 879 }
Chris@29 880 }
Chris@29 881
Chris@29 882 void
Chris@0 883 SpectrogramLayer::cacheInvalid()
Chris@0 884 {
Chris@0 885 m_cacheInvalid = true;
Chris@95 886 invalidatePixmapCaches();
Chris@0 887 fillCache();
Chris@0 888 }
Chris@0 889
Chris@0 890 void
Chris@0 891 SpectrogramLayer::cacheInvalid(size_t, size_t)
Chris@0 892 {
Chris@0 893 // for now (or forever?)
Chris@0 894 cacheInvalid();
Chris@0 895 }
Chris@0 896
Chris@0 897 void
Chris@0 898 SpectrogramLayer::fillCache()
Chris@0 899 {
Chris@0 900 #ifdef DEBUG_SPECTROGRAM_REPAINT
Chris@0 901 std::cerr << "SpectrogramLayer::fillCache" << std::endl;
Chris@0 902 #endif
Chris@0 903 QMutexLocker locker(&m_mutex);
Chris@0 904
Chris@0 905 m_lastFillExtent = 0;
Chris@0 906
Chris@0 907 delete m_updateTimer;
Chris@0 908 m_updateTimer = new QTimer(this);
Chris@0 909 connect(m_updateTimer, SIGNAL(timeout()), this, SLOT(fillTimerTimedOut()));
Chris@0 910 m_updateTimer->start(200);
Chris@0 911
Chris@0 912 if (!m_fillThread) {
Chris@0 913 std::cerr << "SpectrogramLayer::fillCache creating thread" << std::endl;
Chris@0 914 m_fillThread = new CacheFillThread(*this);
Chris@0 915 m_fillThread->start();
Chris@0 916 }
Chris@0 917
Chris@0 918 m_condition.wakeAll();
Chris@0 919 }
Chris@0 920
Chris@0 921 void
Chris@0 922 SpectrogramLayer::fillTimerTimedOut()
Chris@0 923 {
Chris@0 924 if (m_fillThread && m_model) {
Chris@0 925 size_t fillExtent = m_fillThread->getFillExtent();
Chris@0 926 #ifdef DEBUG_SPECTROGRAM_REPAINT
Chris@0 927 std::cerr << "SpectrogramLayer::fillTimerTimedOut: extent " << fillExtent << ", last " << m_lastFillExtent << ", total " << m_model->getEndFrame() << std::endl;
Chris@0 928 #endif
Chris@0 929 if (fillExtent >= m_lastFillExtent) {
Chris@0 930 if (fillExtent >= m_model->getEndFrame() && m_lastFillExtent > 0) {
Chris@0 931 #ifdef DEBUG_SPECTROGRAM_REPAINT
Chris@0 932 std::cerr << "complete!" << std::endl;
Chris@0 933 #endif
Chris@95 934 invalidatePixmapCaches();
Chris@0 935 emit modelChanged();
Chris@0 936 delete m_updateTimer;
Chris@0 937 m_updateTimer = 0;
Chris@0 938 m_lastFillExtent = 0;
Chris@0 939 } else if (fillExtent > m_lastFillExtent) {
Chris@0 940 #ifdef DEBUG_SPECTROGRAM_REPAINT
Chris@0 941 std::cerr << "SpectrogramLayer: emitting modelChanged("
Chris@0 942 << m_lastFillExtent << "," << fillExtent << ")" << std::endl;
Chris@0 943 #endif
Chris@95 944 invalidatePixmapCaches(m_lastFillExtent, fillExtent);
Chris@0 945 emit modelChanged(m_lastFillExtent, fillExtent);
Chris@0 946 m_lastFillExtent = fillExtent;
Chris@0 947 }
Chris@0 948 } else {
Chris@44 949 // if (v) {
Chris@0 950 size_t sf = 0;
Chris@44 951 //!!! if (v->getStartFrame() > 0) sf = v->getStartFrame();
Chris@0 952 #ifdef DEBUG_SPECTROGRAM_REPAINT
Chris@0 953 std::cerr << "SpectrogramLayer: going backwards, emitting modelChanged("
Chris@44 954 << sf << "," << m_model->getEndFrame() << ")" << std::endl;
Chris@0 955 #endif
Chris@95 956 invalidatePixmapCaches();
Chris@44 957 emit modelChanged(sf, m_model->getEndFrame());
Chris@44 958 // }
Chris@0 959 m_lastFillExtent = fillExtent;
Chris@0 960 }
Chris@0 961 }
Chris@0 962 }
Chris@0 963
Chris@0 964 void
Chris@90 965 SpectrogramLayer::setColourmap()
Chris@0 966 {
Chris@10 967 int formerRotation = m_colourRotation;
Chris@10 968
Chris@38 969 if (m_colourScheme == BlackOnWhite) {
Chris@86 970 m_colourMap.setColour(NO_VALUE, Qt::white);
Chris@38 971 } else {
Chris@86 972 m_colourMap.setColour(NO_VALUE, Qt::black);
Chris@38 973 }
Chris@0 974
Chris@0 975 for (int pixel = 1; pixel < 256; ++pixel) {
Chris@0 976
Chris@0 977 QColor colour;
Chris@0 978 int hue, px;
Chris@0 979
Chris@0 980 switch (m_colourScheme) {
Chris@0 981
Chris@0 982 default:
Chris@0 983 case DefaultColours:
Chris@0 984 hue = 256 - pixel;
Chris@0 985 colour = QColor::fromHsv(hue, pixel/2 + 128, pixel);
Chris@77 986 m_crosshairColour = QColor(255, 150, 50);
Chris@77 987 // m_crosshairColour = QColor::fromHsv(240, 160, 255);
Chris@0 988 break;
Chris@0 989
Chris@0 990 case WhiteOnBlack:
Chris@0 991 colour = QColor(pixel, pixel, pixel);
Chris@77 992 m_crosshairColour = Qt::red;
Chris@0 993 break;
Chris@0 994
Chris@0 995 case BlackOnWhite:
Chris@0 996 colour = QColor(256-pixel, 256-pixel, 256-pixel);
Chris@77 997 m_crosshairColour = Qt::darkGreen;
Chris@0 998 break;
Chris@0 999
Chris@0 1000 case RedOnBlue:
Chris@0 1001 colour = QColor(pixel > 128 ? (pixel - 128) * 2 : 0, 0,
Chris@0 1002 pixel < 128 ? pixel : (256 - pixel));
Chris@77 1003 m_crosshairColour = Qt::green;
Chris@0 1004 break;
Chris@0 1005
Chris@0 1006 case YellowOnBlack:
Chris@0 1007 px = 256 - pixel;
Chris@0 1008 colour = QColor(px < 64 ? 255 - px/2 :
Chris@0 1009 px < 128 ? 224 - (px - 64) :
Chris@0 1010 px < 192 ? 160 - (px - 128) * 3 / 2 :
Chris@0 1011 256 - px,
Chris@0 1012 pixel,
Chris@0 1013 pixel / 4);
Chris@77 1014 m_crosshairColour = QColor::fromHsv(240, 255, 255);
Chris@0 1015 break;
Chris@0 1016
Chris@71 1017 case BlueOnBlack:
Chris@71 1018 colour = QColor::fromHsv
Chris@71 1019 (240, pixel > 226 ? 256 - (pixel - 226) * 8 : 255,
Chris@71 1020 (pixel * pixel) / 255);
Chris@77 1021 m_crosshairColour = Qt::red;
Chris@71 1022 break;
Chris@71 1023
Chris@40 1024 case Rainbow:
Chris@40 1025 hue = 250 - pixel;
Chris@40 1026 if (hue < 0) hue += 256;
Chris@40 1027 colour = QColor::fromHsv(pixel, 255, 255);
Chris@77 1028 m_crosshairColour = Qt::white;
Chris@0 1029 break;
Chris@0 1030 }
Chris@0 1031
Chris@86 1032 m_colourMap.setColour(pixel, colour);
Chris@0 1033 }
Chris@9 1034
Chris@9 1035 m_colourRotation = 0;
Chris@90 1036 rotateColourmap(m_colourRotation - formerRotation);
Chris@10 1037 m_colourRotation = formerRotation;
Chris@9 1038 }
Chris@9 1039
Chris@9 1040 void
Chris@90 1041 SpectrogramLayer::rotateColourmap(int distance)
Chris@9 1042 {
Chris@10 1043 if (!m_cache) return;
Chris@10 1044
Chris@31 1045 QColor newPixels[256];
Chris@9 1046
Chris@86 1047 newPixels[NO_VALUE] = m_colourMap.getColour(NO_VALUE);
Chris@9 1048
Chris@9 1049 for (int pixel = 1; pixel < 256; ++pixel) {
Chris@9 1050 int target = pixel + distance;
Chris@9 1051 while (target < 1) target += 255;
Chris@9 1052 while (target > 255) target -= 255;
Chris@86 1053 newPixels[target] = m_colourMap.getColour(pixel);
Chris@9 1054 }
Chris@9 1055
Chris@9 1056 for (int pixel = 0; pixel < 256; ++pixel) {
Chris@86 1057 m_colourMap.setColour(pixel, newPixels[pixel]);
Chris@9 1058 }
Chris@0 1059 }
Chris@0 1060
Chris@38 1061 float
Chris@38 1062 SpectrogramLayer::calculateFrequency(size_t bin,
Chris@38 1063 size_t windowSize,
Chris@38 1064 size_t windowIncrement,
Chris@38 1065 size_t sampleRate,
Chris@38 1066 float oldPhase,
Chris@38 1067 float newPhase,
Chris@38 1068 bool &steadyState)
Chris@38 1069 {
Chris@38 1070 // At frequency f, phase shift of 2pi (one cycle) happens in 1/f sec.
Chris@38 1071 // At hopsize h and sample rate sr, one hop happens in h/sr sec.
Chris@38 1072 // At window size w, for bin b, f is b*sr/w.
Chris@38 1073 // thus 2pi phase shift happens in w/(b*sr) sec.
Chris@38 1074 // We need to know what phase shift we expect from h/sr sec.
Chris@38 1075 // -> 2pi * ((h/sr) / (w/(b*sr)))
Chris@38 1076 // = 2pi * ((h * b * sr) / (w * sr))
Chris@38 1077 // = 2pi * (h * b) / w.
Chris@38 1078
Chris@38 1079 float frequency = (float(bin) * sampleRate) / windowSize;
Chris@38 1080
Chris@38 1081 float expectedPhase =
Chris@38 1082 oldPhase + (2.0 * M_PI * bin * windowIncrement) / windowSize;
Chris@38 1083
Chris@75 1084 float phaseError = princarg(newPhase - expectedPhase);
Chris@38 1085
Chris@38 1086 if (fabs(phaseError) < (1.1 * (windowIncrement * M_PI) / windowSize)) {
Chris@38 1087
Chris@38 1088 // The new frequency estimate based on the phase error
Chris@38 1089 // resulting from assuming the "native" frequency of this bin
Chris@38 1090
Chris@38 1091 float newFrequency =
Chris@38 1092 (sampleRate * (expectedPhase + phaseError - oldPhase)) /
Chris@38 1093 (2 * M_PI * windowIncrement);
Chris@38 1094
Chris@38 1095 steadyState = true;
Chris@38 1096 return newFrequency;
Chris@38 1097 }
Chris@38 1098
Chris@38 1099 steadyState = false;
Chris@38 1100 return frequency;
Chris@38 1101 }
Chris@38 1102
Chris@38 1103 void
Chris@0 1104 SpectrogramLayer::fillCacheColumn(int column, double *input,
Chris@0 1105 fftw_complex *output,
Chris@0 1106 fftw_plan plan,
Chris@9 1107 size_t windowSize,
Chris@9 1108 size_t increment,
Chris@86 1109 float *workbuffer,
Chris@38 1110 const Window<double> &windower) const
Chris@0 1111 {
Chris@38 1112 //!!! we _do_ need a lock for these references to the model
Chris@38 1113 // though, don't we?
Chris@35 1114
Chris@0 1115 int startFrame = increment * column;
Chris@9 1116 int endFrame = startFrame + windowSize;
Chris@0 1117
Chris@9 1118 startFrame -= int(windowSize - increment) / 2;
Chris@9 1119 endFrame -= int(windowSize - increment) / 2;
Chris@0 1120 size_t pfx = 0;
Chris@0 1121
Chris@0 1122 if (startFrame < 0) {
Chris@0 1123 pfx = size_t(-startFrame);
Chris@0 1124 for (size_t i = 0; i < pfx; ++i) {
Chris@0 1125 input[i] = 0.0;
Chris@0 1126 }
Chris@0 1127 }
Chris@0 1128
Chris@0 1129 size_t got = m_model->getValues(m_channel, startFrame + pfx,
Chris@0 1130 endFrame, input + pfx);
Chris@9 1131 while (got + pfx < windowSize) {
Chris@0 1132 input[got + pfx] = 0.0;
Chris@0 1133 ++got;
Chris@0 1134 }
Chris@0 1135
Chris@37 1136 if (m_channel == -1) {
Chris@37 1137 int channels = m_model->getChannelCount();
Chris@37 1138 if (channels > 1) {
Chris@37 1139 for (size_t i = 0; i < windowSize; ++i) {
Chris@37 1140 input[i] /= channels;
Chris@37 1141 }
Chris@37 1142 }
Chris@37 1143 }
Chris@37 1144
Chris@0 1145 windower.cut(input);
Chris@0 1146
Chris@35 1147 for (size_t i = 0; i < windowSize/2; ++i) {
Chris@35 1148 double temp = input[i];
Chris@35 1149 input[i] = input[i + windowSize/2];
Chris@35 1150 input[i + windowSize/2] = temp;
Chris@35 1151 }
Chris@35 1152
Chris@0 1153 fftw_execute(plan);
Chris@0 1154
Chris@38 1155 double factor = 0.0;
Chris@0 1156
Chris@38 1157 for (size_t i = 0; i < windowSize/2; ++i) {
Chris@35 1158
Chris@36 1159 double mag = sqrt(output[i][0] * output[i][0] +
Chris@36 1160 output[i][1] * output[i][1]);
Chris@38 1161 mag /= windowSize / 2;
Chris@37 1162
Chris@38 1163 if (mag > factor) factor = mag;
Chris@37 1164
Chris@38 1165 double phase = atan2(output[i][1], output[i][0]);
Chris@75 1166 phase = princarg(phase);
Chris@37 1167
Chris@86 1168 workbuffer[i] = mag;
Chris@86 1169 workbuffer[i + windowSize/2] = phase;
Chris@38 1170 }
Chris@35 1171
Chris@86 1172 m_writeCache->setColumnAt(column, workbuffer,
Chris@86 1173 workbuffer + windowSize/2, factor);
Chris@38 1174 }
Chris@35 1175
Chris@38 1176 unsigned char
Chris@38 1177 SpectrogramLayer::getDisplayValue(float input) const
Chris@38 1178 {
Chris@38 1179 int value;
Chris@37 1180
Chris@40 1181 switch (m_colourScale) {
Chris@40 1182
Chris@40 1183 default:
Chris@40 1184 case LinearColourScale:
Chris@40 1185 value = int
Chris@40 1186 (input * (m_normalizeColumns ? 1.0 : 50.0) * 255.0) + 1;
Chris@40 1187 break;
Chris@40 1188
Chris@40 1189 case MeterColourScale:
Chris@40 1190 value = AudioLevel::multiplier_to_preview
Chris@40 1191 (input * (m_normalizeColumns ? 1.0 : 50.0), 255) + 1;
Chris@40 1192 break;
Chris@40 1193
Chris@40 1194 case dBColourScale:
Chris@40 1195 input = 20.0 * log10(input);
Chris@40 1196 input = (input + 80.0) / 80.0;
Chris@40 1197 if (input < 0.0) input = 0.0;
Chris@40 1198 if (input > 1.0) input = 1.0;
Chris@40 1199 value = int(input * 255.0) + 1;
Chris@40 1200 break;
Chris@40 1201
Chris@40 1202 case PhaseColourScale:
Chris@40 1203 value = int((input * 127.0 / M_PI) + 128);
Chris@40 1204 break;
Chris@0 1205 }
Chris@38 1206
Chris@38 1207 if (value > UCHAR_MAX) value = UCHAR_MAX;
Chris@38 1208 if (value < 0) value = 0;
Chris@38 1209 return value;
Chris@0 1210 }
Chris@0 1211
Chris@40 1212 float
Chris@40 1213 SpectrogramLayer::getInputForDisplayValue(unsigned char uc) const
Chris@40 1214 {
Chris@40 1215 int value = uc;
Chris@40 1216 float input;
Chris@40 1217
Chris@40 1218 switch (m_colourScale) {
Chris@40 1219
Chris@40 1220 default:
Chris@40 1221 case LinearColourScale:
Chris@40 1222 input = float(value - 1) / 255.0 / (m_normalizeColumns ? 1 : 50);
Chris@40 1223 break;
Chris@40 1224
Chris@40 1225 case MeterColourScale:
Chris@40 1226 input = AudioLevel::preview_to_multiplier(value - 1, 255)
Chris@40 1227 / (m_normalizeColumns ? 1.0 : 50.0);
Chris@40 1228 break;
Chris@40 1229
Chris@40 1230 case dBColourScale:
Chris@40 1231 input = float(value - 1) / 255.0;
Chris@40 1232 input = (input * 80.0) - 80.0;
Chris@40 1233 input = powf(10.0, input) / 20.0;
Chris@40 1234 value = int(input);
Chris@40 1235 break;
Chris@40 1236
Chris@40 1237 case PhaseColourScale:
Chris@40 1238 input = float(value - 128) * M_PI / 127.0;
Chris@40 1239 break;
Chris@40 1240 }
Chris@40 1241
Chris@40 1242 return input;
Chris@40 1243 }
Chris@40 1244
Chris@0 1245 void
Chris@0 1246 SpectrogramLayer::CacheFillThread::run()
Chris@0 1247 {
Chris@0 1248 // std::cerr << "SpectrogramLayer::CacheFillThread::run" << std::endl;
Chris@0 1249
Chris@0 1250 m_layer.m_mutex.lock();
Chris@0 1251
Chris@0 1252 while (!m_layer.m_exiting) {
Chris@0 1253
Chris@0 1254 bool interrupted = false;
Chris@0 1255
Chris@0 1256 // std::cerr << "SpectrogramLayer::CacheFillThread::run in loop" << std::endl;
Chris@0 1257
Chris@48 1258 bool haveUndormantViews = false;
Chris@48 1259
Chris@48 1260 for (std::map<const void *, bool>::iterator i =
Chris@48 1261 m_layer.m_dormancy.begin();
Chris@48 1262 i != m_layer.m_dormancy.end(); ++i) {
Chris@48 1263
Chris@48 1264 if (!i->second) {
Chris@48 1265 haveUndormantViews = true;
Chris@48 1266 break;
Chris@48 1267 }
Chris@48 1268 }
Chris@48 1269
Chris@48 1270 if (!haveUndormantViews) {
Chris@48 1271
Chris@48 1272 if (m_layer.m_cacheInvalid && m_layer.m_cache) {
Chris@48 1273 std::cerr << "All views dormant, freeing spectrogram cache"
Chris@48 1274 << std::endl;
Chris@47 1275
Chris@34 1276 delete m_layer.m_cache;
Chris@34 1277 m_layer.m_cache = 0;
Chris@34 1278 }
Chris@34 1279
Chris@34 1280 } else if (m_layer.m_model && m_layer.m_cacheInvalid) {
Chris@0 1281
Chris@0 1282 // std::cerr << "SpectrogramLayer::CacheFillThread::run: something to do" << std::endl;
Chris@0 1283
Chris@0 1284 while (!m_layer.m_model->isReady()) {
Chris@0 1285 m_layer.m_condition.wait(&m_layer.m_mutex, 100);
Chris@48 1286 if (m_layer.m_exiting) break;
Chris@0 1287 }
Chris@0 1288
Chris@48 1289 if (m_layer.m_exiting) break;
Chris@48 1290
Chris@0 1291 m_layer.m_cacheInvalid = false;
Chris@0 1292 m_fillExtent = 0;
Chris@0 1293 m_fillCompletion = 0;
Chris@0 1294
Chris@101 1295 // std::cerr << "SpectrogramLayer::CacheFillThread::run: model is ready" << std::endl;
Chris@0 1296
Chris@0 1297 size_t start = m_layer.m_model->getStartFrame();
Chris@0 1298 size_t end = m_layer.m_model->getEndFrame();
Chris@9 1299
Chris@101 1300 // std::cerr << "start = " << start << ", end = " << end << std::endl;
Chris@41 1301
Chris@9 1302 WindowType windowType = m_layer.m_windowType;
Chris@0 1303 size_t windowSize = m_layer.m_windowSize;
Chris@0 1304 size_t windowIncrement = m_layer.getWindowIncrement();
Chris@0 1305
Chris@101 1306 // std::cerr << "\nWINDOW INCREMENT: " << windowIncrement << " (for hop level " << m_layer.m_windowHopLevel << ")\n" << std::endl;
Chris@97 1307
Chris@44 1308 size_t visibleStart = m_layer.m_candidateFillStartFrame;
Chris@44 1309 visibleStart = (visibleStart / windowIncrement) * windowIncrement;
Chris@0 1310
Chris@9 1311 size_t width = (end - start) / windowIncrement + 1;
Chris@9 1312 size_t height = windowSize / 2;
Chris@35 1313
Chris@86 1314 //!!! if (!m_layer.m_cache) {
Chris@86 1315 // m_layer.m_cache = new FFTMemoryCache;
Chris@86 1316 // }
Chris@86 1317 if (!m_layer.m_writeCache) {
Chris@86 1318 m_layer.m_writeCache = new FFTFileCache
Chris@86 1319 (QString("%1").arg(getObjectExportId(&m_layer)),
Chris@88 1320 MatrixFile::ReadWrite);
Chris@86 1321 }
Chris@86 1322 m_layer.m_writeCache->resize(width, height);
Chris@86 1323 if (m_layer.m_cache) delete m_layer.m_cache;
Chris@86 1324 m_layer.m_cache = new FFTFileCache
Chris@86 1325 (QString("%1").arg(getObjectExportId(&m_layer)),
Chris@88 1326 MatrixFile::ReadOnly);
Chris@86 1327
Chris@90 1328 m_layer.setColourmap();
Chris@86 1329 //!!! m_layer.m_writeCache->reset();
Chris@35 1330
Chris@33 1331 // We don't need a lock when writing to or reading from
Chris@38 1332 // the pixels in the cache. We do need to ensure we have
Chris@38 1333 // the width and height of the cache and the FFT
Chris@38 1334 // parameters known before we unlock, in case they change
Chris@38 1335 // in the model while we aren't holding a lock. It's safe
Chris@38 1336 // for us to continue to use the "old" values if that
Chris@38 1337 // happens, because they will continue to match the
Chris@80 1338 // dimensions of the actual cache (which this thread
Chris@80 1339 // manages, not the layer's).
Chris@0 1340 m_layer.m_mutex.unlock();
Chris@0 1341
Chris@0 1342 double *input = (double *)
Chris@0 1343 fftw_malloc(windowSize * sizeof(double));
Chris@0 1344
Chris@0 1345 fftw_complex *output = (fftw_complex *)
Chris@0 1346 fftw_malloc(windowSize * sizeof(fftw_complex));
Chris@0 1347
Chris@86 1348 float *workbuffer = (float *)
Chris@86 1349 fftw_malloc(windowSize * sizeof(float));
Chris@86 1350
Chris@0 1351 fftw_plan plan = fftw_plan_dft_r2c_1d(windowSize, input,
Chris@1 1352 output, FFTW_ESTIMATE);
Chris@0 1353
Chris@9 1354 Window<double> windower(windowType, windowSize);
Chris@0 1355
Chris@0 1356 if (!plan) {
Chris@1 1357 std::cerr << "WARNING: fftw_plan_dft_r2c_1d(" << windowSize << ") failed!" << std::endl;
Chris@0 1358 fftw_free(input);
Chris@0 1359 fftw_free(output);
Chris@87 1360 fftw_free(workbuffer);
Chris@37 1361 m_layer.m_mutex.lock();
Chris@0 1362 continue;
Chris@0 1363 }
Chris@0 1364
Chris@0 1365 int counter = 0;
Chris@0 1366 int updateAt = (end / windowIncrement) / 20;
Chris@0 1367 if (updateAt < 100) updateAt = 100;
Chris@0 1368
Chris@44 1369 bool doVisibleFirst = (visibleStart != start);
Chris@0 1370
Chris@0 1371 if (doVisibleFirst) {
Chris@0 1372
Chris@44 1373 for (size_t f = visibleStart; f < end; f += windowIncrement) {
Chris@0 1374
Chris@38 1375 m_layer.fillCacheColumn(int((f - start) / windowIncrement),
Chris@38 1376 input, output, plan,
Chris@38 1377 windowSize, windowIncrement,
Chris@86 1378 workbuffer, windower);
Chris@38 1379
Chris@38 1380 if (m_layer.m_cacheInvalid || m_layer.m_exiting) {
Chris@0 1381 interrupted = true;
Chris@0 1382 m_fillExtent = 0;
Chris@0 1383 break;
Chris@0 1384 }
Chris@0 1385
Chris@38 1386 if (++counter == updateAt) {
Chris@37 1387 m_fillExtent = f;
Chris@0 1388 m_fillCompletion = size_t(100 * fabsf(float(f - visibleStart) /
Chris@0 1389 float(end - start)));
Chris@0 1390 counter = 0;
Chris@0 1391 }
Chris@0 1392 }
Chris@0 1393 }
Chris@0 1394
Chris@0 1395 if (!interrupted) {
Chris@0 1396
Chris@0 1397 size_t remainingEnd = end;
Chris@0 1398 if (doVisibleFirst) {
Chris@0 1399 remainingEnd = visibleStart;
Chris@0 1400 if (remainingEnd > start) --remainingEnd;
Chris@0 1401 else remainingEnd = start;
Chris@0 1402 }
Chris@0 1403 size_t baseCompletion = m_fillCompletion;
Chris@0 1404
Chris@0 1405 for (size_t f = start; f < remainingEnd; f += windowIncrement) {
Chris@0 1406
Chris@38 1407 m_layer.fillCacheColumn(int((f - start) / windowIncrement),
Chris@38 1408 input, output, plan,
Chris@38 1409 windowSize, windowIncrement,
Chris@86 1410 workbuffer, windower);
Chris@38 1411
Chris@38 1412 if (m_layer.m_cacheInvalid || m_layer.m_exiting) {
Chris@0 1413 interrupted = true;
Chris@0 1414 m_fillExtent = 0;
Chris@0 1415 break;
Chris@0 1416 }
Chris@0 1417
Chris@44 1418 if (++counter == updateAt) {
Chris@0 1419 m_fillExtent = f;
Chris@0 1420 m_fillCompletion = baseCompletion +
Chris@0 1421 size_t(100 * fabsf(float(f - start) /
Chris@0 1422 float(end - start)));
Chris@0 1423 counter = 0;
Chris@0 1424 }
Chris@0 1425 }
Chris@0 1426 }
Chris@0 1427
Chris@0 1428 fftw_destroy_plan(plan);
Chris@0 1429 fftw_free(output);
Chris@0 1430 fftw_free(input);
Chris@86 1431 fftw_free(workbuffer);
Chris@0 1432
Chris@0 1433 if (!interrupted) {
Chris@0 1434 m_fillExtent = end;
Chris@0 1435 m_fillCompletion = 100;
Chris@0 1436 }
Chris@0 1437
Chris@0 1438 m_layer.m_mutex.lock();
Chris@0 1439 }
Chris@0 1440
Chris@0 1441 if (!interrupted) m_layer.m_condition.wait(&m_layer.m_mutex, 2000);
Chris@0 1442 }
Chris@0 1443 }
Chris@0 1444
Chris@40 1445 float
Chris@40 1446 SpectrogramLayer::getEffectiveMinFrequency() const
Chris@40 1447 {
Chris@40 1448 int sr = m_model->getSampleRate();
Chris@40 1449 float minf = float(sr) / m_windowSize;
Chris@40 1450
Chris@40 1451 if (m_minFrequency > 0.0) {
Chris@40 1452 size_t minbin = size_t((double(m_minFrequency) * m_windowSize) / sr + 0.01);
Chris@40 1453 if (minbin < 1) minbin = 1;
Chris@40 1454 minf = minbin * sr / m_windowSize;
Chris@40 1455 }
Chris@40 1456
Chris@40 1457 return minf;
Chris@40 1458 }
Chris@40 1459
Chris@40 1460 float
Chris@40 1461 SpectrogramLayer::getEffectiveMaxFrequency() const
Chris@40 1462 {
Chris@40 1463 int sr = m_model->getSampleRate();
Chris@40 1464 float maxf = float(sr) / 2;
Chris@40 1465
Chris@40 1466 if (m_maxFrequency > 0.0) {
Chris@40 1467 size_t maxbin = size_t((double(m_maxFrequency) * m_windowSize) / sr + 0.1);
Chris@40 1468 if (maxbin > m_windowSize / 2) maxbin = m_windowSize / 2;
Chris@40 1469 maxf = maxbin * sr / m_windowSize;
Chris@40 1470 }
Chris@40 1471
Chris@40 1472 return maxf;
Chris@40 1473 }
Chris@40 1474
Chris@0 1475 bool
Chris@44 1476 SpectrogramLayer::getYBinRange(View *v, int y, float &q0, float &q1) const
Chris@0 1477 {
Chris@44 1478 int h = v->height();
Chris@0 1479 if (y < 0 || y >= h) return false;
Chris@0 1480
Chris@38 1481 int sr = m_model->getSampleRate();
Chris@40 1482 float minf = getEffectiveMinFrequency();
Chris@40 1483 float maxf = getEffectiveMaxFrequency();
Chris@0 1484
Chris@38 1485 bool logarithmic = (m_frequencyScale == LogFrequencyScale);
Chris@38 1486
Chris@44 1487 q0 = v->getFrequencyForY(y, minf, maxf, logarithmic);
Chris@44 1488 q1 = v->getFrequencyForY(y - 1, minf, maxf, logarithmic);
Chris@38 1489
Chris@38 1490 // Now map these on to actual bins
Chris@38 1491
Chris@40 1492 int b0 = int((q0 * m_windowSize) / sr);
Chris@40 1493 int b1 = int((q1 * m_windowSize) / sr);
Chris@0 1494
Chris@40 1495 //!!! this is supposed to return fractions-of-bins, as it were, hence the floats
Chris@38 1496 q0 = b0;
Chris@38 1497 q1 = b1;
Chris@38 1498
Chris@38 1499 // q0 = (b0 * sr) / m_windowSize;
Chris@38 1500 // q1 = (b1 * sr) / m_windowSize;
Chris@0 1501
Chris@0 1502 return true;
Chris@0 1503 }
Chris@38 1504
Chris@0 1505 bool
Chris@44 1506 SpectrogramLayer::getXBinRange(View *v, int x, float &s0, float &s1) const
Chris@0 1507 {
Chris@21 1508 size_t modelStart = m_model->getStartFrame();
Chris@21 1509 size_t modelEnd = m_model->getEndFrame();
Chris@0 1510
Chris@0 1511 // Each pixel column covers an exact range of sample frames:
Chris@44 1512 int f0 = v->getFrameForX(x) - modelStart;
Chris@44 1513 int f1 = v->getFrameForX(x + 1) - modelStart - 1;
Chris@20 1514
Chris@41 1515 if (f1 < int(modelStart) || f0 > int(modelEnd)) {
Chris@41 1516 return false;
Chris@41 1517 }
Chris@20 1518
Chris@0 1519 // And that range may be drawn from a possibly non-integral
Chris@0 1520 // range of spectrogram windows:
Chris@0 1521
Chris@0 1522 size_t windowIncrement = getWindowIncrement();
Chris@0 1523 s0 = float(f0) / windowIncrement;
Chris@0 1524 s1 = float(f1) / windowIncrement;
Chris@0 1525
Chris@0 1526 return true;
Chris@0 1527 }
Chris@0 1528
Chris@0 1529 bool
Chris@44 1530 SpectrogramLayer::getXBinSourceRange(View *v, int x, RealTime &min, RealTime &max) const
Chris@0 1531 {
Chris@0 1532 float s0 = 0, s1 = 0;
Chris@44 1533 if (!getXBinRange(v, x, s0, s1)) return false;
Chris@0 1534
Chris@0 1535 int s0i = int(s0 + 0.001);
Chris@0 1536 int s1i = int(s1);
Chris@0 1537
Chris@0 1538 int windowIncrement = getWindowIncrement();
Chris@0 1539 int w0 = s0i * windowIncrement - (m_windowSize - windowIncrement)/2;
Chris@0 1540 int w1 = s1i * windowIncrement + windowIncrement +
Chris@0 1541 (m_windowSize - windowIncrement)/2 - 1;
Chris@0 1542
Chris@0 1543 min = RealTime::frame2RealTime(w0, m_model->getSampleRate());
Chris@0 1544 max = RealTime::frame2RealTime(w1, m_model->getSampleRate());
Chris@0 1545 return true;
Chris@0 1546 }
Chris@0 1547
Chris@0 1548 bool
Chris@44 1549 SpectrogramLayer::getYBinSourceRange(View *v, int y, float &freqMin, float &freqMax)
Chris@0 1550 const
Chris@0 1551 {
Chris@0 1552 float q0 = 0, q1 = 0;
Chris@44 1553 if (!getYBinRange(v, y, q0, q1)) return false;
Chris@0 1554
Chris@0 1555 int q0i = int(q0 + 0.001);
Chris@0 1556 int q1i = int(q1);
Chris@0 1557
Chris@0 1558 int sr = m_model->getSampleRate();
Chris@0 1559
Chris@0 1560 for (int q = q0i; q <= q1i; ++q) {
Chris@35 1561 int binfreq = (sr * q) / m_windowSize;
Chris@0 1562 if (q == q0i) freqMin = binfreq;
Chris@0 1563 if (q == q1i) freqMax = binfreq;
Chris@0 1564 }
Chris@0 1565 return true;
Chris@0 1566 }
Chris@35 1567
Chris@35 1568 bool
Chris@44 1569 SpectrogramLayer::getAdjustedYBinSourceRange(View *v, int x, int y,
Chris@35 1570 float &freqMin, float &freqMax,
Chris@35 1571 float &adjFreqMin, float &adjFreqMax)
Chris@35 1572 const
Chris@35 1573 {
Chris@35 1574 float s0 = 0, s1 = 0;
Chris@44 1575 if (!getXBinRange(v, x, s0, s1)) return false;
Chris@35 1576
Chris@35 1577 float q0 = 0, q1 = 0;
Chris@44 1578 if (!getYBinRange(v, y, q0, q1)) return false;
Chris@35 1579
Chris@35 1580 int s0i = int(s0 + 0.001);
Chris@35 1581 int s1i = int(s1);
Chris@35 1582
Chris@35 1583 int q0i = int(q0 + 0.001);
Chris@35 1584 int q1i = int(q1);
Chris@35 1585
Chris@35 1586 int sr = m_model->getSampleRate();
Chris@35 1587
Chris@38 1588 size_t windowSize = m_windowSize;
Chris@38 1589 size_t windowIncrement = getWindowIncrement();
Chris@38 1590
Chris@35 1591 bool haveAdj = false;
Chris@35 1592
Chris@37 1593 bool peaksOnly = (m_binDisplay == PeakBins ||
Chris@37 1594 m_binDisplay == PeakFrequencies);
Chris@37 1595
Chris@35 1596 for (int q = q0i; q <= q1i; ++q) {
Chris@35 1597
Chris@35 1598 for (int s = s0i; s <= s1i; ++s) {
Chris@35 1599
Chris@35 1600 float binfreq = (sr * q) / m_windowSize;
Chris@35 1601 if (q == q0i) freqMin = binfreq;
Chris@35 1602 if (q == q1i) freqMax = binfreq;
Chris@37 1603
Chris@38 1604 if (!m_cache || m_cacheInvalid) break; //!!! lock?
Chris@38 1605
Chris@38 1606 if (peaksOnly && !m_cache->isLocalPeak(s, q)) continue;
Chris@38 1607
Chris@38 1608 if (!m_cache->isOverThreshold(s, q, m_threshold)) continue;
Chris@38 1609
Chris@38 1610 float freq = binfreq;
Chris@38 1611 bool steady = false;
Chris@40 1612
Chris@40 1613 if (s < int(m_cache->getWidth()) - 1) {
Chris@38 1614
Chris@38 1615 freq = calculateFrequency(q,
Chris@38 1616 windowSize,
Chris@38 1617 windowIncrement,
Chris@38 1618 sr,
Chris@38 1619 m_cache->getPhaseAt(s, q),
Chris@38 1620 m_cache->getPhaseAt(s+1, q),
Chris@38 1621 steady);
Chris@35 1622
Chris@38 1623 if (!haveAdj || freq < adjFreqMin) adjFreqMin = freq;
Chris@38 1624 if (!haveAdj || freq > adjFreqMax) adjFreqMax = freq;
Chris@35 1625
Chris@35 1626 haveAdj = true;
Chris@35 1627 }
Chris@35 1628 }
Chris@35 1629 }
Chris@35 1630
Chris@35 1631 if (!haveAdj) {
Chris@40 1632 adjFreqMin = adjFreqMax = 0.0;
Chris@35 1633 }
Chris@35 1634
Chris@35 1635 return haveAdj;
Chris@35 1636 }
Chris@0 1637
Chris@0 1638 bool
Chris@44 1639 SpectrogramLayer::getXYBinSourceRange(View *v, int x, int y,
Chris@38 1640 float &min, float &max,
Chris@38 1641 float &phaseMin, float &phaseMax) const
Chris@0 1642 {
Chris@0 1643 float q0 = 0, q1 = 0;
Chris@44 1644 if (!getYBinRange(v, y, q0, q1)) return false;
Chris@0 1645
Chris@0 1646 float s0 = 0, s1 = 0;
Chris@44 1647 if (!getXBinRange(v, x, s0, s1)) return false;
Chris@0 1648
Chris@0 1649 int q0i = int(q0 + 0.001);
Chris@0 1650 int q1i = int(q1);
Chris@0 1651
Chris@0 1652 int s0i = int(s0 + 0.001);
Chris@0 1653 int s1i = int(s1);
Chris@0 1654
Chris@37 1655 bool rv = false;
Chris@37 1656
Chris@0 1657 if (m_mutex.tryLock()) {
Chris@0 1658 if (m_cache && !m_cacheInvalid) {
Chris@0 1659
Chris@31 1660 int cw = m_cache->getWidth();
Chris@31 1661 int ch = m_cache->getHeight();
Chris@0 1662
Chris@38 1663 min = 0.0;
Chris@38 1664 max = 0.0;
Chris@38 1665 phaseMin = 0.0;
Chris@38 1666 phaseMax = 0.0;
Chris@38 1667 bool have = false;
Chris@0 1668
Chris@0 1669 for (int q = q0i; q <= q1i; ++q) {
Chris@0 1670 for (int s = s0i; s <= s1i; ++s) {
Chris@0 1671 if (s >= 0 && q >= 0 && s < cw && q < ch) {
Chris@38 1672
Chris@97 1673 if (!m_cache->haveSetColumnAt(s)) continue;
Chris@91 1674
Chris@38 1675 float value;
Chris@38 1676
Chris@38 1677 value = m_cache->getPhaseAt(s, q);
Chris@38 1678 if (!have || value < phaseMin) { phaseMin = value; }
Chris@38 1679 if (!have || value > phaseMax) { phaseMax = value; }
Chris@38 1680
Chris@38 1681 value = m_cache->getMagnitudeAt(s, q);
Chris@38 1682 if (!have || value < min) { min = value; }
Chris@38 1683 if (!have || value > max) { max = value; }
Chris@38 1684
Chris@38 1685 have = true;
Chris@0 1686 }
Chris@0 1687 }
Chris@0 1688 }
Chris@0 1689
Chris@38 1690 if (have) {
Chris@37 1691 rv = true;
Chris@37 1692 }
Chris@0 1693 }
Chris@0 1694
Chris@0 1695 m_mutex.unlock();
Chris@0 1696 }
Chris@0 1697
Chris@37 1698 return rv;
Chris@0 1699 }
Chris@0 1700
Chris@0 1701 void
Chris@44 1702 SpectrogramLayer::paint(View *v, QPainter &paint, QRect rect) const
Chris@0 1703 {
Chris@55 1704 if (m_colourScheme == BlackOnWhite) {
Chris@55 1705 v->setLightBackground(true);
Chris@55 1706 } else {
Chris@55 1707 v->setLightBackground(false);
Chris@55 1708 }
Chris@55 1709
Chris@0 1710 // Profiler profiler("SpectrogramLayer::paint", true);
Chris@0 1711 #ifdef DEBUG_SPECTROGRAM_REPAINT
Chris@95 1712 std::cerr << "SpectrogramLayer::paint(): m_model is " << m_model << ", zoom level is " << v->getZoomLevel() << ", m_updateTimer " << m_updateTimer << std::endl;
Chris@95 1713
Chris@95 1714 std::cerr << "rect is " << rect.x() << "," << rect.y() << " " << rect.width() << "x" << rect.height() << std::endl;
Chris@0 1715 #endif
Chris@95 1716
Chris@45 1717 long sf = v->getStartFrame();
Chris@45 1718 if (sf < 0) m_candidateFillStartFrame = 0;
Chris@45 1719 else m_candidateFillStartFrame = sf;
Chris@44 1720
Chris@0 1721 if (!m_model || !m_model->isOK() || !m_model->isReady()) {
Chris@0 1722 return;
Chris@0 1723 }
Chris@0 1724
Chris@47 1725 if (isLayerDormant(v)) {
Chris@48 1726 std::cerr << "SpectrogramLayer::paint(): Layer is dormant, making it undormant again" << std::endl;
Chris@29 1727 }
Chris@29 1728
Chris@48 1729 // Need to do this even if !isLayerDormant, as that could mean v
Chris@48 1730 // is not in the dormancy map at all -- we need it to be present
Chris@48 1731 // and accountable for when determining whether we need the cache
Chris@48 1732 // in the cache-fill thread above.
Chris@48 1733 m_dormancy[v] = false;
Chris@48 1734
Chris@0 1735 #ifdef DEBUG_SPECTROGRAM_REPAINT
Chris@95 1736 // std::cerr << "SpectrogramLayer::paint(): About to lock" << std::endl;
Chris@0 1737 #endif
Chris@0 1738
Chris@37 1739 m_mutex.lock();
Chris@0 1740
Chris@0 1741 #ifdef DEBUG_SPECTROGRAM_REPAINT
Chris@95 1742 // std::cerr << "SpectrogramLayer::paint(): locked" << std::endl;
Chris@0 1743 #endif
Chris@0 1744
Chris@0 1745 if (m_cacheInvalid) { // lock the mutex before checking this
Chris@0 1746 m_mutex.unlock();
Chris@0 1747 #ifdef DEBUG_SPECTROGRAM_REPAINT
Chris@0 1748 std::cerr << "SpectrogramLayer::paint(): Cache invalid, returning" << std::endl;
Chris@0 1749 #endif
Chris@0 1750 return;
Chris@0 1751 }
Chris@0 1752
Chris@95 1753 PixmapCache &cache = m_pixmapCaches[v];
Chris@95 1754
Chris@95 1755 #ifdef DEBUG_SPECTROGRAM_REPAINT
Chris@95 1756 std::cerr << "SpectrogramLayer::paint(): pixmap cache valid area " << cache.validArea.x() << ", " << cache.validArea.y() << ", " << cache.validArea.width() << "x" << cache.validArea.height() << std::endl;
Chris@95 1757 #endif
Chris@95 1758
Chris@0 1759 bool stillCacheing = (m_updateTimer != 0);
Chris@0 1760
Chris@0 1761 #ifdef DEBUG_SPECTROGRAM_REPAINT
Chris@0 1762 std::cerr << "SpectrogramLayer::paint(): Still cacheing = " << stillCacheing << std::endl;
Chris@0 1763 #endif
Chris@0 1764
Chris@44 1765 long startFrame = v->getStartFrame();
Chris@44 1766 int zoomLevel = v->getZoomLevel();
Chris@0 1767
Chris@0 1768 int x0 = 0;
Chris@44 1769 int x1 = v->width();
Chris@0 1770 int y0 = 0;
Chris@44 1771 int y1 = v->height();
Chris@0 1772
Chris@0 1773 bool recreateWholePixmapCache = true;
Chris@0 1774
Chris@95 1775 x0 = rect.left();
Chris@95 1776 x1 = rect.right() + 1;
Chris@95 1777 y0 = rect.top();
Chris@95 1778 y1 = rect.bottom() + 1;
Chris@95 1779
Chris@95 1780 if (cache.validArea.width() > 0) {
Chris@95 1781
Chris@95 1782 if (int(cache.zoomLevel) == zoomLevel &&
Chris@95 1783 cache.pixmap.width() == v->width() &&
Chris@95 1784 cache.pixmap.height() == v->height()) {
Chris@95 1785
Chris@95 1786 if (v->getXForFrame(cache.startFrame) ==
Chris@95 1787 v->getXForFrame(startFrame) &&
Chris@95 1788 cache.validArea.x() <= x0 &&
Chris@95 1789 cache.validArea.x() + cache.validArea.width() >= x1) {
Chris@0 1790
Chris@0 1791 #ifdef DEBUG_SPECTROGRAM_REPAINT
Chris@0 1792 std::cerr << "SpectrogramLayer: pixmap cache good" << std::endl;
Chris@0 1793 #endif
Chris@0 1794
Chris@0 1795 m_mutex.unlock();
Chris@95 1796 paint.drawPixmap(rect, cache.pixmap, rect);
Chris@0 1797 return;
Chris@0 1798
Chris@0 1799 } else {
Chris@0 1800
Chris@0 1801 #ifdef DEBUG_SPECTROGRAM_REPAINT
Chris@0 1802 std::cerr << "SpectrogramLayer: pixmap cache partially OK" << std::endl;
Chris@0 1803 #endif
Chris@0 1804
Chris@0 1805 recreateWholePixmapCache = false;
Chris@0 1806
Chris@95 1807 int dx = v->getXForFrame(cache.startFrame) -
Chris@44 1808 v->getXForFrame(startFrame);
Chris@0 1809
Chris@0 1810 #ifdef DEBUG_SPECTROGRAM_REPAINT
Chris@95 1811 std::cerr << "SpectrogramLayer: dx = " << dx << " (pixmap cache " << cache.pixmap.width() << "x" << cache.pixmap.height() << ")" << std::endl;
Chris@0 1812 #endif
Chris@0 1813
Chris@95 1814 if (dx != 0 &&
Chris@95 1815 dx > -cache.pixmap.width() &&
Chris@95 1816 dx < cache.pixmap.width()) {
Chris@0 1817
Chris@0 1818 #if defined(Q_WS_WIN32) || defined(Q_WS_MAC)
Chris@0 1819 // Copying a pixmap to itself doesn't work
Chris@0 1820 // properly on Windows or Mac (it only works when
Chris@0 1821 // moving in one direction).
Chris@0 1822
Chris@0 1823 //!!! Need a utility function for this
Chris@0 1824
Chris@0 1825 static QPixmap *tmpPixmap = 0;
Chris@0 1826 if (!tmpPixmap ||
Chris@95 1827 tmpPixmap->width() != cache.pixmap.width() ||
Chris@95 1828 tmpPixmap->height() != cache.pixmap.height()) {
Chris@0 1829 delete tmpPixmap;
Chris@95 1830 tmpPixmap = new QPixmap(cache.pixmap.width(),
Chris@95 1831 cache.pixmap.height());
Chris@0 1832 }
Chris@0 1833 QPainter cachePainter;
Chris@0 1834 cachePainter.begin(tmpPixmap);
Chris@95 1835 cachePainter.drawPixmap(0, 0, cache.pixmap);
Chris@0 1836 cachePainter.end();
Chris@95 1837 cachePainter.begin(&cache.pixmap);
Chris@0 1838 cachePainter.drawPixmap(dx, 0, *tmpPixmap);
Chris@0 1839 cachePainter.end();
Chris@0 1840 #else
Chris@95 1841 QPainter cachePainter(&cache.pixmap);
Chris@95 1842 cachePainter.drawPixmap(dx, 0, cache.pixmap);
Chris@0 1843 cachePainter.end();
Chris@0 1844 #endif
Chris@0 1845
Chris@95 1846 int px = cache.validArea.x();
Chris@95 1847 int pw = cache.validArea.width();
Chris@0 1848
Chris@0 1849 if (dx < 0) {
Chris@95 1850 x0 = cache.pixmap.width() + dx;
Chris@95 1851 x1 = cache.pixmap.width();
Chris@95 1852 px += dx;
Chris@95 1853 if (px < 0) {
Chris@95 1854 pw += px;
Chris@95 1855 px = 0;
Chris@95 1856 if (pw < 0) pw = 0;
Chris@95 1857 }
Chris@0 1858 } else {
Chris@0 1859 x0 = 0;
Chris@0 1860 x1 = dx;
Chris@95 1861 px += dx;
Chris@95 1862 if (px + pw > cache.pixmap.width()) {
Chris@95 1863 pw = int(cache.pixmap.width()) - px;
Chris@95 1864 if (pw < 0) pw = 0;
Chris@95 1865 }
Chris@0 1866 }
Chris@95 1867
Chris@95 1868 cache.validArea =
Chris@95 1869 QRect(px, cache.validArea.y(),
Chris@95 1870 pw, cache.validArea.height());
Chris@95 1871
Chris@95 1872 paint.drawPixmap(rect & cache.validArea,
Chris@95 1873 cache.pixmap,
Chris@95 1874 rect & cache.validArea);
Chris@0 1875 }
Chris@0 1876 }
Chris@0 1877 } else {
Chris@0 1878 #ifdef DEBUG_SPECTROGRAM_REPAINT
Chris@0 1879 std::cerr << "SpectrogramLayer: pixmap cache useless" << std::endl;
Chris@0 1880 #endif
Chris@95 1881 cache.validArea = QRect();
Chris@0 1882 }
Chris@0 1883 }
Chris@95 1884
Chris@92 1885 /*
Chris@0 1886 if (stillCacheing) {
Chris@0 1887 x0 = rect.left();
Chris@0 1888 x1 = rect.right() + 1;
Chris@0 1889 y0 = rect.top();
Chris@0 1890 y1 = rect.bottom() + 1;
Chris@0 1891 }
Chris@92 1892 */
Chris@95 1893
Chris@95 1894 if (recreateWholePixmapCache) {
Chris@95 1895 x0 = 0;
Chris@95 1896 x1 = v->width();
Chris@95 1897 }
Chris@95 1898
Chris@96 1899 int paintBlockWidth = (300000 / zoomLevel);
Chris@96 1900 if (paintBlockWidth < 20) paintBlockWidth = 20;
Chris@96 1901
Chris@96 1902 if (cache.validArea.width() > 0) {
Chris@96 1903
Chris@96 1904 int vx0 = 0, vx1 = 0;
Chris@96 1905 vx0 = cache.validArea.x();
Chris@96 1906 vx1 = cache.validArea.x() + cache.validArea.width();
Chris@96 1907
Chris@96 1908 #ifdef DEBUG_SPECTROGRAM_REPAINT
Chris@96 1909 std::cerr << "x0 " << x0 << ", x1 " << x1 << ", vx0 " << vx0 << ", vx1 " << vx1 << ", paintBlockWidth " << paintBlockWidth << std::endl;
Chris@96 1910 #endif
Chris@96 1911 if (x0 < vx0) {
Chris@96 1912 if (x0 + paintBlockWidth < vx0) {
Chris@96 1913 x0 = vx0 - paintBlockWidth;
Chris@96 1914 } else {
Chris@96 1915 x0 = 0;
Chris@96 1916 }
Chris@96 1917 } else if (x0 > vx1) {
Chris@96 1918 x0 = vx1;
Chris@96 1919 }
Chris@95 1920
Chris@96 1921 if (x1 < vx0) {
Chris@96 1922 x1 = vx0;
Chris@96 1923 } else if (x1 > vx1) {
Chris@96 1924 if (vx1 + paintBlockWidth < x1) {
Chris@96 1925 x1 = vx1 + paintBlockWidth;
Chris@96 1926 } else {
Chris@96 1927 x1 = v->width();
Chris@95 1928 }
Chris@96 1929 }
Chris@95 1930
Chris@96 1931 cache.validArea = QRect
Chris@96 1932 (std::min(vx0, x0), cache.validArea.y(),
Chris@96 1933 std::max(vx1 - std::min(vx0, x0),
Chris@96 1934 x1 - std::min(vx0, x0)),
Chris@96 1935 cache.validArea.height());
Chris@95 1936
Chris@96 1937 } else {
Chris@96 1938 if (x1 > x0 + paintBlockWidth) {
Chris@96 1939 x1 = x0 + paintBlockWidth;
Chris@95 1940 }
Chris@96 1941 cache.validArea = QRect(x0, 0, x1 - x0, v->height());
Chris@95 1942 }
Chris@95 1943
Chris@0 1944 int w = x1 - x0;
Chris@0 1945 int h = y1 - y0;
Chris@0 1946
Chris@95 1947 #ifdef DEBUG_SPECTROGRAM_REPAINT
Chris@95 1948 std::cerr << "x0 " << x0 << ", x1 " << x1 << ", w " << w << ", h " << h << std::endl;
Chris@95 1949 #endif
Chris@95 1950
Chris@95 1951 if (m_drawBuffer.width() < w || m_drawBuffer.height() < h) {
Chris@95 1952 m_drawBuffer = QImage(w, h, QImage::Format_RGB32);
Chris@95 1953 }
Chris@95 1954
Chris@97 1955 m_drawBuffer.fill(m_colourMap.getColour(0).rgb());
Chris@35 1956
Chris@37 1957 int sr = m_model->getSampleRate();
Chris@35 1958
Chris@35 1959 size_t bins = m_windowSize / 2;
Chris@35 1960 if (m_maxFrequency > 0) {
Chris@35 1961 bins = int((double(m_maxFrequency) * m_windowSize) / sr + 0.1);
Chris@35 1962 if (bins > m_windowSize / 2) bins = m_windowSize / 2;
Chris@35 1963 }
Chris@35 1964
Chris@40 1965 size_t minbin = 1;
Chris@37 1966 if (m_minFrequency > 0) {
Chris@37 1967 minbin = int((double(m_minFrequency) * m_windowSize) / sr + 0.1);
Chris@40 1968 if (minbin < 1) minbin = 1;
Chris@37 1969 if (minbin >= bins) minbin = bins - 1;
Chris@37 1970 }
Chris@37 1971
Chris@37 1972 float minFreq = (float(minbin) * sr) / m_windowSize;
Chris@35 1973 float maxFreq = (float(bins) * sr) / m_windowSize;
Chris@0 1974
Chris@92 1975 float ymag[h];
Chris@92 1976 float ydiv[h];
Chris@92 1977 float yval[bins + 1];
Chris@92 1978
Chris@38 1979 size_t increment = getWindowIncrement();
Chris@40 1980
Chris@40 1981 bool logarithmic = (m_frequencyScale == LogFrequencyScale);
Chris@38 1982
Chris@0 1983 m_mutex.unlock();
Chris@0 1984
Chris@92 1985 for (size_t q = minbin; q <= bins; ++q) {
Chris@92 1986 float f0 = (float(q) * sr) / m_windowSize;
Chris@92 1987 yval[q] = v->getYForFrequency(f0, minFreq, maxFreq, logarithmic);
Chris@92 1988 }
Chris@92 1989
Chris@95 1990 m_mutex.lock();
Chris@95 1991
Chris@35 1992 for (int x = 0; x < w; ++x) {
Chris@35 1993
Chris@95 1994 if (x % 10 == 0) {
Chris@95 1995 m_mutex.unlock();
Chris@95 1996 m_mutex.lock();
Chris@95 1997 if (m_cacheInvalid) {
Chris@95 1998 break;
Chris@95 1999 }
Chris@95 2000 }
Chris@35 2001
Chris@35 2002 for (int y = 0; y < h; ++y) {
Chris@40 2003 ymag[y] = 0.0;
Chris@40 2004 ydiv[y] = 0.0;
Chris@35 2005 }
Chris@35 2006
Chris@35 2007 float s0 = 0, s1 = 0;
Chris@35 2008
Chris@44 2009 if (!getXBinRange(v, x0 + x, s0, s1)) {
Chris@95 2010 assert(x <= m_drawBuffer.width());
Chris@35 2011 continue;
Chris@35 2012 }
Chris@35 2013
Chris@35 2014 int s0i = int(s0 + 0.001);
Chris@35 2015 int s1i = int(s1);
Chris@35 2016
Chris@45 2017 if (s1i >= m_cache->getWidth()) {
Chris@45 2018 if (s0i >= m_cache->getWidth()) {
Chris@45 2019 continue;
Chris@45 2020 } else {
Chris@45 2021 s1i = s0i;
Chris@45 2022 }
Chris@45 2023 }
Chris@92 2024
Chris@92 2025 for (int s = s0i; s <= s1i; ++s) {
Chris@92 2026
Chris@97 2027 if (!m_cache->haveSetColumnAt(s)) continue;
Chris@92 2028
Chris@92 2029 for (size_t q = minbin; q < bins; ++q) {
Chris@92 2030
Chris@92 2031 float y0 = yval[q + 1];
Chris@92 2032 float y1 = yval[q];
Chris@92 2033
Chris@40 2034 if (m_binDisplay == PeakBins ||
Chris@40 2035 m_binDisplay == PeakFrequencies) {
Chris@40 2036 if (!m_cache->isLocalPeak(s, q)) continue;
Chris@40 2037 }
Chris@40 2038
Chris@40 2039 if (!m_cache->isOverThreshold(s, q, m_threshold)) continue;
Chris@40 2040
Chris@35 2041 float sprop = 1.0;
Chris@35 2042 if (s == s0i) sprop *= (s + 1) - s0;
Chris@35 2043 if (s == s1i) sprop *= s1 - s;
Chris@35 2044
Chris@38 2045 if (m_binDisplay == PeakFrequencies &&
Chris@40 2046 s < int(m_cache->getWidth()) - 1) {
Chris@35 2047
Chris@38 2048 bool steady = false;
Chris@92 2049 float f = calculateFrequency(q,
Chris@38 2050 m_windowSize,
Chris@38 2051 increment,
Chris@38 2052 sr,
Chris@38 2053 m_cache->getPhaseAt(s, q),
Chris@38 2054 m_cache->getPhaseAt(s+1, q),
Chris@38 2055 steady);
Chris@40 2056
Chris@44 2057 y0 = y1 = v->getYForFrequency
Chris@92 2058 (f, minFreq, maxFreq, logarithmic);
Chris@35 2059 }
Chris@38 2060
Chris@35 2061 int y0i = int(y0 + 0.001);
Chris@35 2062 int y1i = int(y1);
Chris@35 2063
Chris@92 2064 float value;
Chris@92 2065
Chris@92 2066 if (m_colourScale == PhaseColourScale) {
Chris@92 2067 value = m_cache->getPhaseAt(s, q);
Chris@92 2068 } else if (m_normalizeColumns) {
Chris@92 2069 value = m_cache->getNormalizedMagnitudeAt(s, q) * m_gain;
Chris@92 2070 } else {
Chris@92 2071 value = m_cache->getMagnitudeAt(s, q) * m_gain;
Chris@92 2072 }
Chris@92 2073
Chris@35 2074 for (int y = y0i; y <= y1i; ++y) {
Chris@35 2075
Chris@35 2076 if (y < 0 || y >= h) continue;
Chris@35 2077
Chris@35 2078 float yprop = sprop;
Chris@35 2079 if (y == y0i) yprop *= (y + 1) - y0;
Chris@35 2080 if (y == y1i) yprop *= y1 - y;
Chris@37 2081 ymag[y] += yprop * value;
Chris@35 2082 ydiv[y] += yprop;
Chris@35 2083 }
Chris@35 2084 }
Chris@35 2085 }
Chris@35 2086
Chris@35 2087 for (int y = 0; y < h; ++y) {
Chris@35 2088
Chris@35 2089 if (ydiv[y] > 0.0) {
Chris@40 2090
Chris@40 2091 unsigned char pixel = 0;
Chris@40 2092
Chris@38 2093 float avg = ymag[y] / ydiv[y];
Chris@38 2094 pixel = getDisplayValue(avg);
Chris@40 2095
Chris@95 2096 assert(x <= m_drawBuffer.width());
Chris@86 2097 QColor c = m_colourMap.getColour(pixel);
Chris@95 2098 m_drawBuffer.setPixel(x, y,
Chris@95 2099 qRgb(c.red(), c.green(), c.blue()));
Chris@35 2100 }
Chris@35 2101 }
Chris@35 2102 }
Chris@35 2103
Chris@95 2104 m_mutex.unlock();
Chris@95 2105
Chris@95 2106 paint.drawImage(x0, y0, m_drawBuffer, 0, 0, w, h);
Chris@0 2107
Chris@0 2108 if (recreateWholePixmapCache) {
Chris@95 2109 cache.pixmap = QPixmap(v->width(), v->height());
Chris@0 2110 }
Chris@0 2111
Chris@95 2112 QPainter cachePainter(&cache.pixmap);
Chris@95 2113 cachePainter.drawImage(x0, y0, m_drawBuffer, 0, 0, w, h);
Chris@0 2114 cachePainter.end();
Chris@0 2115
Chris@95 2116 // m_pixmapCacheInvalid = false;
Chris@95 2117 cache.startFrame = startFrame;
Chris@95 2118 cache.zoomLevel = zoomLevel;
Chris@95 2119
Chris@95 2120 if (cache.validArea.x() > 0) {
Chris@95 2121 #ifdef DEBUG_SPECTROGRAM_REPAINT
Chris@95 2122 std::cerr << "SpectrogramLayer::paint() updating left" << std::endl;
Chris@95 2123 #endif
Chris@95 2124 v->update(0, 0, cache.validArea.x(), v->height());
Chris@95 2125 }
Chris@95 2126
Chris@95 2127 if (cache.validArea.x() + cache.validArea.width() <
Chris@95 2128 cache.pixmap.width()) {
Chris@95 2129 #ifdef DEBUG_SPECTROGRAM_REPAINT
Chris@95 2130 std::cerr << "SpectrogramLayer::paint() updating right ("
Chris@95 2131 << cache.validArea.x() + cache.validArea.width()
Chris@95 2132 << ", "
Chris@95 2133 << cache.pixmap.width() - (cache.validArea.x() +
Chris@95 2134 cache.validArea.width())
Chris@95 2135 << ")" << std::endl;
Chris@95 2136 #endif
Chris@95 2137 v->update(cache.validArea.x() + cache.validArea.width(),
Chris@95 2138 0,
Chris@95 2139 cache.pixmap.width() - (cache.validArea.x() +
Chris@95 2140 cache.validArea.width()),
Chris@95 2141 v->height());
Chris@95 2142 }
Chris@0 2143
Chris@0 2144 #ifdef DEBUG_SPECTROGRAM_REPAINT
Chris@0 2145 std::cerr << "SpectrogramLayer::paint() returning" << std::endl;
Chris@0 2146 #endif
Chris@0 2147 }
Chris@0 2148
Chris@42 2149 float
Chris@44 2150 SpectrogramLayer::getYForFrequency(View *v, float frequency) const
Chris@42 2151 {
Chris@44 2152 return v->getYForFrequency(frequency,
Chris@44 2153 getEffectiveMinFrequency(),
Chris@44 2154 getEffectiveMaxFrequency(),
Chris@44 2155 m_frequencyScale == LogFrequencyScale);
Chris@42 2156 }
Chris@42 2157
Chris@42 2158 float
Chris@44 2159 SpectrogramLayer::getFrequencyForY(View *v, int y) const
Chris@42 2160 {
Chris@44 2161 return v->getFrequencyForY(y,
Chris@44 2162 getEffectiveMinFrequency(),
Chris@44 2163 getEffectiveMaxFrequency(),
Chris@44 2164 m_frequencyScale == LogFrequencyScale);
Chris@42 2165 }
Chris@42 2166
Chris@0 2167 int
Chris@0 2168 SpectrogramLayer::getCompletion() const
Chris@0 2169 {
Chris@0 2170 if (m_updateTimer == 0) return 100;
Chris@0 2171 size_t completion = m_fillThread->getFillCompletion();
Chris@0 2172 // std::cerr << "SpectrogramLayer::getCompletion: completion = " << completion << std::endl;
Chris@0 2173 return completion;
Chris@0 2174 }
Chris@0 2175
Chris@28 2176 bool
Chris@101 2177 SpectrogramLayer::getValueExtents(float &min, float &max,
Chris@101 2178 bool &logarithmic, QString &unit) const
Chris@79 2179 {
Chris@79 2180 min = getEffectiveMinFrequency();
Chris@79 2181 max = getEffectiveMaxFrequency();
Chris@101 2182 logarithmic = (m_frequencyScale == LogFrequencyScale);
Chris@79 2183 unit = "Hz";
Chris@79 2184 return true;
Chris@79 2185 }
Chris@79 2186
Chris@79 2187 bool
Chris@101 2188 SpectrogramLayer::getDisplayExtents(float &min, float &max) const
Chris@101 2189 {
Chris@101 2190 min = getEffectiveMinFrequency();
Chris@101 2191 max = getEffectiveMaxFrequency();
Chris@101 2192 return true;
Chris@101 2193 }
Chris@101 2194
Chris@101 2195 bool
Chris@44 2196 SpectrogramLayer::snapToFeatureFrame(View *v, int &frame,
Chris@28 2197 size_t &resolution,
Chris@28 2198 SnapType snap) const
Chris@13 2199 {
Chris@13 2200 resolution = getWindowIncrement();
Chris@28 2201 int left = (frame / resolution) * resolution;
Chris@28 2202 int right = left + resolution;
Chris@28 2203
Chris@28 2204 switch (snap) {
Chris@28 2205 case SnapLeft: frame = left; break;
Chris@28 2206 case SnapRight: frame = right; break;
Chris@28 2207 case SnapNearest:
Chris@28 2208 case SnapNeighbouring:
Chris@28 2209 if (frame - left > right - frame) frame = right;
Chris@28 2210 else frame = left;
Chris@28 2211 break;
Chris@28 2212 }
Chris@28 2213
Chris@28 2214 return true;
Chris@28 2215 }
Chris@13 2216
Chris@77 2217 bool
Chris@77 2218 SpectrogramLayer::getCrosshairExtents(View *v, QPainter &paint,
Chris@77 2219 QPoint cursorPos,
Chris@77 2220 std::vector<QRect> &extents) const
Chris@77 2221 {
Chris@77 2222 QRect vertical(cursorPos.x() - 12, 0, 12, v->height());
Chris@77 2223 extents.push_back(vertical);
Chris@77 2224
Chris@77 2225 QRect horizontal(0, cursorPos.y(), cursorPos.x(), 1);
Chris@77 2226 extents.push_back(horizontal);
Chris@77 2227
Chris@77 2228 return true;
Chris@77 2229 }
Chris@77 2230
Chris@77 2231 void
Chris@77 2232 SpectrogramLayer::paintCrosshairs(View *v, QPainter &paint,
Chris@77 2233 QPoint cursorPos) const
Chris@77 2234 {
Chris@77 2235 paint.save();
Chris@77 2236 paint.setPen(m_crosshairColour);
Chris@77 2237
Chris@77 2238 paint.drawLine(0, cursorPos.y(), cursorPos.x() - 1, cursorPos.y());
Chris@77 2239 paint.drawLine(cursorPos.x(), 0, cursorPos.x(), v->height());
Chris@77 2240
Chris@77 2241 float fundamental = getFrequencyForY(v, cursorPos.y());
Chris@77 2242
Chris@77 2243 int harmonic = 2;
Chris@77 2244
Chris@77 2245 while (harmonic < 100) {
Chris@77 2246
Chris@77 2247 float hy = lrintf(getYForFrequency(v, fundamental * harmonic));
Chris@77 2248 if (hy < 0 || hy > v->height()) break;
Chris@77 2249
Chris@77 2250 int len = 7;
Chris@77 2251
Chris@77 2252 if (harmonic % 2 == 0) {
Chris@77 2253 if (harmonic % 4 == 0) {
Chris@77 2254 len = 12;
Chris@77 2255 } else {
Chris@77 2256 len = 10;
Chris@77 2257 }
Chris@77 2258 }
Chris@77 2259
Chris@77 2260 paint.drawLine(cursorPos.x() - len,
Chris@77 2261 hy,
Chris@77 2262 cursorPos.x(),
Chris@77 2263 hy);
Chris@77 2264
Chris@77 2265 ++harmonic;
Chris@77 2266 }
Chris@77 2267
Chris@77 2268 paint.restore();
Chris@77 2269 }
Chris@77 2270
Chris@25 2271 QString
Chris@44 2272 SpectrogramLayer::getFeatureDescription(View *v, QPoint &pos) const
Chris@25 2273 {
Chris@25 2274 int x = pos.x();
Chris@25 2275 int y = pos.y();
Chris@0 2276
Chris@25 2277 if (!m_model || !m_model->isOK()) return "";
Chris@0 2278
Chris@38 2279 float magMin = 0, magMax = 0;
Chris@38 2280 float phaseMin = 0, phaseMax = 0;
Chris@0 2281 float freqMin = 0, freqMax = 0;
Chris@35 2282 float adjFreqMin = 0, adjFreqMax = 0;
Chris@25 2283 QString pitchMin, pitchMax;
Chris@0 2284 RealTime rtMin, rtMax;
Chris@0 2285
Chris@38 2286 bool haveValues = false;
Chris@0 2287
Chris@44 2288 if (!getXBinSourceRange(v, x, rtMin, rtMax)) {
Chris@38 2289 return "";
Chris@38 2290 }
Chris@44 2291 if (getXYBinSourceRange(v, x, y, magMin, magMax, phaseMin, phaseMax)) {
Chris@38 2292 haveValues = true;
Chris@38 2293 }
Chris@0 2294
Chris@35 2295 QString adjFreqText = "", adjPitchText = "";
Chris@35 2296
Chris@38 2297 if (m_binDisplay == PeakFrequencies) {
Chris@35 2298
Chris@44 2299 if (!getAdjustedYBinSourceRange(v, x, y, freqMin, freqMax,
Chris@38 2300 adjFreqMin, adjFreqMax)) {
Chris@38 2301 return "";
Chris@38 2302 }
Chris@35 2303
Chris@35 2304 if (adjFreqMin != adjFreqMax) {
Chris@65 2305 adjFreqText = tr("Peak Frequency:\t%1 - %2 Hz\n")
Chris@35 2306 .arg(adjFreqMin).arg(adjFreqMax);
Chris@35 2307 } else {
Chris@65 2308 adjFreqText = tr("Peak Frequency:\t%1 Hz\n")
Chris@35 2309 .arg(adjFreqMin);
Chris@38 2310 }
Chris@38 2311
Chris@38 2312 QString pmin = Pitch::getPitchLabelForFrequency(adjFreqMin);
Chris@38 2313 QString pmax = Pitch::getPitchLabelForFrequency(adjFreqMax);
Chris@38 2314
Chris@38 2315 if (pmin != pmax) {
Chris@65 2316 adjPitchText = tr("Peak Pitch:\t%3 - %4\n").arg(pmin).arg(pmax);
Chris@38 2317 } else {
Chris@65 2318 adjPitchText = tr("Peak Pitch:\t%2\n").arg(pmin);
Chris@35 2319 }
Chris@35 2320
Chris@35 2321 } else {
Chris@35 2322
Chris@44 2323 if (!getYBinSourceRange(v, y, freqMin, freqMax)) return "";
Chris@35 2324 }
Chris@35 2325
Chris@25 2326 QString text;
Chris@25 2327
Chris@25 2328 if (rtMin != rtMax) {
Chris@25 2329 text += tr("Time:\t%1 - %2\n")
Chris@25 2330 .arg(rtMin.toText(true).c_str())
Chris@25 2331 .arg(rtMax.toText(true).c_str());
Chris@25 2332 } else {
Chris@25 2333 text += tr("Time:\t%1\n")
Chris@25 2334 .arg(rtMin.toText(true).c_str());
Chris@0 2335 }
Chris@0 2336
Chris@25 2337 if (freqMin != freqMax) {
Chris@65 2338 text += tr("%1Bin Frequency:\t%2 - %3 Hz\n%4Bin Pitch:\t%5 - %6\n")
Chris@65 2339 .arg(adjFreqText)
Chris@25 2340 .arg(freqMin)
Chris@25 2341 .arg(freqMax)
Chris@65 2342 .arg(adjPitchText)
Chris@65 2343 .arg(Pitch::getPitchLabelForFrequency(freqMin))
Chris@65 2344 .arg(Pitch::getPitchLabelForFrequency(freqMax));
Chris@65 2345 } else {
Chris@65 2346 text += tr("%1Bin Frequency:\t%2 Hz\n%3Bin Pitch:\t%4\n")
Chris@35 2347 .arg(adjFreqText)
Chris@25 2348 .arg(freqMin)
Chris@65 2349 .arg(adjPitchText)
Chris@65 2350 .arg(Pitch::getPitchLabelForFrequency(freqMin));
Chris@25 2351 }
Chris@25 2352
Chris@38 2353 if (haveValues) {
Chris@38 2354 float dbMin = AudioLevel::multiplier_to_dB(magMin);
Chris@38 2355 float dbMax = AudioLevel::multiplier_to_dB(magMax);
Chris@43 2356 QString dbMinString;
Chris@43 2357 QString dbMaxString;
Chris@43 2358 if (dbMin == AudioLevel::DB_FLOOR) {
Chris@43 2359 dbMinString = tr("-Inf");
Chris@43 2360 } else {
Chris@43 2361 dbMinString = QString("%1").arg(lrintf(dbMin));
Chris@43 2362 }
Chris@43 2363 if (dbMax == AudioLevel::DB_FLOOR) {
Chris@43 2364 dbMaxString = tr("-Inf");
Chris@43 2365 } else {
Chris@43 2366 dbMaxString = QString("%1").arg(lrintf(dbMax));
Chris@43 2367 }
Chris@25 2368 if (lrintf(dbMin) != lrintf(dbMax)) {
Chris@25 2369 text += tr("dB:\t%1 - %2").arg(lrintf(dbMin)).arg(lrintf(dbMax));
Chris@25 2370 } else {
Chris@25 2371 text += tr("dB:\t%1").arg(lrintf(dbMin));
Chris@25 2372 }
Chris@38 2373 if (phaseMin != phaseMax) {
Chris@38 2374 text += tr("\nPhase:\t%1 - %2").arg(phaseMin).arg(phaseMax);
Chris@38 2375 } else {
Chris@38 2376 text += tr("\nPhase:\t%1").arg(phaseMin);
Chris@38 2377 }
Chris@25 2378 }
Chris@25 2379
Chris@25 2380 return text;
Chris@0 2381 }
Chris@25 2382
Chris@0 2383 int
Chris@40 2384 SpectrogramLayer::getColourScaleWidth(QPainter &paint) const
Chris@40 2385 {
Chris@40 2386 int cw;
Chris@40 2387
Chris@40 2388 switch (m_colourScale) {
Chris@40 2389 default:
Chris@40 2390 case LinearColourScale:
Chris@40 2391 cw = paint.fontMetrics().width(QString("0.00"));
Chris@40 2392 break;
Chris@40 2393
Chris@40 2394 case MeterColourScale:
Chris@40 2395 case dBColourScale:
Chris@40 2396 cw = std::max(paint.fontMetrics().width(tr("-Inf")),
Chris@40 2397 paint.fontMetrics().width(tr("-90")));
Chris@40 2398 break;
Chris@40 2399
Chris@40 2400 case PhaseColourScale:
Chris@40 2401 cw = paint.fontMetrics().width(QString("-") + QChar(0x3c0));
Chris@40 2402 break;
Chris@40 2403 }
Chris@40 2404
Chris@40 2405 return cw;
Chris@40 2406 }
Chris@40 2407
Chris@40 2408 int
Chris@44 2409 SpectrogramLayer::getVerticalScaleWidth(View *v, QPainter &paint) const
Chris@0 2410 {
Chris@0 2411 if (!m_model || !m_model->isOK()) return 0;
Chris@0 2412
Chris@40 2413 int cw = getColourScaleWidth(paint);
Chris@40 2414
Chris@0 2415 int tw = paint.fontMetrics().width(QString("%1")
Chris@0 2416 .arg(m_maxFrequency > 0 ?
Chris@0 2417 m_maxFrequency - 1 :
Chris@0 2418 m_model->getSampleRate() / 2));
Chris@0 2419
Chris@0 2420 int fw = paint.fontMetrics().width(QString("43Hz"));
Chris@0 2421 if (tw < fw) tw = fw;
Chris@40 2422
Chris@40 2423 int tickw = (m_frequencyScale == LogFrequencyScale ? 10 : 4);
Chris@0 2424
Chris@40 2425 return cw + tickw + tw + 13;
Chris@0 2426 }
Chris@0 2427
Chris@0 2428 void
Chris@44 2429 SpectrogramLayer::paintVerticalScale(View *v, QPainter &paint, QRect rect) const
Chris@0 2430 {
Chris@0 2431 if (!m_model || !m_model->isOK()) {
Chris@0 2432 return;
Chris@0 2433 }
Chris@0 2434
Chris@0 2435 int h = rect.height(), w = rect.width();
Chris@0 2436
Chris@40 2437 int tickw = (m_frequencyScale == LogFrequencyScale ? 10 : 4);
Chris@40 2438 int pkw = (m_frequencyScale == LogFrequencyScale ? 10 : 0);
Chris@40 2439
Chris@0 2440 size_t bins = m_windowSize / 2;
Chris@0 2441 int sr = m_model->getSampleRate();
Chris@0 2442
Chris@0 2443 if (m_maxFrequency > 0) {
Chris@0 2444 bins = int((double(m_maxFrequency) * m_windowSize) / sr + 0.1);
Chris@0 2445 if (bins > m_windowSize / 2) bins = m_windowSize / 2;
Chris@0 2446 }
Chris@0 2447
Chris@40 2448 int cw = getColourScaleWidth(paint);
Chris@40 2449
Chris@0 2450 int py = -1;
Chris@0 2451 int textHeight = paint.fontMetrics().height();
Chris@0 2452 int toff = -textHeight + paint.fontMetrics().ascent() + 2;
Chris@0 2453
Chris@40 2454 if (m_cache && !m_cacheInvalid && h > textHeight * 2 + 10) { //!!! lock?
Chris@40 2455
Chris@40 2456 int ch = h - textHeight * 2 - 8;
Chris@40 2457 paint.drawRect(4, textHeight + 4, cw - 1, ch + 1);
Chris@40 2458
Chris@40 2459 QString top, bottom;
Chris@40 2460
Chris@40 2461 switch (m_colourScale) {
Chris@40 2462 default:
Chris@40 2463 case LinearColourScale:
Chris@40 2464 top = (m_normalizeColumns ? "1.0" : "0.02");
Chris@40 2465 bottom = (m_normalizeColumns ? "0.0" : "0.00");
Chris@40 2466 break;
Chris@40 2467
Chris@40 2468 case MeterColourScale:
Chris@40 2469 top = (m_normalizeColumns ? QString("0") :
Chris@40 2470 QString("%1").arg(int(AudioLevel::multiplier_to_dB(0.02))));
Chris@40 2471 bottom = QString("%1").
Chris@40 2472 arg(int(AudioLevel::multiplier_to_dB
Chris@40 2473 (AudioLevel::preview_to_multiplier(0, 255))));
Chris@40 2474 break;
Chris@40 2475
Chris@40 2476 case dBColourScale:
Chris@40 2477 top = "0";
Chris@40 2478 bottom = "-80";
Chris@40 2479 break;
Chris@40 2480
Chris@40 2481 case PhaseColourScale:
Chris@40 2482 top = QChar(0x3c0);
Chris@40 2483 bottom = "-" + top;
Chris@40 2484 break;
Chris@40 2485 }
Chris@40 2486
Chris@40 2487 paint.drawText((cw + 6 - paint.fontMetrics().width(top)) / 2,
Chris@40 2488 2 + textHeight + toff, top);
Chris@40 2489
Chris@40 2490 paint.drawText((cw + 6 - paint.fontMetrics().width(bottom)) / 2,
Chris@40 2491 h + toff - 3, bottom);
Chris@40 2492
Chris@40 2493 paint.save();
Chris@40 2494 paint.setBrush(Qt::NoBrush);
Chris@40 2495 for (int i = 0; i < ch; ++i) {
Chris@40 2496 int v = (i * 255) / ch + 1;
Chris@86 2497 paint.setPen(m_colourMap.getColour(v));
Chris@40 2498 paint.drawLine(5, 4 + textHeight + ch - i,
Chris@40 2499 cw + 2, 4 + textHeight + ch - i);
Chris@40 2500 }
Chris@40 2501 paint.restore();
Chris@40 2502 }
Chris@40 2503
Chris@40 2504 paint.drawLine(cw + 7, 0, cw + 7, h);
Chris@40 2505
Chris@0 2506 int bin = -1;
Chris@0 2507
Chris@44 2508 for (int y = 0; y < v->height(); ++y) {
Chris@0 2509
Chris@0 2510 float q0, q1;
Chris@44 2511 if (!getYBinRange(v, v->height() - y, q0, q1)) continue;
Chris@0 2512
Chris@0 2513 int vy;
Chris@0 2514
Chris@0 2515 if (int(q0) > bin) {
Chris@0 2516 vy = y;
Chris@0 2517 bin = int(q0);
Chris@0 2518 } else {
Chris@0 2519 continue;
Chris@0 2520 }
Chris@0 2521
Chris@40 2522 int freq = (sr * bin) / m_windowSize;
Chris@0 2523
Chris@0 2524 if (py >= 0 && (vy - py) < textHeight - 1) {
Chris@40 2525 if (m_frequencyScale == LinearFrequencyScale) {
Chris@40 2526 paint.drawLine(w - tickw, h - vy, w, h - vy);
Chris@40 2527 }
Chris@0 2528 continue;
Chris@0 2529 }
Chris@0 2530
Chris@0 2531 QString text = QString("%1").arg(freq);
Chris@40 2532 if (bin == 1) text = QString("%1Hz").arg(freq); // bin 0 is DC
Chris@40 2533 paint.drawLine(cw + 7, h - vy, w - pkw - 1, h - vy);
Chris@0 2534
Chris@0 2535 if (h - vy - textHeight >= -2) {
Chris@40 2536 int tx = w - 3 - paint.fontMetrics().width(text) - std::max(tickw, pkw);
Chris@0 2537 paint.drawText(tx, h - vy + toff, text);
Chris@0 2538 }
Chris@0 2539
Chris@0 2540 py = vy;
Chris@0 2541 }
Chris@40 2542
Chris@40 2543 if (m_frequencyScale == LogFrequencyScale) {
Chris@40 2544
Chris@40 2545 paint.drawLine(w - pkw - 1, 0, w - pkw - 1, h);
Chris@40 2546
Chris@40 2547 int sr = m_model->getSampleRate();//!!! lock?
Chris@40 2548 float minf = getEffectiveMinFrequency();
Chris@40 2549 float maxf = getEffectiveMaxFrequency();
Chris@40 2550
Chris@40 2551 int py = h;
Chris@40 2552 paint.setBrush(paint.pen().color());
Chris@40 2553
Chris@40 2554 for (int i = 0; i < 128; ++i) {
Chris@40 2555
Chris@40 2556 float f = Pitch::getFrequencyForPitch(i);
Chris@44 2557 int y = lrintf(v->getYForFrequency(f, minf, maxf, true));
Chris@40 2558 int n = (i % 12);
Chris@40 2559 if (n == 1 || n == 3 || n == 6 || n == 8 || n == 10) {
Chris@40 2560 // black notes
Chris@40 2561 paint.drawLine(w - pkw, y, w, y);
Chris@41 2562 int rh = ((py - y) / 4) * 2;
Chris@41 2563 if (rh < 2) rh = 2;
Chris@41 2564 paint.drawRect(w - pkw, y - (py-y)/4, pkw/2, rh);
Chris@40 2565 } else if (n == 0 || n == 5) {
Chris@40 2566 // C, A
Chris@40 2567 if (py < h) {
Chris@40 2568 paint.drawLine(w - pkw, (y + py) / 2, w, (y + py) / 2);
Chris@40 2569 }
Chris@40 2570 }
Chris@40 2571
Chris@40 2572 py = y;
Chris@40 2573 }
Chris@40 2574 }
Chris@0 2575 }
Chris@0 2576
Chris@6 2577 QString
Chris@6 2578 SpectrogramLayer::toXmlString(QString indent, QString extraAttributes) const
Chris@6 2579 {
Chris@6 2580 QString s;
Chris@6 2581
Chris@6 2582 s += QString("channel=\"%1\" "
Chris@6 2583 "windowSize=\"%2\" "
Chris@6 2584 "windowType=\"%3\" "
Chris@97 2585 "windowHopLevel=\"%4\" "
Chris@37 2586 "gain=\"%5\" "
Chris@37 2587 "threshold=\"%6\" ")
Chris@6 2588 .arg(m_channel)
Chris@6 2589 .arg(m_windowSize)
Chris@6 2590 .arg(m_windowType)
Chris@97 2591 .arg(m_windowHopLevel)
Chris@37 2592 .arg(m_gain)
Chris@37 2593 .arg(m_threshold);
Chris@37 2594
Chris@37 2595 s += QString("minFrequency=\"%1\" "
Chris@37 2596 "maxFrequency=\"%2\" "
Chris@37 2597 "colourScale=\"%3\" "
Chris@37 2598 "colourScheme=\"%4\" "
Chris@37 2599 "colourRotation=\"%5\" "
Chris@37 2600 "frequencyScale=\"%6\" "
Chris@37 2601 "binDisplay=\"%7\" "
Chris@37 2602 "normalizeColumns=\"%8\"")
Chris@37 2603 .arg(m_minFrequency)
Chris@6 2604 .arg(m_maxFrequency)
Chris@6 2605 .arg(m_colourScale)
Chris@6 2606 .arg(m_colourScheme)
Chris@37 2607 .arg(m_colourRotation)
Chris@35 2608 .arg(m_frequencyScale)
Chris@37 2609 .arg(m_binDisplay)
Chris@36 2610 .arg(m_normalizeColumns ? "true" : "false");
Chris@6 2611
Chris@6 2612 return Layer::toXmlString(indent, extraAttributes + " " + s);
Chris@6 2613 }
Chris@6 2614
Chris@11 2615 void
Chris@11 2616 SpectrogramLayer::setProperties(const QXmlAttributes &attributes)
Chris@11 2617 {
Chris@11 2618 bool ok = false;
Chris@11 2619
Chris@11 2620 int channel = attributes.value("channel").toInt(&ok);
Chris@11 2621 if (ok) setChannel(channel);
Chris@11 2622
Chris@11 2623 size_t windowSize = attributes.value("windowSize").toUInt(&ok);
Chris@11 2624 if (ok) setWindowSize(windowSize);
Chris@11 2625
Chris@11 2626 WindowType windowType = (WindowType)
Chris@11 2627 attributes.value("windowType").toInt(&ok);
Chris@11 2628 if (ok) setWindowType(windowType);
Chris@11 2629
Chris@97 2630 size_t windowHopLevel = attributes.value("windowHopLevel").toUInt(&ok);
Chris@97 2631 if (ok) setWindowHopLevel(windowHopLevel);
Chris@97 2632 else {
Chris@97 2633 size_t windowOverlap = attributes.value("windowOverlap").toUInt(&ok);
Chris@97 2634 // a percentage value
Chris@97 2635 if (ok) {
Chris@97 2636 if (windowOverlap == 0) setWindowHopLevel(0);
Chris@97 2637 else if (windowOverlap == 25) setWindowHopLevel(1);
Chris@97 2638 else if (windowOverlap == 50) setWindowHopLevel(2);
Chris@97 2639 else if (windowOverlap == 75) setWindowHopLevel(3);
Chris@97 2640 else if (windowOverlap == 90) setWindowHopLevel(4);
Chris@97 2641 }
Chris@97 2642 }
Chris@11 2643
Chris@11 2644 float gain = attributes.value("gain").toFloat(&ok);
Chris@11 2645 if (ok) setGain(gain);
Chris@11 2646
Chris@37 2647 float threshold = attributes.value("threshold").toFloat(&ok);
Chris@37 2648 if (ok) setThreshold(threshold);
Chris@37 2649
Chris@37 2650 size_t minFrequency = attributes.value("minFrequency").toUInt(&ok);
Chris@37 2651 if (ok) setMinFrequency(minFrequency);
Chris@37 2652
Chris@11 2653 size_t maxFrequency = attributes.value("maxFrequency").toUInt(&ok);
Chris@11 2654 if (ok) setMaxFrequency(maxFrequency);
Chris@11 2655
Chris@11 2656 ColourScale colourScale = (ColourScale)
Chris@11 2657 attributes.value("colourScale").toInt(&ok);
Chris@11 2658 if (ok) setColourScale(colourScale);
Chris@11 2659
Chris@11 2660 ColourScheme colourScheme = (ColourScheme)
Chris@11 2661 attributes.value("colourScheme").toInt(&ok);
Chris@11 2662 if (ok) setColourScheme(colourScheme);
Chris@11 2663
Chris@37 2664 int colourRotation = attributes.value("colourRotation").toInt(&ok);
Chris@37 2665 if (ok) setColourRotation(colourRotation);
Chris@37 2666
Chris@11 2667 FrequencyScale frequencyScale = (FrequencyScale)
Chris@11 2668 attributes.value("frequencyScale").toInt(&ok);
Chris@11 2669 if (ok) setFrequencyScale(frequencyScale);
Chris@35 2670
Chris@37 2671 BinDisplay binDisplay = (BinDisplay)
Chris@37 2672 attributes.value("binDisplay").toInt(&ok);
Chris@37 2673 if (ok) setBinDisplay(binDisplay);
Chris@36 2674
Chris@36 2675 bool normalizeColumns =
Chris@36 2676 (attributes.value("normalizeColumns").trimmed() == "true");
Chris@36 2677 setNormalizeColumns(normalizeColumns);
Chris@11 2678 }
Chris@11 2679
Chris@11 2680
Chris@0 2681 #ifdef INCLUDE_MOCFILES
Chris@0 2682 #include "SpectrogramLayer.moc.cpp"
Chris@0 2683 #endif
Chris@0 2684