annotate layer/WaveformLayer.cpp @ 162:f32212631b9c

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