annotate layer/Colour3DPlotLayer.cpp @ 24:6b794a2af3d9

* Add Chromagram plugin, and make a number of fixes to the dense 3d model and colour 3d plot class to accommodate it * Add pitch-conversion methods in base/Pitch * Commit previously overlooked Command.cpp
author Chris Cannam
date Wed, 01 Feb 2006 14:49:49 +0000
parents 1deb5f87a18c
children dcdb21b62dbb
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@0 69 void
Chris@0 70 Colour3DPlotLayer::paint(QPainter &paint, QRect rect) const
Chris@0 71 {
Chris@0 72 // Profiler profiler("Colour3DPlotLayer::paint");
Chris@0 73 // std::cerr << "Colour3DPlotLayer::paint(): m_model is " << m_model << ", zoom level is " << m_view->getZoomLevel() << std::endl;
Chris@0 74
Chris@0 75 //!!! This doesn't yet accommodate the fact that the model may
Chris@0 76 //have a different sample rate from an underlying model. At the
Chris@0 77 //moment our paint mechanism assumes all models have the same
Chris@0 78 //sample rate. If that isn't the case, they won't align and the
Chris@0 79 //time ruler will match whichever model was used to construct it.
Chris@0 80 //Obviously it is not going to be the case in general that models
Chris@0 81 //will have the same samplerate, so we need a pane samplerate as
Chris@0 82 //well which we trivially realign to. (We can probably require
Chris@0 83 //the waveform and spectrogram layers to display at the pane
Chris@0 84 //samplerate.)
Chris@0 85
Chris@0 86 int completion = 0;
Chris@0 87 if (!m_model || !m_model->isOK() || !m_model->isReady(&completion)) {
Chris@0 88 if (completion > 0) {
Chris@0 89 paint.fillRect(0, 10, m_view->width() * completion / 100,
Chris@0 90 10, QColor(120, 120, 120));
Chris@0 91 }
Chris@0 92 return;
Chris@0 93 }
Chris@0 94
Chris@0 95 long startFrame = m_view->getStartFrame();
Chris@0 96 int zoomLevel = m_view->getZoomLevel();
Chris@0 97
Chris@0 98 size_t modelStart = m_model->getStartFrame();
Chris@0 99 size_t modelEnd = m_model->getEndFrame();
Chris@0 100 size_t modelWindow = m_model->getWindowSize();
Chris@0 101
Chris@12 102 size_t cacheWidth = (modelEnd - modelStart) / modelWindow + 1;
Chris@12 103 size_t cacheHeight = m_model->getYBinCount();
Chris@12 104
Chris@12 105 if (m_cache &&
Chris@12 106 (m_cache->width() != cacheWidth ||
Chris@12 107 m_cache->height() != cacheHeight)) {
Chris@12 108
Chris@12 109 delete m_cache;
Chris@12 110 m_cache = 0;
Chris@12 111 }
Chris@12 112
Chris@0 113 if (!m_cache) {
Chris@0 114
Chris@12 115 m_cache = new QImage(cacheWidth, cacheHeight, QImage::Format_Indexed8);
Chris@0 116
Chris@0 117 m_cache->setNumColors(256);
Chris@0 118 DenseThreeDimensionalModel::BinValueSet values;
Chris@0 119 /*
Chris@0 120 for (int pixel = 0; pixel < 256; ++pixel) {
Chris@0 121 int hue = 256 - pixel;
Chris@0 122 // int hue = 220 - pixel;
Chris@0 123 // if (hue < 0) hue += 360;
Chris@0 124 QColor color = QColor::fromHsv(hue, pixel/2 + 128, pixel);
Chris@0 125 m_cache->setColor(pixel, qRgb(color.red(), color.green(), color.blue()));
Chris@0 126 }
Chris@0 127 */
Chris@0 128
Chris@0 129 float min = m_model->getMinimumLevel();
Chris@0 130 float max = m_model->getMaximumLevel();
Chris@0 131
Chris@0 132 if (max == min) max = min + 1.0;
Chris@0 133
Chris@0 134 // int min = lrintf(m_model->getMinimumLevel());
Chris@0 135 // int max = lrintf(m_model->getMaximumLevel());
Chris@0 136 for (int value = 0; value < 256; ++value) {
Chris@0 137 // int spread = ((value - min) * 256) / (max - min);
Chris@0 138 // int hue = 256 - spread;
Chris@0 139 // QColor color = QColor::fromHsv(hue, spread/2 + 128, spread);
Chris@0 140 int hue = 256 - value;
Chris@0 141 QColor color = QColor::fromHsv(hue, value/2 + 128, value);
Chris@0 142 m_cache->setColor(value, qRgba(color.red(), color.green(), color.blue(), 80));
Chris@0 143 // std::cerr << "Colour3DPlotLayer: Index " << value << ": hue " << hue << std::endl;
Chris@0 144 }
Chris@0 145
Chris@0 146 m_cache->fill(min);
Chris@0 147
Chris@0 148 for (size_t f = modelStart; f <= modelEnd; f += modelWindow) {
Chris@0 149
Chris@0 150 values.clear();
Chris@0 151 m_model->getBinValues(f, values);
Chris@0 152
Chris@0 153 for (size_t y = 0; y < m_model->getYBinCount(); ++y) {
Chris@0 154
Chris@0 155 float value = min;
Chris@0 156 if (y < values.size()) value = values[y];
Chris@0 157
Chris@0 158 //!!! divide-by-zero!
Chris@0 159 int pixel = int(((value - min) * 256) / (max - min));
Chris@0 160
Chris@24 161 if (pixel == 256) pixel = 255;
Chris@24 162
Chris@0 163 m_cache->setPixel(f / modelWindow, y, pixel);
Chris@0 164 }
Chris@0 165 }
Chris@0 166 }
Chris@0 167
Chris@0 168 int x0 = rect.left();
Chris@0 169 int x1 = rect.right() + 1;
Chris@0 170
Chris@0 171 // int y0 = rect.top();
Chris@0 172 // int y1 = rect.bottom();
Chris@0 173 int w = x1 - x0;
Chris@0 174 int h = m_view->height();
Chris@0 175
Chris@0 176 // The cache is from the model's start frame to the model's end
Chris@0 177 // frame at the model's window increment frames per pixel. We
Chris@0 178 // want to draw from our start frame + x0 * zoomLevel to our start
Chris@0 179 // frame + x1 * zoomLevel at zoomLevel frames per pixel.
Chris@0 180
Chris@0 181 //!!! Strictly speaking we want quite different paint mechanisms
Chris@0 182 //for models that have more than one bin per pixel in either
Chris@0 183 //direction. This one is only really appropriate for models with
Chris@0 184 //far fewer bins in both directions.
Chris@0 185
Chris@20 186 int sx0 = int((getFrameForX(x0) - long(modelStart)) / long(modelWindow));
Chris@20 187 int sx1 = int((getFrameForX(x1) - long(modelStart)) / long(modelWindow));
Chris@0 188 int sw = sx1 - sx0;
Chris@0 189 int sh = m_model->getYBinCount();
Chris@0 190
Chris@0 191 /*
Chris@0 192 std::cerr << "Colour3DPlotLayer::paint: w " << w << ", h " << h << ", sx0 " << sx0 << ", sx1 " << sx1 << ", sw " << sw << ", sh " << sh << std::endl;
Chris@0 193 std::cerr << "Colour3DPlotLayer: sample rate is " << m_model->getSampleRate() << ", window size " << m_model->getWindowSize() << std::endl;
Chris@0 194 */
Chris@0 195
Chris@0 196 for (int sx = sx0 - 1; sx <= sx1; ++sx) {
Chris@0 197
Chris@0 198 int fx = sx * int(modelWindow);
Chris@0 199
Chris@0 200 if (fx + modelWindow < int(modelStart) ||
Chris@0 201 fx > int(modelEnd)) continue;
Chris@0 202
Chris@0 203 for (int sy = 0; sy < sh; ++sy) {
Chris@0 204
Chris@20 205 int rx0 = getXForFrame(fx + int(modelStart));
Chris@20 206 int rx1 = getXForFrame(fx + int(modelStart) + int(modelWindow));
Chris@0 207
Chris@0 208 int ry0 = h - (sy * h) / sh - 1;
Chris@0 209 int ry1 = h - ((sy + 1) * h) / sh - 2;
Chris@0 210 QRgb pixel = qRgb(255, 255, 255);
Chris@0 211 if (sx >= 0 && sx < m_cache->width() &&
Chris@0 212 sy >= 0 && sy < m_cache->height()) {
Chris@0 213 pixel = m_cache->pixel(sx, sy);
Chris@0 214 }
Chris@0 215
Chris@0 216 QColor pen(255, 255, 255, 80);
Chris@0 217 // QColor pen(pixel);
Chris@0 218 QColor brush(pixel);
Chris@0 219 brush.setAlpha(160);
Chris@0 220 // paint.setPen(pen);
Chris@0 221 paint.setPen(Qt::NoPen);
Chris@0 222 paint.setBrush(brush);
Chris@0 223
Chris@0 224 int w = rx1 - rx0;
Chris@0 225 if (w < 1) w = 1;
Chris@0 226 paint.drawRect(rx0, ry0 - h / sh - 1, w, h / sh + 1);
Chris@0 227
Chris@0 228 if (sx >= 0 && sx < m_cache->width() &&
Chris@0 229 sy >= 0 && sy < m_cache->height()) {
Chris@24 230 if (w > 10) {
Chris@24 231 int dv = m_cache->pixelIndex(sx, sy);
Chris@24 232 // if (dv != 0 && paint.fontMetrics().height() < (h / sh)) {
Chris@24 233 if (paint.fontMetrics().height() < (h / sh)) {
Chris@24 234 float value = m_model->getBinValue(fx, sy);
Chris@24 235 QString text = QString("%1").arg(value); //dv);
Chris@24 236 if (paint.fontMetrics().width(text) < w - 3) {
Chris@24 237 paint.setPen(Qt::white);
Chris@24 238 paint.drawText(rx0 + 2,
Chris@24 239 ry0 - h / sh - 1 + 2 + paint.fontMetrics().ascent(),
Chris@24 240 QString("%1").arg(value));
Chris@24 241 }
Chris@0 242 }
Chris@0 243 }
Chris@0 244 }
Chris@0 245 }
Chris@0 246 }
Chris@0 247
Chris@0 248 /*
Chris@0 249 QRect targetRect(x0, 0, w, h);
Chris@0 250 QRect sourceRect(sx0, 0, sw, sh);
Chris@0 251
Chris@0 252 QImage scaled(w, h, QImage::Format_RGB32);
Chris@0 253
Chris@0 254 for (int x = 0; x < w; ++x) {
Chris@0 255 for (int y = 0; y < h; ++y) {
Chris@0 256
Chris@0 257
Chris@0 258
Chris@0 259 int sx = sx0 + (x * sw) / w;
Chris@0 260 int sy = sh - (y * sh) / h - 1;
Chris@0 261 // std::cerr << "Colour3DPlotLayer::paint: sx " << sx << ", sy " << sy << ", cache w " << m_cache->width() << ", cache h " << m_cache->height() << std::endl;
Chris@0 262 if (sx >= 0 && sy >= 0 &&
Chris@0 263 sx < m_cache->width() && sy < m_cache->height()) {
Chris@0 264 scaled.setPixel(x, y, m_cache->pixel(sx, sy));
Chris@0 265 } else {
Chris@0 266 scaled.setPixel(x, y, qRgba(255, 255, 255, 80));
Chris@0 267 }
Chris@0 268 }
Chris@0 269 }
Chris@0 270
Chris@0 271 paint.drawImage(x0, 0, scaled);
Chris@0 272 */
Chris@0 273 }
Chris@0 274
Chris@24 275 int
Chris@24 276 Colour3DPlotLayer::getNearestFeatureFrame(int frame,
Chris@24 277 size_t &resolution,
Chris@24 278 bool snapRight) const
Chris@24 279 {
Chris@24 280 if (!m_model) {
Chris@24 281 return Layer::getNearestFeatureFrame(frame, resolution, snapRight);
Chris@24 282 }
Chris@24 283
Chris@24 284 resolution = m_model->getWindowSize();
Chris@24 285
Chris@24 286 int returnFrame = (frame / resolution) * resolution;
Chris@24 287 if (snapRight) returnFrame += resolution;
Chris@24 288
Chris@24 289 return returnFrame;
Chris@24 290 }
Chris@24 291
Chris@0 292 #ifdef INCLUDE_MOCFILES
Chris@0 293 #include "Colour3DPlotLayer.moc.cpp"
Chris@0 294 #endif
Chris@0 295
Chris@0 296