annotate layer/Colour3DPlotLayer.cpp @ 38:beb801473743

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