annotate layer/WaveformLayer.cpp @ 1447:8afea53332f3 single-point

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