annotate layer/WaveformLayer.cpp @ 1135:628cd329c241 spectrogram-minor-refactor

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