annotate layer/Colour3DPlotLayer.cpp @ 77:fd348f36c0d3

* Implement harmonic cursor in spectrogram * Implement layer export. This doesn't quite do the right thing for the SV XML layer export yet -- it doesn't include layer display information, so when imported, it only creates an invisible model. Could also do with fixing CSV file import so as to work correctly for note and text layers.
author Chris Cannam
date Mon, 10 Apr 2006 17:22:59 +0000
parents 45ba0b381c5d
children 2be85befe873
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 "Colour3DPlotLayer.h"
Chris@0 17
Chris@0 18 #include "base/View.h"
Chris@0 19 #include "base/Profiler.h"
Chris@0 20
Chris@0 21 #include <QPainter>
Chris@0 22 #include <QImage>
Chris@0 23 #include <QRect>
Chris@0 24
Chris@0 25 #include <iostream>
Chris@0 26
Chris@0 27 #include <cassert>
Chris@0 28
Chris@0 29
Chris@44 30 Colour3DPlotLayer::Colour3DPlotLayer() :
Chris@44 31 Layer(),
Chris@0 32 m_model(0),
Chris@0 33 m_cache(0)
Chris@0 34 {
Chris@44 35
Chris@0 36 }
Chris@0 37
Chris@0 38 Colour3DPlotLayer::~Colour3DPlotLayer()
Chris@0 39 {
Chris@0 40 }
Chris@0 41
Chris@0 42 void
Chris@0 43 Colour3DPlotLayer::setModel(const DenseThreeDimensionalModel *model)
Chris@0 44 {
Chris@0 45 m_model = model;
Chris@0 46 if (!m_model || !m_model->isOK()) return;
Chris@0 47
Chris@0 48 connect(m_model, SIGNAL(modelChanged()), this, SIGNAL(modelChanged()));
Chris@0 49 connect(m_model, SIGNAL(modelChanged(size_t, size_t)),
Chris@0 50 this, SIGNAL(modelChanged(size_t, size_t)));
Chris@0 51
Chris@0 52 connect(m_model, SIGNAL(completionChanged()),
Chris@0 53 this, SIGNAL(modelCompletionChanged()));
Chris@0 54
Chris@0 55 connect(m_model, SIGNAL(modelChanged()), this, SLOT(cacheInvalid()));
Chris@0 56 connect(m_model, SIGNAL(modelChanged(size_t, size_t)),
Chris@0 57 this, SLOT(cacheInvalid(size_t, size_t)));
Chris@0 58
Chris@0 59 emit modelReplaced();
Chris@0 60 }
Chris@0 61
Chris@0 62 void
Chris@0 63 Colour3DPlotLayer::cacheInvalid()
Chris@0 64 {
Chris@0 65 delete m_cache;
Chris@0 66 m_cache = 0;
Chris@0 67 }
Chris@0 68
Chris@0 69 void
Chris@0 70 Colour3DPlotLayer::cacheInvalid(size_t, size_t)
Chris@0 71 {
Chris@0 72 cacheInvalid();
Chris@0 73 }
Chris@0 74
Chris@25 75 bool
Chris@44 76 Colour3DPlotLayer::isLayerScrollable(const View *v) const
Chris@25 77 {
Chris@25 78 QPoint discard;
Chris@44 79 return !v->shouldIlluminateLocalFeatures(this, discard);
Chris@25 80 }
Chris@25 81
Chris@25 82 QString
Chris@44 83 Colour3DPlotLayer::getFeatureDescription(View *v, QPoint &pos) const
Chris@25 84 {
Chris@25 85 if (!m_model) return "";
Chris@25 86
Chris@25 87 int x = pos.x();
Chris@25 88 int y = pos.y();
Chris@25 89
Chris@25 90 size_t modelStart = m_model->getStartFrame();
Chris@25 91 size_t modelWindow = m_model->getWindowSize();
Chris@25 92
Chris@25 93 int sx0 = modelWindow *
Chris@44 94 int((v->getFrameForX(x) - long(modelStart)) / long(modelWindow));
Chris@25 95 int sx1 = sx0 + modelWindow;
Chris@25 96
Chris@44 97 float binHeight = float(v->height()) / m_model->getYBinCount();
Chris@44 98 int sy = (v->height() - y) / binHeight;
Chris@25 99
Chris@25 100 float value = m_model->getBinValue(sx0, sy);
Chris@25 101
Chris@25 102 QString binName = m_model->getBinName(sy);
Chris@25 103 if (binName == "") binName = QString("[%1]").arg(sy + 1);
Chris@25 104 else binName = QString("%1 [%2]").arg(binName).arg(sy + 1);
Chris@25 105
Chris@25 106 QString text = tr("Time:\t%1 - %2\nBin:\t%3\nValue:\t%4")
Chris@25 107 .arg(RealTime::frame2RealTime(sx0, m_model->getSampleRate())
Chris@25 108 .toText(true).c_str())
Chris@25 109 .arg(RealTime::frame2RealTime(sx1, m_model->getSampleRate())
Chris@25 110 .toText(true).c_str())
Chris@25 111 .arg(binName)
Chris@25 112 .arg(value);
Chris@25 113
Chris@25 114 return text;
Chris@25 115 }
Chris@25 116
Chris@25 117 int
Chris@44 118 Colour3DPlotLayer::getVerticalScaleWidth(View *v, QPainter &paint) const
Chris@25 119 {
Chris@25 120 if (!m_model) return 0;
Chris@25 121
Chris@25 122 QString sampleText("123");
Chris@25 123 int tw = paint.fontMetrics().width(sampleText);
Chris@25 124
Chris@25 125 for (size_t i = 0; i < m_model->getYBinCount(); ++i) {
Chris@25 126 if (m_model->getBinName(i).length() > sampleText.length()) {
Chris@25 127 sampleText = m_model->getBinName(i);
Chris@25 128 }
Chris@25 129 }
Chris@25 130 if (sampleText != "123") {
Chris@25 131 tw = std::max(tw, paint.fontMetrics().width(sampleText));
Chris@25 132 }
Chris@25 133
Chris@25 134 return tw + 13;
Chris@25 135 }
Chris@25 136
Chris@25 137 void
Chris@44 138 Colour3DPlotLayer::paintVerticalScale(View *v, QPainter &paint, QRect rect) const
Chris@25 139 {
Chris@25 140 if (!m_model) return;
Chris@25 141
Chris@25 142 int h = rect.height(), w = rect.width();
Chris@44 143 float binHeight = float(v->height()) / m_model->getYBinCount();
Chris@25 144
Chris@25 145 // int textHeight = paint.fontMetrics().height();
Chris@25 146 // int toff = -textHeight + paint.fontMetrics().ascent() + 2;
Chris@25 147
Chris@25 148 for (size_t i = 0; i < m_model->getYBinCount(); ++i) {
Chris@25 149
Chris@44 150 int y0 = v->height() - (i * binHeight) - 1;
Chris@25 151
Chris@25 152 QString text = m_model->getBinName(i);
Chris@25 153 if (text == "") text = QString("[%1]").arg(i + 1);
Chris@25 154
Chris@25 155 paint.drawLine(0, y0, w, y0);
Chris@25 156
Chris@25 157 int cy = y0 - binHeight/2;
Chris@25 158 int ty = cy + paint.fontMetrics().ascent()/2;
Chris@25 159
Chris@25 160 // int tx = w - 10 - paint.fontMetrics().width(text);
Chris@25 161 paint.drawText(10, ty, text);
Chris@25 162 }
Chris@25 163 }
Chris@25 164
Chris@0 165 void
Chris@44 166 Colour3DPlotLayer::paint(View *v, QPainter &paint, QRect rect) const
Chris@0 167 {
Chris@0 168 // Profiler profiler("Colour3DPlotLayer::paint");
Chris@44 169 // std::cerr << "Colour3DPlotLayer::paint(): m_model is " << m_model << ", zoom level is " << v->getZoomLevel() << std::endl;
Chris@0 170
Chris@0 171 //!!! This doesn't yet accommodate the fact that the model may
Chris@0 172 //have a different sample rate from an underlying model. At the
Chris@0 173 //moment our paint mechanism assumes all models have the same
Chris@0 174 //sample rate. If that isn't the case, they won't align and the
Chris@0 175 //time ruler will match whichever model was used to construct it.
Chris@0 176 //Obviously it is not going to be the case in general that models
Chris@0 177 //will have the same samplerate, so we need a pane samplerate as
Chris@0 178 //well which we trivially realign to. (We can probably require
Chris@0 179 //the waveform and spectrogram layers to display at the pane
Chris@0 180 //samplerate.)
Chris@0 181
Chris@0 182 int completion = 0;
Chris@0 183 if (!m_model || !m_model->isOK() || !m_model->isReady(&completion)) {
Chris@0 184 if (completion > 0) {
Chris@44 185 paint.fillRect(0, 10, v->width() * completion / 100,
Chris@0 186 10, QColor(120, 120, 120));
Chris@0 187 }
Chris@0 188 return;
Chris@0 189 }
Chris@0 190
Chris@44 191 long startFrame = v->getStartFrame();
Chris@44 192 int zoomLevel = v->getZoomLevel();
Chris@0 193
Chris@0 194 size_t modelStart = m_model->getStartFrame();
Chris@0 195 size_t modelEnd = m_model->getEndFrame();
Chris@0 196 size_t modelWindow = m_model->getWindowSize();
Chris@0 197
Chris@12 198 size_t cacheWidth = (modelEnd - modelStart) / modelWindow + 1;
Chris@12 199 size_t cacheHeight = m_model->getYBinCount();
Chris@12 200
Chris@12 201 if (m_cache &&
Chris@12 202 (m_cache->width() != cacheWidth ||
Chris@12 203 m_cache->height() != cacheHeight)) {
Chris@12 204
Chris@12 205 delete m_cache;
Chris@12 206 m_cache = 0;
Chris@12 207 }
Chris@12 208
Chris@0 209 if (!m_cache) {
Chris@0 210
Chris@12 211 m_cache = new QImage(cacheWidth, cacheHeight, QImage::Format_Indexed8);
Chris@0 212
Chris@0 213 m_cache->setNumColors(256);
Chris@0 214 DenseThreeDimensionalModel::BinValueSet values;
Chris@0 215 /*
Chris@0 216 for (int pixel = 0; pixel < 256; ++pixel) {
Chris@0 217 int hue = 256 - pixel;
Chris@0 218 // int hue = 220 - pixel;
Chris@0 219 // if (hue < 0) hue += 360;
Chris@0 220 QColor color = QColor::fromHsv(hue, pixel/2 + 128, pixel);
Chris@0 221 m_cache->setColor(pixel, qRgb(color.red(), color.green(), color.blue()));
Chris@0 222 }
Chris@0 223 */
Chris@0 224
Chris@0 225 float min = m_model->getMinimumLevel();
Chris@0 226 float max = m_model->getMaximumLevel();
Chris@0 227
Chris@0 228 if (max == min) max = min + 1.0;
Chris@0 229
Chris@0 230 // int min = lrintf(m_model->getMinimumLevel());
Chris@0 231 // int max = lrintf(m_model->getMaximumLevel());
Chris@0 232 for (int value = 0; value < 256; ++value) {
Chris@0 233 // int spread = ((value - min) * 256) / (max - min);
Chris@0 234 // int hue = 256 - spread;
Chris@0 235 // QColor color = QColor::fromHsv(hue, spread/2 + 128, spread);
Chris@0 236 int hue = 256 - value;
Chris@26 237 QColor colour = QColor::fromHsv(hue, value/2 + 128, value);
Chris@26 238 m_cache->setColor(value, qRgba(colour.red(), colour.green(), colour.blue(), 80));
Chris@0 239 // std::cerr << "Colour3DPlotLayer: Index " << value << ": hue " << hue << std::endl;
Chris@0 240 }
Chris@0 241
Chris@0 242 m_cache->fill(min);
Chris@0 243
Chris@0 244 for (size_t f = modelStart; f <= modelEnd; f += modelWindow) {
Chris@0 245
Chris@0 246 values.clear();
Chris@0 247 m_model->getBinValues(f, values);
Chris@0 248
Chris@0 249 for (size_t y = 0; y < m_model->getYBinCount(); ++y) {
Chris@0 250
Chris@0 251 float value = min;
Chris@0 252 if (y < values.size()) value = values[y];
Chris@0 253
Chris@0 254 //!!! divide-by-zero!
Chris@0 255 int pixel = int(((value - min) * 256) / (max - min));
Chris@0 256
Chris@24 257 if (pixel == 256) pixel = 255;
Chris@24 258
Chris@0 259 m_cache->setPixel(f / modelWindow, y, pixel);
Chris@0 260 }
Chris@0 261 }
Chris@0 262 }
Chris@0 263
Chris@0 264 int x0 = rect.left();
Chris@0 265 int x1 = rect.right() + 1;
Chris@0 266
Chris@0 267 // int y0 = rect.top();
Chris@0 268 // int y1 = rect.bottom();
Chris@0 269 int w = x1 - x0;
Chris@44 270 int h = v->height();
Chris@0 271
Chris@0 272 // The cache is from the model's start frame to the model's end
Chris@0 273 // frame at the model's window increment frames per pixel. We
Chris@0 274 // want to draw from our start frame + x0 * zoomLevel to our start
Chris@0 275 // frame + x1 * zoomLevel at zoomLevel frames per pixel.
Chris@0 276
Chris@0 277 //!!! Strictly speaking we want quite different paint mechanisms
Chris@0 278 //for models that have more than one bin per pixel in either
Chris@0 279 //direction. This one is only really appropriate for models with
Chris@0 280 //far fewer bins in both directions.
Chris@0 281
Chris@44 282 int sx0 = int((v->getFrameForX(x0) - long(modelStart)) / long(modelWindow));
Chris@44 283 int sx1 = int((v->getFrameForX(x1) - long(modelStart)) / long(modelWindow));
Chris@0 284 int sw = sx1 - sx0;
Chris@0 285 int sh = m_model->getYBinCount();
Chris@0 286
Chris@0 287 /*
Chris@0 288 std::cerr << "Colour3DPlotLayer::paint: w " << w << ", h " << h << ", sx0 " << sx0 << ", sx1 " << sx1 << ", sw " << sw << ", sh " << sh << std::endl;
Chris@0 289 std::cerr << "Colour3DPlotLayer: sample rate is " << m_model->getSampleRate() << ", window size " << m_model->getWindowSize() << std::endl;
Chris@0 290 */
Chris@0 291
Chris@25 292 QPoint illuminatePos;
Chris@44 293 bool illuminate = v->shouldIlluminateLocalFeatures(this, illuminatePos);
Chris@54 294 char labelbuf[10];
Chris@25 295
Chris@0 296 for (int sx = sx0 - 1; sx <= sx1; ++sx) {
Chris@0 297
Chris@0 298 int fx = sx * int(modelWindow);
Chris@0 299
Chris@0 300 if (fx + modelWindow < int(modelStart) ||
Chris@0 301 fx > int(modelEnd)) continue;
Chris@0 302
Chris@54 303 int rx0 = v->getXForFrame(fx + int(modelStart));
Chris@54 304 int rx1 = v->getXForFrame(fx + int(modelStart) + int(modelWindow));
Chris@54 305
Chris@54 306 int w = rx1 - rx0;
Chris@54 307 if (w < 1) w = 1;
Chris@54 308
Chris@54 309 bool showLabel = (w > 10 &&
Chris@54 310 paint.fontMetrics().width("0.000000") < w - 3 &&
Chris@54 311 paint.fontMetrics().height() < (h / sh));
Chris@54 312
Chris@0 313 for (int sy = 0; sy < sh; ++sy) {
Chris@0 314
Chris@0 315 int ry0 = h - (sy * h) / sh - 1;
Chris@0 316 int ry1 = h - ((sy + 1) * h) / sh - 2;
Chris@0 317 QRgb pixel = qRgb(255, 255, 255);
Chris@0 318 if (sx >= 0 && sx < m_cache->width() &&
Chris@0 319 sy >= 0 && sy < m_cache->height()) {
Chris@0 320 pixel = m_cache->pixel(sx, sy);
Chris@0 321 }
Chris@0 322
Chris@0 323 QColor pen(255, 255, 255, 80);
Chris@0 324 QColor brush(pixel);
Chris@0 325 brush.setAlpha(160);
Chris@0 326 paint.setPen(Qt::NoPen);
Chris@0 327 paint.setBrush(brush);
Chris@0 328
Chris@25 329 QRect r(rx0, ry0 - h / sh - 1, w, h / sh + 1);
Chris@25 330
Chris@25 331 if (illuminate) {
Chris@25 332 if (r.contains(illuminatePos)) {
Chris@25 333 paint.setPen(Qt::black);//!!!
Chris@25 334 }
Chris@25 335 }
Chris@76 336
Chris@76 337 // std::cout << "rect " << rx0 << "," << (ry0 - h / sh - 1) << " "
Chris@76 338 // << w << "x" << (h / sh + 1) << std::endl;
Chris@25 339
Chris@25 340 paint.drawRect(r);
Chris@0 341
Chris@54 342 if (showLabel) {
Chris@54 343 if (sx >= 0 && sx < m_cache->width() &&
Chris@54 344 sy >= 0 && sy < m_cache->height()) {
Chris@24 345 int dv = m_cache->pixelIndex(sx, sy);
Chris@54 346 float value = m_model->getBinValue(fx, sy);
Chris@54 347 sprintf(labelbuf, "%06f", value);
Chris@54 348 QString text(labelbuf);
Chris@54 349 paint.setPen(Qt::white);
Chris@54 350 paint.drawText(rx0 + 2,
Chris@54 351 ry0 - h / sh - 1 + 2 + paint.fontMetrics().ascent(),
Chris@54 352 text);
Chris@0 353 }
Chris@0 354 }
Chris@0 355 }
Chris@0 356 }
Chris@0 357
Chris@0 358 /*
Chris@0 359 QRect targetRect(x0, 0, w, h);
Chris@0 360 QRect sourceRect(sx0, 0, sw, sh);
Chris@0 361
Chris@0 362 QImage scaled(w, h, QImage::Format_RGB32);
Chris@0 363
Chris@0 364 for (int x = 0; x < w; ++x) {
Chris@0 365 for (int y = 0; y < h; ++y) {
Chris@0 366
Chris@0 367
Chris@0 368
Chris@0 369 int sx = sx0 + (x * sw) / w;
Chris@0 370 int sy = sh - (y * sh) / h - 1;
Chris@0 371 // std::cerr << "Colour3DPlotLayer::paint: sx " << sx << ", sy " << sy << ", cache w " << m_cache->width() << ", cache h " << m_cache->height() << std::endl;
Chris@0 372 if (sx >= 0 && sy >= 0 &&
Chris@0 373 sx < m_cache->width() && sy < m_cache->height()) {
Chris@0 374 scaled.setPixel(x, y, m_cache->pixel(sx, sy));
Chris@0 375 } else {
Chris@0 376 scaled.setPixel(x, y, qRgba(255, 255, 255, 80));
Chris@0 377 }
Chris@0 378 }
Chris@0 379 }
Chris@0 380
Chris@0 381 paint.drawImage(x0, 0, scaled);
Chris@0 382 */
Chris@0 383 }
Chris@0 384
Chris@28 385 bool
Chris@44 386 Colour3DPlotLayer::snapToFeatureFrame(View *v, int &frame,
Chris@28 387 size_t &resolution,
Chris@28 388 SnapType snap) const
Chris@24 389 {
Chris@24 390 if (!m_model) {
Chris@44 391 return Layer::snapToFeatureFrame(v, frame, resolution, snap);
Chris@24 392 }
Chris@24 393
Chris@24 394 resolution = m_model->getWindowSize();
Chris@28 395 int left = (frame / resolution) * resolution;
Chris@28 396 int right = left + resolution;
Chris@28 397
Chris@28 398 switch (snap) {
Chris@28 399 case SnapLeft: frame = left; break;
Chris@28 400 case SnapRight: frame = right; break;
Chris@28 401 case SnapNearest:
Chris@28 402 case SnapNeighbouring:
Chris@28 403 if (frame - left > right - frame) frame = right;
Chris@28 404 else frame = left;
Chris@28 405 break;
Chris@28 406 }
Chris@24 407
Chris@28 408 return true;
Chris@24 409 }
Chris@24 410
Chris@0 411 #ifdef INCLUDE_MOCFILES
Chris@0 412 #include "Colour3DPlotLayer.moc.cpp"
Chris@0 413 #endif
Chris@0 414
Chris@0 415