annotate layer/WaveformLayer.cpp @ 11:2d5005f2b3d9

* Rework handling of layer properties in file I/O -- we now get the individual layers to load and save them rather than doing it via generic property lists in the base class, so as to ensure we read and write meaningful values rather than generic int values requiring conversion.
author Chris Cannam
date Thu, 19 Jan 2006 12:54:38 +0000
parents 8f5b812baaee
children 01849cd277e6
rev   line source
Chris@0 1 /* -*- c-basic-offset: 4 -*- vi:set ts=8 sts=4 sw=4: */
Chris@0 2
Chris@0 3 /*
Chris@0 4 A waveform viewer and audio annotation editor.
Chris@4 5 Chris Cannam, Queen Mary University of London, 2005-2006
Chris@0 6
Chris@0 7 This is experimental software. Not for distribution.
Chris@0 8 */
Chris@0 9
Chris@0 10 #include "WaveformLayer.h"
Chris@0 11
Chris@0 12 #include "base/AudioLevel.h"
Chris@0 13 #include "base/View.h"
Chris@0 14 #include "base/Profiler.h"
Chris@0 15
Chris@0 16 #include <QPainter>
Chris@0 17 #include <QPixmap>
Chris@0 18
Chris@0 19 #include <iostream>
Chris@0 20 #include <cmath>
Chris@0 21
Chris@4 22 //#define DEBUG_WAVEFORM_PAINT 1
Chris@4 23
Chris@0 24 using std::cerr;
Chris@0 25 using std::endl;
Chris@0 26
Chris@0 27 WaveformLayer::WaveformLayer(View *w) :
Chris@0 28 Layer(w),
Chris@0 29 m_model(0),
Chris@0 30 m_gain(1.0f),
Chris@0 31 m_colour(Qt::black),
Chris@0 32 m_showMeans(true),
Chris@0 33 m_greyscale(true),
Chris@0 34 m_channelMode(SeparateChannels),
Chris@0 35 m_channel(-1),
Chris@0 36 m_scale(LinearScale),
Chris@0 37 m_aggressive(false),
Chris@0 38 m_cache(0),
Chris@0 39 m_cacheValid(false)
Chris@0 40 {
Chris@0 41 m_view->addLayer(this);
Chris@0 42 }
Chris@0 43
Chris@0 44 WaveformLayer::~WaveformLayer()
Chris@0 45 {
Chris@0 46 delete m_cache;
Chris@0 47 }
Chris@0 48
Chris@0 49 void
Chris@0 50 WaveformLayer::setModel(const RangeSummarisableTimeValueModel *model)
Chris@0 51 {
Chris@0 52 m_model = model;
Chris@0 53 m_cacheValid = false;
Chris@0 54 if (!m_model || !m_model->isOK()) return;
Chris@0 55
Chris@0 56 connect(m_model, SIGNAL(modelChanged()), this, SIGNAL(modelChanged()));
Chris@0 57 connect(m_model, SIGNAL(modelChanged(size_t, size_t)),
Chris@0 58 this, SIGNAL(modelChanged(size_t, size_t)));
Chris@0 59
Chris@0 60 connect(m_model, SIGNAL(completionChanged()),
Chris@0 61 this, SIGNAL(modelCompletionChanged()));
Chris@0 62
Chris@0 63 emit modelReplaced();
Chris@0 64 }
Chris@0 65
Chris@0 66 Layer::PropertyList
Chris@0 67 WaveformLayer::getProperties() const
Chris@0 68 {
Chris@0 69 PropertyList list;
Chris@0 70 list.push_back(tr("Colour"));
Chris@0 71 list.push_back(tr("Scale"));
Chris@0 72 list.push_back(tr("Gain"));
Chris@0 73 list.push_back(tr("Merge Channels"));
Chris@0 74 return list;
Chris@0 75 }
Chris@0 76
Chris@0 77 Layer::PropertyType
Chris@0 78 WaveformLayer::getPropertyType(const PropertyName &name) const
Chris@0 79 {
Chris@0 80 if (name == tr("Gain")) return RangeProperty;
Chris@0 81 if (name == tr("Colour")) return ValueProperty;
Chris@0 82 if (name == tr("Merge Channels")) return ToggleProperty;
Chris@0 83 if (name == tr("Scale")) return ValueProperty;
Chris@0 84 return InvalidProperty;
Chris@0 85 }
Chris@0 86
Chris@0 87 QString
Chris@0 88 WaveformLayer::getPropertyGroupName(const PropertyName &name) const
Chris@0 89 {
Chris@0 90 if (name == tr("Gain") ||
Chris@0 91 name == tr("Scale")) return tr("Scale");
Chris@0 92 return QString();
Chris@0 93 }
Chris@0 94
Chris@0 95 int
Chris@0 96 WaveformLayer::getPropertyRangeAndValue(const PropertyName &name,
Chris@0 97 int *min, int *max) const
Chris@0 98 {
Chris@0 99 int deft = 0;
Chris@0 100
Chris@10 101 int throwaway;
Chris@10 102 if (!min) min = &throwaway;
Chris@10 103 if (!max) max = &throwaway;
Chris@10 104
Chris@0 105 if (name == tr("Gain")) {
Chris@0 106
Chris@0 107 *min = -50;
Chris@0 108 *max = 50;
Chris@0 109
Chris@0 110 deft = int(nearbyint(log10(m_gain) * 20.0));
Chris@0 111 if (deft < *min) deft = *min;
Chris@0 112 if (deft > *max) deft = *max;
Chris@0 113
Chris@0 114 } else if (name == tr("Colour")) {
Chris@0 115
Chris@0 116 *min = 0;
Chris@0 117 *max = 5;
Chris@0 118
Chris@0 119 if (m_colour == Qt::black) deft = 0;
Chris@0 120 else if (m_colour == Qt::darkRed) deft = 1;
Chris@0 121 else if (m_colour == Qt::darkBlue) deft = 2;
Chris@0 122 else if (m_colour == Qt::darkGreen) deft = 3;
Chris@0 123 else if (m_colour == QColor(200, 50, 255)) deft = 4;
Chris@0 124 else if (m_colour == QColor(255, 150, 50)) deft = 5;
Chris@0 125
Chris@0 126 } else if (name == tr("Merge Channels")) {
Chris@0 127
Chris@0 128 deft = ((m_channelMode == MergeChannels) ? 1 : 0);
Chris@0 129
Chris@0 130 } else if (name == tr("Scale")) {
Chris@0 131
Chris@0 132 *min = 0;
Chris@0 133 *max = 2;
Chris@0 134
Chris@0 135 deft = (int)m_scale;
Chris@0 136
Chris@0 137 } else {
Chris@0 138 deft = Layer::getPropertyRangeAndValue(name, min, max);
Chris@0 139 }
Chris@0 140
Chris@0 141 return deft;
Chris@0 142 }
Chris@0 143
Chris@0 144 QString
Chris@0 145 WaveformLayer::getPropertyValueLabel(const PropertyName &name,
Chris@0 146 int value) const
Chris@0 147 {
Chris@0 148 if (name == tr("Colour")) {
Chris@0 149 switch (value) {
Chris@0 150 default:
Chris@0 151 case 0: return tr("Black");
Chris@0 152 case 1: return tr("Red");
Chris@0 153 case 2: return tr("Blue");
Chris@0 154 case 3: return tr("Green");
Chris@0 155 case 4: return tr("Purple");
Chris@0 156 case 5: return tr("Orange");
Chris@0 157 }
Chris@0 158 }
Chris@0 159 if (name == tr("Scale")) {
Chris@0 160 switch (value) {
Chris@0 161 default:
Chris@0 162 case 0: return tr("Linear");
Chris@0 163 case 1: return tr("Meter");
Chris@0 164 case 2: return tr("dB");
Chris@0 165 }
Chris@0 166 }
Chris@0 167 return tr("<unknown>");
Chris@0 168 }
Chris@0 169
Chris@0 170 void
Chris@0 171 WaveformLayer::setProperty(const PropertyName &name, int value)
Chris@0 172 {
Chris@0 173 if (name == tr("Gain")) {
Chris@0 174 setGain(pow(10, float(value)/20.0));
Chris@0 175 } else if (name == tr("Colour")) {
Chris@0 176 switch (value) {
Chris@0 177 default:
Chris@0 178 case 0: setBaseColour(Qt::black); break;
Chris@0 179 case 1: setBaseColour(Qt::darkRed); break;
Chris@0 180 case 2: setBaseColour(Qt::darkBlue); break;
Chris@0 181 case 3: setBaseColour(Qt::darkGreen); break;
Chris@0 182 case 4: setBaseColour(QColor(200, 50, 255)); break;
Chris@0 183 case 5: setBaseColour(QColor(255, 150, 50)); break;
Chris@0 184 }
Chris@0 185 } else if (name == tr("Merge Channels")) {
Chris@0 186 setChannelMode(value ? MergeChannels : SeparateChannels);
Chris@0 187 } else if (name == tr("Scale")) {
Chris@0 188 switch (value) {
Chris@0 189 default:
Chris@0 190 case 0: setScale(LinearScale); break;
Chris@0 191 case 1: setScale(MeterScale); break;
Chris@0 192 case 2: setScale(dBScale); break;
Chris@0 193 }
Chris@0 194 }
Chris@0 195 }
Chris@0 196
Chris@0 197 /*
Chris@0 198
Chris@0 199 int
Chris@0 200 WaveformLayer::getProperty(const PropertyName &name)
Chris@0 201 {
Chris@0 202 if (name == "Gain") {
Chris@0 203 return int((getGain() - 1.0) * 10.0 + 0.01);
Chris@0 204 }
Chris@0 205 if (name == "Colour") {
Chris@0 206
Chris@0 207 */
Chris@0 208
Chris@0 209 void
Chris@0 210 WaveformLayer::setGain(float gain) //!!! inadequate for floats!
Chris@0 211 {
Chris@0 212 if (m_gain == gain) return;
Chris@0 213 m_gain = gain;
Chris@0 214 m_cacheValid = false;
Chris@0 215 emit layerParametersChanged();
Chris@0 216 }
Chris@0 217
Chris@0 218 void
Chris@0 219 WaveformLayer::setBaseColour(QColor colour)
Chris@0 220 {
Chris@0 221 if (m_colour == colour) return;
Chris@0 222 m_colour = colour;
Chris@0 223 m_cacheValid = false;
Chris@0 224 emit layerParametersChanged();
Chris@0 225 }
Chris@0 226
Chris@0 227 void
Chris@0 228 WaveformLayer::setShowMeans(bool showMeans)
Chris@0 229 {
Chris@0 230 if (m_showMeans == showMeans) return;
Chris@0 231 m_showMeans = showMeans;
Chris@0 232 m_cacheValid = false;
Chris@0 233 emit layerParametersChanged();
Chris@0 234 }
Chris@0 235
Chris@0 236 void
Chris@0 237 WaveformLayer::setUseGreyscale(bool useGreyscale)
Chris@0 238 {
Chris@0 239 if (m_greyscale == useGreyscale) return;
Chris@0 240 m_greyscale = useGreyscale;
Chris@0 241 m_cacheValid = false;
Chris@0 242 emit layerParametersChanged();
Chris@0 243 }
Chris@0 244
Chris@0 245 void
Chris@0 246 WaveformLayer::setChannelMode(ChannelMode channelMode)
Chris@0 247 {
Chris@0 248 if (m_channelMode == channelMode) return;
Chris@0 249 m_channelMode = channelMode;
Chris@0 250 m_cacheValid = false;
Chris@0 251 emit layerParametersChanged();
Chris@0 252 }
Chris@0 253
Chris@0 254 void
Chris@0 255 WaveformLayer::setChannel(int channel)
Chris@0 256 {
Chris@0 257 std::cerr << "WaveformLayer::setChannel(" << channel << ")" << std::endl;
Chris@0 258
Chris@0 259 if (m_channel == channel) return;
Chris@0 260 m_channel = channel;
Chris@0 261 m_cacheValid = false;
Chris@0 262 emit layerParametersChanged();
Chris@0 263 }
Chris@0 264
Chris@0 265 void
Chris@0 266 WaveformLayer::setScale(Scale scale)
Chris@0 267 {
Chris@0 268 if (m_scale == scale) return;
Chris@0 269 m_scale = scale;
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::setAggressiveCacheing(bool aggressive)
Chris@0 276 {
Chris@0 277 if (m_aggressive == aggressive) return;
Chris@0 278 m_aggressive = aggressive;
Chris@0 279 m_cacheValid = false;
Chris@0 280 emit layerParametersChanged();
Chris@0 281 }
Chris@0 282
Chris@0 283 int
Chris@0 284 WaveformLayer::getCompletion() const
Chris@0 285 {
Chris@0 286 int completion = 100;
Chris@0 287 if (!m_model || !m_model->isOK()) return completion;
Chris@0 288 if (m_model->isReady(&completion)) return 100;
Chris@0 289 return completion;
Chris@0 290 }
Chris@0 291
Chris@0 292 int
Chris@0 293 WaveformLayer::dBscale(float sample, int m) const
Chris@0 294 {
Chris@0 295 if (sample < 0.0) return -dBscale(-sample, m);
Chris@0 296 float dB = AudioLevel::multiplier_to_dB(sample);
Chris@0 297 if (dB < -50.0) return 0;
Chris@0 298 if (dB > 0.0) return m;
Chris@0 299 return int(((dB + 50.0) * m) / 50.0 + 0.1);
Chris@0 300 }
Chris@0 301
Chris@0 302 size_t
Chris@0 303 WaveformLayer::getChannelArrangement(size_t &min, size_t &max, bool &merging)
Chris@0 304 const
Chris@0 305 {
Chris@0 306 if (!m_model || !m_model->isOK()) return 0;
Chris@0 307
Chris@0 308 size_t channels = m_model->getChannelCount();
Chris@0 309 if (channels == 0) return 0;
Chris@0 310
Chris@0 311 size_t rawChannels = channels;
Chris@0 312
Chris@0 313 if (m_channel == -1) {
Chris@0 314 min = 0;
Chris@0 315 if (m_channelMode == MergeChannels) {
Chris@0 316 max = 0;
Chris@0 317 channels = 1;
Chris@0 318 } else {
Chris@0 319 max = channels - 1;
Chris@0 320 }
Chris@0 321 } else {
Chris@0 322 min = m_channel;
Chris@0 323 max = m_channel;
Chris@0 324 rawChannels = 1;
Chris@0 325 channels = 1;
Chris@0 326 }
Chris@0 327
Chris@0 328 merging = (m_channelMode == MergeChannels && rawChannels > 1);
Chris@0 329
Chris@0 330 // std::cerr << "WaveformLayer::getChannelArrangement: min " << min << ", max " << max << ", merging " << merging << ", channels " << channels << std::endl;
Chris@0 331
Chris@0 332 return channels;
Chris@0 333 }
Chris@0 334
Chris@0 335 void
Chris@0 336 WaveformLayer::paint(QPainter &viewPainter, QRect rect) const
Chris@0 337 {
Chris@0 338 if (!m_model || !m_model->isOK()) {
Chris@0 339 return;
Chris@0 340 }
Chris@0 341
Chris@0 342 long startFrame = m_view->getStartFrame();
Chris@0 343 int zoomLevel = m_view->getZoomLevel();
Chris@0 344
Chris@2 345 #ifdef DEBUG_WAVEFORM_PAINT
Chris@0 346 Profiler profiler("WaveformLayer::paint", true);
Chris@0 347 std::cerr << "WaveformLayer::paint (" << rect.x() << "," << rect.y()
Chris@0 348 << ") [" << rect.width() << "x" << rect.height() << "]: zoom " << zoomLevel << ", start " << startFrame << std::endl;
Chris@2 349 #endif
Chris@0 350
Chris@0 351 size_t channels = 0, minChannel = 0, maxChannel = 0;
Chris@0 352 bool mergingChannels = false;
Chris@0 353
Chris@0 354 channels = getChannelArrangement(minChannel, maxChannel, mergingChannels);
Chris@0 355 if (channels == 0) return;
Chris@0 356
Chris@0 357 int w = m_view->width();
Chris@0 358 int h = m_view->height();
Chris@0 359
Chris@0 360 bool ready = m_model->isReady();
Chris@0 361 QPainter *paint;
Chris@0 362
Chris@0 363 if (m_aggressive) {
Chris@0 364
Chris@0 365 if (m_cacheValid && (zoomLevel != m_cacheZoomLevel)) {
Chris@0 366 m_cacheValid = false;
Chris@0 367 }
Chris@0 368
Chris@0 369 if (m_cacheValid) {
Chris@0 370 viewPainter.drawPixmap(rect, *m_cache, rect);
Chris@0 371 return;
Chris@0 372 }
Chris@0 373
Chris@0 374 if (!m_cache || m_cache->width() != w || m_cache->height() != h) {
Chris@0 375 delete m_cache;
Chris@0 376 m_cache = new QPixmap(w, h);
Chris@0 377 }
Chris@0 378
Chris@0 379 paint = new QPainter(m_cache);
Chris@0 380
Chris@0 381 paint->setPen(Qt::NoPen);
Chris@0 382 paint->setBrush(m_view->palette().background());
Chris@0 383 paint->drawRect(rect);
Chris@0 384
Chris@0 385 paint->setPen(Qt::black);
Chris@0 386 paint->setBrush(Qt::NoBrush);
Chris@0 387
Chris@0 388 } else {
Chris@0 389 paint = &viewPainter;
Chris@0 390 }
Chris@0 391
Chris@0 392 int x0 = 0, x1 = w - 1;
Chris@0 393 int y0 = 0, y1 = h - 1;
Chris@0 394
Chris@0 395 x0 = rect.left();
Chris@0 396 x1 = rect.right();
Chris@0 397 y0 = rect.top();
Chris@0 398 y1 = rect.bottom();
Chris@0 399
Chris@0 400 long frame0 = startFrame + x0 * zoomLevel;
Chris@0 401 long frame1 = startFrame + (x1 + 1) * zoomLevel;
Chris@0 402
Chris@4 403 #ifdef DEBUG_WAVEFORM_PAINT
Chris@4 404 std::cerr << "Painting waveform from " << frame0 << " to " << frame1 << " (" << (x1-x0+1) << " pixels at zoom " << zoomLevel << ")" << std::endl;
Chris@4 405 #endif
Chris@0 406
Chris@0 407 RangeSummarisableTimeValueModel::RangeBlock ranges;
Chris@0 408 RangeSummarisableTimeValueModel::RangeBlock otherChannelRanges;
Chris@0 409 RangeSummarisableTimeValueModel::Range range;
Chris@0 410
Chris@0 411 QColor greys[3];
Chris@0 412 if (m_colour == Qt::black) {
Chris@0 413 for (int i = 0; i < 3; ++i) {
Chris@0 414 int level = 192 - 64 * i;
Chris@0 415 greys[i] = QColor(level, level, level);
Chris@0 416 }
Chris@0 417 } else {
Chris@0 418 int factor = (m_view->hasLightBackground() ? 120 : 80);
Chris@0 419 greys[2] = m_colour.light(factor);
Chris@0 420 greys[1] = greys[2].light(factor);
Chris@0 421 greys[0] = greys[1].light(factor);
Chris@0 422 }
Chris@0 423
Chris@0 424 QColor midColour = m_colour;
Chris@0 425 if (midColour == Qt::black) {
Chris@0 426 midColour = Qt::gray;
Chris@0 427 } else if (m_view->hasLightBackground()) {
Chris@0 428 midColour = midColour.light(150);
Chris@0 429 } else {
Chris@0 430 midColour = midColour.light(50);
Chris@0 431 }
Chris@0 432
Chris@0 433 for (size_t ch = minChannel; ch <= maxChannel; ++ch) {
Chris@0 434
Chris@0 435 int prevRangeBottom = -1, prevRangeTop = -1;
Chris@0 436
Chris@0 437 int m = (h / channels) / 2;
Chris@0 438 int my = m + (((ch - minChannel) * h) / channels);
Chris@0 439
Chris@0 440 // std::cerr << "ch = " << ch << ", channels = " << channels << ", m = " << m << ", my = " << my << ", h = " << h << std::endl;
Chris@0 441
Chris@0 442 if (my - m > y1 || my + m < y0) continue;
Chris@0 443
Chris@0 444 paint->setPen(greys[0]);
Chris@0 445 paint->drawLine(x0, my, x1, my);
Chris@0 446
Chris@0 447 if (frame1 <= 0) continue;
Chris@0 448
Chris@0 449 size_t modelZoomLevel = zoomLevel;
Chris@0 450
Chris@0 451 ranges = m_model->getRanges
Chris@0 452 (ch, frame0 < 0 ? 0 : frame0, frame1, modelZoomLevel);
Chris@0 453
Chris@0 454 if (mergingChannels) {
Chris@0 455 otherChannelRanges = m_model->getRanges
Chris@0 456 (1, frame0 < 0 ? 0 : frame0, frame1, modelZoomLevel);
Chris@0 457 }
Chris@0 458
Chris@0 459 for (int x = x0; x <= x1; ++x) {
Chris@0 460
Chris@0 461 range = RangeSummarisableTimeValueModel::Range();
Chris@0 462 size_t index = x - x0;
Chris@0 463 size_t maxIndex = index;
Chris@0 464
Chris@0 465 if (frame0 < 0) {
Chris@0 466 if (index < size_t(-frame0 / zoomLevel)) {
Chris@0 467 continue;
Chris@0 468 } else {
Chris@0 469 index -= -frame0 / zoomLevel;
Chris@0 470 maxIndex = index;
Chris@0 471 }
Chris@0 472 }
Chris@0 473
Chris@0 474 if (int(modelZoomLevel) != zoomLevel) {
Chris@0 475
Chris@0 476 index = size_t((double(index) * zoomLevel) / modelZoomLevel);
Chris@0 477
Chris@0 478 if (int(modelZoomLevel) < zoomLevel) {
Chris@0 479 // Peaks may be missed! The model should avoid
Chris@0 480 // this by rounding zoom levels up rather than
Chris@0 481 // down, but we'd better cope in case it doesn't
Chris@0 482 maxIndex = index;
Chris@0 483 } else {
Chris@0 484 maxIndex = size_t((double(index + 1) * zoomLevel)
Chris@0 485 / modelZoomLevel) - 1;
Chris@0 486 }
Chris@0 487 }
Chris@0 488
Chris@0 489 if (index < ranges.size()) {
Chris@0 490
Chris@0 491 range = ranges[index];
Chris@0 492
Chris@0 493 if (maxIndex > index && maxIndex < ranges.size()) {
Chris@0 494 range.max = std::max(range.max, ranges[maxIndex].max);
Chris@0 495 range.min = std::min(range.min, ranges[maxIndex].min);
Chris@0 496 range.absmean = (range.absmean +
Chris@0 497 ranges[maxIndex].absmean) / 2;
Chris@0 498 }
Chris@0 499
Chris@0 500 } else {
Chris@0 501 continue;
Chris@0 502 }
Chris@0 503
Chris@0 504 int rangeBottom = 0, rangeTop = 0, meanBottom = 0, meanTop = 0;
Chris@0 505
Chris@0 506 if (mergingChannels) {
Chris@0 507
Chris@0 508 if (index < otherChannelRanges.size()) {
Chris@0 509
Chris@0 510 range.max = fabsf(range.max);
Chris@0 511 range.min = -fabsf(otherChannelRanges[index].max);
Chris@0 512 range.absmean = (range.absmean +
Chris@0 513 otherChannelRanges[index].absmean) / 2;
Chris@0 514
Chris@0 515 if (maxIndex > index && maxIndex < ranges.size()) {
Chris@0 516 // let's not concern ourselves about the mean
Chris@0 517 range.min = std::min
Chris@0 518 (range.min,
Chris@0 519 -fabsf(otherChannelRanges[maxIndex].max));
Chris@0 520 }
Chris@0 521 }
Chris@0 522 }
Chris@0 523
Chris@0 524 int greyLevels = 1;
Chris@0 525 if (m_greyscale && (m_scale == LinearScale)) greyLevels = 4;
Chris@0 526
Chris@0 527 switch (m_scale) {
Chris@0 528
Chris@0 529 case LinearScale:
Chris@0 530 rangeBottom = int( m * greyLevels * range.min * m_gain);
Chris@0 531 rangeTop = int( m * greyLevels * range.max * m_gain);
Chris@0 532 meanBottom = int(-m * range.absmean * m_gain);
Chris@0 533 meanTop = int( m * range.absmean * m_gain);
Chris@0 534 break;
Chris@0 535
Chris@0 536 case dBScale:
Chris@0 537 rangeBottom = dBscale(range.min * m_gain, m * greyLevels);
Chris@0 538 rangeTop = dBscale(range.max * m_gain, m * greyLevels);
Chris@0 539 meanBottom = -dBscale(range.absmean * m_gain, m);
Chris@0 540 meanTop = dBscale(range.absmean * m_gain, m);
Chris@0 541 break;
Chris@0 542
Chris@0 543 case MeterScale:
Chris@0 544 rangeBottom = AudioLevel::multiplier_to_preview(range.min * m_gain, m * greyLevels);
Chris@0 545 rangeTop = AudioLevel::multiplier_to_preview(range.max * m_gain, m * greyLevels);
Chris@0 546 meanBottom = -AudioLevel::multiplier_to_preview(range.absmean * m_gain, m);
Chris@0 547 meanTop = AudioLevel::multiplier_to_preview(range.absmean * m_gain, m);
Chris@0 548 }
Chris@0 549
Chris@0 550 int topFill = (rangeTop < 0 ? -rangeTop : rangeTop) % greyLevels;
Chris@0 551 int bottomFill = (rangeBottom < 0 ? -rangeBottom : rangeBottom) % greyLevels;
Chris@0 552 rangeTop = rangeTop / greyLevels;
Chris@0 553 rangeBottom = rangeBottom / greyLevels;
Chris@0 554
Chris@0 555 bool clipped = false;
Chris@0 556 if (rangeTop < -m) { rangeTop = -m; clipped = true; }
Chris@0 557 if (rangeTop > m) { rangeTop = m; clipped = true; }
Chris@0 558 if (rangeBottom < -m) { rangeBottom = -m; clipped = true; }
Chris@0 559 if (rangeBottom > m) { rangeBottom = m; clipped = true; }
Chris@0 560
Chris@0 561 rangeBottom = my - rangeBottom;
Chris@0 562 rangeTop = my - rangeTop;
Chris@0 563 meanBottom = my - meanBottom;
Chris@0 564 meanTop = my - meanTop;
Chris@0 565
Chris@0 566 if (meanBottom > rangeBottom) meanBottom = rangeBottom;
Chris@0 567 if (meanTop < rangeTop) meanTop = rangeTop;
Chris@0 568
Chris@0 569 bool drawMean = m_showMeans;
Chris@0 570 if (meanTop == rangeTop) {
Chris@0 571 if (meanTop < meanBottom) ++meanTop;
Chris@0 572 else drawMean = false;
Chris@0 573 }
Chris@0 574 if (meanBottom == rangeBottom) {
Chris@0 575 if (meanBottom > meanTop) --meanBottom;
Chris@0 576 else drawMean = false;
Chris@0 577 }
Chris@0 578
Chris@0 579 if (x != x0 && prevRangeBottom != -1) {
Chris@0 580 if (prevRangeBottom > rangeBottom &&
Chris@0 581 prevRangeTop > rangeBottom) {
Chris@0 582 paint->setPen(midColour);
Chris@0 583 paint->drawLine(x-1, prevRangeTop, x, rangeBottom);
Chris@0 584 paint->setPen(m_colour);
Chris@0 585 paint->drawPoint(x-1, prevRangeTop);
Chris@0 586 } else if (prevRangeBottom < rangeTop &&
Chris@0 587 prevRangeTop < rangeTop) {
Chris@0 588 paint->setPen(midColour);
Chris@0 589 paint->drawLine(x-1, prevRangeBottom, x, rangeTop);
Chris@0 590 paint->setPen(m_colour);
Chris@0 591 paint->drawPoint(x-1, prevRangeBottom);
Chris@0 592 }
Chris@0 593 }
Chris@0 594
Chris@0 595 if (ready) {
Chris@0 596 if (clipped ||
Chris@0 597 range.min * m_gain <= -1.0 ||
Chris@0 598 range.max * m_gain >= 1.0) {
Chris@0 599 paint->setPen(Qt::red);
Chris@0 600 } else {
Chris@0 601 paint->setPen(m_colour);
Chris@0 602 }
Chris@0 603 } else {
Chris@0 604 paint->setPen(midColour);
Chris@0 605 }
Chris@0 606
Chris@0 607 paint->drawLine(x, rangeBottom, x, rangeTop);
Chris@0 608
Chris@0 609 if (m_greyscale && (m_scale == LinearScale) && ready) {
Chris@0 610 if (!clipped) {
Chris@0 611 if (rangeTop < rangeBottom) {
Chris@0 612 if (topFill > 0 &&
Chris@0 613 (!drawMean || (rangeTop < meanTop - 1))) {
Chris@0 614 paint->setPen(greys[topFill - 1]);
Chris@0 615 paint->drawPoint(x, rangeTop - 1);
Chris@0 616 }
Chris@0 617 if (bottomFill > 0 &&
Chris@0 618 (!drawMean || (rangeBottom > meanBottom + 1))) {
Chris@0 619 paint->setPen(greys[bottomFill - 1]);
Chris@0 620 paint->drawPoint(x, rangeBottom + 1);
Chris@0 621 }
Chris@0 622 }
Chris@0 623 }
Chris@0 624 }
Chris@0 625
Chris@0 626 if (drawMean) {
Chris@0 627 paint->setPen(midColour);
Chris@0 628 paint->drawLine(x, meanBottom, x, meanTop);
Chris@0 629 }
Chris@0 630
Chris@0 631 prevRangeBottom = rangeBottom;
Chris@0 632 prevRangeTop = rangeTop;
Chris@0 633 }
Chris@0 634 }
Chris@0 635
Chris@0 636 if (m_aggressive) {
Chris@0 637 if (ready && rect == m_view->rect()) {
Chris@0 638 m_cacheValid = true;
Chris@0 639 m_cacheZoomLevel = zoomLevel;
Chris@0 640 }
Chris@0 641 paint->end();
Chris@0 642 delete paint;
Chris@0 643 viewPainter.drawPixmap(rect, *m_cache, rect);
Chris@0 644 }
Chris@0 645 }
Chris@0 646
Chris@0 647 int
Chris@0 648 WaveformLayer::getVerticalScaleWidth(QPainter &paint) const
Chris@0 649 {
Chris@0 650 if (m_scale == LinearScale) {
Chris@0 651 return paint.fontMetrics().width("0.0") + 13;
Chris@0 652 } else {
Chris@0 653 return std::max(paint.fontMetrics().width(tr("0dB")),
Chris@0 654 paint.fontMetrics().width(tr("-Inf"))) + 13;
Chris@0 655 }
Chris@0 656 }
Chris@0 657
Chris@0 658 void
Chris@0 659 WaveformLayer::paintVerticalScale(QPainter &paint, QRect rect) const
Chris@0 660 {
Chris@0 661 if (!m_model || !m_model->isOK()) {
Chris@0 662 return;
Chris@0 663 }
Chris@0 664
Chris@0 665 size_t channels = 0, minChannel = 0, maxChannel = 0;
Chris@0 666 bool mergingChannels = false;
Chris@0 667
Chris@0 668 channels = getChannelArrangement(minChannel, maxChannel, mergingChannels);
Chris@0 669 if (channels == 0) return;
Chris@0 670
Chris@0 671 int h = rect.height(), w = rect.width();
Chris@0 672 int textHeight = paint.fontMetrics().height();
Chris@0 673 int toff = -textHeight/2 + paint.fontMetrics().ascent() + 1;
Chris@0 674
Chris@0 675 for (size_t ch = minChannel; ch <= maxChannel; ++ch) {
Chris@0 676
Chris@0 677 int m = (h / channels) / 2;
Chris@0 678 int my = m + (((ch - minChannel) * h) / channels);
Chris@0 679 int py = -1;
Chris@0 680
Chris@0 681 for (int i = 0; i <= 10; ++i) {
Chris@0 682
Chris@0 683 int vy = 0;
Chris@0 684 QString text = "";
Chris@0 685
Chris@0 686 if (m_scale == LinearScale) {
Chris@0 687
Chris@0 688 vy = int((m * i * m_gain) / 10);
Chris@0 689
Chris@0 690 text = QString("%1").arg(float(i) / 10.0);
Chris@0 691 if (i == 0) text = "0.0";
Chris@0 692 if (i == 10) text = "1.0";
Chris@0 693
Chris@0 694 } else {
Chris@0 695
Chris@0 696 int db;
Chris@0 697 bool minvalue = false;
Chris@0 698
Chris@0 699 if (m_scale == MeterScale) {
Chris@0 700 static int dbs[] = { -50, -40, -30, -20, -15,
Chris@0 701 -10, -5, -3, -2, -1, 0 };
Chris@0 702 db = dbs[i];
Chris@0 703 if (db == -50) minvalue = true;
Chris@0 704 vy = AudioLevel::multiplier_to_preview
Chris@0 705 (AudioLevel::dB_to_multiplier(db) * m_gain, m);
Chris@0 706 } else {
Chris@0 707 db = -100 + i * 10;
Chris@0 708 if (db == -100) minvalue = true;
Chris@0 709 vy = dBscale
Chris@0 710 (AudioLevel::dB_to_multiplier(db) * m_gain, m);
Chris@0 711 }
Chris@0 712
Chris@0 713 text = QString("%1").arg(db);
Chris@0 714 if (db == 0) text = tr("0dB");
Chris@0 715 if (minvalue) {
Chris@0 716 text = tr("-Inf");
Chris@0 717 vy = 0;
Chris@0 718 }
Chris@0 719 }
Chris@0 720
Chris@0 721 if (vy < 0) vy = -vy;
Chris@0 722 if (vy >= m - 1) continue;
Chris@0 723
Chris@0 724 if (py >= 0 && (vy - py) < textHeight - 1) {
Chris@0 725 paint.drawLine(w - 4, my - vy, w, my - vy);
Chris@0 726 if (vy > 0) paint.drawLine(w - 4, my + vy, w, my + vy);
Chris@0 727 continue;
Chris@0 728 }
Chris@0 729
Chris@0 730 paint.drawLine(w - 7, my - vy, w, my - vy);
Chris@0 731 if (vy > 0) paint.drawLine(w - 7, my + vy, w, my + vy);
Chris@0 732
Chris@0 733 int tx = 3;
Chris@0 734 if (m_scale != LinearScale) {
Chris@0 735 tx = w - 10 - paint.fontMetrics().width(text);
Chris@0 736 }
Chris@0 737
Chris@0 738 paint.drawText(tx, my - vy + toff, text);
Chris@0 739 if (vy > 0) paint.drawText(tx, my + vy + toff, text);
Chris@0 740
Chris@0 741 py = vy;
Chris@0 742 }
Chris@0 743 }
Chris@0 744 }
Chris@0 745
Chris@6 746 QString
Chris@6 747 WaveformLayer::toXmlString(QString indent, QString extraAttributes) const
Chris@6 748 {
Chris@6 749 QString s;
Chris@6 750
Chris@6 751 s += QString("gain=\"%1\" "
Chris@6 752 "colour=\"%2\" "
Chris@6 753 "showMeans=\"%3\" "
Chris@6 754 "greyscale=\"%4\" "
Chris@6 755 "channelMode=\"%5\" "
Chris@6 756 "channel=\"%6\" "
Chris@6 757 "scale=\"%7\" "
Chris@6 758 "aggressive=\"%8\"")
Chris@6 759 .arg(m_gain)
Chris@6 760 .arg(encodeColour(m_colour))
Chris@6 761 .arg(m_showMeans)
Chris@6 762 .arg(m_greyscale)
Chris@6 763 .arg(m_channelMode)
Chris@6 764 .arg(m_channel)
Chris@6 765 .arg(m_scale)
Chris@6 766 .arg(m_aggressive);
Chris@6 767
Chris@6 768 return Layer::toXmlString(indent, extraAttributes + " " + s);
Chris@6 769 }
Chris@6 770
Chris@11 771 void
Chris@11 772 WaveformLayer::setProperties(const QXmlAttributes &attributes)
Chris@11 773 {
Chris@11 774 bool ok = false;
Chris@11 775
Chris@11 776 float gain = attributes.value("gain").toFloat(&ok);
Chris@11 777 if (ok) setGain(gain);
Chris@11 778
Chris@11 779 QString colourSpec = attributes.value("colour");
Chris@11 780 if (colourSpec != "") {
Chris@11 781 QColor colour(colourSpec);
Chris@11 782 if (colour.isValid()) {
Chris@11 783 setBaseColour(QColor(colourSpec));
Chris@11 784 }
Chris@11 785 }
Chris@11 786
Chris@11 787 bool showMeans = (attributes.value("showMeans") == "1" ||
Chris@11 788 attributes.value("showMeans") == "true");
Chris@11 789 setShowMeans(showMeans);
Chris@11 790
Chris@11 791 bool greyscale = (attributes.value("greyscale") == "1" ||
Chris@11 792 attributes.value("greyscale") == "true");
Chris@11 793 setUseGreyscale(greyscale);
Chris@11 794
Chris@11 795 ChannelMode channelMode = (ChannelMode)
Chris@11 796 attributes.value("channelMode").toInt(&ok);
Chris@11 797 if (ok) setChannelMode(channelMode);
Chris@11 798
Chris@11 799 int channel = attributes.value("channel").toInt(&ok);
Chris@11 800 if (ok) setChannel(channel);
Chris@11 801
Chris@11 802 Scale scale = (Scale)
Chris@11 803 attributes.value("scale").toInt(&ok);
Chris@11 804 if (ok) setScale(scale);
Chris@11 805
Chris@11 806 bool aggressive = (attributes.value("aggressive") == "1" ||
Chris@11 807 attributes.value("aggressive") == "true");
Chris@11 808 setUseGreyscale(aggressive);
Chris@11 809 }
Chris@11 810
Chris@0 811 #ifdef INCLUDE_MOCFILES
Chris@0 812 #include "WaveformLayer.moc.cpp"
Chris@0 813 #endif
Chris@0 814