annotate layer/SpectrogramLayer.cpp @ 38:beb801473743

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