annotate layer/WaveformLayer.cpp @ 37:21d061e66177

* Make the frequency estimation mode in the spectrogram layer actually useful, and make sure it gets mostly the right results... Still some tidying to do in here.
author Chris Cannam
date Wed, 22 Feb 2006 17:45:18 +0000
parents c43f2c4f66f2
children f2c416cbdaa9
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@34 110 deft = lrint(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@28 392 paint->setRenderHint(QPainter::Antialiasing, false);
Chris@28 393
Chris@0 394 int x0 = 0, x1 = w - 1;
Chris@0 395 int y0 = 0, y1 = h - 1;
Chris@0 396
Chris@0 397 x0 = rect.left();
Chris@0 398 x1 = rect.right();
Chris@0 399 y0 = rect.top();
Chris@0 400 y1 = rect.bottom();
Chris@0 401
Chris@28 402 if (x0 > 0) --x0;
Chris@28 403 if (x1 < m_view->width()) ++x1;
Chris@28 404
Chris@25 405 long frame0 = getFrameForX(x0);
Chris@25 406 long frame1 = getFrameForX(x1 + 1);
Chris@0 407
Chris@4 408 #ifdef DEBUG_WAVEFORM_PAINT
Chris@4 409 std::cerr << "Painting waveform from " << frame0 << " to " << frame1 << " (" << (x1-x0+1) << " pixels at zoom " << zoomLevel << ")" << std::endl;
Chris@4 410 #endif
Chris@0 411
Chris@0 412 RangeSummarisableTimeValueModel::RangeBlock ranges;
Chris@0 413 RangeSummarisableTimeValueModel::RangeBlock otherChannelRanges;
Chris@0 414 RangeSummarisableTimeValueModel::Range range;
Chris@0 415
Chris@0 416 QColor greys[3];
Chris@0 417 if (m_colour == Qt::black) {
Chris@27 418 for (int i = 0; i < 3; ++i) { // 0 lightest, 2 darkest
Chris@0 419 int level = 192 - 64 * i;
Chris@0 420 greys[i] = QColor(level, level, level);
Chris@0 421 }
Chris@0 422 } else {
Chris@27 423 int h, s, v;
Chris@27 424 m_colour.getHsv(&h, &s, &v);
Chris@27 425 for (int i = 0; i < 3; ++i) { // 0 lightest, 2 darkest
Chris@27 426 if (m_view->hasLightBackground()) {
Chris@27 427 greys[i] = QColor::fromHsv(h, s * (i + 1) / 4, v);
Chris@27 428 } else {
Chris@27 429 greys[i] = QColor::fromHsv(h, s * (3 - i) / 4, v);
Chris@27 430 }
Chris@27 431 }
Chris@0 432 }
Chris@0 433
Chris@0 434 QColor midColour = m_colour;
Chris@0 435 if (midColour == Qt::black) {
Chris@0 436 midColour = Qt::gray;
Chris@0 437 } else if (m_view->hasLightBackground()) {
Chris@0 438 midColour = midColour.light(150);
Chris@0 439 } else {
Chris@0 440 midColour = midColour.light(50);
Chris@0 441 }
Chris@0 442
Chris@0 443 for (size_t ch = minChannel; ch <= maxChannel; ++ch) {
Chris@0 444
Chris@0 445 int prevRangeBottom = -1, prevRangeTop = -1;
Chris@28 446 QColor prevRangeBottomColour = m_colour, prevRangeTopColour = m_colour;
Chris@0 447
Chris@0 448 int m = (h / channels) / 2;
Chris@0 449 int my = m + (((ch - minChannel) * h) / channels);
Chris@0 450
Chris@0 451 // std::cerr << "ch = " << ch << ", channels = " << channels << ", m = " << m << ", my = " << my << ", h = " << h << std::endl;
Chris@0 452
Chris@0 453 if (my - m > y1 || my + m < y0) continue;
Chris@0 454
Chris@0 455 paint->setPen(greys[0]);
Chris@0 456 paint->drawLine(x0, my, x1, my);
Chris@0 457
Chris@0 458 if (frame1 <= 0) continue;
Chris@0 459
Chris@0 460 size_t modelZoomLevel = zoomLevel;
Chris@0 461
Chris@0 462 ranges = m_model->getRanges
Chris@0 463 (ch, frame0 < 0 ? 0 : frame0, frame1, modelZoomLevel);
Chris@0 464
Chris@0 465 if (mergingChannels) {
Chris@0 466 otherChannelRanges = m_model->getRanges
Chris@0 467 (1, frame0 < 0 ? 0 : frame0, frame1, modelZoomLevel);
Chris@0 468 }
Chris@0 469
Chris@0 470 for (int x = x0; x <= x1; ++x) {
Chris@0 471
Chris@0 472 range = RangeSummarisableTimeValueModel::Range();
Chris@0 473 size_t index = x - x0;
Chris@0 474 size_t maxIndex = index;
Chris@0 475
Chris@0 476 if (frame0 < 0) {
Chris@0 477 if (index < size_t(-frame0 / zoomLevel)) {
Chris@0 478 continue;
Chris@0 479 } else {
Chris@0 480 index -= -frame0 / zoomLevel;
Chris@0 481 maxIndex = index;
Chris@0 482 }
Chris@0 483 }
Chris@0 484
Chris@0 485 if (int(modelZoomLevel) != zoomLevel) {
Chris@0 486
Chris@0 487 index = size_t((double(index) * zoomLevel) / modelZoomLevel);
Chris@0 488
Chris@0 489 if (int(modelZoomLevel) < zoomLevel) {
Chris@0 490 // Peaks may be missed! The model should avoid
Chris@0 491 // this by rounding zoom levels up rather than
Chris@0 492 // down, but we'd better cope in case it doesn't
Chris@0 493 maxIndex = index;
Chris@0 494 } else {
Chris@0 495 maxIndex = size_t((double(index + 1) * zoomLevel)
Chris@0 496 / modelZoomLevel) - 1;
Chris@0 497 }
Chris@0 498 }
Chris@0 499
Chris@0 500 if (index < ranges.size()) {
Chris@0 501
Chris@0 502 range = ranges[index];
Chris@0 503
Chris@0 504 if (maxIndex > index && maxIndex < ranges.size()) {
Chris@0 505 range.max = std::max(range.max, ranges[maxIndex].max);
Chris@0 506 range.min = std::min(range.min, ranges[maxIndex].min);
Chris@0 507 range.absmean = (range.absmean +
Chris@0 508 ranges[maxIndex].absmean) / 2;
Chris@0 509 }
Chris@0 510
Chris@0 511 } else {
Chris@0 512 continue;
Chris@0 513 }
Chris@0 514
Chris@0 515 int rangeBottom = 0, rangeTop = 0, meanBottom = 0, meanTop = 0;
Chris@0 516
Chris@0 517 if (mergingChannels) {
Chris@0 518
Chris@0 519 if (index < otherChannelRanges.size()) {
Chris@0 520
Chris@0 521 range.max = fabsf(range.max);
Chris@0 522 range.min = -fabsf(otherChannelRanges[index].max);
Chris@0 523 range.absmean = (range.absmean +
Chris@0 524 otherChannelRanges[index].absmean) / 2;
Chris@0 525
Chris@0 526 if (maxIndex > index && maxIndex < ranges.size()) {
Chris@0 527 // let's not concern ourselves about the mean
Chris@0 528 range.min = std::min
Chris@0 529 (range.min,
Chris@0 530 -fabsf(otherChannelRanges[maxIndex].max));
Chris@0 531 }
Chris@0 532 }
Chris@0 533 }
Chris@0 534
Chris@0 535 int greyLevels = 1;
Chris@0 536 if (m_greyscale && (m_scale == LinearScale)) greyLevels = 4;
Chris@0 537
Chris@0 538 switch (m_scale) {
Chris@0 539
Chris@0 540 case LinearScale:
Chris@0 541 rangeBottom = int( m * greyLevels * range.min * m_gain);
Chris@0 542 rangeTop = int( m * greyLevels * range.max * m_gain);
Chris@0 543 meanBottom = int(-m * range.absmean * m_gain);
Chris@0 544 meanTop = int( m * range.absmean * m_gain);
Chris@0 545 break;
Chris@0 546
Chris@0 547 case dBScale:
Chris@0 548 rangeBottom = dBscale(range.min * m_gain, m * greyLevels);
Chris@0 549 rangeTop = dBscale(range.max * m_gain, m * greyLevels);
Chris@0 550 meanBottom = -dBscale(range.absmean * m_gain, m);
Chris@0 551 meanTop = dBscale(range.absmean * m_gain, m);
Chris@0 552 break;
Chris@0 553
Chris@0 554 case MeterScale:
Chris@0 555 rangeBottom = AudioLevel::multiplier_to_preview(range.min * m_gain, m * greyLevels);
Chris@0 556 rangeTop = AudioLevel::multiplier_to_preview(range.max * m_gain, m * greyLevels);
Chris@0 557 meanBottom = -AudioLevel::multiplier_to_preview(range.absmean * m_gain, m);
Chris@0 558 meanTop = AudioLevel::multiplier_to_preview(range.absmean * m_gain, m);
Chris@0 559 }
Chris@0 560
Chris@27 561 rangeBottom = my * greyLevels - rangeBottom;
Chris@27 562 rangeTop = my * greyLevels - rangeTop;
Chris@27 563 meanBottom = my - meanBottom;
Chris@27 564 meanTop = my - meanTop;
Chris@27 565
Chris@27 566 int topFill = (rangeTop % greyLevels);
Chris@27 567 if (topFill > 0) topFill = greyLevels - topFill;
Chris@27 568
Chris@27 569 int bottomFill = (rangeBottom % greyLevels);
Chris@27 570
Chris@0 571 rangeTop = rangeTop / greyLevels;
Chris@0 572 rangeBottom = rangeBottom / greyLevels;
Chris@0 573
Chris@0 574 bool clipped = false;
Chris@27 575
Chris@27 576 if (rangeTop < my - m) { rangeTop = my - m; }
Chris@27 577 if (rangeTop > my + m) { rangeTop = my + m; }
Chris@27 578 if (rangeBottom < my - m) { rangeBottom = my - m; }
Chris@27 579 if (rangeBottom > my + m) { rangeBottom = my + m; }
Chris@27 580
Chris@27 581 if (range.max * m_gain <= -1.0 ||
Chris@27 582 range.max * m_gain >= 1.0) clipped = true;
Chris@0 583
Chris@0 584 if (meanBottom > rangeBottom) meanBottom = rangeBottom;
Chris@0 585 if (meanTop < rangeTop) meanTop = rangeTop;
Chris@0 586
Chris@0 587 bool drawMean = m_showMeans;
Chris@0 588 if (meanTop == rangeTop) {
Chris@0 589 if (meanTop < meanBottom) ++meanTop;
Chris@0 590 else drawMean = false;
Chris@0 591 }
Chris@0 592 if (meanBottom == rangeBottom) {
Chris@0 593 if (meanBottom > meanTop) --meanBottom;
Chris@0 594 else drawMean = false;
Chris@0 595 }
Chris@0 596
Chris@0 597 if (x != x0 && prevRangeBottom != -1) {
Chris@0 598 if (prevRangeBottom > rangeBottom &&
Chris@0 599 prevRangeTop > rangeBottom) {
Chris@28 600 // paint->setPen(midColour);
Chris@28 601 paint->setPen(m_colour);
Chris@0 602 paint->drawLine(x-1, prevRangeTop, x, rangeBottom);
Chris@28 603 paint->setPen(prevRangeTopColour);
Chris@0 604 paint->drawPoint(x-1, prevRangeTop);
Chris@0 605 } else if (prevRangeBottom < rangeTop &&
Chris@0 606 prevRangeTop < rangeTop) {
Chris@28 607 // paint->setPen(midColour);
Chris@28 608 paint->setPen(m_colour);
Chris@0 609 paint->drawLine(x-1, prevRangeBottom, x, rangeTop);
Chris@28 610 paint->setPen(prevRangeBottomColour);
Chris@0 611 paint->drawPoint(x-1, prevRangeBottom);
Chris@0 612 }
Chris@0 613 }
Chris@0 614
Chris@0 615 if (ready) {
Chris@0 616 if (clipped ||
Chris@0 617 range.min * m_gain <= -1.0 ||
Chris@0 618 range.max * m_gain >= 1.0) {
Chris@0 619 paint->setPen(Qt::red);
Chris@0 620 } else {
Chris@0 621 paint->setPen(m_colour);
Chris@0 622 }
Chris@0 623 } else {
Chris@0 624 paint->setPen(midColour);
Chris@0 625 }
Chris@0 626
Chris@0 627 paint->drawLine(x, rangeBottom, x, rangeTop);
Chris@0 628
Chris@28 629 prevRangeTopColour = m_colour;
Chris@28 630 prevRangeBottomColour = m_colour;
Chris@28 631
Chris@0 632 if (m_greyscale && (m_scale == LinearScale) && ready) {
Chris@0 633 if (!clipped) {
Chris@0 634 if (rangeTop < rangeBottom) {
Chris@0 635 if (topFill > 0 &&
Chris@0 636 (!drawMean || (rangeTop < meanTop - 1))) {
Chris@0 637 paint->setPen(greys[topFill - 1]);
Chris@27 638 paint->drawPoint(x, rangeTop);
Chris@28 639 prevRangeTopColour = greys[topFill - 1];
Chris@0 640 }
Chris@0 641 if (bottomFill > 0 &&
Chris@0 642 (!drawMean || (rangeBottom > meanBottom + 1))) {
Chris@0 643 paint->setPen(greys[bottomFill - 1]);
Chris@27 644 paint->drawPoint(x, rangeBottom);
Chris@28 645 prevRangeBottomColour = greys[bottomFill - 1];
Chris@0 646 }
Chris@0 647 }
Chris@0 648 }
Chris@0 649 }
Chris@0 650
Chris@0 651 if (drawMean) {
Chris@0 652 paint->setPen(midColour);
Chris@0 653 paint->drawLine(x, meanBottom, x, meanTop);
Chris@0 654 }
Chris@0 655
Chris@0 656 prevRangeBottom = rangeBottom;
Chris@0 657 prevRangeTop = rangeTop;
Chris@0 658 }
Chris@0 659 }
Chris@0 660
Chris@0 661 if (m_aggressive) {
Chris@0 662 if (ready && rect == m_view->rect()) {
Chris@0 663 m_cacheValid = true;
Chris@0 664 m_cacheZoomLevel = zoomLevel;
Chris@0 665 }
Chris@0 666 paint->end();
Chris@0 667 delete paint;
Chris@0 668 viewPainter.drawPixmap(rect, *m_cache, rect);
Chris@0 669 }
Chris@0 670 }
Chris@0 671
Chris@25 672 QString
Chris@25 673 WaveformLayer::getFeatureDescription(QPoint &pos) const
Chris@25 674 {
Chris@25 675 int x = pos.x();
Chris@25 676
Chris@25 677 if (!m_model || !m_model->isOK()) return "";
Chris@25 678
Chris@25 679 long f0 = getFrameForX(x);
Chris@25 680 long f1 = getFrameForX(x + 1);
Chris@25 681
Chris@25 682 if (f0 < 0) f0 = 0;
Chris@25 683 if (f1 <= f0) return "";
Chris@25 684
Chris@25 685 QString text;
Chris@25 686
Chris@25 687 RealTime rt0 = RealTime::frame2RealTime(f0, m_model->getSampleRate());
Chris@25 688 RealTime rt1 = RealTime::frame2RealTime(f1, m_model->getSampleRate());
Chris@25 689
Chris@25 690 if (f1 != f0 + 1 && (rt0.sec != rt1.sec || rt0.msec() != rt1.msec())) {
Chris@25 691 text += tr("Time:\t%1 - %2")
Chris@25 692 .arg(rt0.toText(true).c_str())
Chris@25 693 .arg(rt1.toText(true).c_str());
Chris@25 694 } else {
Chris@25 695 text += tr("Time:\t%1")
Chris@25 696 .arg(rt0.toText(true).c_str());
Chris@25 697 }
Chris@25 698
Chris@25 699 size_t channels = 0, minChannel = 0, maxChannel = 0;
Chris@25 700 bool mergingChannels = false;
Chris@25 701
Chris@25 702 channels = getChannelArrangement(minChannel, maxChannel, mergingChannels);
Chris@25 703 if (channels == 0) return "";
Chris@25 704
Chris@25 705 for (size_t ch = minChannel; ch <= maxChannel; ++ch) {
Chris@25 706
Chris@25 707 size_t blockSize = m_view->getZoomLevel();
Chris@25 708 RangeSummarisableTimeValueModel::RangeBlock ranges =
Chris@25 709 m_model->getRanges(ch, f0, f1, blockSize);
Chris@25 710
Chris@25 711 if (ranges.empty()) continue;
Chris@25 712
Chris@25 713 RangeSummarisableTimeValueModel::Range range = ranges[0];
Chris@25 714
Chris@25 715 QString label = tr("Level:");
Chris@25 716 if (minChannel != maxChannel) {
Chris@25 717 if (ch == 0) label = tr("Left:");
Chris@25 718 else if (ch == 1) label = tr("Right:");
Chris@25 719 else label = tr("Channel %1").arg(ch + 1);
Chris@25 720 }
Chris@25 721
Chris@25 722 int min = int(range.min * 1000);
Chris@25 723 int max = int(range.max * 1000);
Chris@25 724 int db = int(AudioLevel::multiplier_to_dB(std::max(fabsf(range.min),
Chris@25 725 fabsf(range.max)))
Chris@25 726 * 100);
Chris@25 727
Chris@25 728 if (min != max) {
Chris@25 729 text += tr("\n%1\t%2 - %3 (%4 dB peak)")
Chris@25 730 .arg(label).arg(float(min)/1000).arg(float(max)/1000).arg(float(db)/100);
Chris@25 731 } else {
Chris@25 732 text += tr("\n%1\t%2 (%3 dB peak)")
Chris@25 733 .arg(label).arg(float(min)/1000).arg(float(db)/100);
Chris@25 734 }
Chris@25 735 }
Chris@25 736
Chris@25 737 return text;
Chris@25 738 }
Chris@25 739
Chris@0 740 int
Chris@0 741 WaveformLayer::getVerticalScaleWidth(QPainter &paint) const
Chris@0 742 {
Chris@0 743 if (m_scale == LinearScale) {
Chris@0 744 return paint.fontMetrics().width("0.0") + 13;
Chris@0 745 } else {
Chris@0 746 return std::max(paint.fontMetrics().width(tr("0dB")),
Chris@0 747 paint.fontMetrics().width(tr("-Inf"))) + 13;
Chris@0 748 }
Chris@0 749 }
Chris@0 750
Chris@0 751 void
Chris@0 752 WaveformLayer::paintVerticalScale(QPainter &paint, QRect rect) const
Chris@0 753 {
Chris@0 754 if (!m_model || !m_model->isOK()) {
Chris@0 755 return;
Chris@0 756 }
Chris@0 757
Chris@0 758 size_t channels = 0, minChannel = 0, maxChannel = 0;
Chris@0 759 bool mergingChannels = false;
Chris@0 760
Chris@0 761 channels = getChannelArrangement(minChannel, maxChannel, mergingChannels);
Chris@0 762 if (channels == 0) return;
Chris@0 763
Chris@0 764 int h = rect.height(), w = rect.width();
Chris@0 765 int textHeight = paint.fontMetrics().height();
Chris@0 766 int toff = -textHeight/2 + paint.fontMetrics().ascent() + 1;
Chris@0 767
Chris@0 768 for (size_t ch = minChannel; ch <= maxChannel; ++ch) {
Chris@0 769
Chris@0 770 int m = (h / channels) / 2;
Chris@0 771 int my = m + (((ch - minChannel) * h) / channels);
Chris@0 772 int py = -1;
Chris@0 773
Chris@0 774 for (int i = 0; i <= 10; ++i) {
Chris@0 775
Chris@0 776 int vy = 0;
Chris@0 777 QString text = "";
Chris@0 778
Chris@0 779 if (m_scale == LinearScale) {
Chris@0 780
Chris@0 781 vy = int((m * i * m_gain) / 10);
Chris@0 782
Chris@0 783 text = QString("%1").arg(float(i) / 10.0);
Chris@0 784 if (i == 0) text = "0.0";
Chris@0 785 if (i == 10) text = "1.0";
Chris@0 786
Chris@0 787 } else {
Chris@0 788
Chris@0 789 int db;
Chris@0 790 bool minvalue = false;
Chris@0 791
Chris@0 792 if (m_scale == MeterScale) {
Chris@0 793 static int dbs[] = { -50, -40, -30, -20, -15,
Chris@0 794 -10, -5, -3, -2, -1, 0 };
Chris@0 795 db = dbs[i];
Chris@0 796 if (db == -50) minvalue = true;
Chris@0 797 vy = AudioLevel::multiplier_to_preview
Chris@0 798 (AudioLevel::dB_to_multiplier(db) * m_gain, m);
Chris@0 799 } else {
Chris@0 800 db = -100 + i * 10;
Chris@0 801 if (db == -100) minvalue = true;
Chris@0 802 vy = dBscale
Chris@0 803 (AudioLevel::dB_to_multiplier(db) * m_gain, m);
Chris@0 804 }
Chris@0 805
Chris@0 806 text = QString("%1").arg(db);
Chris@0 807 if (db == 0) text = tr("0dB");
Chris@0 808 if (minvalue) {
Chris@0 809 text = tr("-Inf");
Chris@0 810 vy = 0;
Chris@0 811 }
Chris@0 812 }
Chris@0 813
Chris@0 814 if (vy < 0) vy = -vy;
Chris@0 815 if (vy >= m - 1) continue;
Chris@0 816
Chris@0 817 if (py >= 0 && (vy - py) < textHeight - 1) {
Chris@0 818 paint.drawLine(w - 4, my - vy, w, my - vy);
Chris@0 819 if (vy > 0) paint.drawLine(w - 4, my + vy, w, my + vy);
Chris@0 820 continue;
Chris@0 821 }
Chris@0 822
Chris@0 823 paint.drawLine(w - 7, my - vy, w, my - vy);
Chris@0 824 if (vy > 0) paint.drawLine(w - 7, my + vy, w, my + vy);
Chris@0 825
Chris@0 826 int tx = 3;
Chris@0 827 if (m_scale != LinearScale) {
Chris@0 828 tx = w - 10 - paint.fontMetrics().width(text);
Chris@0 829 }
Chris@0 830
Chris@0 831 paint.drawText(tx, my - vy + toff, text);
Chris@0 832 if (vy > 0) paint.drawText(tx, my + vy + toff, text);
Chris@0 833
Chris@0 834 py = vy;
Chris@0 835 }
Chris@0 836 }
Chris@0 837 }
Chris@0 838
Chris@6 839 QString
Chris@6 840 WaveformLayer::toXmlString(QString indent, QString extraAttributes) const
Chris@6 841 {
Chris@6 842 QString s;
Chris@6 843
Chris@6 844 s += QString("gain=\"%1\" "
Chris@6 845 "colour=\"%2\" "
Chris@6 846 "showMeans=\"%3\" "
Chris@6 847 "greyscale=\"%4\" "
Chris@6 848 "channelMode=\"%5\" "
Chris@6 849 "channel=\"%6\" "
Chris@6 850 "scale=\"%7\" "
Chris@6 851 "aggressive=\"%8\"")
Chris@6 852 .arg(m_gain)
Chris@6 853 .arg(encodeColour(m_colour))
Chris@6 854 .arg(m_showMeans)
Chris@6 855 .arg(m_greyscale)
Chris@6 856 .arg(m_channelMode)
Chris@6 857 .arg(m_channel)
Chris@6 858 .arg(m_scale)
Chris@6 859 .arg(m_aggressive);
Chris@6 860
Chris@6 861 return Layer::toXmlString(indent, extraAttributes + " " + s);
Chris@6 862 }
Chris@6 863
Chris@11 864 void
Chris@11 865 WaveformLayer::setProperties(const QXmlAttributes &attributes)
Chris@11 866 {
Chris@11 867 bool ok = false;
Chris@11 868
Chris@11 869 float gain = attributes.value("gain").toFloat(&ok);
Chris@11 870 if (ok) setGain(gain);
Chris@11 871
Chris@11 872 QString colourSpec = attributes.value("colour");
Chris@11 873 if (colourSpec != "") {
Chris@11 874 QColor colour(colourSpec);
Chris@11 875 if (colour.isValid()) {
Chris@11 876 setBaseColour(QColor(colourSpec));
Chris@11 877 }
Chris@11 878 }
Chris@11 879
Chris@11 880 bool showMeans = (attributes.value("showMeans") == "1" ||
Chris@11 881 attributes.value("showMeans") == "true");
Chris@11 882 setShowMeans(showMeans);
Chris@11 883
Chris@11 884 bool greyscale = (attributes.value("greyscale") == "1" ||
Chris@11 885 attributes.value("greyscale") == "true");
Chris@11 886 setUseGreyscale(greyscale);
Chris@11 887
Chris@11 888 ChannelMode channelMode = (ChannelMode)
Chris@11 889 attributes.value("channelMode").toInt(&ok);
Chris@11 890 if (ok) setChannelMode(channelMode);
Chris@11 891
Chris@11 892 int channel = attributes.value("channel").toInt(&ok);
Chris@11 893 if (ok) setChannel(channel);
Chris@11 894
Chris@11 895 Scale scale = (Scale)
Chris@11 896 attributes.value("scale").toInt(&ok);
Chris@11 897 if (ok) setScale(scale);
Chris@11 898
Chris@11 899 bool aggressive = (attributes.value("aggressive") == "1" ||
Chris@11 900 attributes.value("aggressive") == "true");
Chris@11 901 setUseGreyscale(aggressive);
Chris@11 902 }
Chris@11 903
Chris@0 904 #ifdef INCLUDE_MOCFILES
Chris@0 905 #include "WaveformLayer.moc.cpp"
Chris@0 906 #endif
Chris@0 907