annotate layer/WaveformLayer.cpp @ 1386:fc3d89f88690 spectrogramparam

Use log-frequency rather than log-bin for calculating x coord in spectrum. This has the advantage that frequency positions don't move when we change the window size or oversampling ratio, but it does give us an unhelpfully large amount of space for very low frequencies - to be considered
author Chris Cannam
date Mon, 12 Nov 2018 11:34:34 +0000
parents 694004228ab7
children a0bfe366f9cb
rev   line source
Chris@58 1 /* -*- c-basic-offset: 4 indent-tabs-mode: nil -*- vi:set ts=8 sts=4 sw=4: */
Chris@0 2
Chris@0 3 /*
Chris@59 4 Sonic Visualiser
Chris@59 5 An audio file viewer and annotation editor.
Chris@59 6 Centre for Digital Music, Queen Mary, University of London.
Chris@182 7 This file copyright 2006 Chris Cannam and QMUL.
Chris@0 8
Chris@59 9 This program is free software; you can redistribute it and/or
Chris@59 10 modify it under the terms of the GNU General Public License as
Chris@59 11 published by the Free Software Foundation; either version 2 of the
Chris@59 12 License, or (at your option) any later version. See the file
Chris@59 13 COPYING included with this distribution for more information.
Chris@0 14 */
Chris@0 15
Chris@0 16 #include "WaveformLayer.h"
Chris@0 17
Chris@0 18 #include "base/AudioLevel.h"
Chris@128 19 #include "view/View.h"
Chris@0 20 #include "base/Profiler.h"
Chris@167 21 #include "base/RangeMapper.h"
Chris@1147 22 #include "base/Strings.h"
Chris@1078 23
Chris@376 24 #include "ColourDatabase.h"
Chris@1078 25 #include "PaintAssistant.h"
Chris@1340 26
Chris@1340 27 #include "data/model/WaveformOversampler.h"
Chris@0 28
Chris@0 29 #include <QPainter>
Chris@0 30 #include <QPixmap>
Chris@316 31 #include <QTextStream>
Chris@0 32
Chris@0 33 #include <iostream>
Chris@0 34 #include <cmath>
Chris@0 35
Chris@1352 36 //#define DEBUG_WAVEFORM_PAINT 1
Chris@1338 37 //#define DEBUG_WAVEFORM_PAINT_BY_PIXEL 1
Chris@4 38
Chris@1333 39 using std::vector;
Chris@682 40
Chris@0 41
Chris@44 42 WaveformLayer::WaveformLayer() :
Chris@287 43 SingleColourLayer(),
Chris@0 44 m_model(0),
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@0 53 m_cache(0),
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@167 221 return 0;
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@1332 786 paint->drawLine(x0, my, x1, my);
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@1367 922 double r0 = fabs(AudioLevel::multiplier_to_preview
Chris@1367 923 (range.min() * gain, m));
Chris@1367 924 double r1 = fabs(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@1367 1032
Chris@1367 1033 if (m_model->isReady()) {
Chris@1367 1034 paint->setPen(QPen(baseColour, penWidth));
Chris@1367 1035 } else {
Chris@1367 1036 paint->setPen(QPen(midColour, penWidth));
Chris@1367 1037 }
Chris@1367 1038 paint->drawPath(waveformPath);
Chris@1367 1039
Chris@1367 1040 if (!clipPath.isEmpty()) {
Chris@1367 1041 paint->save();
Chris@1367 1042 paint->setPen(QPen(ColourDatabase::getInstance()->
Chris@1367 1043 getContrastingColour(m_colour), penWidth));
Chris@1367 1044 paint->drawPath(clipPath);
Chris@1367 1045 paint->restore();
Chris@1367 1046 }
Chris@1367 1047
Chris@1367 1048 if (!meanPath.isEmpty()) {
Chris@1367 1049 paint->save();
Chris@1367 1050 paint->setPen(QPen(midColour, penWidth));
Chris@1367 1051 paint->drawPath(meanPath);
Chris@1367 1052 paint->restore();
Chris@1367 1053 }
Chris@1367 1054
Chris@1367 1055 if (!individualSamplePoints.empty()) {
Chris@1373 1056 double sz = PaintAssistant::scaleSize(2.0);
Chris@1371 1057 if (v->getZoomLevel().zone == ZoomLevel::PixelsPerFrame) {
Chris@1371 1058 if (v->getZoomLevel().level < 10) {
Chris@1373 1059 sz = PaintAssistant::scaleSize(1.2);
Chris@1371 1060 }
Chris@1371 1061 }
Chris@1367 1062 paint->save();
Chris@1367 1063 paint->setPen(QPen(baseColour, penWidth));
Chris@1367 1064 for (QPointF p: individualSamplePoints) {
Chris@1367 1065 paint->drawRect(QRectF(p.x() - sz/2, p.y() - sz/2, sz, sz));
Chris@1367 1066 }
Chris@1367 1067 paint->restore();
Chris@1332 1068 }
Chris@1332 1069 }
Chris@1332 1070
Chris@1335 1071 void
Chris@1335 1072 WaveformLayer::paintChannelScaleGuides(LayerGeometryProvider *v,
Chris@1335 1073 QPainter *paint,
Chris@1335 1074 QRect rect,
Chris@1335 1075 int ch) const
Chris@1335 1076 {
Chris@1335 1077 int x0 = rect.left();
Chris@1335 1078 int x1 = rect.right();
Chris@1335 1079
Chris@1335 1080 int n = 10;
Chris@1335 1081 int py = -1;
Chris@1335 1082
Chris@1335 1083 double gain = m_effectiveGains[ch];
Chris@1335 1084
Chris@1335 1085 if (v->hasLightBackground() &&
Chris@1335 1086 v->getViewManager() &&
Chris@1335 1087 v->getViewManager()->shouldShowScaleGuides()) {
Chris@1335 1088
Chris@1335 1089 paint->setPen(QColor(240, 240, 240));
Chris@1335 1090
Chris@1335 1091 for (int i = 1; i < n; ++i) {
Chris@1335 1092
Chris@1335 1093 double val = 0.0, nval = 0.0;
Chris@1335 1094
Chris@1335 1095 switch (m_scale) {
Chris@1335 1096
Chris@1335 1097 case LinearScale:
Chris@1335 1098 val = (i * gain) / n;
Chris@1335 1099 if (i > 0) nval = -val;
Chris@1335 1100 break;
Chris@1335 1101
Chris@1335 1102 case MeterScale:
Chris@1335 1103 val = AudioLevel::dB_to_multiplier(meterdbs[i]) * gain;
Chris@1335 1104 break;
Chris@1335 1105
Chris@1335 1106 case dBScale:
Chris@1335 1107 val = AudioLevel::dB_to_multiplier(-(10*n) + i * 10) * gain;
Chris@1335 1108 break;
Chris@1335 1109 }
Chris@1335 1110
Chris@1335 1111 if (val < -1.0 || val > 1.0) continue;
Chris@1335 1112
Chris@1335 1113 int y = getYForValue(v, val, ch);
Chris@1335 1114
Chris@1335 1115 if (py >= 0 && abs(y - py) < 10) continue;
Chris@1335 1116 else py = y;
Chris@1335 1117
Chris@1335 1118 int ny = y;
Chris@1335 1119 if (nval != 0.0) {
Chris@1335 1120 ny = getYForValue(v, nval, ch);
Chris@1335 1121 }
Chris@1335 1122
Chris@1335 1123 paint->drawLine(x0, y, x1, y);
Chris@1335 1124 if (ny != y) {
Chris@1335 1125 paint->drawLine(x0, ny, x1, ny);
Chris@1335 1126 }
Chris@1335 1127 }
Chris@1335 1128 }
Chris@1335 1129 }
Chris@1335 1130
Chris@25 1131 QString
Chris@918 1132 WaveformLayer::getFeatureDescription(LayerGeometryProvider *v, QPoint &pos) const
Chris@25 1133 {
Chris@25 1134 int x = pos.x();
Chris@25 1135
Chris@25 1136 if (!m_model || !m_model->isOK()) return "";
Chris@25 1137
Chris@1325 1138 ZoomLevel zoomLevel = v->getZoomLevel();
Chris@25 1139
Chris@1325 1140 int desiredBlockSize = 1;
Chris@1325 1141 if (zoomLevel.zone == ZoomLevel::FramesPerPixel) {
Chris@1325 1142 desiredBlockSize = zoomLevel.level;
Chris@1325 1143 }
Chris@1325 1144
Chris@1325 1145 int blockSize = m_model->getSummaryBlockSize(desiredBlockSize);
Chris@365 1146
Chris@908 1147 sv_frame_t f0, f1;
Chris@1325 1148 if (!getSourceFramesForX(v, x, blockSize, f0, f1)) return "";
Chris@25 1149
Chris@25 1150 QString text;
Chris@25 1151
Chris@25 1152 RealTime rt0 = RealTime::frame2RealTime(f0, m_model->getSampleRate());
Chris@25 1153 RealTime rt1 = RealTime::frame2RealTime(f1, m_model->getSampleRate());
Chris@25 1154
Chris@25 1155 if (f1 != f0 + 1 && (rt0.sec != rt1.sec || rt0.msec() != rt1.msec())) {
Chris@1266 1156 text += tr("Time:\t%1 - %2")
Chris@1266 1157 .arg(rt0.toText(true).c_str())
Chris@1266 1158 .arg(rt1.toText(true).c_str());
Chris@25 1159 } else {
Chris@1266 1160 text += tr("Time:\t%1")
Chris@1266 1161 .arg(rt0.toText(true).c_str());
Chris@25 1162 }
Chris@25 1163
Chris@805 1164 int channels = 0, minChannel = 0, maxChannel = 0;
Chris@67 1165 bool mergingChannels = false, mixingChannels = false;
Chris@25 1166
Chris@67 1167 channels = getChannelArrangement(minChannel, maxChannel,
Chris@67 1168 mergingChannels, mixingChannels);
Chris@25 1169 if (channels == 0) return "";
Chris@25 1170
Chris@805 1171 for (int ch = minChannel; ch <= maxChannel; ++ch) {
Chris@25 1172
Chris@1266 1173 RangeSummarisableTimeValueModel::RangeBlock ranges;
Chris@302 1174 m_model->getSummaries(ch, f0, f1 - f0, ranges, blockSize);
Chris@25 1175
Chris@1266 1176 if (ranges.empty()) continue;
Chris@1266 1177
Chris@1266 1178 RangeSummarisableTimeValueModel::Range range = ranges[0];
Chris@1266 1179
Chris@1266 1180 QString label = tr("Level:");
Chris@1266 1181 if (minChannel != maxChannel) {
Chris@1266 1182 if (ch == 0) label = tr("Left:");
Chris@1266 1183 else if (ch == 1) label = tr("Right:");
Chris@1266 1184 else label = tr("Channel %1").arg(ch + 1);
Chris@1266 1185 }
Chris@25 1186
Chris@76 1187 bool singleValue = false;
Chris@908 1188 double min, max;
Chris@76 1189
Chris@386 1190 if (fabs(range.min()) < 0.01) {
Chris@386 1191 min = range.min();
Chris@386 1192 max = range.max();
Chris@76 1193 singleValue = (min == max);
Chris@76 1194 } else {
Chris@908 1195 int imin = int(lrint(range.min() * 10000));
Chris@908 1196 int imax = int(lrint(range.max() * 10000));
Chris@76 1197 singleValue = (imin == imax);
Chris@908 1198 min = double(imin)/10000;
Chris@908 1199 max = double(imax)/10000;
Chris@76 1200 }
Chris@76 1201
Chris@1266 1202 int db = int(AudioLevel::multiplier_to_dB(std::max(fabsf(range.min()),
Chris@1266 1203 fabsf(range.max())))
Chris@1266 1204 * 100);
Chris@25 1205
Chris@1266 1206 if (!singleValue) {
Chris@1266 1207 text += tr("\n%1\t%2 - %3 (%4 dB peak)")
Chris@1266 1208 .arg(label).arg(min).arg(max).arg(double(db)/100);
Chris@1266 1209 } else {
Chris@1266 1210 text += tr("\n%1\t%2 (%3 dB peak)")
Chris@1266 1211 .arg(label).arg(min).arg(double(db)/100);
Chris@1266 1212 }
Chris@25 1213 }
Chris@25 1214
Chris@25 1215 return text;
Chris@25 1216 }
Chris@25 1217
Chris@0 1218 int
Chris@918 1219 WaveformLayer::getYForValue(const LayerGeometryProvider *v, double value, int channel) const
Chris@68 1220 {
Chris@805 1221 int channels = 0, minChannel = 0, maxChannel = 0;
Chris@274 1222 bool mergingChannels = false, mixingChannels = false;
Chris@274 1223
Chris@274 1224 channels = getChannelArrangement(minChannel, maxChannel,
Chris@274 1225 mergingChannels, mixingChannels);
Chris@853 1226 if (channels == 0) return 0;
Chris@68 1227 if (maxChannel < minChannel || channel < minChannel) return 0;
Chris@68 1228
Chris@918 1229 int h = v->getPaintHeight();
Chris@68 1230 int m = (h / channels) / 2;
Chris@1266 1231
Chris@68 1232 if ((m_scale == dBScale || m_scale == MeterScale) &&
Chris@68 1233 m_channelMode != MergeChannels) {
Chris@68 1234 m = (h / channels);
Chris@68 1235 }
Chris@68 1236
Chris@274 1237 int my = m + (((channel - minChannel) * h) / channels);
Chris@274 1238
Chris@68 1239 int vy = 0;
Chris@68 1240
Chris@274 1241 switch (m_scale) {
Chris@68 1242
Chris@68 1243 case LinearScale:
Chris@68 1244 vy = int(m * value);
Chris@68 1245 break;
Chris@68 1246
Chris@68 1247 case MeterScale:
Chris@68 1248 vy = AudioLevel::multiplier_to_preview(value, m);
Chris@68 1249 break;
Chris@68 1250
Chris@68 1251 case dBScale:
Chris@1367 1252 vy = int(dBscale(value, m));
Chris@68 1253 break;
Chris@68 1254 }
Chris@68 1255
Chris@1338 1256 // SVCERR << "mergingChannels= " << mergingChannels << ", channel = " << channel << ", value = " << value << ", vy = " << vy << endl;
Chris@324 1257
Chris@68 1258 return my - vy;
Chris@68 1259 }
Chris@68 1260
Chris@908 1261 double
Chris@918 1262 WaveformLayer::getValueForY(const LayerGeometryProvider *v, int y, int &channel) const
Chris@261 1263 {
Chris@805 1264 int channels = 0, minChannel = 0, maxChannel = 0;
Chris@274 1265 bool mergingChannels = false, mixingChannels = false;
Chris@274 1266
Chris@274 1267 channels = getChannelArrangement(minChannel, maxChannel,
Chris@274 1268 mergingChannels, mixingChannels);
Chris@853 1269 if (channels == 0) return 0;
Chris@261 1270 if (maxChannel < minChannel) return 0;
Chris@261 1271
Chris@918 1272 int h = v->getPaintHeight();
Chris@261 1273 int m = (h / channels) / 2;
Chris@261 1274
Chris@261 1275 if ((m_scale == dBScale || m_scale == MeterScale) &&
Chris@261 1276 m_channelMode != MergeChannels) {
Chris@261 1277 m = (h / channels);
Chris@261 1278 }
Chris@274 1279
Chris@274 1280 channel = (y * channels) / h + minChannel;
Chris@261 1281
Chris@261 1282 int my = m + (((channel - minChannel) * h) / channels);
Chris@261 1283
Chris@262 1284 int vy = my - y;
Chris@908 1285 double value = 0;
Chris@908 1286 double thresh = -50.f;
Chris@261 1287
Chris@274 1288 switch (m_scale) {
Chris@261 1289
Chris@261 1290 case LinearScale:
Chris@908 1291 value = double(vy) / m;
Chris@261 1292 break;
Chris@261 1293
Chris@261 1294 case MeterScale:
Chris@261 1295 value = AudioLevel::preview_to_multiplier(vy, m);
Chris@261 1296 break;
Chris@261 1297
Chris@261 1298 case dBScale:
Chris@908 1299 value = (-thresh * double(vy)) / m + thresh;
Chris@274 1300 value = AudioLevel::dB_to_multiplier(value);
Chris@261 1301 break;
Chris@261 1302 }
Chris@261 1303
Chris@274 1304 return value / m_gain;
Chris@261 1305 }
Chris@261 1306
Chris@261 1307 bool
Chris@918 1308 WaveformLayer::getYScaleValue(const LayerGeometryProvider *v, int y,
Chris@908 1309 double &value, QString &unit) const
Chris@261 1310 {
Chris@805 1311 int channel;
Chris@261 1312
Chris@274 1313 value = getValueForY(v, y, channel);
Chris@261 1314
Chris@274 1315 if (m_scale == dBScale || m_scale == MeterScale) {
Chris@261 1316
Chris@908 1317 double thresh = -50.f;
Chris@274 1318
Chris@908 1319 if (value > 0.0) {
Chris@908 1320 value = 10.0 * log10(value);
Chris@274 1321 if (value < thresh) value = thresh;
Chris@274 1322 } else value = thresh;
Chris@274 1323
Chris@274 1324 unit = "dBV";
Chris@274 1325
Chris@274 1326 } else {
Chris@274 1327 unit = "V";
Chris@274 1328 }
Chris@274 1329
Chris@274 1330 return true;
Chris@274 1331 }
Chris@274 1332
Chris@274 1333 bool
Chris@918 1334 WaveformLayer::getYScaleDifference(const LayerGeometryProvider *v, int y0, int y1,
Chris@908 1335 double &diff, QString &unit) const
Chris@274 1336 {
Chris@805 1337 int c0, c1;
Chris@908 1338 double v0 = getValueForY(v, y0, c0);
Chris@908 1339 double v1 = getValueForY(v, y1, c1);
Chris@274 1340
Chris@274 1341 if (c0 != c1) {
Chris@274 1342 // different channels, not comparable
Chris@908 1343 diff = 0.0;
Chris@274 1344 unit = "";
Chris@274 1345 return false;
Chris@274 1346 }
Chris@274 1347
Chris@274 1348 if (m_scale == dBScale || m_scale == MeterScale) {
Chris@274 1349
Chris@908 1350 double thresh = -50.0;
Chris@274 1351
Chris@274 1352 if (v1 == v0) diff = thresh;
Chris@274 1353 else {
Chris@274 1354 if (v1 > v0) diff = v0 / v1;
Chris@274 1355 else diff = v1 / v0;
Chris@274 1356
Chris@908 1357 diff = 10.0 * log10(diff);
Chris@274 1358 if (diff < thresh) diff = thresh;
Chris@274 1359 }
Chris@274 1360
Chris@274 1361 unit = "dBV";
Chris@274 1362
Chris@274 1363 } else {
Chris@908 1364 diff = fabs(v1 - v0);
Chris@274 1365 unit = "V";
Chris@274 1366 }
Chris@274 1367
Chris@261 1368 return true;
Chris@261 1369 }
Chris@261 1370
Chris@68 1371 int
Chris@918 1372 WaveformLayer::getVerticalScaleWidth(LayerGeometryProvider *, bool, QPainter &paint) const
Chris@0 1373 {
Chris@0 1374 if (m_scale == LinearScale) {
Chris@1266 1375 return paint.fontMetrics().width("0.0") + 13;
Chris@0 1376 } else {
Chris@1266 1377 return std::max(paint.fontMetrics().width(tr("0dB")),
Chris@1266 1378 paint.fontMetrics().width(Strings::minus_infinity)) + 13;
Chris@0 1379 }
Chris@0 1380 }
Chris@0 1381
Chris@0 1382 void
Chris@918 1383 WaveformLayer::paintVerticalScale(LayerGeometryProvider *v, bool, QPainter &paint, QRect rect) const
Chris@0 1384 {
Chris@0 1385 if (!m_model || !m_model->isOK()) {
Chris@1266 1386 return;
Chris@0 1387 }
Chris@0 1388
Chris@805 1389 int channels = 0, minChannel = 0, maxChannel = 0;
Chris@67 1390 bool mergingChannels = false, mixingChannels = false;
Chris@0 1391
Chris@67 1392 channels = getChannelArrangement(minChannel, maxChannel,
Chris@67 1393 mergingChannels, mixingChannels);
Chris@0 1394 if (channels == 0) return;
Chris@0 1395
Chris@0 1396 int h = rect.height(), w = rect.width();
Chris@0 1397 int textHeight = paint.fontMetrics().height();
Chris@0 1398 int toff = -textHeight/2 + paint.fontMetrics().ascent() + 1;
Chris@0 1399
Chris@908 1400 double gain = m_gain;
Chris@67 1401
Chris@805 1402 for (int ch = minChannel; ch <= maxChannel; ++ch) {
Chris@0 1403
Chris@1266 1404 int lastLabelledY = -1;
Chris@0 1405
Chris@805 1406 if (ch < (int)m_effectiveGains.size()) gain = m_effectiveGains[ch];
Chris@67 1407
Chris@68 1408 int n = 10;
Chris@0 1409
Chris@1266 1410 for (int i = 0; i <= n; ++i) {
Chris@68 1411
Chris@908 1412 double val = 0.0, nval = 0.0;
Chris@1266 1413 QString text = "";
Chris@0 1414
Chris@68 1415 switch (m_scale) {
Chris@68 1416
Chris@68 1417 case LinearScale:
Chris@68 1418 val = (i * gain) / n;
Chris@1266 1419 text = QString("%1").arg(double(i) / n);
Chris@1266 1420 if (i == 0) text = "0.0";
Chris@68 1421 else {
Chris@68 1422 nval = -val;
Chris@68 1423 if (i == n) text = "1.0";
Chris@68 1424 }
Chris@68 1425 break;
Chris@0 1426
Chris@68 1427 case MeterScale:
Chris@68 1428 val = AudioLevel::dB_to_multiplier(meterdbs[i]) * gain;
Chris@1266 1429 text = QString("%1").arg(meterdbs[i]);
Chris@1266 1430 if (i == n) text = tr("0dB");
Chris@1266 1431 if (i == 0) {
Chris@1147 1432 text = Strings::minus_infinity;
Chris@68 1433 val = 0.0;
Chris@1266 1434 }
Chris@68 1435 break;
Chris@0 1436
Chris@68 1437 case dBScale:
Chris@68 1438 val = AudioLevel::dB_to_multiplier(-(10*n) + i * 10) * gain;
Chris@1266 1439 text = QString("%1").arg(-(10*n) + i * 10);
Chris@1266 1440 if (i == n) text = tr("0dB");
Chris@1266 1441 if (i == 0) {
Chris@1147 1442 text = Strings::minus_infinity;
Chris@68 1443 val = 0.0;
Chris@1266 1444 }
Chris@68 1445 break;
Chris@68 1446 }
Chris@0 1447
Chris@68 1448 if (val < -1.0 || val > 1.0) continue;
Chris@0 1449
Chris@274 1450 int y = getYForValue(v, val, ch);
Chris@0 1451
Chris@68 1452 int ny = y;
Chris@68 1453 if (nval != 0.0) {
Chris@274 1454 ny = getYForValue(v, nval, ch);
Chris@68 1455 }
Chris@0 1456
Chris@68 1457 bool spaceForLabel = (i == 0 ||
Chris@68 1458 abs(y - lastLabelledY) >= textHeight - 1);
Chris@0 1459
Chris@68 1460 if (spaceForLabel) {
Chris@0 1461
Chris@68 1462 int tx = 3;
Chris@68 1463 if (m_scale != LinearScale) {
Chris@68 1464 tx = w - 10 - paint.fontMetrics().width(text);
Chris@68 1465 }
Chris@68 1466
Chris@68 1467 int ty = y;
Chris@68 1468 if (ty < paint.fontMetrics().ascent()) {
Chris@68 1469 ty = paint.fontMetrics().ascent();
Chris@68 1470 } else if (ty > h - paint.fontMetrics().descent()) {
Chris@68 1471 ty = h - paint.fontMetrics().descent();
Chris@68 1472 } else {
Chris@68 1473 ty += toff;
Chris@68 1474 }
Chris@68 1475 paint.drawText(tx, ty, text);
Chris@0 1476
Chris@68 1477 lastLabelledY = ty - toff;
Chris@67 1478
Chris@68 1479 if (ny != y) {
Chris@68 1480 ty = ny;
Chris@68 1481 if (ty < paint.fontMetrics().ascent()) {
Chris@68 1482 ty = paint.fontMetrics().ascent();
Chris@68 1483 } else if (ty > h - paint.fontMetrics().descent()) {
Chris@68 1484 ty = h - paint.fontMetrics().descent();
Chris@68 1485 } else {
Chris@68 1486 ty += toff;
Chris@68 1487 }
Chris@68 1488 paint.drawText(tx, ty, text);
Chris@68 1489 }
Chris@68 1490
Chris@68 1491 paint.drawLine(w - 7, y, w, y);
Chris@68 1492 if (ny != y) paint.drawLine(w - 7, ny, w, ny);
Chris@68 1493
Chris@68 1494 } else {
Chris@68 1495
Chris@68 1496 paint.drawLine(w - 4, y, w, y);
Chris@68 1497 if (ny != y) paint.drawLine(w - 4, ny, w, ny);
Chris@68 1498 }
Chris@1266 1499 }
Chris@0 1500 }
Chris@0 1501 }
Chris@0 1502
Chris@316 1503 void
Chris@316 1504 WaveformLayer::toXml(QTextStream &stream,
Chris@316 1505 QString indent, QString extraAttributes) const
Chris@6 1506 {
Chris@6 1507 QString s;
Chris@6 1508
Chris@285 1509 QString colourName, colourSpec, darkbg;
Chris@285 1510 ColourDatabase::getInstance()->getStringValues
Chris@285 1511 (m_colour, colourName, colourSpec, darkbg);
Chris@285 1512
Chris@6 1513 s += QString("gain=\"%1\" "
Chris@1266 1514 "showMeans=\"%2\" "
Chris@1266 1515 "greyscale=\"%3\" "
Chris@1266 1516 "channelMode=\"%4\" "
Chris@1266 1517 "channel=\"%5\" "
Chris@287 1518 "scale=\"%6\" "
Chris@709 1519 "middleLineHeight=\"%7\" "
Chris@1266 1520 "aggressive=\"%8\" "
Chris@709 1521 "autoNormalize=\"%9\"")
Chris@1266 1522 .arg(m_gain)
Chris@1266 1523 .arg(m_showMeans)
Chris@1367 1524 .arg(true) // Option removed, but effectively always on, so
Chris@1367 1525 // retained in the session file for compatibility
Chris@1266 1526 .arg(m_channelMode)
Chris@1266 1527 .arg(m_channel)
Chris@1266 1528 .arg(m_scale)
Chris@709 1529 .arg(m_middleLineHeight)
Chris@1266 1530 .arg(m_aggressive)
Chris@67 1531 .arg(m_autoNormalize);
Chris@6 1532
Chris@316 1533 SingleColourLayer::toXml(stream, indent, extraAttributes + " " + s);
Chris@6 1534 }
Chris@6 1535
Chris@11 1536 void
Chris@11 1537 WaveformLayer::setProperties(const QXmlAttributes &attributes)
Chris@11 1538 {
Chris@11 1539 bool ok = false;
Chris@11 1540
Chris@287 1541 SingleColourLayer::setProperties(attributes);
Chris@287 1542
Chris@11 1543 float gain = attributes.value("gain").toFloat(&ok);
Chris@11 1544 if (ok) setGain(gain);
Chris@11 1545
Chris@11 1546 bool showMeans = (attributes.value("showMeans") == "1" ||
Chris@1266 1547 attributes.value("showMeans") == "true");
Chris@11 1548 setShowMeans(showMeans);
Chris@11 1549
Chris@11 1550 ChannelMode channelMode = (ChannelMode)
Chris@1266 1551 attributes.value("channelMode").toInt(&ok);
Chris@11 1552 if (ok) setChannelMode(channelMode);
Chris@11 1553
Chris@11 1554 int channel = attributes.value("channel").toInt(&ok);
Chris@11 1555 if (ok) setChannel(channel);
Chris@11 1556
Chris@709 1557 Scale scale = (Scale)attributes.value("scale").toInt(&ok);
Chris@11 1558 if (ok) setScale(scale);
Chris@11 1559
Chris@709 1560 float middleLineHeight = attributes.value("middleLineHeight").toFloat(&ok);
Chris@709 1561 if (ok) setMiddleLineHeight(middleLineHeight);
Chris@709 1562
Chris@11 1563 bool aggressive = (attributes.value("aggressive") == "1" ||
Chris@1266 1564 attributes.value("aggressive") == "true");
Chris@1367 1565 setAggressiveCacheing(aggressive);
Chris@67 1566
Chris@67 1567 bool autoNormalize = (attributes.value("autoNormalize") == "1" ||
Chris@67 1568 attributes.value("autoNormalize") == "true");
Chris@67 1569 setAutoNormalize(autoNormalize);
Chris@11 1570 }
Chris@11 1571
Chris@133 1572 int
Chris@133 1573 WaveformLayer::getVerticalZoomSteps(int &defaultStep) const
Chris@133 1574 {
Chris@133 1575 defaultStep = 50;
Chris@133 1576 return 100;
Chris@133 1577 }
Chris@0 1578
Chris@133 1579 int
Chris@133 1580 WaveformLayer::getCurrentVerticalZoomStep() const
Chris@133 1581 {
Chris@908 1582 int val = int(lrint(log10(m_gain) * 20.0) + 50);
Chris@133 1583 if (val < 0) val = 0;
Chris@133 1584 if (val > 100) val = 100;
Chris@133 1585 return val;
Chris@133 1586 }
Chris@133 1587
Chris@133 1588 void
Chris@133 1589 WaveformLayer::setVerticalZoomStep(int step)
Chris@133 1590 {
Chris@908 1591 setGain(powf(10, float(step - 50) / 20.f));
Chris@133 1592 }
Chris@133 1593