annotate layer/WaveformLayer.cpp @ 77:fd348f36c0d3

* Implement harmonic cursor in spectrogram * Implement layer export. This doesn't quite do the right thing for the SV XML layer export yet -- it doesn't include layer display information, so when imported, it only creates an invisible model. Could also do with fixing CSV file import so as to work correctly for note and text layers.
author Chris Cannam
date Mon, 10 Apr 2006 17:22:59 +0000
parents 45ba0b381c5d
children 19bf27e4fb29
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@59 7 This file copyright 2006 Chris Cannam.
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@0 19 #include "base/View.h"
Chris@0 20 #include "base/Profiler.h"
Chris@0 21
Chris@0 22 #include <QPainter>
Chris@0 23 #include <QPixmap>
Chris@0 24
Chris@0 25 #include <iostream>
Chris@0 26 #include <cmath>
Chris@0 27
Chris@4 28 //#define DEBUG_WAVEFORM_PAINT 1
Chris@4 29
Chris@0 30 using std::cerr;
Chris@0 31 using std::endl;
Chris@0 32
Chris@44 33 WaveformLayer::WaveformLayer() :
Chris@44 34 Layer(),
Chris@0 35 m_model(0),
Chris@0 36 m_gain(1.0f),
Chris@67 37 m_autoNormalize(false),
Chris@0 38 m_colour(Qt::black),
Chris@0 39 m_showMeans(true),
Chris@0 40 m_greyscale(true),
Chris@0 41 m_channelMode(SeparateChannels),
Chris@0 42 m_channel(-1),
Chris@0 43 m_scale(LinearScale),
Chris@0 44 m_aggressive(false),
Chris@0 45 m_cache(0),
Chris@0 46 m_cacheValid(false)
Chris@0 47 {
Chris@44 48
Chris@0 49 }
Chris@0 50
Chris@0 51 WaveformLayer::~WaveformLayer()
Chris@0 52 {
Chris@0 53 delete m_cache;
Chris@0 54 }
Chris@0 55
Chris@0 56 void
Chris@0 57 WaveformLayer::setModel(const RangeSummarisableTimeValueModel *model)
Chris@0 58 {
Chris@69 59 bool channelsChanged = false;
Chris@69 60 if (m_channel == -1) {
Chris@69 61 if (!m_model) {
Chris@69 62 if (model) {
Chris@69 63 channelsChanged = true;
Chris@69 64 }
Chris@69 65 } else {
Chris@69 66 if (model &&
Chris@69 67 m_model->getChannelCount() != model->getChannelCount()) {
Chris@69 68 channelsChanged = true;
Chris@69 69 }
Chris@69 70 }
Chris@69 71 }
Chris@69 72
Chris@0 73 m_model = model;
Chris@0 74 m_cacheValid = false;
Chris@0 75 if (!m_model || !m_model->isOK()) return;
Chris@0 76
Chris@0 77 connect(m_model, SIGNAL(modelChanged()), this, SIGNAL(modelChanged()));
Chris@0 78 connect(m_model, SIGNAL(modelChanged(size_t, size_t)),
Chris@0 79 this, SIGNAL(modelChanged(size_t, size_t)));
Chris@0 80
Chris@0 81 connect(m_model, SIGNAL(completionChanged()),
Chris@0 82 this, SIGNAL(modelCompletionChanged()));
Chris@0 83
Chris@0 84 emit modelReplaced();
Chris@69 85
Chris@69 86 if (channelsChanged) emit layerParametersChanged();
Chris@0 87 }
Chris@0 88
Chris@0 89 Layer::PropertyList
Chris@0 90 WaveformLayer::getProperties() const
Chris@0 91 {
Chris@0 92 PropertyList list;
Chris@0 93 list.push_back(tr("Colour"));
Chris@0 94 list.push_back(tr("Scale"));
Chris@0 95 list.push_back(tr("Gain"));
Chris@67 96 list.push_back(tr("Normalize Visible Area"));
Chris@68 97
Chris@68 98 if (m_model && m_model->getChannelCount() > 1 && m_channel == -1) {
Chris@68 99 list.push_back(tr("Channels"));
Chris@68 100 }
Chris@68 101
Chris@0 102 return list;
Chris@0 103 }
Chris@0 104
Chris@0 105 Layer::PropertyType
Chris@0 106 WaveformLayer::getPropertyType(const PropertyName &name) const
Chris@0 107 {
Chris@0 108 if (name == tr("Gain")) return RangeProperty;
Chris@67 109 if (name == tr("Normalize Visible Area")) return ToggleProperty;
Chris@0 110 if (name == tr("Colour")) return ValueProperty;
Chris@67 111 if (name == tr("Channels")) return ValueProperty;
Chris@0 112 if (name == tr("Scale")) return ValueProperty;
Chris@0 113 return InvalidProperty;
Chris@0 114 }
Chris@0 115
Chris@0 116 QString
Chris@0 117 WaveformLayer::getPropertyGroupName(const PropertyName &name) const
Chris@0 118 {
Chris@0 119 if (name == tr("Gain") ||
Chris@67 120 name == tr("Normalize Visible Area") ||
Chris@0 121 name == tr("Scale")) return tr("Scale");
Chris@0 122 return QString();
Chris@0 123 }
Chris@0 124
Chris@0 125 int
Chris@0 126 WaveformLayer::getPropertyRangeAndValue(const PropertyName &name,
Chris@67 127 int *min, int *max) const
Chris@0 128 {
Chris@0 129 int deft = 0;
Chris@0 130
Chris@56 131 int garbage0, garbage1;
Chris@56 132 if (!min) min = &garbage0;
Chris@56 133 if (!max) max = &garbage1;
Chris@10 134
Chris@0 135 if (name == tr("Gain")) {
Chris@0 136
Chris@0 137 *min = -50;
Chris@0 138 *max = 50;
Chris@0 139
Chris@34 140 deft = lrint(log10(m_gain) * 20.0);
Chris@0 141 if (deft < *min) deft = *min;
Chris@0 142 if (deft > *max) deft = *max;
Chris@0 143
Chris@67 144 } else if (name == tr("Normalize Visible Area")) {
Chris@67 145
Chris@67 146 deft = (m_autoNormalize ? 1 : 0);
Chris@67 147
Chris@0 148 } else if (name == tr("Colour")) {
Chris@0 149
Chris@0 150 *min = 0;
Chris@0 151 *max = 5;
Chris@0 152
Chris@0 153 if (m_colour == Qt::black) deft = 0;
Chris@0 154 else if (m_colour == Qt::darkRed) deft = 1;
Chris@0 155 else if (m_colour == Qt::darkBlue) deft = 2;
Chris@0 156 else if (m_colour == Qt::darkGreen) deft = 3;
Chris@0 157 else if (m_colour == QColor(200, 50, 255)) deft = 4;
Chris@0 158 else if (m_colour == QColor(255, 150, 50)) deft = 5;
Chris@0 159
Chris@67 160 } else if (name == tr("Channels")) {
Chris@0 161
Chris@67 162 *min = 0;
Chris@67 163 *max = 2;
Chris@67 164 if (m_channelMode == MixChannels) deft = 1;
Chris@67 165 else if (m_channelMode == MergeChannels) deft = 2;
Chris@67 166 else deft = 0;
Chris@0 167
Chris@0 168 } else if (name == tr("Scale")) {
Chris@0 169
Chris@0 170 *min = 0;
Chris@0 171 *max = 2;
Chris@0 172
Chris@0 173 deft = (int)m_scale;
Chris@0 174
Chris@0 175 } else {
Chris@0 176 deft = Layer::getPropertyRangeAndValue(name, min, max);
Chris@0 177 }
Chris@0 178
Chris@0 179 return deft;
Chris@0 180 }
Chris@0 181
Chris@0 182 QString
Chris@0 183 WaveformLayer::getPropertyValueLabel(const PropertyName &name,
Chris@0 184 int value) const
Chris@0 185 {
Chris@0 186 if (name == tr("Colour")) {
Chris@0 187 switch (value) {
Chris@0 188 default:
Chris@0 189 case 0: return tr("Black");
Chris@0 190 case 1: return tr("Red");
Chris@0 191 case 2: return tr("Blue");
Chris@0 192 case 3: return tr("Green");
Chris@0 193 case 4: return tr("Purple");
Chris@0 194 case 5: return tr("Orange");
Chris@0 195 }
Chris@0 196 }
Chris@0 197 if (name == tr("Scale")) {
Chris@0 198 switch (value) {
Chris@0 199 default:
Chris@0 200 case 0: return tr("Linear");
Chris@0 201 case 1: return tr("Meter");
Chris@0 202 case 2: return tr("dB");
Chris@0 203 }
Chris@0 204 }
Chris@67 205 if (name == tr("Channels")) {
Chris@67 206 switch (value) {
Chris@67 207 default:
Chris@67 208 case 0: return tr("Separate");
Chris@67 209 case 1: return tr("Mean");
Chris@67 210 case 2: return tr("Butterfly");
Chris@67 211 }
Chris@67 212 }
Chris@0 213 return tr("<unknown>");
Chris@0 214 }
Chris@0 215
Chris@0 216 void
Chris@0 217 WaveformLayer::setProperty(const PropertyName &name, int value)
Chris@0 218 {
Chris@0 219 if (name == tr("Gain")) {
Chris@0 220 setGain(pow(10, float(value)/20.0));
Chris@67 221 } else if (name == tr("Normalize Visible Area")) {
Chris@67 222 setAutoNormalize(value ? true : false);
Chris@0 223 } else if (name == tr("Colour")) {
Chris@0 224 switch (value) {
Chris@0 225 default:
Chris@0 226 case 0: setBaseColour(Qt::black); break;
Chris@0 227 case 1: setBaseColour(Qt::darkRed); break;
Chris@0 228 case 2: setBaseColour(Qt::darkBlue); break;
Chris@0 229 case 3: setBaseColour(Qt::darkGreen); break;
Chris@0 230 case 4: setBaseColour(QColor(200, 50, 255)); break;
Chris@0 231 case 5: setBaseColour(QColor(255, 150, 50)); break;
Chris@0 232 }
Chris@67 233 } else if (name == tr("Channels")) {
Chris@67 234 if (value == 1) setChannelMode(MixChannels);
Chris@67 235 else if (value == 2) setChannelMode(MergeChannels);
Chris@67 236 else setChannelMode(SeparateChannels);
Chris@0 237 } else if (name == tr("Scale")) {
Chris@0 238 switch (value) {
Chris@0 239 default:
Chris@0 240 case 0: setScale(LinearScale); break;
Chris@0 241 case 1: setScale(MeterScale); break;
Chris@0 242 case 2: setScale(dBScale); break;
Chris@0 243 }
Chris@0 244 }
Chris@0 245 }
Chris@0 246
Chris@0 247 void
Chris@67 248 WaveformLayer::setGain(float gain)
Chris@0 249 {
Chris@0 250 if (m_gain == gain) return;
Chris@0 251 m_gain = gain;
Chris@0 252 m_cacheValid = false;
Chris@0 253 emit layerParametersChanged();
Chris@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::setBaseColour(QColor colour)
Chris@0 267 {
Chris@0 268 if (m_colour == colour) return;
Chris@0 269 m_colour = colour;
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::setShowMeans(bool showMeans)
Chris@0 276 {
Chris@0 277 if (m_showMeans == showMeans) return;
Chris@0 278 m_showMeans = showMeans;
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::setUseGreyscale(bool useGreyscale)
Chris@0 285 {
Chris@0 286 if (m_greyscale == useGreyscale) return;
Chris@0 287 m_greyscale = useGreyscale;
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::setChannelMode(ChannelMode channelMode)
Chris@0 294 {
Chris@0 295 if (m_channelMode == channelMode) return;
Chris@0 296 m_channelMode = channelMode;
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::setChannel(int channel)
Chris@0 303 {
Chris@0 304 std::cerr << "WaveformLayer::setChannel(" << channel << ")" << std::endl;
Chris@0 305
Chris@0 306 if (m_channel == channel) return;
Chris@0 307 m_channel = channel;
Chris@0 308 m_cacheValid = false;
Chris@0 309 emit layerParametersChanged();
Chris@0 310 }
Chris@0 311
Chris@0 312 void
Chris@0 313 WaveformLayer::setScale(Scale scale)
Chris@0 314 {
Chris@0 315 if (m_scale == scale) return;
Chris@0 316 m_scale = scale;
Chris@0 317 m_cacheValid = false;
Chris@0 318 emit layerParametersChanged();
Chris@0 319 }
Chris@0 320
Chris@0 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@0 331 WaveformLayer::getCompletion() 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@0 339 int
Chris@0 340 WaveformLayer::dBscale(float sample, int m) const
Chris@0 341 {
Chris@67 342 //!!! if (sample < 0.0) return -dBscale(-sample, m);
Chris@67 343 if (sample < 0.0) return dBscale(-sample, m);
Chris@0 344 float dB = AudioLevel::multiplier_to_dB(sample);
Chris@0 345 if (dB < -50.0) return 0;
Chris@0 346 if (dB > 0.0) return m;
Chris@0 347 return int(((dB + 50.0) * m) / 50.0 + 0.1);
Chris@0 348 }
Chris@0 349
Chris@0 350 size_t
Chris@67 351 WaveformLayer::getChannelArrangement(size_t &min, size_t &max,
Chris@67 352 bool &merging, bool &mixing)
Chris@0 353 const
Chris@0 354 {
Chris@0 355 if (!m_model || !m_model->isOK()) return 0;
Chris@0 356
Chris@0 357 size_t channels = m_model->getChannelCount();
Chris@0 358 if (channels == 0) return 0;
Chris@0 359
Chris@0 360 size_t rawChannels = channels;
Chris@0 361
Chris@0 362 if (m_channel == -1) {
Chris@0 363 min = 0;
Chris@67 364 if (m_channelMode == MergeChannels ||
Chris@67 365 m_channelMode == MixChannels) {
Chris@0 366 max = 0;
Chris@0 367 channels = 1;
Chris@0 368 } else {
Chris@0 369 max = channels - 1;
Chris@0 370 }
Chris@0 371 } else {
Chris@0 372 min = m_channel;
Chris@0 373 max = m_channel;
Chris@0 374 rawChannels = 1;
Chris@0 375 channels = 1;
Chris@0 376 }
Chris@0 377
Chris@0 378 merging = (m_channelMode == MergeChannels && rawChannels > 1);
Chris@67 379 mixing = (m_channelMode == MixChannels && rawChannels > 1);
Chris@0 380
Chris@0 381 // std::cerr << "WaveformLayer::getChannelArrangement: min " << min << ", max " << max << ", merging " << merging << ", channels " << channels << std::endl;
Chris@0 382
Chris@0 383 return channels;
Chris@0 384 }
Chris@0 385
Chris@67 386 bool
Chris@67 387 WaveformLayer::isLayerScrollable(const View *) const
Chris@67 388 {
Chris@67 389 return !m_autoNormalize;
Chris@67 390 }
Chris@67 391
Chris@68 392 static float meterdbs[] = { -40, -30, -20, -15, -10,
Chris@68 393 -5, -3, -2, -1, -0.5, 0 };
Chris@68 394
Chris@0 395 void
Chris@44 396 WaveformLayer::paint(View *v, QPainter &viewPainter, QRect rect) const
Chris@0 397 {
Chris@0 398 if (!m_model || !m_model->isOK()) {
Chris@0 399 return;
Chris@0 400 }
Chris@0 401
Chris@44 402 long startFrame = v->getStartFrame();
Chris@44 403 int zoomLevel = v->getZoomLevel();
Chris@0 404
Chris@2 405 #ifdef DEBUG_WAVEFORM_PAINT
Chris@0 406 Profiler profiler("WaveformLayer::paint", true);
Chris@0 407 std::cerr << "WaveformLayer::paint (" << rect.x() << "," << rect.y()
Chris@0 408 << ") [" << rect.width() << "x" << rect.height() << "]: zoom " << zoomLevel << ", start " << startFrame << std::endl;
Chris@2 409 #endif
Chris@0 410
Chris@0 411 size_t channels = 0, minChannel = 0, maxChannel = 0;
Chris@67 412 bool mergingChannels = false, mixingChannels = false;
Chris@0 413
Chris@67 414 channels = getChannelArrangement(minChannel, maxChannel,
Chris@67 415 mergingChannels, mixingChannels);
Chris@0 416 if (channels == 0) return;
Chris@0 417
Chris@44 418 int w = v->width();
Chris@44 419 int h = v->height();
Chris@0 420
Chris@0 421 bool ready = m_model->isReady();
Chris@0 422 QPainter *paint;
Chris@0 423
Chris@0 424 if (m_aggressive) {
Chris@0 425
Chris@0 426 if (m_cacheValid && (zoomLevel != m_cacheZoomLevel)) {
Chris@0 427 m_cacheValid = false;
Chris@0 428 }
Chris@0 429
Chris@0 430 if (m_cacheValid) {
Chris@0 431 viewPainter.drawPixmap(rect, *m_cache, rect);
Chris@0 432 return;
Chris@0 433 }
Chris@0 434
Chris@0 435 if (!m_cache || m_cache->width() != w || m_cache->height() != h) {
Chris@0 436 delete m_cache;
Chris@0 437 m_cache = new QPixmap(w, h);
Chris@0 438 }
Chris@0 439
Chris@0 440 paint = new QPainter(m_cache);
Chris@0 441
Chris@0 442 paint->setPen(Qt::NoPen);
Chris@44 443 paint->setBrush(v->palette().background());
Chris@0 444 paint->drawRect(rect);
Chris@0 445
Chris@0 446 paint->setPen(Qt::black);
Chris@0 447 paint->setBrush(Qt::NoBrush);
Chris@0 448
Chris@0 449 } else {
Chris@0 450 paint = &viewPainter;
Chris@0 451 }
Chris@0 452
Chris@28 453 paint->setRenderHint(QPainter::Antialiasing, false);
Chris@28 454
Chris@0 455 int x0 = 0, x1 = w - 1;
Chris@0 456 int y0 = 0, y1 = h - 1;
Chris@0 457
Chris@0 458 x0 = rect.left();
Chris@0 459 x1 = rect.right();
Chris@0 460 y0 = rect.top();
Chris@0 461 y1 = rect.bottom();
Chris@0 462
Chris@28 463 if (x0 > 0) --x0;
Chris@44 464 if (x1 < v->width()) ++x1;
Chris@28 465
Chris@44 466 long frame0 = v->getFrameForX(x0);
Chris@44 467 long frame1 = v->getFrameForX(x1 + 1);
Chris@0 468
Chris@4 469 #ifdef DEBUG_WAVEFORM_PAINT
Chris@4 470 std::cerr << "Painting waveform from " << frame0 << " to " << frame1 << " (" << (x1-x0+1) << " pixels at zoom " << zoomLevel << ")" << std::endl;
Chris@4 471 #endif
Chris@0 472
Chris@0 473 RangeSummarisableTimeValueModel::RangeBlock ranges;
Chris@0 474 RangeSummarisableTimeValueModel::RangeBlock otherChannelRanges;
Chris@0 475 RangeSummarisableTimeValueModel::Range range;
Chris@0 476
Chris@0 477 QColor greys[3];
Chris@0 478 if (m_colour == Qt::black) {
Chris@27 479 for (int i = 0; i < 3; ++i) { // 0 lightest, 2 darkest
Chris@0 480 int level = 192 - 64 * i;
Chris@0 481 greys[i] = QColor(level, level, level);
Chris@0 482 }
Chris@0 483 } else {
Chris@44 484 int hue, sat, val;
Chris@44 485 m_colour.getHsv(&hue, &sat, &val);
Chris@27 486 for (int i = 0; i < 3; ++i) { // 0 lightest, 2 darkest
Chris@44 487 if (v->hasLightBackground()) {
Chris@44 488 greys[i] = QColor::fromHsv(hue, sat * (i + 1) / 4, val);
Chris@27 489 } else {
Chris@44 490 greys[i] = QColor::fromHsv(hue, sat * (3 - i) / 4, val);
Chris@27 491 }
Chris@27 492 }
Chris@0 493 }
Chris@0 494
Chris@0 495 QColor midColour = m_colour;
Chris@0 496 if (midColour == Qt::black) {
Chris@0 497 midColour = Qt::gray;
Chris@44 498 } else if (v->hasLightBackground()) {
Chris@0 499 midColour = midColour.light(150);
Chris@0 500 } else {
Chris@0 501 midColour = midColour.light(50);
Chris@0 502 }
Chris@0 503
Chris@67 504 while (m_effectiveGains.size() <= maxChannel) {
Chris@67 505 m_effectiveGains.push_back(m_gain);
Chris@67 506 }
Chris@67 507
Chris@0 508 for (size_t ch = minChannel; ch <= maxChannel; ++ch) {
Chris@0 509
Chris@0 510 int prevRangeBottom = -1, prevRangeTop = -1;
Chris@28 511 QColor prevRangeBottomColour = m_colour, prevRangeTopColour = m_colour;
Chris@0 512
Chris@67 513 m_effectiveGains[ch] = m_gain;
Chris@67 514
Chris@67 515 if (m_autoNormalize) {
Chris@67 516 RangeSummarisableTimeValueModel::Range range =
Chris@67 517 m_model->getRange(ch, startFrame < 0 ? 0 : startFrame,
Chris@67 518 v->getEndFrame());
Chris@67 519 if (mergingChannels || mixingChannels) {
Chris@67 520 RangeSummarisableTimeValueModel::Range otherRange =
Chris@67 521 m_model->getRange(1, startFrame < 0 ? 0 : startFrame,
Chris@67 522 v->getEndFrame());
Chris@67 523 range.max = std::max(range.max, otherRange.max);
Chris@67 524 range.min = std::min(range.min, otherRange.min);
Chris@67 525 range.absmean = std::min(range.absmean, otherRange.absmean);
Chris@67 526 }
Chris@67 527 m_effectiveGains[ch] = 1.0 / std::max(fabsf(range.max),
Chris@67 528 fabsf(range.min));
Chris@67 529 }
Chris@67 530
Chris@67 531 float gain = m_effectiveGains[ch];
Chris@67 532
Chris@68 533 int m = (h / channels) / 2;
Chris@68 534 int my = m + (((ch - minChannel) * h) / channels);
Chris@68 535
Chris@68 536 // std::cerr << "ch = " << ch << ", channels = " << channels << ", m = " << m << ", my = " << my << ", h = " << h << std::endl;
Chris@68 537
Chris@68 538 if (my - m > y1 || my + m < y0) continue;
Chris@68 539
Chris@68 540 if ((m_scale == dBScale || m_scale == MeterScale) &&
Chris@68 541 m_channelMode != MergeChannels) {
Chris@68 542 m = (h / channels);
Chris@68 543 my = m + (((ch - minChannel) * h) / channels);
Chris@68 544 }
Chris@68 545
Chris@68 546 paint->setPen(greys[0]);
Chris@68 547 paint->drawLine(x0, my, x1, my);
Chris@68 548
Chris@68 549 int n = 10;
Chris@68 550 int py = -1;
Chris@68 551
Chris@68 552 if (v->hasLightBackground()) {
Chris@68 553
Chris@68 554 paint->setPen(QColor(240, 240, 240));
Chris@68 555
Chris@68 556 for (int i = 1; i < n; ++i) {
Chris@68 557
Chris@68 558 float val = 0.0, nval = 0.0;
Chris@68 559
Chris@68 560 switch (m_scale) {
Chris@68 561
Chris@68 562 case LinearScale:
Chris@68 563 val = (i * gain) / n;
Chris@68 564 if (i > 0) nval = -val;
Chris@68 565 break;
Chris@68 566
Chris@68 567 case MeterScale:
Chris@68 568 val = AudioLevel::dB_to_multiplier(meterdbs[i]) * gain;
Chris@68 569 break;
Chris@68 570
Chris@68 571 case dBScale:
Chris@68 572 val = AudioLevel::dB_to_multiplier(-(10*n) + i * 10) * gain;
Chris@68 573 break;
Chris@68 574 }
Chris@68 575
Chris@68 576 if (val < -1.0 || val > 1.0) continue;
Chris@68 577
Chris@68 578 int y = getYForValue(v, m_scale, val, ch, minChannel, maxChannel);
Chris@68 579
Chris@68 580 if (py >= 0 && abs(y - py) < 10) continue;
Chris@68 581 else py = y;
Chris@68 582
Chris@68 583 int ny = y;
Chris@68 584 if (nval != 0.0) {
Chris@68 585 ny = getYForValue(v, m_scale, nval, ch, minChannel, maxChannel);
Chris@68 586 }
Chris@68 587
Chris@68 588 paint->drawLine(x0, y, x1, y);
Chris@68 589 if (ny != y) {
Chris@68 590 paint->drawLine(x0, ny, x1, ny);
Chris@68 591 }
Chris@68 592 }
Chris@68 593 }
Chris@68 594
Chris@68 595 if (frame1 <= 0) continue;
Chris@68 596
Chris@68 597 size_t modelZoomLevel = zoomLevel;
Chris@68 598
Chris@68 599 ranges = m_model->getRanges
Chris@68 600 (ch, frame0 < 0 ? 0 : frame0, frame1, modelZoomLevel);
Chris@68 601
Chris@68 602 if (mergingChannels || mixingChannels) {
Chris@71 603 if (m_model->getChannelCount() > 1) {
Chris@71 604 otherChannelRanges = m_model->getRanges
Chris@71 605 (1, frame0 < 0 ? 0 : frame0, frame1, modelZoomLevel);
Chris@71 606 } else {
Chris@71 607 otherChannelRanges = ranges;
Chris@71 608 }
Chris@68 609 }
Chris@68 610
Chris@0 611 for (int x = x0; x <= x1; ++x) {
Chris@0 612
Chris@0 613 range = RangeSummarisableTimeValueModel::Range();
Chris@0 614 size_t index = x - x0;
Chris@0 615 size_t maxIndex = index;
Chris@0 616
Chris@0 617 if (frame0 < 0) {
Chris@0 618 if (index < size_t(-frame0 / zoomLevel)) {
Chris@0 619 continue;
Chris@0 620 } else {
Chris@0 621 index -= -frame0 / zoomLevel;
Chris@0 622 maxIndex = index;
Chris@0 623 }
Chris@0 624 }
Chris@0 625
Chris@0 626 if (int(modelZoomLevel) != zoomLevel) {
Chris@0 627
Chris@0 628 index = size_t((double(index) * zoomLevel) / modelZoomLevel);
Chris@0 629
Chris@0 630 if (int(modelZoomLevel) < zoomLevel) {
Chris@0 631 // Peaks may be missed! The model should avoid
Chris@0 632 // this by rounding zoom levels up rather than
Chris@0 633 // down, but we'd better cope in case it doesn't
Chris@0 634 maxIndex = index;
Chris@0 635 } else {
Chris@0 636 maxIndex = size_t((double(index + 1) * zoomLevel)
Chris@0 637 / modelZoomLevel) - 1;
Chris@0 638 }
Chris@0 639 }
Chris@0 640
Chris@0 641 if (index < ranges.size()) {
Chris@0 642
Chris@0 643 range = ranges[index];
Chris@0 644
Chris@0 645 if (maxIndex > index && maxIndex < ranges.size()) {
Chris@0 646 range.max = std::max(range.max, ranges[maxIndex].max);
Chris@0 647 range.min = std::min(range.min, ranges[maxIndex].min);
Chris@0 648 range.absmean = (range.absmean +
Chris@0 649 ranges[maxIndex].absmean) / 2;
Chris@0 650 }
Chris@0 651
Chris@0 652 } else {
Chris@0 653 continue;
Chris@0 654 }
Chris@0 655
Chris@0 656 int rangeBottom = 0, rangeTop = 0, meanBottom = 0, meanTop = 0;
Chris@0 657
Chris@0 658 if (mergingChannels) {
Chris@0 659
Chris@0 660 if (index < otherChannelRanges.size()) {
Chris@0 661
Chris@0 662 range.max = fabsf(range.max);
Chris@0 663 range.min = -fabsf(otherChannelRanges[index].max);
Chris@0 664 range.absmean = (range.absmean +
Chris@0 665 otherChannelRanges[index].absmean) / 2;
Chris@0 666
Chris@0 667 if (maxIndex > index && maxIndex < ranges.size()) {
Chris@0 668 // let's not concern ourselves about the mean
Chris@0 669 range.min = std::min
Chris@0 670 (range.min,
Chris@0 671 -fabsf(otherChannelRanges[maxIndex].max));
Chris@0 672 }
Chris@0 673 }
Chris@67 674
Chris@67 675 } else if (mixingChannels) {
Chris@67 676
Chris@67 677 if (index < otherChannelRanges.size()) {
Chris@67 678
Chris@67 679 range.max = (range.max + otherChannelRanges[index].max) / 2;
Chris@67 680 range.min = (range.min + otherChannelRanges[index].min) / 2;
Chris@67 681 range.absmean = (range.absmean + otherChannelRanges[index].absmean) / 2;
Chris@67 682 }
Chris@67 683 }
Chris@0 684
Chris@0 685 int greyLevels = 1;
Chris@0 686 if (m_greyscale && (m_scale == LinearScale)) greyLevels = 4;
Chris@0 687
Chris@0 688 switch (m_scale) {
Chris@0 689
Chris@0 690 case LinearScale:
Chris@67 691 rangeBottom = int( m * greyLevels * range.min * gain);
Chris@67 692 rangeTop = int( m * greyLevels * range.max * gain);
Chris@67 693 meanBottom = int(-m * range.absmean * gain);
Chris@67 694 meanTop = int( m * range.absmean * gain);
Chris@0 695 break;
Chris@0 696
Chris@0 697 case dBScale:
Chris@67 698 if (!mergingChannels) {
Chris@67 699 int db0 = dBscale(range.min * gain, m);
Chris@67 700 int db1 = dBscale(range.max * gain, m);
Chris@67 701 rangeTop = std::max(db0, db1);
Chris@67 702 meanTop = std::min(db0, db1);
Chris@67 703 if (mixingChannels) rangeBottom = meanTop;
Chris@67 704 else rangeBottom = dBscale(range.absmean * gain, m);
Chris@67 705 meanBottom = rangeBottom;
Chris@67 706 } else {
Chris@67 707 rangeBottom = -dBscale(range.min * gain, m * greyLevels);
Chris@67 708 rangeTop = dBscale(range.max * gain, m * greyLevels);
Chris@67 709 meanBottom = -dBscale(range.absmean * gain, m);
Chris@67 710 meanTop = dBscale(range.absmean * gain, m);
Chris@67 711 }
Chris@0 712 break;
Chris@0 713
Chris@0 714 case MeterScale:
Chris@67 715 if (!mergingChannels) {
Chris@67 716 int r0 = abs(AudioLevel::multiplier_to_preview(range.min * gain, m));
Chris@67 717 int r1 = abs(AudioLevel::multiplier_to_preview(range.max * gain, m));
Chris@67 718 rangeTop = std::max(r0, r1);
Chris@67 719 meanTop = std::min(r0, r1);
Chris@67 720 if (mixingChannels) rangeBottom = meanTop;
Chris@67 721 else rangeBottom = AudioLevel::multiplier_to_preview(range.absmean * gain, m);
Chris@67 722 meanBottom = rangeBottom;
Chris@67 723 } else {
Chris@67 724 rangeBottom = AudioLevel::multiplier_to_preview(range.min * gain, m * greyLevels);
Chris@67 725 rangeTop = AudioLevel::multiplier_to_preview(range.max * gain, m * greyLevels);
Chris@67 726 meanBottom = -AudioLevel::multiplier_to_preview(range.absmean * gain, m);
Chris@67 727 meanTop = AudioLevel::multiplier_to_preview(range.absmean * gain, m);
Chris@67 728 }
Chris@67 729 break;
Chris@0 730 }
Chris@0 731
Chris@27 732 rangeBottom = my * greyLevels - rangeBottom;
Chris@27 733 rangeTop = my * greyLevels - rangeTop;
Chris@27 734 meanBottom = my - meanBottom;
Chris@27 735 meanTop = my - meanTop;
Chris@27 736
Chris@27 737 int topFill = (rangeTop % greyLevels);
Chris@27 738 if (topFill > 0) topFill = greyLevels - topFill;
Chris@27 739
Chris@27 740 int bottomFill = (rangeBottom % greyLevels);
Chris@27 741
Chris@0 742 rangeTop = rangeTop / greyLevels;
Chris@0 743 rangeBottom = rangeBottom / greyLevels;
Chris@0 744
Chris@0 745 bool clipped = false;
Chris@27 746
Chris@27 747 if (rangeTop < my - m) { rangeTop = my - m; }
Chris@27 748 if (rangeTop > my + m) { rangeTop = my + m; }
Chris@27 749 if (rangeBottom < my - m) { rangeBottom = my - m; }
Chris@27 750 if (rangeBottom > my + m) { rangeBottom = my + m; }
Chris@27 751
Chris@67 752 if (range.max <= -1.0 ||
Chris@67 753 range.max >= 1.0) clipped = true;
Chris@0 754
Chris@0 755 if (meanBottom > rangeBottom) meanBottom = rangeBottom;
Chris@0 756 if (meanTop < rangeTop) meanTop = rangeTop;
Chris@0 757
Chris@0 758 bool drawMean = m_showMeans;
Chris@0 759 if (meanTop == rangeTop) {
Chris@0 760 if (meanTop < meanBottom) ++meanTop;
Chris@0 761 else drawMean = false;
Chris@0 762 }
Chris@67 763 if (meanBottom == rangeBottom && m_scale == LinearScale) {
Chris@0 764 if (meanBottom > meanTop) --meanBottom;
Chris@0 765 else drawMean = false;
Chris@0 766 }
Chris@0 767
Chris@0 768 if (x != x0 && prevRangeBottom != -1) {
Chris@0 769 if (prevRangeBottom > rangeBottom &&
Chris@0 770 prevRangeTop > rangeBottom) {
Chris@28 771 // paint->setPen(midColour);
Chris@28 772 paint->setPen(m_colour);
Chris@0 773 paint->drawLine(x-1, prevRangeTop, x, rangeBottom);
Chris@28 774 paint->setPen(prevRangeTopColour);
Chris@0 775 paint->drawPoint(x-1, prevRangeTop);
Chris@0 776 } else if (prevRangeBottom < rangeTop &&
Chris@0 777 prevRangeTop < rangeTop) {
Chris@28 778 // paint->setPen(midColour);
Chris@28 779 paint->setPen(m_colour);
Chris@0 780 paint->drawLine(x-1, prevRangeBottom, x, rangeTop);
Chris@28 781 paint->setPen(prevRangeBottomColour);
Chris@0 782 paint->drawPoint(x-1, prevRangeBottom);
Chris@0 783 }
Chris@0 784 }
Chris@0 785
Chris@0 786 if (ready) {
Chris@67 787 if (clipped /*!!! ||
Chris@67 788 range.min * gain <= -1.0 ||
Chris@67 789 range.max * gain >= 1.0 */) {
Chris@0 790 paint->setPen(Qt::red);
Chris@0 791 } else {
Chris@0 792 paint->setPen(m_colour);
Chris@0 793 }
Chris@0 794 } else {
Chris@0 795 paint->setPen(midColour);
Chris@0 796 }
Chris@0 797
Chris@0 798 paint->drawLine(x, rangeBottom, x, rangeTop);
Chris@0 799
Chris@28 800 prevRangeTopColour = m_colour;
Chris@28 801 prevRangeBottomColour = m_colour;
Chris@28 802
Chris@0 803 if (m_greyscale && (m_scale == LinearScale) && ready) {
Chris@0 804 if (!clipped) {
Chris@0 805 if (rangeTop < rangeBottom) {
Chris@0 806 if (topFill > 0 &&
Chris@0 807 (!drawMean || (rangeTop < meanTop - 1))) {
Chris@0 808 paint->setPen(greys[topFill - 1]);
Chris@27 809 paint->drawPoint(x, rangeTop);
Chris@28 810 prevRangeTopColour = greys[topFill - 1];
Chris@0 811 }
Chris@0 812 if (bottomFill > 0 &&
Chris@0 813 (!drawMean || (rangeBottom > meanBottom + 1))) {
Chris@0 814 paint->setPen(greys[bottomFill - 1]);
Chris@27 815 paint->drawPoint(x, rangeBottom);
Chris@28 816 prevRangeBottomColour = greys[bottomFill - 1];
Chris@0 817 }
Chris@0 818 }
Chris@0 819 }
Chris@0 820 }
Chris@0 821
Chris@0 822 if (drawMean) {
Chris@0 823 paint->setPen(midColour);
Chris@0 824 paint->drawLine(x, meanBottom, x, meanTop);
Chris@0 825 }
Chris@0 826
Chris@0 827 prevRangeBottom = rangeBottom;
Chris@0 828 prevRangeTop = rangeTop;
Chris@0 829 }
Chris@0 830 }
Chris@0 831
Chris@0 832 if (m_aggressive) {
Chris@41 833
Chris@44 834 if (ready && rect == v->rect()) {
Chris@0 835 m_cacheValid = true;
Chris@0 836 m_cacheZoomLevel = zoomLevel;
Chris@0 837 }
Chris@0 838 paint->end();
Chris@0 839 delete paint;
Chris@0 840 viewPainter.drawPixmap(rect, *m_cache, rect);
Chris@0 841 }
Chris@0 842 }
Chris@0 843
Chris@25 844 QString
Chris@44 845 WaveformLayer::getFeatureDescription(View *v, QPoint &pos) const
Chris@25 846 {
Chris@25 847 int x = pos.x();
Chris@25 848
Chris@25 849 if (!m_model || !m_model->isOK()) return "";
Chris@25 850
Chris@44 851 long f0 = v->getFrameForX(x);
Chris@44 852 long f1 = v->getFrameForX(x + 1);
Chris@25 853
Chris@25 854 if (f0 < 0) f0 = 0;
Chris@25 855 if (f1 <= f0) return "";
Chris@25 856
Chris@25 857 QString text;
Chris@25 858
Chris@25 859 RealTime rt0 = RealTime::frame2RealTime(f0, m_model->getSampleRate());
Chris@25 860 RealTime rt1 = RealTime::frame2RealTime(f1, m_model->getSampleRate());
Chris@25 861
Chris@25 862 if (f1 != f0 + 1 && (rt0.sec != rt1.sec || rt0.msec() != rt1.msec())) {
Chris@25 863 text += tr("Time:\t%1 - %2")
Chris@25 864 .arg(rt0.toText(true).c_str())
Chris@25 865 .arg(rt1.toText(true).c_str());
Chris@25 866 } else {
Chris@25 867 text += tr("Time:\t%1")
Chris@25 868 .arg(rt0.toText(true).c_str());
Chris@25 869 }
Chris@25 870
Chris@25 871 size_t channels = 0, minChannel = 0, maxChannel = 0;
Chris@67 872 bool mergingChannels = false, mixingChannels = false;
Chris@25 873
Chris@67 874 channels = getChannelArrangement(minChannel, maxChannel,
Chris@67 875 mergingChannels, mixingChannels);
Chris@25 876 if (channels == 0) return "";
Chris@25 877
Chris@25 878 for (size_t ch = minChannel; ch <= maxChannel; ++ch) {
Chris@25 879
Chris@44 880 size_t blockSize = v->getZoomLevel();
Chris@25 881 RangeSummarisableTimeValueModel::RangeBlock ranges =
Chris@25 882 m_model->getRanges(ch, f0, f1, blockSize);
Chris@25 883
Chris@25 884 if (ranges.empty()) continue;
Chris@25 885
Chris@25 886 RangeSummarisableTimeValueModel::Range range = ranges[0];
Chris@25 887
Chris@25 888 QString label = tr("Level:");
Chris@25 889 if (minChannel != maxChannel) {
Chris@25 890 if (ch == 0) label = tr("Left:");
Chris@25 891 else if (ch == 1) label = tr("Right:");
Chris@25 892 else label = tr("Channel %1").arg(ch + 1);
Chris@25 893 }
Chris@25 894
Chris@76 895 bool singleValue = false;
Chris@76 896 float min, max;
Chris@76 897
Chris@76 898 if (fabs(range.min) < 0.01) {
Chris@76 899 min = range.min;
Chris@76 900 max = range.max;
Chris@76 901 singleValue = (min == max);
Chris@76 902 } else {
Chris@76 903 int imin = int(range.min * 1000);
Chris@76 904 int imax = int(range.max * 1000);
Chris@76 905 singleValue = (imin == imax);
Chris@76 906 min = float(imin)/1000;
Chris@76 907 max = float(imax)/1000;
Chris@76 908 }
Chris@76 909
Chris@25 910 int db = int(AudioLevel::multiplier_to_dB(std::max(fabsf(range.min),
Chris@25 911 fabsf(range.max)))
Chris@25 912 * 100);
Chris@25 913
Chris@76 914 if (!singleValue) {
Chris@25 915 text += tr("\n%1\t%2 - %3 (%4 dB peak)")
Chris@76 916 .arg(label).arg(min).arg(max).arg(float(db)/100);
Chris@25 917 } else {
Chris@25 918 text += tr("\n%1\t%2 (%3 dB peak)")
Chris@76 919 .arg(label).arg(min).arg(float(db)/100);
Chris@25 920 }
Chris@25 921 }
Chris@25 922
Chris@25 923 return text;
Chris@25 924 }
Chris@25 925
Chris@0 926 int
Chris@68 927 WaveformLayer::getYForValue(View *v, Scale scale, float value, size_t channel,
Chris@68 928 size_t minChannel, size_t maxChannel) const
Chris@68 929 {
Chris@68 930 if (maxChannel < minChannel || channel < minChannel) return 0;
Chris@68 931
Chris@68 932 int w = v->width();
Chris@68 933 int h = v->height();
Chris@68 934
Chris@68 935 int channels = maxChannel - minChannel + 1;
Chris@68 936 int m = (h / channels) / 2;
Chris@68 937 int my = m + (((channel - minChannel) * h) / channels);
Chris@68 938
Chris@68 939 if ((m_scale == dBScale || m_scale == MeterScale) &&
Chris@68 940 m_channelMode != MergeChannels) {
Chris@68 941 m = (h / channels);
Chris@68 942 my = m + (((channel - minChannel) * h) / channels);
Chris@68 943 }
Chris@68 944
Chris@68 945 int vy = 0;
Chris@68 946
Chris@68 947 switch (scale) {
Chris@68 948
Chris@68 949 case LinearScale:
Chris@68 950 vy = int(m * value);
Chris@68 951 break;
Chris@68 952
Chris@68 953 case MeterScale:
Chris@68 954 vy = AudioLevel::multiplier_to_preview(value, m);
Chris@68 955 break;
Chris@68 956
Chris@68 957 case dBScale:
Chris@68 958 vy = dBscale(value, m);
Chris@68 959 break;
Chris@68 960 }
Chris@68 961
Chris@68 962 return my - vy;
Chris@68 963 }
Chris@68 964
Chris@68 965 int
Chris@44 966 WaveformLayer::getVerticalScaleWidth(View *v, QPainter &paint) const
Chris@0 967 {
Chris@0 968 if (m_scale == LinearScale) {
Chris@0 969 return paint.fontMetrics().width("0.0") + 13;
Chris@0 970 } else {
Chris@0 971 return std::max(paint.fontMetrics().width(tr("0dB")),
Chris@0 972 paint.fontMetrics().width(tr("-Inf"))) + 13;
Chris@0 973 }
Chris@0 974 }
Chris@0 975
Chris@0 976 void
Chris@44 977 WaveformLayer::paintVerticalScale(View *v, QPainter &paint, QRect rect) const
Chris@0 978 {
Chris@0 979 if (!m_model || !m_model->isOK()) {
Chris@0 980 return;
Chris@0 981 }
Chris@0 982
Chris@0 983 size_t channels = 0, minChannel = 0, maxChannel = 0;
Chris@67 984 bool mergingChannels = false, mixingChannels = false;
Chris@0 985
Chris@67 986 channels = getChannelArrangement(minChannel, maxChannel,
Chris@67 987 mergingChannels, mixingChannels);
Chris@0 988 if (channels == 0) return;
Chris@0 989
Chris@0 990 int h = rect.height(), w = rect.width();
Chris@0 991 int textHeight = paint.fontMetrics().height();
Chris@0 992 int toff = -textHeight/2 + paint.fontMetrics().ascent() + 1;
Chris@0 993
Chris@67 994 float gain = m_gain;
Chris@67 995
Chris@0 996 for (size_t ch = minChannel; ch <= maxChannel; ++ch) {
Chris@0 997
Chris@68 998 int lastLabelledY = -1;
Chris@0 999
Chris@67 1000 if (ch < m_effectiveGains.size()) gain = m_effectiveGains[ch];
Chris@67 1001
Chris@68 1002 int n = 10;
Chris@0 1003
Chris@68 1004 for (int i = 0; i <= n; ++i) {
Chris@68 1005
Chris@68 1006 float val = 0.0, nval = 0.0;
Chris@0 1007 QString text = "";
Chris@0 1008
Chris@68 1009 switch (m_scale) {
Chris@68 1010
Chris@68 1011 case LinearScale:
Chris@68 1012 val = (i * gain) / n;
Chris@68 1013 text = QString("%1").arg(float(i) / n);
Chris@68 1014 if (i == 0) text = "0.0";
Chris@68 1015 else {
Chris@68 1016 nval = -val;
Chris@68 1017 if (i == n) text = "1.0";
Chris@68 1018 }
Chris@68 1019 break;
Chris@0 1020
Chris@68 1021 case MeterScale:
Chris@68 1022 val = AudioLevel::dB_to_multiplier(meterdbs[i]) * gain;
Chris@68 1023 text = QString("%1").arg(meterdbs[i]);
Chris@68 1024 if (i == n) text = tr("0dB");
Chris@68 1025 if (i == 0) {
Chris@68 1026 text = tr("-Inf");
Chris@68 1027 val = 0.0;
Chris@68 1028 }
Chris@68 1029 break;
Chris@0 1030
Chris@68 1031 case dBScale:
Chris@68 1032 val = AudioLevel::dB_to_multiplier(-(10*n) + i * 10) * gain;
Chris@68 1033 text = QString("%1").arg(-(10*n) + i * 10);
Chris@68 1034 if (i == n) text = tr("0dB");
Chris@68 1035 if (i == 0) {
Chris@68 1036 text = tr("-Inf");
Chris@68 1037 val = 0.0;
Chris@68 1038 }
Chris@68 1039 break;
Chris@68 1040 }
Chris@0 1041
Chris@68 1042 if (val < -1.0 || val > 1.0) continue;
Chris@0 1043
Chris@68 1044 int y = getYForValue(v, m_scale, val, ch, minChannel, maxChannel);
Chris@0 1045
Chris@68 1046 int ny = y;
Chris@68 1047 if (nval != 0.0) {
Chris@68 1048 ny = getYForValue(v, m_scale, nval, ch, minChannel, maxChannel);
Chris@68 1049 }
Chris@0 1050
Chris@68 1051 bool spaceForLabel = (i == 0 ||
Chris@68 1052 abs(y - lastLabelledY) >= textHeight - 1);
Chris@0 1053
Chris@68 1054 if (spaceForLabel) {
Chris@0 1055
Chris@68 1056 int tx = 3;
Chris@68 1057 if (m_scale != LinearScale) {
Chris@68 1058 tx = w - 10 - paint.fontMetrics().width(text);
Chris@68 1059 }
Chris@68 1060
Chris@68 1061 int ty = y;
Chris@68 1062 if (ty < paint.fontMetrics().ascent()) {
Chris@68 1063 ty = paint.fontMetrics().ascent();
Chris@68 1064 } else if (ty > h - paint.fontMetrics().descent()) {
Chris@68 1065 ty = h - paint.fontMetrics().descent();
Chris@68 1066 } else {
Chris@68 1067 ty += toff;
Chris@68 1068 }
Chris@68 1069 paint.drawText(tx, ty, text);
Chris@0 1070
Chris@68 1071 lastLabelledY = ty - toff;
Chris@67 1072
Chris@68 1073 if (ny != y) {
Chris@68 1074 ty = ny;
Chris@68 1075 if (ty < paint.fontMetrics().ascent()) {
Chris@68 1076 ty = paint.fontMetrics().ascent();
Chris@68 1077 } else if (ty > h - paint.fontMetrics().descent()) {
Chris@68 1078 ty = h - paint.fontMetrics().descent();
Chris@68 1079 } else {
Chris@68 1080 ty += toff;
Chris@68 1081 }
Chris@68 1082 paint.drawText(tx, ty, text);
Chris@68 1083 }
Chris@68 1084
Chris@68 1085 paint.drawLine(w - 7, y, w, y);
Chris@68 1086 if (ny != y) paint.drawLine(w - 7, ny, w, ny);
Chris@68 1087
Chris@68 1088 } else {
Chris@68 1089
Chris@68 1090 paint.drawLine(w - 4, y, w, y);
Chris@68 1091 if (ny != y) paint.drawLine(w - 4, ny, w, ny);
Chris@68 1092 }
Chris@0 1093 }
Chris@0 1094 }
Chris@0 1095 }
Chris@0 1096
Chris@6 1097 QString
Chris@6 1098 WaveformLayer::toXmlString(QString indent, QString extraAttributes) const
Chris@6 1099 {
Chris@6 1100 QString s;
Chris@6 1101
Chris@6 1102 s += QString("gain=\"%1\" "
Chris@6 1103 "colour=\"%2\" "
Chris@6 1104 "showMeans=\"%3\" "
Chris@6 1105 "greyscale=\"%4\" "
Chris@6 1106 "channelMode=\"%5\" "
Chris@6 1107 "channel=\"%6\" "
Chris@6 1108 "scale=\"%7\" "
Chris@67 1109 "aggressive=\"%8\" "
Chris@67 1110 "autoNormalize=\"%9\"")
Chris@6 1111 .arg(m_gain)
Chris@6 1112 .arg(encodeColour(m_colour))
Chris@6 1113 .arg(m_showMeans)
Chris@6 1114 .arg(m_greyscale)
Chris@6 1115 .arg(m_channelMode)
Chris@6 1116 .arg(m_channel)
Chris@6 1117 .arg(m_scale)
Chris@67 1118 .arg(m_aggressive)
Chris@67 1119 .arg(m_autoNormalize);
Chris@6 1120
Chris@6 1121 return Layer::toXmlString(indent, extraAttributes + " " + s);
Chris@6 1122 }
Chris@6 1123
Chris@11 1124 void
Chris@11 1125 WaveformLayer::setProperties(const QXmlAttributes &attributes)
Chris@11 1126 {
Chris@11 1127 bool ok = false;
Chris@11 1128
Chris@11 1129 float gain = attributes.value("gain").toFloat(&ok);
Chris@11 1130 if (ok) setGain(gain);
Chris@11 1131
Chris@11 1132 QString colourSpec = attributes.value("colour");
Chris@11 1133 if (colourSpec != "") {
Chris@11 1134 QColor colour(colourSpec);
Chris@11 1135 if (colour.isValid()) {
Chris@11 1136 setBaseColour(QColor(colourSpec));
Chris@11 1137 }
Chris@11 1138 }
Chris@11 1139
Chris@11 1140 bool showMeans = (attributes.value("showMeans") == "1" ||
Chris@11 1141 attributes.value("showMeans") == "true");
Chris@11 1142 setShowMeans(showMeans);
Chris@11 1143
Chris@11 1144 bool greyscale = (attributes.value("greyscale") == "1" ||
Chris@11 1145 attributes.value("greyscale") == "true");
Chris@11 1146 setUseGreyscale(greyscale);
Chris@11 1147
Chris@11 1148 ChannelMode channelMode = (ChannelMode)
Chris@11 1149 attributes.value("channelMode").toInt(&ok);
Chris@11 1150 if (ok) setChannelMode(channelMode);
Chris@11 1151
Chris@11 1152 int channel = attributes.value("channel").toInt(&ok);
Chris@11 1153 if (ok) setChannel(channel);
Chris@11 1154
Chris@11 1155 Scale scale = (Scale)
Chris@11 1156 attributes.value("scale").toInt(&ok);
Chris@11 1157 if (ok) setScale(scale);
Chris@11 1158
Chris@11 1159 bool aggressive = (attributes.value("aggressive") == "1" ||
Chris@11 1160 attributes.value("aggressive") == "true");
Chris@11 1161 setUseGreyscale(aggressive);
Chris@67 1162
Chris@67 1163 bool autoNormalize = (attributes.value("autoNormalize") == "1" ||
Chris@67 1164 attributes.value("autoNormalize") == "true");
Chris@67 1165 setAutoNormalize(autoNormalize);
Chris@11 1166 }
Chris@11 1167
Chris@0 1168 #ifdef INCLUDE_MOCFILES
Chris@0 1169 #include "WaveformLayer.moc.cpp"
Chris@0 1170 #endif
Chris@0 1171