annotate layer/WaveformLayer.cpp @ 454:e2a40fdadd8c

Various fixes: * Fix handling of HTTP redirects (avoiding crashes... I hope) * Fix failure to delete FFT models when a feature extraction model transformer was abandoned (also a cause of crashes in the past) * Fix deadlock when said transform was abandoned before its source model was ready because the session was being cleared (and so the source model would never be ready)
author Chris Cannam
date Fri, 28 Nov 2008 13:36:13 +0000
parents 590b899b7e45
children f4960f8ce798
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@182 7 This file copyright 2006 Chris Cannam and QMUL.
Chris@0 8
Chris@59 9 This program is free software; you can redistribute it and/or
Chris@59 10 modify it under the terms of the GNU General Public License as
Chris@59 11 published by the Free Software Foundation; either version 2 of the
Chris@59 12 License, or (at your option) any later version. See the file
Chris@59 13 COPYING included with this distribution for more information.
Chris@0 14 */
Chris@0 15
Chris@0 16 #include "WaveformLayer.h"
Chris@0 17
Chris@0 18 #include "base/AudioLevel.h"
Chris@128 19 #include "view/View.h"
Chris@0 20 #include "base/Profiler.h"
Chris@167 21 #include "base/RangeMapper.h"
Chris@376 22 #include "ColourDatabase.h"
Chris@0 23
Chris@0 24 #include <QPainter>
Chris@0 25 #include <QPixmap>
Chris@316 26 #include <QTextStream>
Chris@0 27
Chris@0 28 #include <iostream>
Chris@0 29 #include <cmath>
Chris@0 30
Chris@4 31 //#define DEBUG_WAVEFORM_PAINT 1
Chris@4 32
Chris@0 33 using std::cerr;
Chris@0 34 using std::endl;
Chris@0 35
Chris@44 36 WaveformLayer::WaveformLayer() :
Chris@287 37 SingleColourLayer(),
Chris@0 38 m_model(0),
Chris@0 39 m_gain(1.0f),
Chris@67 40 m_autoNormalize(false),
Chris@0 41 m_showMeans(true),
Chris@0 42 m_greyscale(true),
Chris@0 43 m_channelMode(SeparateChannels),
Chris@0 44 m_channel(-1),
Chris@0 45 m_scale(LinearScale),
Chris@0 46 m_aggressive(false),
Chris@0 47 m_cache(0),
Chris@0 48 m_cacheValid(false)
Chris@0 49 {
Chris@44 50
Chris@0 51 }
Chris@0 52
Chris@0 53 WaveformLayer::~WaveformLayer()
Chris@0 54 {
Chris@0 55 delete m_cache;
Chris@0 56 }
Chris@0 57
Chris@0 58 void
Chris@0 59 WaveformLayer::setModel(const RangeSummarisableTimeValueModel *model)
Chris@0 60 {
Chris@69 61 bool channelsChanged = false;
Chris@69 62 if (m_channel == -1) {
Chris@69 63 if (!m_model) {
Chris@69 64 if (model) {
Chris@69 65 channelsChanged = true;
Chris@69 66 }
Chris@69 67 } else {
Chris@69 68 if (model &&
Chris@69 69 m_model->getChannelCount() != model->getChannelCount()) {
Chris@69 70 channelsChanged = true;
Chris@69 71 }
Chris@69 72 }
Chris@69 73 }
Chris@69 74
Chris@0 75 m_model = model;
Chris@0 76 m_cacheValid = false;
Chris@0 77 if (!m_model || !m_model->isOK()) return;
Chris@0 78
Chris@320 79 connectSignals(m_model);
Chris@301 80
Chris@0 81 emit modelReplaced();
Chris@69 82
Chris@69 83 if (channelsChanged) emit layerParametersChanged();
Chris@0 84 }
Chris@0 85
Chris@0 86 Layer::PropertyList
Chris@0 87 WaveformLayer::getProperties() const
Chris@0 88 {
Chris@287 89 PropertyList list = SingleColourLayer::getProperties();
Chris@87 90 list.push_back("Scale");
Chris@87 91 list.push_back("Gain");
Chris@87 92 list.push_back("Normalize Visible Area");
Chris@68 93
Chris@68 94 if (m_model && m_model->getChannelCount() > 1 && m_channel == -1) {
Chris@87 95 list.push_back("Channels");
Chris@68 96 }
Chris@68 97
Chris@0 98 return list;
Chris@0 99 }
Chris@0 100
Chris@87 101 QString
Chris@87 102 WaveformLayer::getPropertyLabel(const PropertyName &name) const
Chris@87 103 {
Chris@87 104 if (name == "Scale") return tr("Scale");
Chris@87 105 if (name == "Gain") return tr("Gain");
Chris@87 106 if (name == "Normalize Visible Area") return tr("Normalize Visible Area");
Chris@87 107 if (name == "Channels") return tr("Channels");
Chris@287 108 return SingleColourLayer::getPropertyLabel(name);
Chris@87 109 }
Chris@87 110
Chris@335 111 QString
Chris@335 112 WaveformLayer::getPropertyIconName(const PropertyName &name) const
Chris@335 113 {
Chris@335 114 if (name == "Normalize Visible Area") return "normalise";
Chris@335 115 return "";
Chris@335 116 }
Chris@335 117
Chris@0 118 Layer::PropertyType
Chris@0 119 WaveformLayer::getPropertyType(const PropertyName &name) const
Chris@0 120 {
Chris@87 121 if (name == "Gain") return RangeProperty;
Chris@87 122 if (name == "Normalize Visible Area") return ToggleProperty;
Chris@87 123 if (name == "Channels") return ValueProperty;
Chris@87 124 if (name == "Scale") return ValueProperty;
Chris@287 125 return SingleColourLayer::getPropertyType(name);
Chris@0 126 }
Chris@0 127
Chris@0 128 QString
Chris@0 129 WaveformLayer::getPropertyGroupName(const PropertyName &name) const
Chris@0 130 {
Chris@87 131 if (name == "Gain" ||
Chris@87 132 name == "Normalize Visible Area" ||
Chris@87 133 name == "Scale") return tr("Scale");
Chris@0 134 return QString();
Chris@0 135 }
Chris@0 136
Chris@0 137 int
Chris@0 138 WaveformLayer::getPropertyRangeAndValue(const PropertyName &name,
Chris@216 139 int *min, int *max, int *deflt) const
Chris@0 140 {
Chris@216 141 int val = 0;
Chris@0 142
Chris@216 143 int garbage0, garbage1, garbage2;
Chris@56 144 if (!min) min = &garbage0;
Chris@56 145 if (!max) max = &garbage1;
Chris@216 146 if (!deflt) deflt = &garbage2;
Chris@10 147
Chris@87 148 if (name == "Gain") {
Chris@0 149
Chris@0 150 *min = -50;
Chris@0 151 *max = 50;
Chris@216 152 *deflt = 0;
Chris@0 153
Chris@216 154 val = lrint(log10(m_gain) * 20.0);
Chris@216 155 if (val < *min) val = *min;
Chris@216 156 if (val > *max) val = *max;
Chris@0 157
Chris@87 158 } else if (name == "Normalize Visible Area") {
Chris@67 159
Chris@216 160 val = (m_autoNormalize ? 1 : 0);
Chris@216 161 *deflt = 0;
Chris@67 162
Chris@87 163 } else if (name == "Channels") {
Chris@0 164
Chris@67 165 *min = 0;
Chris@67 166 *max = 2;
Chris@216 167 *deflt = 0;
Chris@216 168 if (m_channelMode == MixChannels) val = 1;
Chris@216 169 else if (m_channelMode == MergeChannels) val = 2;
Chris@216 170 else val = 0;
Chris@0 171
Chris@87 172 } else if (name == "Scale") {
Chris@0 173
Chris@0 174 *min = 0;
Chris@0 175 *max = 2;
Chris@216 176 *deflt = 0;
Chris@0 177
Chris@216 178 val = (int)m_scale;
Chris@0 179
Chris@0 180 } else {
Chris@287 181 val = SingleColourLayer::getPropertyRangeAndValue(name, min, max, deflt);
Chris@0 182 }
Chris@0 183
Chris@216 184 return val;
Chris@0 185 }
Chris@0 186
Chris@0 187 QString
Chris@0 188 WaveformLayer::getPropertyValueLabel(const PropertyName &name,
Chris@0 189 int value) const
Chris@0 190 {
Chris@87 191 if (name == "Scale") {
Chris@0 192 switch (value) {
Chris@0 193 default:
Chris@0 194 case 0: return tr("Linear");
Chris@0 195 case 1: return tr("Meter");
Chris@0 196 case 2: return tr("dB");
Chris@0 197 }
Chris@0 198 }
Chris@87 199 if (name == "Channels") {
Chris@67 200 switch (value) {
Chris@67 201 default:
Chris@67 202 case 0: return tr("Separate");
Chris@67 203 case 1: return tr("Mean");
Chris@67 204 case 2: return tr("Butterfly");
Chris@67 205 }
Chris@67 206 }
Chris@287 207 return SingleColourLayer::getPropertyValueLabel(name, value);
Chris@0 208 }
Chris@0 209
Chris@167 210 RangeMapper *
Chris@167 211 WaveformLayer::getNewPropertyRangeMapper(const PropertyName &name) const
Chris@167 212 {
Chris@167 213 if (name == "Gain") {
Chris@167 214 return new LinearRangeMapper(-50, 50, -25, 25, tr("dB"));
Chris@167 215 }
Chris@167 216 return 0;
Chris@167 217 }
Chris@167 218
Chris@0 219 void
Chris@0 220 WaveformLayer::setProperty(const PropertyName &name, int value)
Chris@0 221 {
Chris@87 222 if (name == "Gain") {
Chris@0 223 setGain(pow(10, float(value)/20.0));
Chris@87 224 } else if (name == "Normalize Visible Area") {
Chris@67 225 setAutoNormalize(value ? true : false);
Chris@87 226 } else if (name == "Channels") {
Chris@67 227 if (value == 1) setChannelMode(MixChannels);
Chris@67 228 else if (value == 2) setChannelMode(MergeChannels);
Chris@67 229 else setChannelMode(SeparateChannels);
Chris@87 230 } else if (name == "Scale") {
Chris@0 231 switch (value) {
Chris@0 232 default:
Chris@0 233 case 0: setScale(LinearScale); break;
Chris@0 234 case 1: setScale(MeterScale); break;
Chris@0 235 case 2: setScale(dBScale); break;
Chris@0 236 }
Chris@287 237 } else {
Chris@287 238 SingleColourLayer::setProperty(name, value);
Chris@0 239 }
Chris@0 240 }
Chris@0 241
Chris@0 242 void
Chris@67 243 WaveformLayer::setGain(float gain)
Chris@0 244 {
Chris@0 245 if (m_gain == gain) return;
Chris@0 246 m_gain = gain;
Chris@0 247 m_cacheValid = false;
Chris@0 248 emit layerParametersChanged();
Chris@133 249 emit verticalZoomChanged();
Chris@0 250 }
Chris@0 251
Chris@0 252 void
Chris@67 253 WaveformLayer::setAutoNormalize(bool autoNormalize)
Chris@67 254 {
Chris@67 255 if (m_autoNormalize == autoNormalize) return;
Chris@67 256 m_autoNormalize = autoNormalize;
Chris@67 257 m_cacheValid = false;
Chris@67 258 emit layerParametersChanged();
Chris@67 259 }
Chris@67 260
Chris@67 261 void
Chris@0 262 WaveformLayer::setShowMeans(bool showMeans)
Chris@0 263 {
Chris@0 264 if (m_showMeans == showMeans) return;
Chris@0 265 m_showMeans = showMeans;
Chris@0 266 m_cacheValid = false;
Chris@0 267 emit layerParametersChanged();
Chris@0 268 }
Chris@0 269
Chris@0 270 void
Chris@0 271 WaveformLayer::setUseGreyscale(bool useGreyscale)
Chris@0 272 {
Chris@0 273 if (m_greyscale == useGreyscale) return;
Chris@0 274 m_greyscale = useGreyscale;
Chris@0 275 m_cacheValid = false;
Chris@0 276 emit layerParametersChanged();
Chris@0 277 }
Chris@0 278
Chris@0 279 void
Chris@0 280 WaveformLayer::setChannelMode(ChannelMode channelMode)
Chris@0 281 {
Chris@0 282 if (m_channelMode == channelMode) return;
Chris@0 283 m_channelMode = channelMode;
Chris@0 284 m_cacheValid = false;
Chris@0 285 emit layerParametersChanged();
Chris@0 286 }
Chris@0 287
Chris@0 288 void
Chris@0 289 WaveformLayer::setChannel(int channel)
Chris@0 290 {
Chris@101 291 // std::cerr << "WaveformLayer::setChannel(" << channel << ")" << std::endl;
Chris@0 292
Chris@0 293 if (m_channel == channel) return;
Chris@0 294 m_channel = channel;
Chris@0 295 m_cacheValid = false;
Chris@0 296 emit layerParametersChanged();
Chris@0 297 }
Chris@0 298
Chris@0 299 void
Chris@0 300 WaveformLayer::setScale(Scale scale)
Chris@0 301 {
Chris@0 302 if (m_scale == scale) return;
Chris@0 303 m_scale = scale;
Chris@0 304 m_cacheValid = false;
Chris@0 305 emit layerParametersChanged();
Chris@0 306 }
Chris@0 307
Chris@0 308 void
Chris@0 309 WaveformLayer::setAggressiveCacheing(bool aggressive)
Chris@0 310 {
Chris@0 311 if (m_aggressive == aggressive) return;
Chris@0 312 m_aggressive = aggressive;
Chris@0 313 m_cacheValid = false;
Chris@0 314 emit layerParametersChanged();
Chris@0 315 }
Chris@0 316
Chris@0 317 int
Chris@115 318 WaveformLayer::getCompletion(View *) const
Chris@0 319 {
Chris@0 320 int completion = 100;
Chris@0 321 if (!m_model || !m_model->isOK()) return completion;
Chris@0 322 if (m_model->isReady(&completion)) return 100;
Chris@0 323 return completion;
Chris@0 324 }
Chris@0 325
Chris@79 326 bool
Chris@101 327 WaveformLayer::getValueExtents(float &min, float &max,
Chris@248 328 bool &, QString &unit) const
Chris@79 329 {
Chris@79 330 if (m_scale == LinearScale) {
Chris@79 331 min = 0.0;
Chris@79 332 max = 1.0;
Chris@79 333 unit = "V";
Chris@79 334 } else if (m_scale == MeterScale) {
Chris@79 335 return false; //!!!
Chris@79 336 } else {
Chris@79 337 min = AudioLevel::multiplier_to_dB(0.0);
Chris@79 338 max = AudioLevel::multiplier_to_dB(1.0);
Chris@79 339 unit = "dB";
Chris@79 340 }
Chris@79 341 return true;
Chris@79 342 }
Chris@79 343
Chris@0 344 int
Chris@0 345 WaveformLayer::dBscale(float sample, int m) const
Chris@0 346 {
Chris@67 347 if (sample < 0.0) return dBscale(-sample, m);
Chris@0 348 float dB = AudioLevel::multiplier_to_dB(sample);
Chris@0 349 if (dB < -50.0) return 0;
Chris@0 350 if (dB > 0.0) return m;
Chris@0 351 return int(((dB + 50.0) * m) / 50.0 + 0.1);
Chris@0 352 }
Chris@0 353
Chris@0 354 size_t
Chris@67 355 WaveformLayer::getChannelArrangement(size_t &min, size_t &max,
Chris@67 356 bool &merging, bool &mixing)
Chris@0 357 const
Chris@0 358 {
Chris@0 359 if (!m_model || !m_model->isOK()) return 0;
Chris@0 360
Chris@0 361 size_t channels = m_model->getChannelCount();
Chris@0 362 if (channels == 0) return 0;
Chris@0 363
Chris@0 364 size_t rawChannels = channels;
Chris@0 365
Chris@0 366 if (m_channel == -1) {
Chris@0 367 min = 0;
Chris@67 368 if (m_channelMode == MergeChannels ||
Chris@67 369 m_channelMode == MixChannels) {
Chris@0 370 max = 0;
Chris@0 371 channels = 1;
Chris@0 372 } else {
Chris@0 373 max = channels - 1;
Chris@0 374 }
Chris@0 375 } else {
Chris@0 376 min = m_channel;
Chris@0 377 max = m_channel;
Chris@0 378 rawChannels = 1;
Chris@0 379 channels = 1;
Chris@0 380 }
Chris@0 381
Chris@0 382 merging = (m_channelMode == MergeChannels && rawChannels > 1);
Chris@67 383 mixing = (m_channelMode == MixChannels && rawChannels > 1);
Chris@0 384
Chris@0 385 // std::cerr << "WaveformLayer::getChannelArrangement: min " << min << ", max " << max << ", merging " << merging << ", channels " << channels << std::endl;
Chris@0 386
Chris@0 387 return channels;
Chris@0 388 }
Chris@0 389
Chris@67 390 bool
Chris@67 391 WaveformLayer::isLayerScrollable(const View *) const
Chris@67 392 {
Chris@67 393 return !m_autoNormalize;
Chris@67 394 }
Chris@67 395
Chris@68 396 static float meterdbs[] = { -40, -30, -20, -15, -10,
Chris@68 397 -5, -3, -2, -1, -0.5, 0 };
Chris@68 398
Chris@365 399 bool
Chris@365 400 WaveformLayer::getSourceFramesForX(View *v, int x, size_t modelZoomLevel,
Chris@365 401 size_t &f0, size_t &f1) const
Chris@365 402 {
Chris@365 403 long viewFrame = v->getFrameForX(x);
Chris@365 404 if (viewFrame < 0) {
Chris@365 405 f0 = 0;
Chris@365 406 f1 = 0;
Chris@365 407 return false;
Chris@365 408 }
Chris@365 409
Chris@365 410 f0 = viewFrame;
Chris@365 411
Chris@365 412 f0 = f0 / modelZoomLevel;
Chris@365 413 f0 = f0 * modelZoomLevel;
Chris@365 414
Chris@365 415 viewFrame = v->getFrameForX(x + 1);
Chris@365 416
Chris@365 417 f1 = viewFrame;
Chris@365 418 f1 = f1 / modelZoomLevel;
Chris@365 419 f1 = f1 * modelZoomLevel;
Chris@365 420
Chris@365 421 return (f0 < m_model->getEndFrame());
Chris@365 422 }
Chris@365 423
Chris@365 424 float
Chris@365 425 WaveformLayer::getNormalizeGain(View *v, int channel) const
Chris@365 426 {
Chris@365 427 long startFrame = v->getStartFrame();
Chris@365 428 long endFrame = v->getEndFrame();
Chris@365 429
Chris@365 430 // Although a long for purposes of comparison against the view
Chris@365 431 // start and end frames, these are known to be non-negative
Chris@365 432 long modelStart = long(m_model->getStartFrame());
Chris@365 433 long modelEnd = long(m_model->getEndFrame());
Chris@365 434
Chris@365 435 size_t rangeStart, rangeEnd;
Chris@365 436
Chris@365 437 if (startFrame < modelStart) rangeStart = modelStart;
Chris@365 438 else rangeStart = startFrame;
Chris@365 439
Chris@365 440 if (endFrame < 0) rangeEnd = 0;
Chris@365 441 else if (endFrame > modelEnd) rangeEnd = modelEnd;
Chris@365 442 else rangeEnd = endFrame;
Chris@365 443
Chris@365 444 if (rangeEnd < rangeStart) rangeEnd = rangeStart;
Chris@365 445
Chris@365 446 RangeSummarisableTimeValueModel::Range range =
Chris@365 447 m_model->getSummary(channel, rangeStart, rangeEnd - rangeStart);
Chris@365 448
Chris@365 449 size_t minChannel = 0, maxChannel = 0;
Chris@365 450 bool mergingChannels = false, mixingChannels = false;
Chris@365 451
Chris@365 452 getChannelArrangement(minChannel, maxChannel,
Chris@365 453 mergingChannels, mixingChannels);
Chris@365 454
Chris@365 455 if (mergingChannels || mixingChannels) {
Chris@365 456 RangeSummarisableTimeValueModel::Range otherRange =
Chris@365 457 m_model->getSummary(1, rangeStart, rangeEnd - rangeStart);
Chris@386 458 range.setMax(std::max(range.max(), otherRange.max()));
Chris@386 459 range.setMin(std::min(range.min(), otherRange.min()));
Chris@386 460 range.setAbsmean(std::min(range.absmean(), otherRange.absmean()));
Chris@365 461 }
Chris@365 462
Chris@386 463 return 1.0 / std::max(fabsf(range.max()), fabsf(range.min()));
Chris@365 464 }
Chris@365 465
Chris@0 466 void
Chris@44 467 WaveformLayer::paint(View *v, QPainter &viewPainter, QRect rect) const
Chris@0 468 {
Chris@0 469 if (!m_model || !m_model->isOK()) {
Chris@0 470 return;
Chris@0 471 }
Chris@0 472
Chris@44 473 int zoomLevel = v->getZoomLevel();
Chris@0 474
Chris@2 475 #ifdef DEBUG_WAVEFORM_PAINT
Chris@0 476 Profiler profiler("WaveformLayer::paint", true);
Chris@0 477 std::cerr << "WaveformLayer::paint (" << rect.x() << "," << rect.y()
Chris@0 478 << ") [" << rect.width() << "x" << rect.height() << "]: zoom " << zoomLevel << ", start " << startFrame << std::endl;
Chris@2 479 #endif
Chris@0 480
Chris@0 481 size_t channels = 0, minChannel = 0, maxChannel = 0;
Chris@67 482 bool mergingChannels = false, mixingChannels = false;
Chris@0 483
Chris@67 484 channels = getChannelArrangement(minChannel, maxChannel,
Chris@67 485 mergingChannels, mixingChannels);
Chris@0 486 if (channels == 0) return;
Chris@0 487
Chris@44 488 int w = v->width();
Chris@44 489 int h = v->height();
Chris@0 490
Chris@0 491 bool ready = m_model->isReady();
Chris@0 492 QPainter *paint;
Chris@0 493
Chris@0 494 if (m_aggressive) {
Chris@0 495
Chris@214 496 #ifdef DEBUG_WAVEFORM_PAINT
Chris@214 497 std::cerr << "WaveformLayer::paint: aggressive is true" << std::endl;
Chris@214 498 #endif
Chris@214 499
Chris@0 500 if (m_cacheValid && (zoomLevel != m_cacheZoomLevel)) {
Chris@0 501 m_cacheValid = false;
Chris@0 502 }
Chris@0 503
Chris@214 504 if (!m_cache || m_cache->width() != w || m_cache->height() != h) {
Chris@214 505 #ifdef DEBUG_WAVEFORM_PAINT
Chris@214 506 if (m_cache) {
Chris@214 507 std::cerr << "WaveformLayer::paint: cache size " << m_cache->width() << "x" << m_cache->height() << " differs from view size " << w << "x" << h << ": regenerating aggressive cache" << std::endl;
Chris@214 508 }
Chris@214 509 #endif
Chris@214 510 delete m_cache;
Chris@214 511 m_cache = new QPixmap(w, h);
Chris@214 512 m_cacheValid = false;
Chris@214 513 }
Chris@214 514
Chris@0 515 if (m_cacheValid) {
Chris@0 516 viewPainter.drawPixmap(rect, *m_cache, rect);
Chris@0 517 return;
Chris@0 518 }
Chris@0 519
Chris@0 520 paint = new QPainter(m_cache);
Chris@0 521
Chris@0 522 paint->setPen(Qt::NoPen);
Chris@287 523 paint->setBrush(getBackgroundQColor(v));
Chris@0 524 paint->drawRect(rect);
Chris@0 525
Chris@287 526 paint->setPen(getForegroundQColor(v));
Chris@0 527 paint->setBrush(Qt::NoBrush);
Chris@0 528
Chris@0 529 } else {
Chris@0 530 paint = &viewPainter;
Chris@0 531 }
Chris@0 532
Chris@28 533 paint->setRenderHint(QPainter::Antialiasing, false);
Chris@28 534
Chris@0 535 int x0 = 0, x1 = w - 1;
Chris@0 536 int y0 = 0, y1 = h - 1;
Chris@0 537
Chris@0 538 x0 = rect.left();
Chris@0 539 x1 = rect.right();
Chris@0 540 y0 = rect.top();
Chris@0 541 y1 = rect.bottom();
Chris@0 542
Chris@28 543 if (x0 > 0) --x0;
Chris@44 544 if (x1 < v->width()) ++x1;
Chris@28 545
Chris@365 546 // Our zoom level may differ from that at which the underlying
Chris@365 547 // model has its blocks.
Chris@302 548
Chris@365 549 // Each pixel within our visible range must always draw from
Chris@365 550 // exactly the same set of underlying audio frames, no matter what
Chris@365 551 // the range being drawn is. And that set of underlying frames
Chris@365 552 // must remain the same when we scroll one or more pixels left or
Chris@365 553 // right.
Chris@365 554
Chris@365 555 size_t modelZoomLevel = m_model->getSummaryBlockSize(zoomLevel);
Chris@365 556
Chris@365 557 size_t frame0;
Chris@365 558 size_t frame1;
Chris@365 559 size_t spare;
Chris@365 560
Chris@365 561 getSourceFramesForX(v, x0, modelZoomLevel, frame0, spare);
Chris@365 562 getSourceFramesForX(v, x1, modelZoomLevel, spare, frame1);
Chris@365 563
Chris@4 564 #ifdef DEBUG_WAVEFORM_PAINT
Chris@365 565 std::cerr << "Painting waveform from " << frame0 << " to " << frame1 << " (" << (x1-x0+1) << " pixels at zoom " << zoomLevel << " and model zoom " << modelZoomLevel << ")" << std::endl;
Chris@4 566 #endif
Chris@0 567
Chris@200 568 RangeSummarisableTimeValueModel::RangeBlock *ranges =
Chris@200 569 new RangeSummarisableTimeValueModel::RangeBlock;
Chris@200 570
Chris@200 571 RangeSummarisableTimeValueModel::RangeBlock *otherChannelRanges = 0;
Chris@0 572 RangeSummarisableTimeValueModel::Range range;
Chris@287 573
Chris@287 574 QColor baseColour = getBaseQColor();
Chris@287 575 std::vector<QColor> greys = getPartialShades(v);
Chris@0 576
Chris@285 577 QColor midColour = baseColour;
Chris@0 578 if (midColour == Qt::black) {
Chris@0 579 midColour = Qt::gray;
Chris@44 580 } else if (v->hasLightBackground()) {
Chris@0 581 midColour = midColour.light(150);
Chris@0 582 } else {
Chris@0 583 midColour = midColour.light(50);
Chris@0 584 }
Chris@0 585
Chris@67 586 while (m_effectiveGains.size() <= maxChannel) {
Chris@67 587 m_effectiveGains.push_back(m_gain);
Chris@67 588 }
Chris@67 589
Chris@0 590 for (size_t ch = minChannel; ch <= maxChannel; ++ch) {
Chris@0 591
Chris@0 592 int prevRangeBottom = -1, prevRangeTop = -1;
Chris@285 593 QColor prevRangeBottomColour = baseColour, prevRangeTopColour = baseColour;
Chris@0 594
Chris@67 595 m_effectiveGains[ch] = m_gain;
Chris@67 596
Chris@67 597 if (m_autoNormalize) {
Chris@365 598 m_effectiveGains[ch] = getNormalizeGain(v, ch);
Chris@67 599 }
Chris@67 600
Chris@67 601 float gain = m_effectiveGains[ch];
Chris@67 602
Chris@68 603 int m = (h / channels) / 2;
Chris@68 604 int my = m + (((ch - minChannel) * h) / channels);
Chris@68 605
Chris@68 606 // std::cerr << "ch = " << ch << ", channels = " << channels << ", m = " << m << ", my = " << my << ", h = " << h << std::endl;
Chris@68 607
Chris@68 608 if (my - m > y1 || my + m < y0) continue;
Chris@68 609
Chris@68 610 if ((m_scale == dBScale || m_scale == MeterScale) &&
Chris@68 611 m_channelMode != MergeChannels) {
Chris@68 612 m = (h / channels);
Chris@68 613 my = m + (((ch - minChannel) * h) / channels);
Chris@68 614 }
Chris@68 615
Chris@295 616 paint->setPen(greys[1]);
Chris@68 617 paint->drawLine(x0, my, x1, my);
Chris@68 618
Chris@68 619 int n = 10;
Chris@68 620 int py = -1;
Chris@68 621
Chris@195 622 if (v->hasLightBackground() &&
Chris@195 623 v->getViewManager() &&
Chris@195 624 v->getViewManager()->shouldShowScaleGuides()) {
Chris@68 625
Chris@68 626 paint->setPen(QColor(240, 240, 240));
Chris@68 627
Chris@68 628 for (int i = 1; i < n; ++i) {
Chris@68 629
Chris@68 630 float val = 0.0, nval = 0.0;
Chris@68 631
Chris@68 632 switch (m_scale) {
Chris@68 633
Chris@68 634 case LinearScale:
Chris@68 635 val = (i * gain) / n;
Chris@68 636 if (i > 0) nval = -val;
Chris@68 637 break;
Chris@68 638
Chris@68 639 case MeterScale:
Chris@68 640 val = AudioLevel::dB_to_multiplier(meterdbs[i]) * gain;
Chris@68 641 break;
Chris@68 642
Chris@68 643 case dBScale:
Chris@68 644 val = AudioLevel::dB_to_multiplier(-(10*n) + i * 10) * gain;
Chris@68 645 break;
Chris@68 646 }
Chris@68 647
Chris@68 648 if (val < -1.0 || val > 1.0) continue;
Chris@68 649
Chris@274 650 int y = getYForValue(v, val, ch);
Chris@68 651
Chris@68 652 if (py >= 0 && abs(y - py) < 10) continue;
Chris@68 653 else py = y;
Chris@68 654
Chris@68 655 int ny = y;
Chris@68 656 if (nval != 0.0) {
Chris@274 657 ny = getYForValue(v, nval, ch);
Chris@68 658 }
Chris@68 659
Chris@68 660 paint->drawLine(x0, y, x1, y);
Chris@68 661 if (ny != y) {
Chris@68 662 paint->drawLine(x0, ny, x1, ny);
Chris@68 663 }
Chris@68 664 }
Chris@68 665 }
Chris@365 666
Chris@365 667 m_model->getSummaries(ch, frame0, frame1 - frame0,
Chris@365 668 *ranges, modelZoomLevel);
Chris@68 669
Chris@365 670 #ifdef DEBUG_WAVEFORM_PAINT
Chris@365 671 std::cerr << ranges->size() << " ranges from " << frame0 << " to " << frame1 << std::endl;
Chris@365 672 #endif
Chris@68 673
Chris@68 674 if (mergingChannels || mixingChannels) {
Chris@71 675 if (m_model->getChannelCount() > 1) {
Chris@200 676 if (!otherChannelRanges) {
Chris@200 677 otherChannelRanges =
Chris@200 678 new RangeSummarisableTimeValueModel::RangeBlock;
Chris@200 679 }
Chris@302 680 m_model->getSummaries
Chris@365 681 (1, frame0, frame1 - frame0, *otherChannelRanges,
Chris@200 682 modelZoomLevel);
Chris@71 683 } else {
Chris@200 684 if (otherChannelRanges != ranges) delete otherChannelRanges;
Chris@71 685 otherChannelRanges = ranges;
Chris@71 686 }
Chris@68 687 }
Chris@68 688
Chris@0 689 for (int x = x0; x <= x1; ++x) {
Chris@0 690
Chris@0 691 range = RangeSummarisableTimeValueModel::Range();
Chris@0 692
Chris@365 693 size_t f0, f1;
Chris@365 694 if (!getSourceFramesForX(v, x, modelZoomLevel, f0, f1)) continue;
Chris@365 695 f1 = f1 - 1;
Chris@365 696
Chris@365 697 if (f0 < frame0) {
Chris@365 698 std::cerr << "ERROR: WaveformLayer::paint: pixel " << x << " has f0 = " << f0 << " which is less than range frame0 " << frame0 << " for x0 = " << x0 << std::endl;
Chris@365 699 continue;
Chris@302 700 }
Chris@0 701
Chris@365 702 size_t i0 = (f0 - frame0) / modelZoomLevel;
Chris@365 703 size_t i1 = (f1 - frame0) / modelZoomLevel;
Chris@362 704
Chris@365 705 #ifdef DEBUG_WAVEFORM_PAINT
Chris@365 706 std::cerr << "WaveformLayer::paint: pixel " << x << ": i0 " << i0 << " (f " << f0 << "), i1 " << i1 << " (f " << f1 << ")" << std::endl;
Chris@365 707 #endif
Chris@0 708
Chris@365 709 if (i1 > i0 + 1) {
Chris@365 710 std::cerr << "WaveformLayer::paint: ERROR: i1 " << i1 << " > i0 " << i0 << " plus one (zoom = " << zoomLevel << ", model zoom = " << modelZoomLevel << ")" << std::endl;
Chris@365 711 }
Chris@362 712
Chris@365 713 if (ranges && i0 < ranges->size()) {
Chris@362 714
Chris@365 715 range = (*ranges)[i0];
Chris@0 716
Chris@365 717 if (i1 > i0 && i1 < ranges->size()) {
Chris@386 718 range.setMax(std::max(range.max(), (*ranges)[i1].max()));
Chris@386 719 range.setMin(std::min(range.min(), (*ranges)[i1].min()));
Chris@386 720 range.setAbsmean((range.absmean() + (*ranges)[i1].absmean()) / 2);
Chris@0 721 }
Chris@0 722
Chris@0 723 } else {
Chris@0 724 continue;
Chris@0 725 }
Chris@0 726
Chris@0 727 int rangeBottom = 0, rangeTop = 0, meanBottom = 0, meanTop = 0;
Chris@0 728
Chris@0 729 if (mergingChannels) {
Chris@0 730
Chris@365 731 if (otherChannelRanges && i0 < otherChannelRanges->size()) {
Chris@0 732
Chris@386 733 range.setMax(fabsf(range.max()));
Chris@386 734 range.setMin(-fabsf((*otherChannelRanges)[i0].max()));
Chris@386 735 range.setAbsmean
Chris@386 736 ((range.absmean() +
Chris@386 737 (*otherChannelRanges)[i0].absmean()) / 2);
Chris@0 738
Chris@365 739 if (i1 > i0 && i1 < otherChannelRanges->size()) {
Chris@0 740 // let's not concern ourselves about the mean
Chris@386 741 range.setMin
Chris@386 742 (std::min
Chris@386 743 (range.min(),
Chris@386 744 -fabsf((*otherChannelRanges)[i1].max())));
Chris@0 745 }
Chris@0 746 }
Chris@67 747
Chris@67 748 } else if (mixingChannels) {
Chris@67 749
Chris@365 750 if (otherChannelRanges && i0 < otherChannelRanges->size()) {
Chris@67 751
Chris@386 752 range.setMax((range.max() + (*otherChannelRanges)[i0].max()) / 2);
Chris@386 753 range.setMin((range.min() + (*otherChannelRanges)[i0].min()) / 2);
Chris@386 754 range.setAbsmean((range.absmean() + (*otherChannelRanges)[i0].absmean()) / 2);
Chris@67 755 }
Chris@67 756 }
Chris@0 757
Chris@0 758 int greyLevels = 1;
Chris@0 759 if (m_greyscale && (m_scale == LinearScale)) greyLevels = 4;
Chris@0 760
Chris@0 761 switch (m_scale) {
Chris@0 762
Chris@0 763 case LinearScale:
Chris@386 764 rangeBottom = int( m * greyLevels * range.min() * gain);
Chris@386 765 rangeTop = int( m * greyLevels * range.max() * gain);
Chris@386 766 meanBottom = int(-m * range.absmean() * gain);
Chris@386 767 meanTop = int( m * range.absmean() * gain);
Chris@0 768 break;
Chris@0 769
Chris@0 770 case dBScale:
Chris@67 771 if (!mergingChannels) {
Chris@386 772 int db0 = dBscale(range.min() * gain, m);
Chris@386 773 int db1 = dBscale(range.max() * gain, m);
Chris@67 774 rangeTop = std::max(db0, db1);
Chris@67 775 meanTop = std::min(db0, db1);
Chris@67 776 if (mixingChannels) rangeBottom = meanTop;
Chris@386 777 else rangeBottom = dBscale(range.absmean() * gain, m);
Chris@67 778 meanBottom = rangeBottom;
Chris@67 779 } else {
Chris@386 780 rangeBottom = -dBscale(range.min() * gain, m * greyLevels);
Chris@386 781 rangeTop = dBscale(range.max() * gain, m * greyLevels);
Chris@386 782 meanBottom = -dBscale(range.absmean() * gain, m);
Chris@386 783 meanTop = dBscale(range.absmean() * gain, m);
Chris@67 784 }
Chris@0 785 break;
Chris@0 786
Chris@0 787 case MeterScale:
Chris@67 788 if (!mergingChannels) {
Chris@386 789 int r0 = abs(AudioLevel::multiplier_to_preview(range.min() * gain, m));
Chris@386 790 int r1 = abs(AudioLevel::multiplier_to_preview(range.max() * gain, m));
Chris@67 791 rangeTop = std::max(r0, r1);
Chris@67 792 meanTop = std::min(r0, r1);
Chris@67 793 if (mixingChannels) rangeBottom = meanTop;
Chris@386 794 else rangeBottom = AudioLevel::multiplier_to_preview(range.absmean() * gain, m);
Chris@67 795 meanBottom = rangeBottom;
Chris@67 796 } else {
Chris@386 797 rangeBottom = -AudioLevel::multiplier_to_preview(range.min() * gain, m * greyLevels);
Chris@386 798 rangeTop = AudioLevel::multiplier_to_preview(range.max() * gain, m * greyLevels);
Chris@386 799 meanBottom = -AudioLevel::multiplier_to_preview(range.absmean() * gain, m);
Chris@386 800 meanTop = AudioLevel::multiplier_to_preview(range.absmean() * gain, m);
Chris@67 801 }
Chris@67 802 break;
Chris@0 803 }
Chris@0 804
Chris@27 805 rangeBottom = my * greyLevels - rangeBottom;
Chris@27 806 rangeTop = my * greyLevels - rangeTop;
Chris@27 807 meanBottom = my - meanBottom;
Chris@27 808 meanTop = my - meanTop;
Chris@27 809
Chris@27 810 int topFill = (rangeTop % greyLevels);
Chris@27 811 if (topFill > 0) topFill = greyLevels - topFill;
Chris@27 812
Chris@27 813 int bottomFill = (rangeBottom % greyLevels);
Chris@27 814
Chris@0 815 rangeTop = rangeTop / greyLevels;
Chris@0 816 rangeBottom = rangeBottom / greyLevels;
Chris@0 817
Chris@0 818 bool clipped = false;
Chris@27 819
Chris@27 820 if (rangeTop < my - m) { rangeTop = my - m; }
Chris@27 821 if (rangeTop > my + m) { rangeTop = my + m; }
Chris@27 822 if (rangeBottom < my - m) { rangeBottom = my - m; }
Chris@27 823 if (rangeBottom > my + m) { rangeBottom = my + m; }
Chris@27 824
Chris@386 825 if (range.max() <= -1.0 ||
Chris@386 826 range.max() >= 1.0) clipped = true;
Chris@0 827
Chris@0 828 if (meanBottom > rangeBottom) meanBottom = rangeBottom;
Chris@0 829 if (meanTop < rangeTop) meanTop = rangeTop;
Chris@0 830
Chris@0 831 bool drawMean = m_showMeans;
Chris@0 832 if (meanTop == rangeTop) {
Chris@0 833 if (meanTop < meanBottom) ++meanTop;
Chris@0 834 else drawMean = false;
Chris@0 835 }
Chris@67 836 if (meanBottom == rangeBottom && m_scale == LinearScale) {
Chris@0 837 if (meanBottom > meanTop) --meanBottom;
Chris@0 838 else drawMean = false;
Chris@0 839 }
Chris@0 840
Chris@0 841 if (x != x0 && prevRangeBottom != -1) {
Chris@0 842 if (prevRangeBottom > rangeBottom &&
Chris@0 843 prevRangeTop > rangeBottom) {
Chris@28 844 // paint->setPen(midColour);
Chris@285 845 paint->setPen(baseColour);
Chris@0 846 paint->drawLine(x-1, prevRangeTop, x, rangeBottom);
Chris@28 847 paint->setPen(prevRangeTopColour);
Chris@0 848 paint->drawPoint(x-1, prevRangeTop);
Chris@0 849 } else if (prevRangeBottom < rangeTop &&
Chris@0 850 prevRangeTop < rangeTop) {
Chris@28 851 // paint->setPen(midColour);
Chris@285 852 paint->setPen(baseColour);
Chris@0 853 paint->drawLine(x-1, prevRangeBottom, x, rangeTop);
Chris@28 854 paint->setPen(prevRangeBottomColour);
Chris@0 855 paint->drawPoint(x-1, prevRangeBottom);
Chris@0 856 }
Chris@0 857 }
Chris@0 858
Chris@0 859 if (ready) {
Chris@67 860 if (clipped /*!!! ||
Chris@386 861 range.min() * gain <= -1.0 ||
Chris@386 862 range.max() * gain >= 1.0 */) {
Chris@285 863 paint->setPen(Qt::red); //!!! getContrastingColour
Chris@0 864 } else {
Chris@285 865 paint->setPen(baseColour);
Chris@0 866 }
Chris@0 867 } else {
Chris@0 868 paint->setPen(midColour);
Chris@0 869 }
Chris@0 870
Chris@0 871 paint->drawLine(x, rangeBottom, x, rangeTop);
Chris@0 872
Chris@285 873 prevRangeTopColour = baseColour;
Chris@285 874 prevRangeBottomColour = baseColour;
Chris@28 875
Chris@0 876 if (m_greyscale && (m_scale == LinearScale) && ready) {
Chris@0 877 if (!clipped) {
Chris@0 878 if (rangeTop < rangeBottom) {
Chris@0 879 if (topFill > 0 &&
Chris@0 880 (!drawMean || (rangeTop < meanTop - 1))) {
Chris@0 881 paint->setPen(greys[topFill - 1]);
Chris@27 882 paint->drawPoint(x, rangeTop);
Chris@28 883 prevRangeTopColour = greys[topFill - 1];
Chris@0 884 }
Chris@0 885 if (bottomFill > 0 &&
Chris@0 886 (!drawMean || (rangeBottom > meanBottom + 1))) {
Chris@0 887 paint->setPen(greys[bottomFill - 1]);
Chris@27 888 paint->drawPoint(x, rangeBottom);
Chris@28 889 prevRangeBottomColour = greys[bottomFill - 1];
Chris@0 890 }
Chris@0 891 }
Chris@0 892 }
Chris@0 893 }
Chris@0 894
Chris@0 895 if (drawMean) {
Chris@0 896 paint->setPen(midColour);
Chris@0 897 paint->drawLine(x, meanBottom, x, meanTop);
Chris@0 898 }
Chris@0 899
Chris@0 900 prevRangeBottom = rangeBottom;
Chris@0 901 prevRangeTop = rangeTop;
Chris@0 902 }
Chris@0 903 }
Chris@0 904
Chris@0 905 if (m_aggressive) {
Chris@41 906
Chris@44 907 if (ready && rect == v->rect()) {
Chris@0 908 m_cacheValid = true;
Chris@0 909 m_cacheZoomLevel = zoomLevel;
Chris@0 910 }
Chris@0 911 paint->end();
Chris@0 912 delete paint;
Chris@0 913 viewPainter.drawPixmap(rect, *m_cache, rect);
Chris@0 914 }
Chris@200 915
Chris@200 916 if (otherChannelRanges != ranges) delete otherChannelRanges;
Chris@200 917 delete ranges;
Chris@0 918 }
Chris@0 919
Chris@25 920 QString
Chris@44 921 WaveformLayer::getFeatureDescription(View *v, QPoint &pos) const
Chris@25 922 {
Chris@25 923 int x = pos.x();
Chris@25 924
Chris@25 925 if (!m_model || !m_model->isOK()) return "";
Chris@25 926
Chris@365 927 int zoomLevel = v->getZoomLevel();
Chris@25 928
Chris@365 929 size_t modelZoomLevel = m_model->getSummaryBlockSize(zoomLevel);
Chris@365 930
Chris@365 931 size_t f0, f1;
Chris@365 932 if (!getSourceFramesForX(v, x, modelZoomLevel, f0, f1)) return "";
Chris@25 933
Chris@25 934 QString text;
Chris@25 935
Chris@25 936 RealTime rt0 = RealTime::frame2RealTime(f0, m_model->getSampleRate());
Chris@25 937 RealTime rt1 = RealTime::frame2RealTime(f1, m_model->getSampleRate());
Chris@25 938
Chris@25 939 if (f1 != f0 + 1 && (rt0.sec != rt1.sec || rt0.msec() != rt1.msec())) {
Chris@25 940 text += tr("Time:\t%1 - %2")
Chris@25 941 .arg(rt0.toText(true).c_str())
Chris@25 942 .arg(rt1.toText(true).c_str());
Chris@25 943 } else {
Chris@25 944 text += tr("Time:\t%1")
Chris@25 945 .arg(rt0.toText(true).c_str());
Chris@25 946 }
Chris@25 947
Chris@25 948 size_t channels = 0, minChannel = 0, maxChannel = 0;
Chris@67 949 bool mergingChannels = false, mixingChannels = false;
Chris@25 950
Chris@67 951 channels = getChannelArrangement(minChannel, maxChannel,
Chris@67 952 mergingChannels, mixingChannels);
Chris@25 953 if (channels == 0) return "";
Chris@25 954
Chris@25 955 for (size_t ch = minChannel; ch <= maxChannel; ++ch) {
Chris@25 956
Chris@44 957 size_t blockSize = v->getZoomLevel();
Chris@200 958 RangeSummarisableTimeValueModel::RangeBlock ranges;
Chris@302 959 m_model->getSummaries(ch, f0, f1 - f0, ranges, blockSize);
Chris@25 960
Chris@25 961 if (ranges.empty()) continue;
Chris@25 962
Chris@25 963 RangeSummarisableTimeValueModel::Range range = ranges[0];
Chris@25 964
Chris@25 965 QString label = tr("Level:");
Chris@25 966 if (minChannel != maxChannel) {
Chris@25 967 if (ch == 0) label = tr("Left:");
Chris@25 968 else if (ch == 1) label = tr("Right:");
Chris@25 969 else label = tr("Channel %1").arg(ch + 1);
Chris@25 970 }
Chris@25 971
Chris@76 972 bool singleValue = false;
Chris@76 973 float min, max;
Chris@76 974
Chris@386 975 if (fabs(range.min()) < 0.01) {
Chris@386 976 min = range.min();
Chris@386 977 max = range.max();
Chris@76 978 singleValue = (min == max);
Chris@76 979 } else {
Chris@386 980 int imin = int(range.min() * 1000);
Chris@386 981 int imax = int(range.max() * 1000);
Chris@76 982 singleValue = (imin == imax);
Chris@76 983 min = float(imin)/1000;
Chris@76 984 max = float(imax)/1000;
Chris@76 985 }
Chris@76 986
Chris@386 987 int db = int(AudioLevel::multiplier_to_dB(std::max(fabsf(range.min()),
Chris@386 988 fabsf(range.max())))
Chris@25 989 * 100);
Chris@25 990
Chris@76 991 if (!singleValue) {
Chris@25 992 text += tr("\n%1\t%2 - %3 (%4 dB peak)")
Chris@76 993 .arg(label).arg(min).arg(max).arg(float(db)/100);
Chris@25 994 } else {
Chris@25 995 text += tr("\n%1\t%2 (%3 dB peak)")
Chris@76 996 .arg(label).arg(min).arg(float(db)/100);
Chris@25 997 }
Chris@25 998 }
Chris@25 999
Chris@25 1000 return text;
Chris@25 1001 }
Chris@25 1002
Chris@0 1003 int
Chris@274 1004 WaveformLayer::getYForValue(const View *v, float value, size_t channel) const
Chris@68 1005 {
Chris@274 1006 size_t channels = 0, minChannel = 0, maxChannel = 0;
Chris@274 1007 bool mergingChannels = false, mixingChannels = false;
Chris@274 1008
Chris@274 1009 channels = getChannelArrangement(minChannel, maxChannel,
Chris@274 1010 mergingChannels, mixingChannels);
Chris@274 1011
Chris@68 1012 if (maxChannel < minChannel || channel < minChannel) return 0;
Chris@68 1013
Chris@68 1014 int h = v->height();
Chris@68 1015 int m = (h / channels) / 2;
Chris@68 1016
Chris@68 1017 if ((m_scale == dBScale || m_scale == MeterScale) &&
Chris@68 1018 m_channelMode != MergeChannels) {
Chris@68 1019 m = (h / channels);
Chris@68 1020 }
Chris@68 1021
Chris@274 1022 int my = m + (((channel - minChannel) * h) / channels);
Chris@274 1023
Chris@68 1024 int vy = 0;
Chris@68 1025
Chris@274 1026 switch (m_scale) {
Chris@68 1027
Chris@68 1028 case LinearScale:
Chris@68 1029 vy = int(m * value);
Chris@68 1030 break;
Chris@68 1031
Chris@68 1032 case MeterScale:
Chris@68 1033 vy = AudioLevel::multiplier_to_preview(value, m);
Chris@68 1034 break;
Chris@68 1035
Chris@68 1036 case dBScale:
Chris@68 1037 vy = dBscale(value, m);
Chris@68 1038 break;
Chris@68 1039 }
Chris@68 1040
Chris@324 1041 // std::cerr << "mergingChannels= " << mergingChannels << ", channel = " << channel << ", value = " << value << ", vy = " << vy << std::endl;
Chris@324 1042
Chris@68 1043 return my - vy;
Chris@68 1044 }
Chris@68 1045
Chris@261 1046 float
Chris@274 1047 WaveformLayer::getValueForY(const View *v, int y, size_t &channel) const
Chris@261 1048 {
Chris@274 1049 size_t channels = 0, minChannel = 0, maxChannel = 0;
Chris@274 1050 bool mergingChannels = false, mixingChannels = false;
Chris@274 1051
Chris@274 1052 channels = getChannelArrangement(minChannel, maxChannel,
Chris@274 1053 mergingChannels, mixingChannels);
Chris@274 1054
Chris@261 1055 if (maxChannel < minChannel) return 0;
Chris@261 1056
Chris@261 1057 int h = v->height();
Chris@261 1058 int m = (h / channels) / 2;
Chris@261 1059
Chris@261 1060 if ((m_scale == dBScale || m_scale == MeterScale) &&
Chris@261 1061 m_channelMode != MergeChannels) {
Chris@261 1062 m = (h / channels);
Chris@261 1063 }
Chris@274 1064
Chris@274 1065 channel = (y * channels) / h + minChannel;
Chris@261 1066
Chris@261 1067 int my = m + (((channel - minChannel) * h) / channels);
Chris@261 1068
Chris@262 1069 int vy = my - y;
Chris@261 1070 float value = 0;
Chris@274 1071 float thresh = -50.f;
Chris@261 1072
Chris@274 1073 switch (m_scale) {
Chris@261 1074
Chris@261 1075 case LinearScale:
Chris@261 1076 value = float(vy) / m;
Chris@261 1077 break;
Chris@261 1078
Chris@261 1079 case MeterScale:
Chris@261 1080 value = AudioLevel::preview_to_multiplier(vy, m);
Chris@261 1081 break;
Chris@261 1082
Chris@261 1083 case dBScale:
Chris@274 1084 value = (-thresh * float(vy)) / m + thresh;
Chris@274 1085 value = AudioLevel::dB_to_multiplier(value);
Chris@261 1086 break;
Chris@261 1087 }
Chris@261 1088
Chris@274 1089 return value / m_gain;
Chris@261 1090 }
Chris@261 1091
Chris@261 1092 bool
Chris@267 1093 WaveformLayer::getYScaleValue(const View *v, int y,
Chris@261 1094 float &value, QString &unit) const
Chris@261 1095 {
Chris@274 1096 size_t channel;
Chris@261 1097
Chris@274 1098 value = getValueForY(v, y, channel);
Chris@261 1099
Chris@274 1100 if (m_scale == dBScale || m_scale == MeterScale) {
Chris@261 1101
Chris@274 1102 float thresh = -50.f;
Chris@274 1103
Chris@274 1104 if (value > 0.f) {
Chris@274 1105 value = 10.f * log10f(value);
Chris@274 1106 if (value < thresh) value = thresh;
Chris@274 1107 } else value = thresh;
Chris@274 1108
Chris@274 1109 unit = "dBV";
Chris@274 1110
Chris@274 1111 } else {
Chris@274 1112 unit = "V";
Chris@274 1113 }
Chris@274 1114
Chris@274 1115 return true;
Chris@274 1116 }
Chris@274 1117
Chris@274 1118 bool
Chris@274 1119 WaveformLayer::getYScaleDifference(const View *v, int y0, int y1,
Chris@274 1120 float &diff, QString &unit) const
Chris@274 1121 {
Chris@274 1122 size_t c0, c1;
Chris@274 1123 float v0 = getValueForY(v, y0, c0);
Chris@274 1124 float v1 = getValueForY(v, y1, c1);
Chris@274 1125
Chris@274 1126 if (c0 != c1) {
Chris@274 1127 // different channels, not comparable
Chris@274 1128 diff = 0.f;
Chris@274 1129 unit = "";
Chris@274 1130 return false;
Chris@274 1131 }
Chris@274 1132
Chris@274 1133 if (m_scale == dBScale || m_scale == MeterScale) {
Chris@274 1134
Chris@274 1135 float thresh = -50.f;
Chris@274 1136
Chris@274 1137 if (v1 == v0) diff = thresh;
Chris@274 1138 else {
Chris@274 1139 if (v1 > v0) diff = v0 / v1;
Chris@274 1140 else diff = v1 / v0;
Chris@274 1141
Chris@274 1142 diff = 10.f * log10f(diff);
Chris@274 1143 if (diff < thresh) diff = thresh;
Chris@274 1144 }
Chris@274 1145
Chris@274 1146 unit = "dBV";
Chris@274 1147
Chris@274 1148 } else {
Chris@274 1149 diff = fabsf(v1 - v0);
Chris@274 1150 unit = "V";
Chris@274 1151 }
Chris@274 1152
Chris@261 1153 return true;
Chris@261 1154 }
Chris@261 1155
Chris@68 1156 int
Chris@248 1157 WaveformLayer::getVerticalScaleWidth(View *, QPainter &paint) const
Chris@0 1158 {
Chris@0 1159 if (m_scale == LinearScale) {
Chris@0 1160 return paint.fontMetrics().width("0.0") + 13;
Chris@0 1161 } else {
Chris@0 1162 return std::max(paint.fontMetrics().width(tr("0dB")),
Chris@0 1163 paint.fontMetrics().width(tr("-Inf"))) + 13;
Chris@0 1164 }
Chris@0 1165 }
Chris@0 1166
Chris@0 1167 void
Chris@44 1168 WaveformLayer::paintVerticalScale(View *v, QPainter &paint, QRect rect) const
Chris@0 1169 {
Chris@0 1170 if (!m_model || !m_model->isOK()) {
Chris@0 1171 return;
Chris@0 1172 }
Chris@0 1173
Chris@0 1174 size_t channels = 0, minChannel = 0, maxChannel = 0;
Chris@67 1175 bool mergingChannels = false, mixingChannels = false;
Chris@0 1176
Chris@67 1177 channels = getChannelArrangement(minChannel, maxChannel,
Chris@67 1178 mergingChannels, mixingChannels);
Chris@0 1179 if (channels == 0) return;
Chris@0 1180
Chris@0 1181 int h = rect.height(), w = rect.width();
Chris@0 1182 int textHeight = paint.fontMetrics().height();
Chris@0 1183 int toff = -textHeight/2 + paint.fontMetrics().ascent() + 1;
Chris@0 1184
Chris@67 1185 float gain = m_gain;
Chris@67 1186
Chris@0 1187 for (size_t ch = minChannel; ch <= maxChannel; ++ch) {
Chris@0 1188
Chris@68 1189 int lastLabelledY = -1;
Chris@0 1190
Chris@67 1191 if (ch < m_effectiveGains.size()) gain = m_effectiveGains[ch];
Chris@67 1192
Chris@68 1193 int n = 10;
Chris@0 1194
Chris@68 1195 for (int i = 0; i <= n; ++i) {
Chris@68 1196
Chris@68 1197 float val = 0.0, nval = 0.0;
Chris@0 1198 QString text = "";
Chris@0 1199
Chris@68 1200 switch (m_scale) {
Chris@68 1201
Chris@68 1202 case LinearScale:
Chris@68 1203 val = (i * gain) / n;
Chris@68 1204 text = QString("%1").arg(float(i) / n);
Chris@68 1205 if (i == 0) text = "0.0";
Chris@68 1206 else {
Chris@68 1207 nval = -val;
Chris@68 1208 if (i == n) text = "1.0";
Chris@68 1209 }
Chris@68 1210 break;
Chris@0 1211
Chris@68 1212 case MeterScale:
Chris@68 1213 val = AudioLevel::dB_to_multiplier(meterdbs[i]) * gain;
Chris@68 1214 text = QString("%1").arg(meterdbs[i]);
Chris@68 1215 if (i == n) text = tr("0dB");
Chris@68 1216 if (i == 0) {
Chris@68 1217 text = tr("-Inf");
Chris@68 1218 val = 0.0;
Chris@68 1219 }
Chris@68 1220 break;
Chris@0 1221
Chris@68 1222 case dBScale:
Chris@68 1223 val = AudioLevel::dB_to_multiplier(-(10*n) + i * 10) * gain;
Chris@68 1224 text = QString("%1").arg(-(10*n) + i * 10);
Chris@68 1225 if (i == n) text = tr("0dB");
Chris@68 1226 if (i == 0) {
Chris@68 1227 text = tr("-Inf");
Chris@68 1228 val = 0.0;
Chris@68 1229 }
Chris@68 1230 break;
Chris@68 1231 }
Chris@0 1232
Chris@68 1233 if (val < -1.0 || val > 1.0) continue;
Chris@0 1234
Chris@274 1235 int y = getYForValue(v, val, ch);
Chris@0 1236
Chris@68 1237 int ny = y;
Chris@68 1238 if (nval != 0.0) {
Chris@274 1239 ny = getYForValue(v, nval, ch);
Chris@68 1240 }
Chris@0 1241
Chris@68 1242 bool spaceForLabel = (i == 0 ||
Chris@68 1243 abs(y - lastLabelledY) >= textHeight - 1);
Chris@0 1244
Chris@68 1245 if (spaceForLabel) {
Chris@0 1246
Chris@68 1247 int tx = 3;
Chris@68 1248 if (m_scale != LinearScale) {
Chris@68 1249 tx = w - 10 - paint.fontMetrics().width(text);
Chris@68 1250 }
Chris@68 1251
Chris@68 1252 int ty = y;
Chris@68 1253 if (ty < paint.fontMetrics().ascent()) {
Chris@68 1254 ty = paint.fontMetrics().ascent();
Chris@68 1255 } else if (ty > h - paint.fontMetrics().descent()) {
Chris@68 1256 ty = h - paint.fontMetrics().descent();
Chris@68 1257 } else {
Chris@68 1258 ty += toff;
Chris@68 1259 }
Chris@68 1260 paint.drawText(tx, ty, text);
Chris@0 1261
Chris@68 1262 lastLabelledY = ty - toff;
Chris@67 1263
Chris@68 1264 if (ny != y) {
Chris@68 1265 ty = ny;
Chris@68 1266 if (ty < paint.fontMetrics().ascent()) {
Chris@68 1267 ty = paint.fontMetrics().ascent();
Chris@68 1268 } else if (ty > h - paint.fontMetrics().descent()) {
Chris@68 1269 ty = h - paint.fontMetrics().descent();
Chris@68 1270 } else {
Chris@68 1271 ty += toff;
Chris@68 1272 }
Chris@68 1273 paint.drawText(tx, ty, text);
Chris@68 1274 }
Chris@68 1275
Chris@68 1276 paint.drawLine(w - 7, y, w, y);
Chris@68 1277 if (ny != y) paint.drawLine(w - 7, ny, w, ny);
Chris@68 1278
Chris@68 1279 } else {
Chris@68 1280
Chris@68 1281 paint.drawLine(w - 4, y, w, y);
Chris@68 1282 if (ny != y) paint.drawLine(w - 4, ny, w, ny);
Chris@68 1283 }
Chris@0 1284 }
Chris@0 1285 }
Chris@0 1286 }
Chris@0 1287
Chris@316 1288 void
Chris@316 1289 WaveformLayer::toXml(QTextStream &stream,
Chris@316 1290 QString indent, QString extraAttributes) const
Chris@6 1291 {
Chris@6 1292 QString s;
Chris@6 1293
Chris@285 1294 QString colourName, colourSpec, darkbg;
Chris@285 1295 ColourDatabase::getInstance()->getStringValues
Chris@285 1296 (m_colour, colourName, colourSpec, darkbg);
Chris@285 1297
Chris@6 1298 s += QString("gain=\"%1\" "
Chris@287 1299 "showMeans=\"%2\" "
Chris@287 1300 "greyscale=\"%3\" "
Chris@287 1301 "channelMode=\"%4\" "
Chris@287 1302 "channel=\"%5\" "
Chris@287 1303 "scale=\"%6\" "
Chris@287 1304 "aggressive=\"%7\" "
Chris@287 1305 "autoNormalize=\"%8\"")
Chris@6 1306 .arg(m_gain)
Chris@6 1307 .arg(m_showMeans)
Chris@6 1308 .arg(m_greyscale)
Chris@6 1309 .arg(m_channelMode)
Chris@287 1310 .arg(m_channel)
Chris@6 1311 .arg(m_scale)
Chris@67 1312 .arg(m_aggressive)
Chris@67 1313 .arg(m_autoNormalize);
Chris@6 1314
Chris@316 1315 SingleColourLayer::toXml(stream, indent, extraAttributes + " " + s);
Chris@6 1316 }
Chris@6 1317
Chris@11 1318 void
Chris@11 1319 WaveformLayer::setProperties(const QXmlAttributes &attributes)
Chris@11 1320 {
Chris@11 1321 bool ok = false;
Chris@11 1322
Chris@287 1323 SingleColourLayer::setProperties(attributes);
Chris@287 1324
Chris@11 1325 float gain = attributes.value("gain").toFloat(&ok);
Chris@11 1326 if (ok) setGain(gain);
Chris@11 1327
Chris@11 1328 bool showMeans = (attributes.value("showMeans") == "1" ||
Chris@11 1329 attributes.value("showMeans") == "true");
Chris@11 1330 setShowMeans(showMeans);
Chris@11 1331
Chris@11 1332 bool greyscale = (attributes.value("greyscale") == "1" ||
Chris@11 1333 attributes.value("greyscale") == "true");
Chris@11 1334 setUseGreyscale(greyscale);
Chris@11 1335
Chris@11 1336 ChannelMode channelMode = (ChannelMode)
Chris@11 1337 attributes.value("channelMode").toInt(&ok);
Chris@11 1338 if (ok) setChannelMode(channelMode);
Chris@11 1339
Chris@11 1340 int channel = attributes.value("channel").toInt(&ok);
Chris@11 1341 if (ok) setChannel(channel);
Chris@11 1342
Chris@11 1343 Scale scale = (Scale)
Chris@11 1344 attributes.value("scale").toInt(&ok);
Chris@11 1345 if (ok) setScale(scale);
Chris@11 1346
Chris@11 1347 bool aggressive = (attributes.value("aggressive") == "1" ||
Chris@11 1348 attributes.value("aggressive") == "true");
Chris@11 1349 setUseGreyscale(aggressive);
Chris@67 1350
Chris@67 1351 bool autoNormalize = (attributes.value("autoNormalize") == "1" ||
Chris@67 1352 attributes.value("autoNormalize") == "true");
Chris@67 1353 setAutoNormalize(autoNormalize);
Chris@11 1354 }
Chris@11 1355
Chris@133 1356 int
Chris@133 1357 WaveformLayer::getVerticalZoomSteps(int &defaultStep) const
Chris@133 1358 {
Chris@133 1359 defaultStep = 50;
Chris@133 1360 return 100;
Chris@133 1361 }
Chris@0 1362
Chris@133 1363 int
Chris@133 1364 WaveformLayer::getCurrentVerticalZoomStep() const
Chris@133 1365 {
Chris@133 1366 int val = lrint(log10(m_gain) * 20.0) + 50;
Chris@133 1367 if (val < 0) val = 0;
Chris@133 1368 if (val > 100) val = 100;
Chris@133 1369 return val;
Chris@133 1370 }
Chris@133 1371
Chris@133 1372 void
Chris@133 1373 WaveformLayer::setVerticalZoomStep(int step)
Chris@133 1374 {
Chris@133 1375 setGain(pow(10, float(step - 50) / 20.0));
Chris@133 1376 }
Chris@133 1377