annotate layer/WaveformLayer.cpp @ 1:ab83c415a6cd

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