annotate layer/WaveformLayer.cpp @ 333:e74b56f07c73

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