annotate layer/WaveformLayer.cpp @ 1551:e79731086b0f

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