annotate layer/SpectrogramLayer.cpp @ 77:fd348f36c0d3

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