annotate layer/WaveformLayer.cpp @ 1470:696e569ff21b by-id

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