annotate layer/WaveformLayer.cpp @ 1496:d09345e578a7

Separate out handling of alignment progress bar from the layer progress bars and fix tendency to have them hanging around even when alignment has completed
author Chris Cannam
date Wed, 14 Aug 2019 10:58:24 +0100
parents 36ad3cdabf55
children 1ccb64bfb22b
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@1147 22 #include "base/Strings.h"
Chris@1078 23
Chris@376 24 #include "ColourDatabase.h"
Chris@1078 25 #include "PaintAssistant.h"
Chris@1340 26
Chris@1340 27 #include "data/model/WaveformOversampler.h"
Chris@0 28
Chris@0 29 #include <QPainter>
Chris@0 30 #include <QPixmap>
Chris@316 31 #include <QTextStream>
Chris@0 32
Chris@0 33 #include <iostream>
Chris@0 34 #include <cmath>
Chris@0 35
Chris@1352 36 //#define DEBUG_WAVEFORM_PAINT 1
Chris@1338 37 //#define DEBUG_WAVEFORM_PAINT_BY_PIXEL 1
Chris@4 38
Chris@1333 39 using std::vector;
Chris@682 40
Chris@0 41
Chris@44 42 WaveformLayer::WaveformLayer() :
Chris@287 43 SingleColourLayer(),
Chris@0 44 m_gain(1.0f),
Chris@67 45 m_autoNormalize(false),
Chris@0 46 m_showMeans(true),
Chris@0 47 m_channelMode(SeparateChannels),
Chris@0 48 m_channel(-1),
Chris@0 49 m_scale(LinearScale),
Chris@709 50 m_middleLineHeight(0.5),
Chris@0 51 m_aggressive(false),
Chris@1408 52 m_cache(nullptr),
Chris@1325 53 m_cacheValid(false)
Chris@0 54 {
Chris@0 55 }
Chris@0 56
Chris@0 57 WaveformLayer::~WaveformLayer()
Chris@0 58 {
Chris@0 59 delete m_cache;
Chris@0 60 }
Chris@0 61
Chris@1470 62 const ZoomConstraint *
Chris@1470 63 WaveformLayer::getZoomConstraint() const
Chris@1470 64 {
Chris@1470 65 auto model = ModelById::get(m_model);
Chris@1474 66 if (model) return model->getZoomConstraint();
Chris@1470 67 else return nullptr;
Chris@1470 68 }
Chris@1470 69
Chris@0 70 void
Chris@1470 71 WaveformLayer::setModel(ModelId modelId)
Chris@0 72 {
Chris@1470 73 auto oldModel = ModelById::getAs<RangeSummarisableTimeValueModel>(m_model);
Chris@1470 74 auto newModel = ModelById::getAs<RangeSummarisableTimeValueModel>(modelId);
Chris@1470 75
Chris@1471 76 if (!modelId.isNone() && !newModel) {
Chris@1471 77 throw std::logic_error("Not a RangeSummarisableTimeValueModel");
Chris@1470 78 }
Chris@1470 79
Chris@1471 80 if (m_model == modelId) return;
Chris@1471 81 m_model = modelId;
Chris@1471 82
Chris@1471 83 m_cacheValid = false;
Chris@1471 84
Chris@69 85 bool channelsChanged = false;
Chris@69 86 if (m_channel == -1) {
Chris@1470 87 if (!oldModel) {
Chris@1470 88 if (newModel) {
Chris@69 89 channelsChanged = true;
Chris@69 90 }
Chris@69 91 } else {
Chris@1470 92 if (newModel &&
Chris@1470 93 oldModel->getChannelCount() != newModel->getChannelCount()) {
Chris@69 94 channelsChanged = true;
Chris@69 95 }
Chris@69 96 }
Chris@69 97 }
Chris@69 98
Chris@1471 99 if (newModel) {
Chris@1471 100 connectSignals(m_model);
Chris@1471 101 }
Chris@1471 102
Chris@0 103 emit modelReplaced();
Chris@69 104
Chris@69 105 if (channelsChanged) emit layerParametersChanged();
Chris@0 106 }
Chris@0 107
Chris@0 108 Layer::PropertyList
Chris@0 109 WaveformLayer::getProperties() const
Chris@0 110 {
Chris@287 111 PropertyList list = SingleColourLayer::getProperties();
Chris@87 112 list.push_back("Scale");
Chris@87 113 list.push_back("Gain");
Chris@87 114 list.push_back("Normalize Visible Area");
Chris@68 115
Chris@1474 116 auto model = ModelById::getAs<RangeSummarisableTimeValueModel>(m_model);
Chris@1474 117 if (model && model->getChannelCount() > 1 && m_channel == -1) {
Chris@87 118 list.push_back("Channels");
Chris@68 119 }
Chris@68 120
Chris@0 121 return list;
Chris@0 122 }
Chris@0 123
Chris@87 124 QString
Chris@87 125 WaveformLayer::getPropertyLabel(const PropertyName &name) const
Chris@87 126 {
Chris@87 127 if (name == "Scale") return tr("Scale");
Chris@87 128 if (name == "Gain") return tr("Gain");
Chris@87 129 if (name == "Normalize Visible Area") return tr("Normalize Visible Area");
Chris@87 130 if (name == "Channels") return tr("Channels");
Chris@287 131 return SingleColourLayer::getPropertyLabel(name);
Chris@87 132 }
Chris@87 133
Chris@335 134 QString
Chris@335 135 WaveformLayer::getPropertyIconName(const PropertyName &name) const
Chris@335 136 {
Chris@335 137 if (name == "Normalize Visible Area") return "normalise";
Chris@335 138 return "";
Chris@335 139 }
Chris@335 140
Chris@0 141 Layer::PropertyType
Chris@0 142 WaveformLayer::getPropertyType(const PropertyName &name) const
Chris@0 143 {
Chris@87 144 if (name == "Gain") return RangeProperty;
Chris@87 145 if (name == "Normalize Visible Area") return ToggleProperty;
Chris@87 146 if (name == "Channels") return ValueProperty;
Chris@87 147 if (name == "Scale") return ValueProperty;
Chris@287 148 return SingleColourLayer::getPropertyType(name);
Chris@0 149 }
Chris@0 150
Chris@0 151 QString
Chris@0 152 WaveformLayer::getPropertyGroupName(const PropertyName &name) const
Chris@0 153 {
Chris@87 154 if (name == "Gain" ||
Chris@87 155 name == "Normalize Visible Area" ||
Chris@1266 156 name == "Scale") return tr("Scale");
Chris@0 157 return QString();
Chris@0 158 }
Chris@0 159
Chris@0 160 int
Chris@0 161 WaveformLayer::getPropertyRangeAndValue(const PropertyName &name,
Chris@216 162 int *min, int *max, int *deflt) const
Chris@0 163 {
Chris@216 164 int val = 0;
Chris@0 165
Chris@216 166 int garbage0, garbage1, garbage2;
Chris@56 167 if (!min) min = &garbage0;
Chris@56 168 if (!max) max = &garbage1;
Chris@216 169 if (!deflt) deflt = &garbage2;
Chris@10 170
Chris@87 171 if (name == "Gain") {
Chris@0 172
Chris@1266 173 *min = -50;
Chris@1266 174 *max = 50;
Chris@216 175 *deflt = 0;
Chris@0 176
Chris@1266 177 val = int(lrint(log10(m_gain) * 20.0));
Chris@1266 178 if (val < *min) val = *min;
Chris@1266 179 if (val > *max) val = *max;
Chris@0 180
Chris@87 181 } else if (name == "Normalize Visible Area") {
Chris@67 182
Chris@216 183 val = (m_autoNormalize ? 1 : 0);
Chris@216 184 *deflt = 0;
Chris@67 185
Chris@87 186 } else if (name == "Channels") {
Chris@0 187
Chris@67 188 *min = 0;
Chris@67 189 *max = 2;
Chris@216 190 *deflt = 0;
Chris@216 191 if (m_channelMode == MixChannels) val = 1;
Chris@216 192 else if (m_channelMode == MergeChannels) val = 2;
Chris@216 193 else val = 0;
Chris@0 194
Chris@87 195 } else if (name == "Scale") {
Chris@0 196
Chris@1266 197 *min = 0;
Chris@1266 198 *max = 2;
Chris@216 199 *deflt = 0;
Chris@0 200
Chris@1266 201 val = (int)m_scale;
Chris@0 202
Chris@0 203 } else {
Chris@1266 204 val = SingleColourLayer::getPropertyRangeAndValue(name, min, max, deflt);
Chris@0 205 }
Chris@0 206
Chris@216 207 return val;
Chris@0 208 }
Chris@0 209
Chris@0 210 QString
Chris@0 211 WaveformLayer::getPropertyValueLabel(const PropertyName &name,
Chris@1266 212 int value) const
Chris@0 213 {
Chris@87 214 if (name == "Scale") {
Chris@1266 215 switch (value) {
Chris@1266 216 default:
Chris@1266 217 case 0: return tr("Linear");
Chris@1266 218 case 1: return tr("Meter");
Chris@1266 219 case 2: return tr("dB");
Chris@1266 220 }
Chris@0 221 }
Chris@87 222 if (name == "Channels") {
Chris@67 223 switch (value) {
Chris@67 224 default:
Chris@67 225 case 0: return tr("Separate");
Chris@67 226 case 1: return tr("Mean");
Chris@67 227 case 2: return tr("Butterfly");
Chris@67 228 }
Chris@67 229 }
Chris@287 230 return SingleColourLayer::getPropertyValueLabel(name, value);
Chris@0 231 }
Chris@0 232
Chris@167 233 RangeMapper *
Chris@167 234 WaveformLayer::getNewPropertyRangeMapper(const PropertyName &name) const
Chris@167 235 {
Chris@167 236 if (name == "Gain") {
Chris@167 237 return new LinearRangeMapper(-50, 50, -25, 25, tr("dB"));
Chris@167 238 }
Chris@1408 239 return nullptr;
Chris@167 240 }
Chris@167 241
Chris@0 242 void
Chris@0 243 WaveformLayer::setProperty(const PropertyName &name, int value)
Chris@0 244 {
Chris@87 245 if (name == "Gain") {
Chris@1266 246 setGain(float(pow(10, float(value)/20.0)));
Chris@87 247 } else if (name == "Normalize Visible Area") {
Chris@67 248 setAutoNormalize(value ? true : false);
Chris@87 249 } else if (name == "Channels") {
Chris@67 250 if (value == 1) setChannelMode(MixChannels);
Chris@67 251 else if (value == 2) setChannelMode(MergeChannels);
Chris@67 252 else setChannelMode(SeparateChannels);
Chris@87 253 } else if (name == "Scale") {
Chris@1266 254 switch (value) {
Chris@1266 255 default:
Chris@1266 256 case 0: setScale(LinearScale); break;
Chris@1266 257 case 1: setScale(MeterScale); break;
Chris@1266 258 case 2: setScale(dBScale); break;
Chris@1266 259 }
Chris@287 260 } else {
Chris@287 261 SingleColourLayer::setProperty(name, value);
Chris@0 262 }
Chris@0 263 }
Chris@0 264
Chris@0 265 void
Chris@67 266 WaveformLayer::setGain(float gain)
Chris@0 267 {
Chris@0 268 if (m_gain == gain) return;
Chris@0 269 m_gain = gain;
Chris@0 270 m_cacheValid = false;
Chris@0 271 emit layerParametersChanged();
Chris@133 272 emit verticalZoomChanged();
Chris@0 273 }
Chris@0 274
Chris@0 275 void
Chris@67 276 WaveformLayer::setAutoNormalize(bool autoNormalize)
Chris@67 277 {
Chris@67 278 if (m_autoNormalize == autoNormalize) return;
Chris@67 279 m_autoNormalize = autoNormalize;
Chris@67 280 m_cacheValid = false;
Chris@67 281 emit layerParametersChanged();
Chris@67 282 }
Chris@67 283
Chris@67 284 void
Chris@0 285 WaveformLayer::setShowMeans(bool showMeans)
Chris@0 286 {
Chris@0 287 if (m_showMeans == showMeans) return;
Chris@0 288 m_showMeans = showMeans;
Chris@0 289 m_cacheValid = false;
Chris@0 290 emit layerParametersChanged();
Chris@0 291 }
Chris@0 292
Chris@0 293 void
Chris@0 294 WaveformLayer::setChannelMode(ChannelMode channelMode)
Chris@0 295 {
Chris@0 296 if (m_channelMode == channelMode) return;
Chris@0 297 m_channelMode = channelMode;
Chris@0 298 m_cacheValid = false;
Chris@0 299 emit layerParametersChanged();
Chris@0 300 }
Chris@0 301
Chris@0 302 void
Chris@0 303 WaveformLayer::setChannel(int channel)
Chris@0 304 {
Chris@587 305 // SVDEBUG << "WaveformLayer::setChannel(" << channel << ")" << endl;
Chris@0 306
Chris@0 307 if (m_channel == channel) return;
Chris@0 308 m_channel = channel;
Chris@0 309 m_cacheValid = false;
Chris@0 310 emit layerParametersChanged();
Chris@0 311 }
Chris@0 312
Chris@0 313 void
Chris@0 314 WaveformLayer::setScale(Scale scale)
Chris@0 315 {
Chris@0 316 if (m_scale == scale) return;
Chris@0 317 m_scale = scale;
Chris@0 318 m_cacheValid = false;
Chris@0 319 emit layerParametersChanged();
Chris@0 320 }
Chris@0 321
Chris@0 322 void
Chris@908 323 WaveformLayer::setMiddleLineHeight(double height)
Chris@709 324 {
Chris@709 325 if (m_middleLineHeight == height) return;
Chris@709 326 m_middleLineHeight = height;
Chris@709 327 m_cacheValid = false;
Chris@709 328 emit layerParametersChanged();
Chris@709 329 }
Chris@709 330
Chris@709 331 void
Chris@0 332 WaveformLayer::setAggressiveCacheing(bool aggressive)
Chris@0 333 {
Chris@0 334 if (m_aggressive == aggressive) return;
Chris@0 335 m_aggressive = aggressive;
Chris@0 336 m_cacheValid = false;
Chris@0 337 emit layerParametersChanged();
Chris@0 338 }
Chris@0 339
Chris@0 340 int
Chris@918 341 WaveformLayer::getCompletion(LayerGeometryProvider *) const
Chris@0 342 {
Chris@0 343 int completion = 100;
Chris@1474 344 auto model = ModelById::getAs<RangeSummarisableTimeValueModel>(m_model);
Chris@1474 345 if (!model || !model->isOK()) return completion;
Chris@1474 346 if (model->isReady(&completion)) return 100;
Chris@0 347 return completion;
Chris@0 348 }
Chris@0 349
Chris@79 350 bool
Chris@908 351 WaveformLayer::getValueExtents(double &min, double &max,
Chris@248 352 bool &, QString &unit) const
Chris@79 353 {
Chris@79 354 if (m_scale == LinearScale) {
Chris@79 355 min = 0.0;
Chris@79 356 max = 1.0;
Chris@79 357 unit = "V";
Chris@79 358 } else if (m_scale == MeterScale) {
Chris@79 359 return false; //!!!
Chris@79 360 } else {
Chris@79 361 min = AudioLevel::multiplier_to_dB(0.0);
Chris@79 362 max = AudioLevel::multiplier_to_dB(1.0);
Chris@79 363 unit = "dB";
Chris@79 364 }
Chris@79 365 return true;
Chris@79 366 }
Chris@79 367
Chris@1367 368 double
Chris@908 369 WaveformLayer::dBscale(double sample, int m) const
Chris@0 370 {
Chris@67 371 if (sample < 0.0) return dBscale(-sample, m);
Chris@908 372 double dB = AudioLevel::multiplier_to_dB(sample);
Chris@0 373 if (dB < -50.0) return 0;
Chris@0 374 if (dB > 0.0) return m;
Chris@1367 375 return ((dB + 50.0) * m) / 50.0;
Chris@0 376 }
Chris@0 377
Chris@805 378 int
Chris@805 379 WaveformLayer::getChannelArrangement(int &min, int &max,
Chris@67 380 bool &merging, bool &mixing)
Chris@0 381 const
Chris@0 382 {
Chris@1474 383 auto model = ModelById::getAs<RangeSummarisableTimeValueModel>(m_model);
Chris@1474 384 if (!model || !model->isOK()) return 0;
Chris@0 385
Chris@1474 386 int channels = model->getChannelCount();
Chris@0 387 if (channels == 0) return 0;
Chris@0 388
Chris@805 389 int rawChannels = channels;
Chris@0 390
Chris@0 391 if (m_channel == -1) {
Chris@1266 392 min = 0;
Chris@1266 393 if (m_channelMode == MergeChannels ||
Chris@67 394 m_channelMode == MixChannels) {
Chris@1266 395 max = 0;
Chris@1266 396 channels = 1;
Chris@1266 397 } else {
Chris@1266 398 max = channels - 1;
Chris@1266 399 }
Chris@0 400 } else {
Chris@1266 401 min = m_channel;
Chris@1266 402 max = m_channel;
Chris@1266 403 rawChannels = 1;
Chris@1266 404 channels = 1;
Chris@0 405 }
Chris@0 406
Chris@0 407 merging = (m_channelMode == MergeChannels && rawChannels > 1);
Chris@67 408 mixing = (m_channelMode == MixChannels && rawChannels > 1);
Chris@0 409
Chris@587 410 // SVDEBUG << "WaveformLayer::getChannelArrangement: min " << min << ", max " << max << ", merging " << merging << ", channels " << channels << endl;
Chris@0 411
Chris@0 412 return channels;
Chris@0 413 }
Chris@0 414
Chris@67 415 bool
Chris@918 416 WaveformLayer::isLayerScrollable(const LayerGeometryProvider *) const
Chris@67 417 {
Chris@67 418 return !m_autoNormalize;
Chris@67 419 }
Chris@67 420
Chris@68 421 static float meterdbs[] = { -40, -30, -20, -15, -10,
Chris@68 422 -5, -3, -2, -1, -0.5, 0 };
Chris@68 423
Chris@365 424 bool
Chris@1336 425 WaveformLayer::getSourceFramesForX(LayerGeometryProvider *v,
Chris@1336 426 int x, int modelZoomLevel,
Chris@908 427 sv_frame_t &f0, sv_frame_t &f1) const
Chris@365 428 {
Chris@1474 429 auto model = ModelById::getAs<RangeSummarisableTimeValueModel>(m_model);
Chris@1474 430 if (!model) return false;
Chris@1474 431
Chris@908 432 sv_frame_t viewFrame = v->getFrameForX(x);
Chris@365 433 if (viewFrame < 0) {
Chris@365 434 f0 = 0;
Chris@365 435 f1 = 0;
Chris@365 436 return false;
Chris@365 437 }
Chris@365 438
Chris@365 439 f0 = viewFrame;
Chris@365 440 f0 = f0 / modelZoomLevel;
Chris@365 441 f0 = f0 * modelZoomLevel;
Chris@365 442
Chris@1341 443 if (v->getZoomLevel().zone == ZoomLevel::PixelsPerFrame) {
Chris@1341 444 f1 = f0 + 1;
Chris@1341 445 } else {
Chris@1341 446 viewFrame = v->getFrameForX(x + 1);
Chris@1341 447 f1 = viewFrame;
Chris@1341 448 f1 = f1 / modelZoomLevel;
Chris@1341 449 f1 = f1 * modelZoomLevel;
Chris@1341 450 }
Chris@365 451
Chris@1474 452 return (f0 < model->getEndFrame());
Chris@365 453 }
Chris@365 454
Chris@365 455 float
Chris@918 456 WaveformLayer::getNormalizeGain(LayerGeometryProvider *v, int channel) const
Chris@365 457 {
Chris@1474 458 auto model = ModelById::getAs<RangeSummarisableTimeValueModel>(m_model);
Chris@1474 459 if (!model) return 0.f;
Chris@1474 460
Chris@908 461 sv_frame_t startFrame = v->getStartFrame();
Chris@908 462 sv_frame_t endFrame = v->getEndFrame();
Chris@365 463
Chris@1474 464 sv_frame_t modelStart = model->getStartFrame();
Chris@1474 465 sv_frame_t modelEnd = model->getEndFrame();
Chris@365 466
Chris@908 467 sv_frame_t rangeStart, rangeEnd;
Chris@365 468
Chris@365 469 if (startFrame < modelStart) rangeStart = modelStart;
Chris@365 470 else rangeStart = startFrame;
Chris@365 471
Chris@365 472 if (endFrame < 0) rangeEnd = 0;
Chris@365 473 else if (endFrame > modelEnd) rangeEnd = modelEnd;
Chris@365 474 else rangeEnd = endFrame;
Chris@365 475
Chris@365 476 if (rangeEnd < rangeStart) rangeEnd = rangeStart;
Chris@365 477
Chris@365 478 RangeSummarisableTimeValueModel::Range range =
Chris@1474 479 model->getSummary(channel, rangeStart, rangeEnd - rangeStart);
Chris@365 480
Chris@805 481 int minChannel = 0, maxChannel = 0;
Chris@365 482 bool mergingChannels = false, mixingChannels = false;
Chris@365 483
Chris@859 484 (void)getChannelArrangement(minChannel, maxChannel,
Chris@859 485 mergingChannels, mixingChannels);
Chris@365 486
Chris@365 487 if (mergingChannels || mixingChannels) {
Chris@365 488 RangeSummarisableTimeValueModel::Range otherRange =
Chris@1474 489 model->getSummary(1, rangeStart, rangeEnd - rangeStart);
Chris@386 490 range.setMax(std::max(range.max(), otherRange.max()));
Chris@386 491 range.setMin(std::min(range.min(), otherRange.min()));
Chris@386 492 range.setAbsmean(std::min(range.absmean(), otherRange.absmean()));
Chris@365 493 }
Chris@365 494
Chris@908 495 return float(1.0 / std::max(fabs(range.max()), fabs(range.min())));
Chris@365 496 }
Chris@365 497
Chris@0 498 void
Chris@916 499 WaveformLayer::paint(LayerGeometryProvider *v, QPainter &viewPainter, QRect rect) const
Chris@0 500 {
Chris@1474 501 auto model = ModelById::getAs<RangeSummarisableTimeValueModel>(m_model);
Chris@1474 502 if (!model || !model->isOK()) {
Chris@1266 503 return;
Chris@0 504 }
Chris@0 505
Chris@1325 506 ZoomLevel zoomLevel = v->getZoomLevel();
Chris@0 507
Chris@2 508 #ifdef DEBUG_WAVEFORM_PAINT
Chris@0 509 Profiler profiler("WaveformLayer::paint", true);
Chris@1338 510 SVCERR << "WaveformLayer::paint (" << rect.x() << "," << rect.y()
Chris@1266 511 << ") [" << rect.width() << "x" << rect.height() << "]: zoom " << zoomLevel << endl;
Chris@2 512 #endif
Chris@0 513
Chris@805 514 int channels = 0, minChannel = 0, maxChannel = 0;
Chris@67 515 bool mergingChannels = false, mixingChannels = false;
Chris@0 516
Chris@67 517 channels = getChannelArrangement(minChannel, maxChannel,
Chris@67 518 mergingChannels, mixingChannels);
Chris@0 519 if (channels == 0) return;
Chris@0 520
Chris@915 521 int w = v->getPaintWidth();
Chris@915 522 int h = v->getPaintHeight();
Chris@0 523
Chris@0 524 QPainter *paint;
Chris@0 525
Chris@0 526 if (m_aggressive) {
Chris@0 527
Chris@214 528 #ifdef DEBUG_WAVEFORM_PAINT
Chris@1338 529 SVCERR << "WaveformLayer::paint: aggressive is true" << endl;
Chris@214 530 #endif
Chris@214 531
Chris@1325 532 using namespace std::rel_ops;
Chris@1325 533
Chris@1266 534 if (m_cacheValid && (zoomLevel != m_cacheZoomLevel)) {
Chris@1266 535 m_cacheValid = false;
Chris@1266 536 }
Chris@0 537
Chris@1266 538 if (!m_cache || m_cache->width() != w || m_cache->height() != h) {
Chris@214 539 #ifdef DEBUG_WAVEFORM_PAINT
Chris@214 540 if (m_cache) {
Chris@1338 541 SVCERR << "WaveformLayer::paint: cache size " << m_cache->width() << "x" << m_cache->height() << " differs from view size " << w << "x" << h << ": regenerating aggressive cache" << endl;
Chris@214 542 }
Chris@214 543 #endif
Chris@1266 544 delete m_cache;
Chris@1266 545 m_cache = new QPixmap(w, h);
Chris@214 546 m_cacheValid = false;
Chris@1266 547 }
Chris@214 548
Chris@1266 549 if (m_cacheValid) {
Chris@1266 550 viewPainter.drawPixmap(rect, *m_cache, rect);
Chris@1266 551 return;
Chris@1266 552 }
Chris@0 553
Chris@1266 554 paint = new QPainter(m_cache);
Chris@0 555
Chris@1266 556 paint->setPen(Qt::NoPen);
Chris@1266 557 paint->setBrush(getBackgroundQColor(v));
Chris@1266 558 paint->drawRect(rect);
Chris@0 559
Chris@1266 560 paint->setPen(getForegroundQColor(v));
Chris@1266 561 paint->setBrush(Qt::NoBrush);
Chris@0 562
Chris@0 563 } else {
Chris@1266 564 paint = &viewPainter;
Chris@0 565 }
Chris@0 566
Chris@1367 567 paint->setRenderHint(QPainter::Antialiasing, true);
Chris@28 568
Chris@709 569 if (m_middleLineHeight != 0.5) {
Chris@709 570 paint->save();
Chris@908 571 double space = m_middleLineHeight * 2;
Chris@709 572 if (space > 1.0) space = 2.0 - space;
Chris@908 573 double yt = h * (m_middleLineHeight - space/2);
Chris@709 574 paint->translate(QPointF(0, yt));
Chris@709 575 paint->scale(1.0, space);
Chris@709 576 }
Chris@709 577
Chris@0 578 int x0 = 0, x1 = w - 1;
Chris@0 579
Chris@0 580 x0 = rect.left();
Chris@0 581 x1 = rect.right();
Chris@0 582
Chris@1334 583 if (x0 > 0) {
Chris@1334 584 rect.adjust(-1, 0, 0, 0);
Chris@1334 585 x0 = rect.left();
Chris@1334 586 }
Chris@1334 587
Chris@1334 588 if (x1 < w) {
Chris@1334 589 rect.adjust(0, 0, 1, 0);
Chris@1334 590 x1 = rect.right();
Chris@1334 591 }
Chris@28 592
Chris@365 593 // Our zoom level may differ from that at which the underlying
Chris@365 594 // model has its blocks.
Chris@302 595
Chris@365 596 // Each pixel within our visible range must always draw from
Chris@365 597 // exactly the same set of underlying audio frames, no matter what
Chris@365 598 // the range being drawn is. And that set of underlying frames
Chris@365 599 // must remain the same when we scroll one or more pixels left or
Chris@365 600 // right.
Chris@1325 601
Chris@1325 602 int desiredBlockSize = 1;
Chris@1325 603 if (zoomLevel.zone == ZoomLevel::FramesPerPixel) {
Chris@1325 604 desiredBlockSize = zoomLevel.level;
Chris@1325 605 }
Chris@1474 606 int blockSize = model->getSummaryBlockSize(desiredBlockSize);
Chris@365 607
Chris@908 608 sv_frame_t frame0;
Chris@908 609 sv_frame_t frame1;
Chris@908 610 sv_frame_t spare;
Chris@365 611
Chris@1325 612 getSourceFramesForX(v, x0, blockSize, frame0, spare);
Chris@1325 613 getSourceFramesForX(v, x1, blockSize, spare, frame1);
Chris@365 614
Chris@4 615 #ifdef DEBUG_WAVEFORM_PAINT
Chris@1338 616 SVCERR << "Painting waveform from " << frame0 << " to " << frame1 << " (" << (x1-x0+1) << " pixels at zoom " << zoomLevel << " and model zoom " << blockSize << ")" << endl;
Chris@4 617 #endif
Chris@0 618
Chris@1338 619 m_effectiveGains.clear();
Chris@805 620 while ((int)m_effectiveGains.size() <= maxChannel) {
Chris@67 621 m_effectiveGains.push_back(m_gain);
Chris@67 622 }
Chris@1337 623 if (m_autoNormalize) {
Chris@1337 624 for (int ch = minChannel; ch <= maxChannel; ++ch) {
Chris@1337 625 m_effectiveGains[ch] = getNormalizeGain(v, ch);
Chris@1337 626 }
Chris@1336 627 }
Chris@67 628
Chris@1338 629 RangeVec ranges;
Chris@1338 630
Chris@1336 631 if (v->getZoomLevel().zone == ZoomLevel::FramesPerPixel) {
Chris@1338 632 getSummaryRanges(minChannel, maxChannel,
Chris@1338 633 mixingChannels || mergingChannels,
Chris@1338 634 frame0, frame1,
Chris@1338 635 blockSize, ranges);
Chris@1338 636 } else {
Chris@1338 637 getOversampledRanges(minChannel, maxChannel,
Chris@1338 638 mixingChannels || mergingChannels,
Chris@1338 639 frame0, frame1,
Chris@1338 640 v->getZoomLevel().level, ranges);
Chris@1338 641 }
Chris@1333 642
Chris@1343 643 if (!ranges.empty()) {
Chris@1343 644 for (int ch = minChannel; ch <= maxChannel; ++ch) {
Chris@1343 645 paintChannel(v, paint, rect, ch, ranges, blockSize,
Chris@1343 646 frame0, frame1);
Chris@1343 647 }
Chris@1333 648 }
Chris@1333 649
Chris@709 650 if (m_middleLineHeight != 0.5) {
Chris@709 651 paint->restore();
Chris@709 652 }
Chris@709 653
Chris@0 654 if (m_aggressive) {
Chris@1474 655 if (model->isReady() && rect == v->getPaintRect()) {
Chris@1266 656 m_cacheValid = true;
Chris@1266 657 m_cacheZoomLevel = zoomLevel;
Chris@1266 658 }
Chris@1266 659 paint->end();
Chris@1266 660 delete paint;
Chris@1266 661 viewPainter.drawPixmap(rect, *m_cache, rect);
Chris@0 662 }
Chris@0 663 }
Chris@0 664
Chris@1332 665 void
Chris@1338 666 WaveformLayer::getSummaryRanges(int minChannel, int maxChannel,
Chris@1338 667 bool mixingOrMerging,
Chris@1338 668 sv_frame_t frame0, sv_frame_t frame1,
Chris@1338 669 int blockSize, RangeVec &ranges)
Chris@1338 670 const
Chris@1338 671 {
Chris@1474 672 auto model = ModelById::getAs<RangeSummarisableTimeValueModel>(m_model);
Chris@1474 673 if (!model) return;
Chris@1474 674
Chris@1338 675 for (int ch = minChannel; ch <= maxChannel; ++ch) {
Chris@1338 676 ranges.push_back({});
Chris@1474 677 model->getSummaries(ch, frame0, frame1 - frame0,
Chris@1338 678 ranges[ch - minChannel], blockSize);
Chris@1338 679 #ifdef DEBUG_WAVEFORM_PAINT
Chris@1338 680 SVCERR << "channel " << ch << ": " << ranges[ch - minChannel].size() << " ranges from " << frame0 << " to " << frame1 << " at zoom level " << blockSize << endl;
Chris@1338 681 #endif
Chris@1338 682 }
Chris@1338 683
Chris@1338 684 if (mixingOrMerging) {
Chris@1338 685 if (minChannel != 0 || maxChannel != 0) {
Chris@1366 686 throw std::logic_error("Internal error: min & max channels should be 0 when merging or mixing all channels");
Chris@1474 687 } else if (model->getChannelCount() > 1) {
Chris@1338 688 ranges.push_back({});
Chris@1474 689 model->getSummaries
Chris@1338 690 (1, frame0, frame1 - frame0, ranges[1], blockSize);
Chris@1338 691 }
Chris@1338 692 }
Chris@1338 693 }
Chris@1338 694
Chris@1338 695 void
Chris@1338 696 WaveformLayer::getOversampledRanges(int minChannel, int maxChannel,
Chris@1366 697 bool mixingOrMerging,
Chris@1338 698 sv_frame_t frame0, sv_frame_t frame1,
Chris@1338 699 int oversampleBy, RangeVec &ranges)
Chris@1338 700 const
Chris@1338 701 {
Chris@1474 702 auto model = ModelById::getAs<RangeSummarisableTimeValueModel>(m_model);
Chris@1474 703 if (!model) return;
Chris@1474 704
Chris@1366 705 if (mixingOrMerging) {
Chris@1366 706 if (minChannel != 0 || maxChannel != 0) {
Chris@1366 707 throw std::logic_error("Internal error: min & max channels should be 0 when merging or mixing all channels");
Chris@1366 708 }
Chris@1474 709 if (model->getChannelCount() > 1) {
Chris@1366 710 // call back on self for the individual channels with
Chris@1366 711 // mixingOrMerging false
Chris@1366 712 getOversampledRanges
Chris@1366 713 (0, 1, false, frame0, frame1, oversampleBy, ranges);
Chris@1366 714 return;
Chris@1366 715 }
Chris@1366 716 }
Chris@1366 717
Chris@1338 718 // These frame values, tail length, etc variables are at the model
Chris@1338 719 // sample rate, not the oversampled rate
Chris@1338 720
Chris@1338 721 sv_frame_t tail = 16;
Chris@1474 722 sv_frame_t startFrame = model->getStartFrame();
Chris@1474 723 sv_frame_t endFrame = model->getEndFrame();
Chris@1338 724
Chris@1338 725 sv_frame_t rf0 = frame0 - tail;
Chris@1338 726 if (rf0 < startFrame) {
Chris@1338 727 rf0 = 0;
Chris@1338 728 }
Chris@1338 729
Chris@1338 730 sv_frame_t rf1 = frame1 + tail;
Chris@1338 731 if (rf1 >= endFrame) {
Chris@1338 732 rf1 = endFrame - 1;
Chris@1338 733 }
Chris@1338 734 if (rf1 <= rf0) {
Chris@1338 735 SVCERR << "WARNING: getOversampledRanges: rf1 (" << rf1 << ") <= rf0 ("
Chris@1338 736 << rf0 << ")" << endl;
Chris@1338 737 return;
Chris@1338 738 }
Chris@1338 739
Chris@1338 740 for (int ch = minChannel; ch <= maxChannel; ++ch) {
Chris@1339 741 floatvec_t oversampled = WaveformOversampler::getOversampledData
Chris@1474 742 (*model, ch, frame0, frame1 - frame0, oversampleBy);
Chris@1338 743 RangeSummarisableTimeValueModel::RangeBlock rr;
Chris@1339 744 for (float v: oversampled) {
Chris@1338 745 RangeSummarisableTimeValueModel::Range r;
Chris@1339 746 r.sample(v);
Chris@1338 747 rr.push_back(r);
Chris@1338 748 }
Chris@1338 749 ranges.push_back(rr);
Chris@1338 750
Chris@1338 751 #ifdef DEBUG_WAVEFORM_PAINT
Chris@1338 752 SVCERR << "getOversampledRanges: " << frame0 << " -> " << frame1
Chris@1338 753 << " (" << frame1 - frame0 << "-frame range) at ratio "
Chris@1338 754 << oversampleBy << " with tail " << tail
Chris@1338 755 << " -> got " << oversampled.size()
Chris@1338 756 << " oversampled values for channel " << ch
Chris@1338 757 << ", from which returning " << rr.size() << " ranges" << endl;
Chris@1338 758 #endif
Chris@1338 759 }
Chris@1338 760
Chris@1338 761 return;
Chris@1338 762 }
Chris@1338 763
Chris@1338 764 void
Chris@1338 765 WaveformLayer::paintChannel(LayerGeometryProvider *v,
Chris@1338 766 QPainter *paint,
Chris@1338 767 QRect rect, int ch,
Chris@1338 768 const RangeVec &ranges,
Chris@1338 769 int blockSize,
Chris@1352 770 sv_frame_t frame0,
Chris@1352 771 sv_frame_t frame1)
Chris@1332 772 const
Chris@1332 773 {
Chris@1474 774 auto model = ModelById::getAs<RangeSummarisableTimeValueModel>(m_model);
Chris@1474 775 if (!model) return;
Chris@1474 776
Chris@1334 777 int x0 = rect.left();
Chris@1334 778 int y0 = rect.top();
Chris@1334 779
Chris@1334 780 int x1 = rect.right();
Chris@1334 781 int y1 = rect.bottom();
Chris@1334 782
Chris@1332 783 int h = v->getPaintHeight();
Chris@1332 784
Chris@1332 785 int channels = 0, minChannel = 0, maxChannel = 0;
Chris@1332 786 bool mergingChannels = false, mixingChannels = false;
Chris@1332 787
Chris@1332 788 channels = getChannelArrangement(minChannel, maxChannel,
Chris@1332 789 mergingChannels, mixingChannels);
Chris@1332 790 if (channels == 0) return;
Chris@1332 791
Chris@1332 792 QColor baseColour = getBaseQColor();
Chris@1332 793 QColor midColour = baseColour;
Chris@1367 794
Chris@1332 795 if (midColour == Qt::black) {
Chris@1332 796 midColour = Qt::gray;
Chris@1332 797 } else if (v->hasLightBackground()) {
Chris@1474 798 midColour = midColour.lighter(150);
Chris@1332 799 } else {
Chris@1474 800 midColour = midColour.lighter(50);
Chris@1332 801 }
Chris@1332 802
Chris@1332 803 double gain = m_effectiveGains[ch];
Chris@1332 804
Chris@1332 805 int m = (h / channels) / 2;
Chris@1332 806 int my = m + (((ch - minChannel) * h) / channels);
Chris@1332 807
Chris@1332 808 #ifdef DEBUG_WAVEFORM_PAINT
Chris@1338 809 SVCERR << "ch = " << ch << ", channels = " << channels << ", m = " << m << ", my = " << my << ", h = " << h << endl;
Chris@1332 810 #endif
Chris@1332 811
Chris@1332 812 if (my - m > y1 || my + m < y0) return;
Chris@1332 813
Chris@1332 814 if ((m_scale == dBScale || m_scale == MeterScale) &&
Chris@1332 815 m_channelMode != MergeChannels) {
Chris@1332 816 m = (h / channels);
Chris@1332 817 my = m + (((ch - minChannel) * h) / channels);
Chris@1332 818 }
Chris@1332 819
Chris@1367 820 // Horizontal axis along middle
Chris@1367 821 paint->setPen(QPen(midColour, 0));
Chris@1393 822 paint->drawLine(QPointF(x0, my + 0.5), QPointF(x1, my + 0.5));
Chris@1332 823
Chris@1335 824 paintChannelScaleGuides(v, paint, rect, ch);
Chris@1332 825
Chris@1333 826 int rangeix = ch - minChannel;
Chris@1338 827
Chris@1338 828 #ifdef DEBUG_WAVEFORM_PAINT
Chris@1338 829 SVCERR << "paint channel " << ch << ": frame0 = " << frame0 << ", frame1 = " << frame1 << ", blockSize = " << blockSize << ", have " << ranges.size() << " range blocks of which ours is index " << rangeix << " with " << ranges[rangeix].size() << " ranges in it" << endl;
Chris@1352 830 #else
Chris@1352 831 (void)frame1; // not actually used
Chris@1338 832 #endif
Chris@1338 833
Chris@1367 834 QPainterPath waveformPath;
Chris@1367 835 QPainterPath meanPath;
Chris@1367 836 QPainterPath clipPath;
Chris@1367 837 vector<QPointF> individualSamplePoints;
Chris@1367 838
Chris@1367 839 bool firstPoint = true;
Chris@1372 840 double prevRangeBottom = 0, prevRangeTop = 0;
Chris@1367 841
Chris@1332 842 for (int x = x0; x <= x1; ++x) {
Chris@1332 843
Chris@1332 844 sv_frame_t f0, f1;
Chris@1338 845 sv_frame_t i0, i1;
Chris@1338 846
Chris@1338 847 bool showIndividualSample = false;
Chris@1338 848
Chris@1338 849 if (v->getZoomLevel().zone == ZoomLevel::FramesPerPixel) {
Chris@1338 850 if (!getSourceFramesForX(v, x, blockSize, f0, f1)) {
Chris@1338 851 continue;
Chris@1338 852 }
Chris@1338 853 f1 = f1 - 1;
Chris@1338 854 i0 = (f0 - frame0) / blockSize;
Chris@1338 855 i1 = (f1 - frame0) / blockSize;
Chris@1338 856 } else {
Chris@1338 857 int oversampleBy = v->getZoomLevel().level;
Chris@1338 858 f0 = f1 = v->getFrameForX(x);
Chris@1338 859 int xf0 = v->getXForFrame(f0);
Chris@1338 860 showIndividualSample = (x == xf0);
Chris@1338 861 i0 = i1 = (f0 - frame0) * oversampleBy + (x - xf0);
Chris@1338 862 }
Chris@1332 863
Chris@1332 864 if (f0 < frame0) {
Chris@1375 865 // Not an error, this simply occurs when painting the
Chris@1375 866 // start of a signal in PixelsPerFrame zone
Chris@1332 867 continue;
Chris@1332 868 }
Chris@1332 869
Chris@1338 870 #ifdef DEBUG_WAVEFORM_PAINT_BY_PIXEL
Chris@1338 871 SVCERR << "WaveformLayer::paint: pixel " << x << ": i0 " << i0 << " (f " << f0 << "), i1 " << i1 << " (f " << f1 << ")" << endl;
Chris@1332 872 #endif
Chris@1332 873
Chris@1332 874 if (i1 > i0 + 1) {
Chris@1338 875 SVCERR << "WaveformLayer::paint: ERROR: i1 " << i1 << " > i0 " << i0 << " plus one (zoom = " << v->getZoomLevel() << ", model zoom = " << blockSize << ")" << endl;
Chris@1332 876 }
Chris@1332 877
Chris@1333 878 const auto &r = ranges[rangeix];
Chris@1333 879 RangeSummarisableTimeValueModel::Range range;
Chris@1333 880
Chris@1333 881 if (in_range_for(r, i0)) {
Chris@1332 882
Chris@1333 883 range = r[i0];
Chris@1332 884
Chris@1333 885 if (i1 > i0 && in_range_for(r, i1)) {
Chris@1333 886 range.setMax(std::max(range.max(), r[i1].max()));
Chris@1333 887 range.setMin(std::min(range.min(), r[i1].min()));
Chris@1333 888 range.setAbsmean((range.absmean() + r[i1].absmean()) / 2);
Chris@1332 889 }
Chris@1332 890
Chris@1332 891 } else {
Chris@1332 892 #ifdef DEBUG_WAVEFORM_PAINT
Chris@1338 893 SVCERR << "No (or not enough) ranges for index i0 = " << i0 << " (there are " << r.size() << " range(s))" << endl;
Chris@1332 894 #endif
Chris@1332 895 continue;
Chris@1332 896 }
Chris@1332 897
Chris@1367 898 double rangeBottom = 0, rangeTop = 0, meanBottom = 0, meanTop = 0;
Chris@1332 899
Chris@1333 900 if (mergingChannels && ranges.size() > 1) {
Chris@1332 901
Chris@1333 902 const auto &other = ranges[1];
Chris@1333 903
Chris@1333 904 if (in_range_for(other, i0)) {
Chris@1332 905
Chris@1332 906 range.setMax(fabsf(range.max()));
Chris@1333 907 range.setMin(-fabsf(other[i0].max()));
Chris@1332 908 range.setAbsmean
Chris@1333 909 ((range.absmean() + other[i0].absmean()) / 2);
Chris@1332 910
Chris@1333 911 if (i1 > i0 && in_range_for(other, i1)) {
Chris@1332 912 // let's not concern ourselves about the mean
Chris@1333 913 range.setMin(std::min(range.min(),
Chris@1333 914 -fabsf(other[i1].max())));
Chris@1332 915 }
Chris@1332 916 }
Chris@1332 917
Chris@1333 918 } else if (mixingChannels && ranges.size() > 1) {
Chris@1332 919
Chris@1333 920 const auto &other = ranges[1];
Chris@1333 921
Chris@1333 922 if (in_range_for(other, i0)) {
Chris@1332 923
Chris@1333 924 range.setMax((range.max() + other[i0].max()) / 2);
Chris@1333 925 range.setMin((range.min() + other[i0].min()) / 2);
Chris@1333 926 range.setAbsmean((range.absmean() + other[i0].absmean()) / 2);
Chris@1332 927 }
Chris@1332 928 }
Chris@1332 929
Chris@1332 930 switch (m_scale) {
Chris@1332 931
Chris@1332 932 case LinearScale:
Chris@1367 933 rangeBottom = range.min() * gain * m;
Chris@1367 934 rangeTop = range.max() * gain * m;
Chris@1367 935 meanBottom = range.absmean() * gain * (-m);
Chris@1367 936 meanTop = range.absmean() * gain * m;
Chris@1332 937 break;
Chris@1332 938
Chris@1332 939 case dBScale:
Chris@1332 940 if (!mergingChannels) {
Chris@1367 941 double db0 = dBscale(range.min() * gain, m);
Chris@1367 942 double db1 = dBscale(range.max() * gain, m);
Chris@1367 943 rangeTop = std::max(db0, db1);
Chris@1367 944 meanTop = std::min(db0, db1);
Chris@1332 945 if (mixingChannels) rangeBottom = meanTop;
Chris@1332 946 else rangeBottom = dBscale(range.absmean() * gain, m);
Chris@1367 947 meanBottom = rangeBottom;
Chris@1332 948 } else {
Chris@1367 949 rangeBottom = -dBscale(range.min() * gain, m);
Chris@1367 950 rangeTop = dBscale(range.max() * gain, m);
Chris@1367 951 meanBottom = -dBscale(range.absmean() * gain, m);
Chris@1367 952 meanTop = dBscale(range.absmean() * gain, m);
Chris@1332 953 }
Chris@1332 954 break;
Chris@1332 955
Chris@1332 956 case MeterScale:
Chris@1332 957 if (!mergingChannels) {
Chris@1405 958 double r0 = std::abs(AudioLevel::multiplier_to_preview
Chris@1367 959 (range.min() * gain, m));
Chris@1405 960 double r1 = std::abs(AudioLevel::multiplier_to_preview
Chris@1367 961 (range.max() * gain, m));
Chris@1367 962 rangeTop = std::max(r0, r1);
Chris@1367 963 meanTop = std::min(r0, r1);
Chris@1332 964 if (mixingChannels) rangeBottom = meanTop;
Chris@1367 965 else rangeBottom = AudioLevel::multiplier_to_preview
Chris@1367 966 (range.absmean() * gain, m);
Chris@1332 967 meanBottom = rangeBottom;
Chris@1332 968 } else {
Chris@1367 969 rangeBottom = -AudioLevel::multiplier_to_preview
Chris@1367 970 (range.min() * gain, m);
Chris@1367 971 rangeTop = AudioLevel::multiplier_to_preview
Chris@1367 972 (range.max() * gain, m);
Chris@1367 973 meanBottom = -AudioLevel::multiplier_to_preview
Chris@1367 974 (range.absmean() * gain, m);
Chris@1367 975 meanTop = AudioLevel::multiplier_to_preview
Chris@1367 976 (range.absmean() * gain, m);
Chris@1332 977 }
Chris@1332 978 break;
Chris@1332 979 }
Chris@1332 980
Chris@1367 981 rangeBottom = my - rangeBottom;
Chris@1367 982 rangeTop = my - rangeTop;
Chris@1367 983 meanBottom = my - meanBottom;
Chris@1367 984 meanTop = my - meanTop;
Chris@1332 985
Chris@1332 986 bool clipped = false;
Chris@1332 987
Chris@1332 988 if (rangeTop < my - m) { rangeTop = my - m; }
Chris@1332 989 if (rangeTop > my + m) { rangeTop = my + m; }
Chris@1332 990 if (rangeBottom < my - m) { rangeBottom = my - m; }
Chris@1332 991 if (rangeBottom > my + m) { rangeBottom = my + m; }
Chris@1332 992
Chris@1367 993 if (range.max() <= -1.0 || range.max() >= 1.0) {
Chris@1367 994 clipped = true;
Chris@1367 995 }
Chris@1332 996
Chris@1367 997 bool drawMean = m_showMeans;
Chris@1332 998
Chris@1367 999 meanTop = meanTop - 0.5;
Chris@1367 1000 meanBottom = meanBottom + 0.5;
Chris@1367 1001
Chris@1367 1002 if (meanTop <= rangeTop + 1.0) {
Chris@1367 1003 meanTop = rangeTop + 1.0;
Chris@1332 1004 }
Chris@1367 1005 if (meanBottom >= rangeBottom - 1.0 && m_scale == LinearScale) {
Chris@1367 1006 meanBottom = rangeBottom - 1.0;
Chris@1332 1007 }
Chris@1367 1008 if (meanTop > meanBottom - 1.0) {
Chris@1367 1009 drawMean = false;
Chris@1332 1010 }
Chris@1332 1011
Chris@1338 1012 #ifdef DEBUG_WAVEFORM_PAINT_BY_PIXEL
Chris@1338 1013 SVCERR << "range " << rangeBottom << " -> " << rangeTop << ", means " << meanBottom << " -> " << meanTop << ", raw range " << range.min() << " -> " << range.max() << endl;
Chris@1332 1014 #endif
Chris@1332 1015
Chris@1367 1016 double rangeMiddle = (rangeTop + rangeBottom) / 2.0;
Chris@1367 1017 bool trivialRange = (fabs(rangeTop - rangeBottom) < 1.0);
Chris@1367 1018 double px = x + 0.5;
Chris@1367 1019
Chris@1367 1020 if (showIndividualSample) {
Chris@1367 1021 individualSamplePoints.push_back(QPointF(px, rangeTop));
Chris@1367 1022 if (!trivialRange) {
Chris@1367 1023 // common e.g. in "butterfly" merging mode
Chris@1367 1024 individualSamplePoints.push_back(QPointF(px, rangeBottom));
Chris@1332 1025 }
Chris@1332 1026 }
Chris@1372 1027
Chris@1372 1028 bool contiguous = true;
Chris@1372 1029 if (rangeTop > prevRangeBottom + 0.5 ||
Chris@1372 1030 rangeBottom < prevRangeTop - 0.5) {
Chris@1372 1031 contiguous = false;
Chris@1372 1032 }
Chris@1332 1033
Chris@1372 1034 if (firstPoint || (contiguous && !trivialRange)) {
Chris@1372 1035 waveformPath.moveTo(QPointF(px, rangeTop));
Chris@1372 1036 waveformPath.lineTo(QPointF(px, rangeBottom));
Chris@1372 1037 waveformPath.moveTo(QPointF(px, rangeMiddle));
Chris@1367 1038 } else {
Chris@1367 1039 waveformPath.lineTo(QPointF(px, rangeMiddle));
Chris@1372 1040 if (!trivialRange) {
Chris@1372 1041 waveformPath.lineTo(QPointF(px, rangeTop));
Chris@1372 1042 waveformPath.lineTo(QPointF(px, rangeBottom));
Chris@1372 1043 waveformPath.lineTo(QPointF(px, rangeMiddle));
Chris@1372 1044 }
Chris@1367 1045 }
Chris@1367 1046
Chris@1372 1047 firstPoint = false;
Chris@1372 1048 prevRangeTop = rangeTop;
Chris@1372 1049 prevRangeBottom = rangeBottom;
Chris@1372 1050
Chris@1332 1051 if (drawMean) {
Chris@1367 1052 meanPath.moveTo(QPointF(px, meanBottom));
Chris@1367 1053 meanPath.lineTo(QPointF(px, meanTop));
Chris@1332 1054 }
Chris@1367 1055
Chris@1367 1056 if (clipped) {
Chris@1367 1057 if (trivialRange) {
Chris@1367 1058 clipPath.moveTo(QPointF(px, rangeMiddle));
Chris@1367 1059 clipPath.lineTo(QPointF(px+1, rangeMiddle));
Chris@1367 1060 } else {
Chris@1367 1061 clipPath.moveTo(QPointF(px, rangeBottom));
Chris@1367 1062 clipPath.lineTo(QPointF(px, rangeTop));
Chris@1367 1063 }
Chris@1367 1064 }
Chris@1367 1065 }
Chris@1367 1066
Chris@1367 1067 double penWidth = 1.0;
Chris@1418 1068 if (v->getZoomLevel().zone == ZoomLevel::FramesPerPixel) {
Chris@1418 1069 penWidth = 0.0;
Chris@1418 1070 }
Chris@1367 1071
Chris@1474 1072 if (model->isReady()) {
Chris@1367 1073 paint->setPen(QPen(baseColour, penWidth));
Chris@1367 1074 } else {
Chris@1367 1075 paint->setPen(QPen(midColour, penWidth));
Chris@1367 1076 }
Chris@1367 1077 paint->drawPath(waveformPath);
Chris@1367 1078
Chris@1367 1079 if (!clipPath.isEmpty()) {
Chris@1367 1080 paint->save();
Chris@1367 1081 paint->setPen(QPen(ColourDatabase::getInstance()->
Chris@1367 1082 getContrastingColour(m_colour), penWidth));
Chris@1367 1083 paint->drawPath(clipPath);
Chris@1367 1084 paint->restore();
Chris@1367 1085 }
Chris@1367 1086
Chris@1367 1087 if (!meanPath.isEmpty()) {
Chris@1367 1088 paint->save();
Chris@1367 1089 paint->setPen(QPen(midColour, penWidth));
Chris@1367 1090 paint->drawPath(meanPath);
Chris@1367 1091 paint->restore();
Chris@1367 1092 }
Chris@1367 1093
Chris@1367 1094 if (!individualSamplePoints.empty()) {
Chris@1401 1095 double sz = v->scaleSize(2.0);
Chris@1371 1096 if (v->getZoomLevel().zone == ZoomLevel::PixelsPerFrame) {
Chris@1371 1097 if (v->getZoomLevel().level < 10) {
Chris@1401 1098 sz = v->scaleSize(1.2);
Chris@1371 1099 }
Chris@1371 1100 }
Chris@1367 1101 paint->save();
Chris@1367 1102 paint->setPen(QPen(baseColour, penWidth));
Chris@1367 1103 for (QPointF p: individualSamplePoints) {
Chris@1367 1104 paint->drawRect(QRectF(p.x() - sz/2, p.y() - sz/2, sz, sz));
Chris@1367 1105 }
Chris@1367 1106 paint->restore();
Chris@1332 1107 }
Chris@1332 1108 }
Chris@1332 1109
Chris@1335 1110 void
Chris@1335 1111 WaveformLayer::paintChannelScaleGuides(LayerGeometryProvider *v,
Chris@1335 1112 QPainter *paint,
Chris@1335 1113 QRect rect,
Chris@1335 1114 int ch) const
Chris@1335 1115 {
Chris@1335 1116 int x0 = rect.left();
Chris@1335 1117 int x1 = rect.right();
Chris@1335 1118
Chris@1335 1119 int n = 10;
Chris@1335 1120 int py = -1;
Chris@1335 1121
Chris@1335 1122 double gain = m_effectiveGains[ch];
Chris@1335 1123
Chris@1335 1124 if (v->hasLightBackground() &&
Chris@1335 1125 v->getViewManager() &&
Chris@1335 1126 v->getViewManager()->shouldShowScaleGuides()) {
Chris@1335 1127
Chris@1335 1128 paint->setPen(QColor(240, 240, 240));
Chris@1335 1129
Chris@1335 1130 for (int i = 1; i < n; ++i) {
Chris@1335 1131
Chris@1335 1132 double val = 0.0, nval = 0.0;
Chris@1335 1133
Chris@1335 1134 switch (m_scale) {
Chris@1335 1135
Chris@1335 1136 case LinearScale:
Chris@1335 1137 val = (i * gain) / n;
Chris@1335 1138 if (i > 0) nval = -val;
Chris@1335 1139 break;
Chris@1335 1140
Chris@1335 1141 case MeterScale:
Chris@1335 1142 val = AudioLevel::dB_to_multiplier(meterdbs[i]) * gain;
Chris@1335 1143 break;
Chris@1335 1144
Chris@1335 1145 case dBScale:
Chris@1335 1146 val = AudioLevel::dB_to_multiplier(-(10*n) + i * 10) * gain;
Chris@1335 1147 break;
Chris@1335 1148 }
Chris@1335 1149
Chris@1335 1150 if (val < -1.0 || val > 1.0) continue;
Chris@1335 1151
Chris@1335 1152 int y = getYForValue(v, val, ch);
Chris@1335 1153
Chris@1335 1154 if (py >= 0 && abs(y - py) < 10) continue;
Chris@1335 1155 else py = y;
Chris@1335 1156
Chris@1335 1157 int ny = y;
Chris@1335 1158 if (nval != 0.0) {
Chris@1335 1159 ny = getYForValue(v, nval, ch);
Chris@1335 1160 }
Chris@1335 1161
Chris@1335 1162 paint->drawLine(x0, y, x1, y);
Chris@1335 1163 if (ny != y) {
Chris@1335 1164 paint->drawLine(x0, ny, x1, ny);
Chris@1335 1165 }
Chris@1335 1166 }
Chris@1335 1167 }
Chris@1335 1168 }
Chris@1335 1169
Chris@25 1170 QString
Chris@918 1171 WaveformLayer::getFeatureDescription(LayerGeometryProvider *v, QPoint &pos) const
Chris@25 1172 {
Chris@25 1173 int x = pos.x();
Chris@25 1174
Chris@1474 1175 auto model = ModelById::getAs<RangeSummarisableTimeValueModel>(m_model);
Chris@1474 1176 if (!model || !model->isOK()) return "";
Chris@25 1177
Chris@1325 1178 ZoomLevel zoomLevel = v->getZoomLevel();
Chris@25 1179
Chris@1325 1180 int desiredBlockSize = 1;
Chris@1325 1181 if (zoomLevel.zone == ZoomLevel::FramesPerPixel) {
Chris@1325 1182 desiredBlockSize = zoomLevel.level;
Chris@1325 1183 }
Chris@1325 1184
Chris@1474 1185 int blockSize = model->getSummaryBlockSize(desiredBlockSize);
Chris@365 1186
Chris@908 1187 sv_frame_t f0, f1;
Chris@1325 1188 if (!getSourceFramesForX(v, x, blockSize, f0, f1)) return "";
Chris@25 1189
Chris@25 1190 QString text;
Chris@25 1191
Chris@1474 1192 RealTime rt0 = RealTime::frame2RealTime(f0, model->getSampleRate());
Chris@1474 1193 RealTime rt1 = RealTime::frame2RealTime(f1, model->getSampleRate());
Chris@25 1194
Chris@25 1195 if (f1 != f0 + 1 && (rt0.sec != rt1.sec || rt0.msec() != rt1.msec())) {
Chris@1266 1196 text += tr("Time:\t%1 - %2")
Chris@1266 1197 .arg(rt0.toText(true).c_str())
Chris@1266 1198 .arg(rt1.toText(true).c_str());
Chris@25 1199 } else {
Chris@1266 1200 text += tr("Time:\t%1")
Chris@1266 1201 .arg(rt0.toText(true).c_str());
Chris@25 1202 }
Chris@25 1203
Chris@805 1204 int channels = 0, minChannel = 0, maxChannel = 0;
Chris@67 1205 bool mergingChannels = false, mixingChannels = false;
Chris@25 1206
Chris@67 1207 channels = getChannelArrangement(minChannel, maxChannel,
Chris@67 1208 mergingChannels, mixingChannels);
Chris@25 1209 if (channels == 0) return "";
Chris@25 1210
Chris@805 1211 for (int ch = minChannel; ch <= maxChannel; ++ch) {
Chris@25 1212
Chris@1266 1213 RangeSummarisableTimeValueModel::RangeBlock ranges;
Chris@1474 1214 model->getSummaries(ch, f0, f1 - f0, ranges, blockSize);
Chris@25 1215
Chris@1266 1216 if (ranges.empty()) continue;
Chris@1266 1217
Chris@1266 1218 RangeSummarisableTimeValueModel::Range range = ranges[0];
Chris@1266 1219
Chris@1266 1220 QString label = tr("Level:");
Chris@1266 1221 if (minChannel != maxChannel) {
Chris@1266 1222 if (ch == 0) label = tr("Left:");
Chris@1266 1223 else if (ch == 1) label = tr("Right:");
Chris@1266 1224 else label = tr("Channel %1").arg(ch + 1);
Chris@1266 1225 }
Chris@25 1226
Chris@76 1227 bool singleValue = false;
Chris@908 1228 double min, max;
Chris@76 1229
Chris@386 1230 if (fabs(range.min()) < 0.01) {
Chris@386 1231 min = range.min();
Chris@386 1232 max = range.max();
Chris@76 1233 singleValue = (min == max);
Chris@76 1234 } else {
Chris@908 1235 int imin = int(lrint(range.min() * 10000));
Chris@908 1236 int imax = int(lrint(range.max() * 10000));
Chris@76 1237 singleValue = (imin == imax);
Chris@908 1238 min = double(imin)/10000;
Chris@908 1239 max = double(imax)/10000;
Chris@76 1240 }
Chris@76 1241
Chris@1266 1242 int db = int(AudioLevel::multiplier_to_dB(std::max(fabsf(range.min()),
Chris@1266 1243 fabsf(range.max())))
Chris@1266 1244 * 100);
Chris@25 1245
Chris@1266 1246 if (!singleValue) {
Chris@1266 1247 text += tr("\n%1\t%2 - %3 (%4 dB peak)")
Chris@1266 1248 .arg(label).arg(min).arg(max).arg(double(db)/100);
Chris@1266 1249 } else {
Chris@1266 1250 text += tr("\n%1\t%2 (%3 dB peak)")
Chris@1266 1251 .arg(label).arg(min).arg(double(db)/100);
Chris@1266 1252 }
Chris@25 1253 }
Chris@25 1254
Chris@25 1255 return text;
Chris@25 1256 }
Chris@25 1257
Chris@0 1258 int
Chris@918 1259 WaveformLayer::getYForValue(const LayerGeometryProvider *v, double value, int channel) const
Chris@68 1260 {
Chris@805 1261 int channels = 0, minChannel = 0, maxChannel = 0;
Chris@274 1262 bool mergingChannels = false, mixingChannels = false;
Chris@274 1263
Chris@274 1264 channels = getChannelArrangement(minChannel, maxChannel,
Chris@274 1265 mergingChannels, mixingChannels);
Chris@853 1266 if (channels == 0) return 0;
Chris@68 1267 if (maxChannel < minChannel || channel < minChannel) return 0;
Chris@68 1268
Chris@918 1269 int h = v->getPaintHeight();
Chris@68 1270 int m = (h / channels) / 2;
Chris@1266 1271
Chris@68 1272 if ((m_scale == dBScale || m_scale == MeterScale) &&
Chris@68 1273 m_channelMode != MergeChannels) {
Chris@68 1274 m = (h / channels);
Chris@68 1275 }
Chris@68 1276
Chris@274 1277 int my = m + (((channel - minChannel) * h) / channels);
Chris@274 1278
Chris@68 1279 int vy = 0;
Chris@68 1280
Chris@274 1281 switch (m_scale) {
Chris@68 1282
Chris@68 1283 case LinearScale:
Chris@68 1284 vy = int(m * value);
Chris@68 1285 break;
Chris@68 1286
Chris@68 1287 case MeterScale:
Chris@68 1288 vy = AudioLevel::multiplier_to_preview(value, m);
Chris@68 1289 break;
Chris@68 1290
Chris@68 1291 case dBScale:
Chris@1367 1292 vy = int(dBscale(value, m));
Chris@68 1293 break;
Chris@68 1294 }
Chris@68 1295
Chris@1338 1296 // SVCERR << "mergingChannels= " << mergingChannels << ", channel = " << channel << ", value = " << value << ", vy = " << vy << endl;
Chris@324 1297
Chris@68 1298 return my - vy;
Chris@68 1299 }
Chris@68 1300
Chris@908 1301 double
Chris@918 1302 WaveformLayer::getValueForY(const LayerGeometryProvider *v, int y, int &channel) const
Chris@261 1303 {
Chris@805 1304 int channels = 0, minChannel = 0, maxChannel = 0;
Chris@274 1305 bool mergingChannels = false, mixingChannels = false;
Chris@274 1306
Chris@274 1307 channels = getChannelArrangement(minChannel, maxChannel,
Chris@274 1308 mergingChannels, mixingChannels);
Chris@853 1309 if (channels == 0) return 0;
Chris@261 1310 if (maxChannel < minChannel) return 0;
Chris@261 1311
Chris@918 1312 int h = v->getPaintHeight();
Chris@261 1313 int m = (h / channels) / 2;
Chris@261 1314
Chris@261 1315 if ((m_scale == dBScale || m_scale == MeterScale) &&
Chris@261 1316 m_channelMode != MergeChannels) {
Chris@261 1317 m = (h / channels);
Chris@261 1318 }
Chris@274 1319
Chris@274 1320 channel = (y * channels) / h + minChannel;
Chris@261 1321
Chris@261 1322 int my = m + (((channel - minChannel) * h) / channels);
Chris@261 1323
Chris@262 1324 int vy = my - y;
Chris@908 1325 double value = 0;
Chris@908 1326 double thresh = -50.f;
Chris@261 1327
Chris@274 1328 switch (m_scale) {
Chris@261 1329
Chris@261 1330 case LinearScale:
Chris@908 1331 value = double(vy) / m;
Chris@261 1332 break;
Chris@261 1333
Chris@261 1334 case MeterScale:
Chris@261 1335 value = AudioLevel::preview_to_multiplier(vy, m);
Chris@261 1336 break;
Chris@261 1337
Chris@261 1338 case dBScale:
Chris@908 1339 value = (-thresh * double(vy)) / m + thresh;
Chris@274 1340 value = AudioLevel::dB_to_multiplier(value);
Chris@261 1341 break;
Chris@261 1342 }
Chris@261 1343
Chris@274 1344 return value / m_gain;
Chris@261 1345 }
Chris@261 1346
Chris@261 1347 bool
Chris@918 1348 WaveformLayer::getYScaleValue(const LayerGeometryProvider *v, int y,
Chris@908 1349 double &value, QString &unit) const
Chris@261 1350 {
Chris@805 1351 int channel;
Chris@261 1352
Chris@274 1353 value = getValueForY(v, y, channel);
Chris@261 1354
Chris@274 1355 if (m_scale == dBScale || m_scale == MeterScale) {
Chris@261 1356
Chris@908 1357 double thresh = -50.f;
Chris@274 1358
Chris@908 1359 if (value > 0.0) {
Chris@908 1360 value = 10.0 * log10(value);
Chris@274 1361 if (value < thresh) value = thresh;
Chris@274 1362 } else value = thresh;
Chris@274 1363
Chris@274 1364 unit = "dBV";
Chris@274 1365
Chris@274 1366 } else {
Chris@274 1367 unit = "V";
Chris@274 1368 }
Chris@274 1369
Chris@274 1370 return true;
Chris@274 1371 }
Chris@274 1372
Chris@274 1373 bool
Chris@918 1374 WaveformLayer::getYScaleDifference(const LayerGeometryProvider *v, int y0, int y1,
Chris@908 1375 double &diff, QString &unit) const
Chris@274 1376 {
Chris@805 1377 int c0, c1;
Chris@908 1378 double v0 = getValueForY(v, y0, c0);
Chris@908 1379 double v1 = getValueForY(v, y1, c1);
Chris@274 1380
Chris@274 1381 if (c0 != c1) {
Chris@274 1382 // different channels, not comparable
Chris@908 1383 diff = 0.0;
Chris@274 1384 unit = "";
Chris@274 1385 return false;
Chris@274 1386 }
Chris@274 1387
Chris@274 1388 if (m_scale == dBScale || m_scale == MeterScale) {
Chris@274 1389
Chris@908 1390 double thresh = -50.0;
Chris@274 1391
Chris@274 1392 if (v1 == v0) diff = thresh;
Chris@274 1393 else {
Chris@274 1394 if (v1 > v0) diff = v0 / v1;
Chris@274 1395 else diff = v1 / v0;
Chris@274 1396
Chris@908 1397 diff = 10.0 * log10(diff);
Chris@274 1398 if (diff < thresh) diff = thresh;
Chris@274 1399 }
Chris@274 1400
Chris@274 1401 unit = "dBV";
Chris@274 1402
Chris@274 1403 } else {
Chris@908 1404 diff = fabs(v1 - v0);
Chris@274 1405 unit = "V";
Chris@274 1406 }
Chris@274 1407
Chris@261 1408 return true;
Chris@261 1409 }
Chris@261 1410
Chris@68 1411 int
Chris@918 1412 WaveformLayer::getVerticalScaleWidth(LayerGeometryProvider *, bool, QPainter &paint) const
Chris@0 1413 {
Chris@1474 1414 // Qt 5.13 deprecates QFontMetrics::width(), but its suggested
Chris@1474 1415 // replacement (horizontalAdvance) was only added in Qt 5.11
Chris@1474 1416 // which is too new for us
Chris@1474 1417 #pragma GCC diagnostic ignored "-Wdeprecated-declarations"
Chris@1474 1418
Chris@0 1419 if (m_scale == LinearScale) {
Chris@1266 1420 return paint.fontMetrics().width("0.0") + 13;
Chris@0 1421 } else {
Chris@1266 1422 return std::max(paint.fontMetrics().width(tr("0dB")),
Chris@1266 1423 paint.fontMetrics().width(Strings::minus_infinity)) + 13;
Chris@0 1424 }
Chris@0 1425 }
Chris@0 1426
Chris@0 1427 void
Chris@918 1428 WaveformLayer::paintVerticalScale(LayerGeometryProvider *v, bool, QPainter &paint, QRect rect) const
Chris@0 1429 {
Chris@1474 1430 auto model = ModelById::getAs<RangeSummarisableTimeValueModel>(m_model);
Chris@1474 1431 if (!model || !model->isOK()) {
Chris@1266 1432 return;
Chris@0 1433 }
Chris@0 1434
Chris@805 1435 int channels = 0, minChannel = 0, maxChannel = 0;
Chris@67 1436 bool mergingChannels = false, mixingChannels = false;
Chris@0 1437
Chris@67 1438 channels = getChannelArrangement(minChannel, maxChannel,
Chris@67 1439 mergingChannels, mixingChannels);
Chris@0 1440 if (channels == 0) return;
Chris@0 1441
Chris@0 1442 int h = rect.height(), w = rect.width();
Chris@0 1443 int textHeight = paint.fontMetrics().height();
Chris@0 1444 int toff = -textHeight/2 + paint.fontMetrics().ascent() + 1;
Chris@0 1445
Chris@908 1446 double gain = m_gain;
Chris@67 1447
Chris@805 1448 for (int ch = minChannel; ch <= maxChannel; ++ch) {
Chris@0 1449
Chris@1266 1450 int lastLabelledY = -1;
Chris@0 1451
Chris@805 1452 if (ch < (int)m_effectiveGains.size()) gain = m_effectiveGains[ch];
Chris@67 1453
Chris@68 1454 int n = 10;
Chris@0 1455
Chris@1266 1456 for (int i = 0; i <= n; ++i) {
Chris@68 1457
Chris@908 1458 double val = 0.0, nval = 0.0;
Chris@1266 1459 QString text = "";
Chris@0 1460
Chris@68 1461 switch (m_scale) {
Chris@68 1462
Chris@68 1463 case LinearScale:
Chris@68 1464 val = (i * gain) / n;
Chris@1266 1465 text = QString("%1").arg(double(i) / n);
Chris@1266 1466 if (i == 0) text = "0.0";
Chris@68 1467 else {
Chris@68 1468 nval = -val;
Chris@68 1469 if (i == n) text = "1.0";
Chris@68 1470 }
Chris@68 1471 break;
Chris@0 1472
Chris@68 1473 case MeterScale:
Chris@68 1474 val = AudioLevel::dB_to_multiplier(meterdbs[i]) * gain;
Chris@1266 1475 text = QString("%1").arg(meterdbs[i]);
Chris@1266 1476 if (i == n) text = tr("0dB");
Chris@1266 1477 if (i == 0) {
Chris@1147 1478 text = Strings::minus_infinity;
Chris@68 1479 val = 0.0;
Chris@1266 1480 }
Chris@68 1481 break;
Chris@0 1482
Chris@68 1483 case dBScale:
Chris@68 1484 val = AudioLevel::dB_to_multiplier(-(10*n) + i * 10) * gain;
Chris@1266 1485 text = QString("%1").arg(-(10*n) + i * 10);
Chris@1266 1486 if (i == n) text = tr("0dB");
Chris@1266 1487 if (i == 0) {
Chris@1147 1488 text = Strings::minus_infinity;
Chris@68 1489 val = 0.0;
Chris@1266 1490 }
Chris@68 1491 break;
Chris@68 1492 }
Chris@0 1493
Chris@68 1494 if (val < -1.0 || val > 1.0) continue;
Chris@0 1495
Chris@274 1496 int y = getYForValue(v, val, ch);
Chris@0 1497
Chris@68 1498 int ny = y;
Chris@68 1499 if (nval != 0.0) {
Chris@274 1500 ny = getYForValue(v, nval, ch);
Chris@68 1501 }
Chris@0 1502
Chris@68 1503 bool spaceForLabel = (i == 0 ||
Chris@68 1504 abs(y - lastLabelledY) >= textHeight - 1);
Chris@0 1505
Chris@68 1506 if (spaceForLabel) {
Chris@0 1507
Chris@68 1508 int tx = 3;
Chris@68 1509 if (m_scale != LinearScale) {
Chris@68 1510 tx = w - 10 - paint.fontMetrics().width(text);
Chris@68 1511 }
Chris@68 1512
Chris@68 1513 int ty = y;
Chris@68 1514 if (ty < paint.fontMetrics().ascent()) {
Chris@68 1515 ty = paint.fontMetrics().ascent();
Chris@68 1516 } else if (ty > h - paint.fontMetrics().descent()) {
Chris@68 1517 ty = h - paint.fontMetrics().descent();
Chris@68 1518 } else {
Chris@68 1519 ty += toff;
Chris@68 1520 }
Chris@68 1521 paint.drawText(tx, ty, text);
Chris@0 1522
Chris@68 1523 lastLabelledY = ty - toff;
Chris@67 1524
Chris@68 1525 if (ny != y) {
Chris@68 1526 ty = ny;
Chris@68 1527 if (ty < paint.fontMetrics().ascent()) {
Chris@68 1528 ty = paint.fontMetrics().ascent();
Chris@68 1529 } else if (ty > h - paint.fontMetrics().descent()) {
Chris@68 1530 ty = h - paint.fontMetrics().descent();
Chris@68 1531 } else {
Chris@68 1532 ty += toff;
Chris@68 1533 }
Chris@68 1534 paint.drawText(tx, ty, text);
Chris@68 1535 }
Chris@68 1536
Chris@68 1537 paint.drawLine(w - 7, y, w, y);
Chris@68 1538 if (ny != y) paint.drawLine(w - 7, ny, w, ny);
Chris@68 1539
Chris@68 1540 } else {
Chris@68 1541
Chris@68 1542 paint.drawLine(w - 4, y, w, y);
Chris@68 1543 if (ny != y) paint.drawLine(w - 4, ny, w, ny);
Chris@68 1544 }
Chris@1266 1545 }
Chris@0 1546 }
Chris@0 1547 }
Chris@0 1548
Chris@316 1549 void
Chris@316 1550 WaveformLayer::toXml(QTextStream &stream,
Chris@316 1551 QString indent, QString extraAttributes) const
Chris@6 1552 {
Chris@6 1553 QString s;
Chris@6 1554
Chris@285 1555 QString colourName, colourSpec, darkbg;
Chris@285 1556 ColourDatabase::getInstance()->getStringValues
Chris@285 1557 (m_colour, colourName, colourSpec, darkbg);
Chris@285 1558
Chris@6 1559 s += QString("gain=\"%1\" "
Chris@1266 1560 "showMeans=\"%2\" "
Chris@1266 1561 "greyscale=\"%3\" "
Chris@1266 1562 "channelMode=\"%4\" "
Chris@1266 1563 "channel=\"%5\" "
Chris@287 1564 "scale=\"%6\" "
Chris@709 1565 "middleLineHeight=\"%7\" "
Chris@1266 1566 "aggressive=\"%8\" "
Chris@709 1567 "autoNormalize=\"%9\"")
Chris@1266 1568 .arg(m_gain)
Chris@1266 1569 .arg(m_showMeans)
Chris@1367 1570 .arg(true) // Option removed, but effectively always on, so
Chris@1367 1571 // retained in the session file for compatibility
Chris@1266 1572 .arg(m_channelMode)
Chris@1266 1573 .arg(m_channel)
Chris@1266 1574 .arg(m_scale)
Chris@709 1575 .arg(m_middleLineHeight)
Chris@1266 1576 .arg(m_aggressive)
Chris@67 1577 .arg(m_autoNormalize);
Chris@6 1578
Chris@316 1579 SingleColourLayer::toXml(stream, indent, extraAttributes + " " + s);
Chris@6 1580 }
Chris@6 1581
Chris@11 1582 void
Chris@11 1583 WaveformLayer::setProperties(const QXmlAttributes &attributes)
Chris@11 1584 {
Chris@11 1585 bool ok = false;
Chris@11 1586
Chris@287 1587 SingleColourLayer::setProperties(attributes);
Chris@287 1588
Chris@11 1589 float gain = attributes.value("gain").toFloat(&ok);
Chris@11 1590 if (ok) setGain(gain);
Chris@11 1591
Chris@11 1592 bool showMeans = (attributes.value("showMeans") == "1" ||
Chris@1266 1593 attributes.value("showMeans") == "true");
Chris@11 1594 setShowMeans(showMeans);
Chris@11 1595
Chris@11 1596 ChannelMode channelMode = (ChannelMode)
Chris@1266 1597 attributes.value("channelMode").toInt(&ok);
Chris@11 1598 if (ok) setChannelMode(channelMode);
Chris@11 1599
Chris@11 1600 int channel = attributes.value("channel").toInt(&ok);
Chris@11 1601 if (ok) setChannel(channel);
Chris@11 1602
Chris@709 1603 Scale scale = (Scale)attributes.value("scale").toInt(&ok);
Chris@11 1604 if (ok) setScale(scale);
Chris@11 1605
Chris@709 1606 float middleLineHeight = attributes.value("middleLineHeight").toFloat(&ok);
Chris@709 1607 if (ok) setMiddleLineHeight(middleLineHeight);
Chris@709 1608
Chris@11 1609 bool aggressive = (attributes.value("aggressive") == "1" ||
Chris@1266 1610 attributes.value("aggressive") == "true");
Chris@1367 1611 setAggressiveCacheing(aggressive);
Chris@67 1612
Chris@67 1613 bool autoNormalize = (attributes.value("autoNormalize") == "1" ||
Chris@67 1614 attributes.value("autoNormalize") == "true");
Chris@67 1615 setAutoNormalize(autoNormalize);
Chris@11 1616 }
Chris@11 1617
Chris@133 1618 int
Chris@133 1619 WaveformLayer::getVerticalZoomSteps(int &defaultStep) const
Chris@133 1620 {
Chris@133 1621 defaultStep = 50;
Chris@133 1622 return 100;
Chris@133 1623 }
Chris@0 1624
Chris@133 1625 int
Chris@133 1626 WaveformLayer::getCurrentVerticalZoomStep() const
Chris@133 1627 {
Chris@908 1628 int val = int(lrint(log10(m_gain) * 20.0) + 50);
Chris@133 1629 if (val < 0) val = 0;
Chris@133 1630 if (val > 100) val = 100;
Chris@133 1631 return val;
Chris@133 1632 }
Chris@133 1633
Chris@133 1634 void
Chris@133 1635 WaveformLayer::setVerticalZoomStep(int step)
Chris@133 1636 {
Chris@908 1637 setGain(powf(10, float(step - 50) / 20.f));
Chris@133 1638 }
Chris@133 1639