annotate layer/TimeValueLayer.cpp @ 10:8f5b812baaee

* Hook up SV file i/o. You can now save and load sessions. Some problems -- gain is not reloaded correctly for waveforms, reloaded panes are not properly reconnected to the panner, and no doubt plenty of others.
author Chris Cannam
date Tue, 17 Jan 2006 17:45:55 +0000
parents 634324c6296e
children 2d5005f2b3d9
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@3 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 "TimeValueLayer.h"
Chris@0 11
Chris@0 12 #include "base/Model.h"
Chris@0 13 #include "base/RealTime.h"
Chris@0 14 #include "base/Profiler.h"
Chris@0 15 #include "base/View.h"
Chris@0 16
Chris@0 17 #include "model/SparseTimeValueModel.h"
Chris@0 18
Chris@0 19 #include <QPainter>
Chris@6 20 #include <QPainterPath>
Chris@0 21
Chris@0 22 #include <iostream>
Chris@0 23 #include <cmath>
Chris@0 24
Chris@0 25 TimeValueLayer::TimeValueLayer(View *w) :
Chris@0 26 Layer(w),
Chris@0 27 m_model(0),
Chris@0 28 m_colour(Qt::black),
Chris@6 29 m_plotStyle(PlotConnectedPoints)
Chris@0 30 {
Chris@0 31 m_view->addLayer(this);
Chris@0 32 }
Chris@0 33
Chris@0 34 void
Chris@0 35 TimeValueLayer::setModel(SparseTimeValueModel *model)
Chris@0 36 {
Chris@0 37 if (m_model == model) return;
Chris@0 38 m_model = model;
Chris@0 39
Chris@0 40 connect(m_model, SIGNAL(modelChanged()), this, SIGNAL(modelChanged()));
Chris@0 41 connect(m_model, SIGNAL(modelChanged(size_t, size_t)),
Chris@0 42 this, SIGNAL(modelChanged(size_t, size_t)));
Chris@0 43
Chris@0 44 connect(m_model, SIGNAL(completionChanged()),
Chris@0 45 this, SIGNAL(modelCompletionChanged()));
Chris@0 46
Chris@0 47 std::cerr << "TimeValueLayer::setModel(" << model << ")" << std::endl;
Chris@0 48
Chris@0 49 emit modelReplaced();
Chris@0 50 }
Chris@0 51
Chris@0 52 Layer::PropertyList
Chris@0 53 TimeValueLayer::getProperties() const
Chris@0 54 {
Chris@0 55 PropertyList list;
Chris@0 56 list.push_back(tr("Colour"));
Chris@0 57 list.push_back(tr("Plot Type"));
Chris@0 58 return list;
Chris@0 59 }
Chris@0 60
Chris@0 61 Layer::PropertyType
Chris@0 62 TimeValueLayer::getPropertyType(const PropertyName &name) const
Chris@0 63 {
Chris@0 64 return ValueProperty;
Chris@0 65 }
Chris@0 66
Chris@0 67 int
Chris@0 68 TimeValueLayer::getPropertyRangeAndValue(const PropertyName &name,
Chris@0 69 int *min, int *max) const
Chris@0 70 {
Chris@0 71 //!!! factor this colour handling stuff out into a colour manager class
Chris@0 72
Chris@0 73 int deft = 0;
Chris@0 74
Chris@0 75 if (name == tr("Colour")) {
Chris@0 76
Chris@10 77 if (min) *min = 0;
Chris@10 78 if (max) *max = 5;
Chris@0 79
Chris@0 80 if (m_colour == Qt::black) deft = 0;
Chris@0 81 else if (m_colour == Qt::darkRed) deft = 1;
Chris@0 82 else if (m_colour == Qt::darkBlue) deft = 2;
Chris@0 83 else if (m_colour == Qt::darkGreen) deft = 3;
Chris@0 84 else if (m_colour == QColor(200, 50, 255)) deft = 4;
Chris@0 85 else if (m_colour == QColor(255, 150, 50)) deft = 5;
Chris@0 86
Chris@0 87 } else if (name == tr("Plot Type")) {
Chris@0 88
Chris@10 89 if (min) *min = 0;
Chris@10 90 if (max) *max = 4;
Chris@0 91
Chris@0 92 deft = int(m_plotStyle);
Chris@0 93
Chris@0 94 } else {
Chris@0 95
Chris@0 96 deft = Layer::getPropertyRangeAndValue(name, min, max);
Chris@0 97 }
Chris@0 98
Chris@0 99 return deft;
Chris@0 100 }
Chris@0 101
Chris@0 102 QString
Chris@0 103 TimeValueLayer::getPropertyValueLabel(const PropertyName &name,
Chris@0 104 int value) const
Chris@0 105 {
Chris@0 106 if (name == tr("Colour")) {
Chris@0 107 switch (value) {
Chris@0 108 default:
Chris@0 109 case 0: return tr("Black");
Chris@0 110 case 1: return tr("Red");
Chris@0 111 case 2: return tr("Blue");
Chris@0 112 case 3: return tr("Green");
Chris@0 113 case 4: return tr("Purple");
Chris@0 114 case 5: return tr("Orange");
Chris@0 115 }
Chris@0 116 } else if (name == tr("Plot Type")) {
Chris@0 117 switch (value) {
Chris@0 118 default:
Chris@0 119 case 0: return tr("Points");
Chris@0 120 case 1: return tr("Stems");
Chris@6 121 case 2: return tr("Connected Points");
Chris@6 122 case 3: return tr("Lines");
Chris@6 123 case 4: return tr("Curve");
Chris@0 124 }
Chris@0 125 }
Chris@0 126 return tr("<unknown>");
Chris@0 127 }
Chris@0 128
Chris@0 129 void
Chris@0 130 TimeValueLayer::setProperty(const PropertyName &name, int value)
Chris@0 131 {
Chris@0 132 if (name == tr("Colour")) {
Chris@0 133 switch (value) {
Chris@0 134 default:
Chris@0 135 case 0: setBaseColour(Qt::black); break;
Chris@0 136 case 1: setBaseColour(Qt::darkRed); break;
Chris@0 137 case 2: setBaseColour(Qt::darkBlue); break;
Chris@0 138 case 3: setBaseColour(Qt::darkGreen); break;
Chris@0 139 case 4: setBaseColour(QColor(200, 50, 255)); break;
Chris@0 140 case 5: setBaseColour(QColor(255, 150, 50)); break;
Chris@0 141 }
Chris@0 142 } else if (name == tr("Plot Type")) {
Chris@0 143 setPlotStyle(PlotStyle(value));
Chris@0 144 }
Chris@0 145 }
Chris@0 146
Chris@0 147 void
Chris@0 148 TimeValueLayer::setBaseColour(QColor colour)
Chris@0 149 {
Chris@0 150 if (m_colour == colour) return;
Chris@0 151 m_colour = colour;
Chris@0 152 emit layerParametersChanged();
Chris@0 153 }
Chris@0 154
Chris@0 155 void
Chris@0 156 TimeValueLayer::setPlotStyle(PlotStyle style)
Chris@0 157 {
Chris@0 158 if (m_plotStyle == style) return;
Chris@0 159 m_plotStyle = style;
Chris@0 160 emit layerParametersChanged();
Chris@0 161 }
Chris@0 162
Chris@0 163 bool
Chris@0 164 TimeValueLayer::isLayerScrollable() const
Chris@0 165 {
Chris@6 166 // We don't illuminate sections in the line or curve modes, so
Chris@6 167 // they're always scrollable
Chris@6 168
Chris@6 169 if (m_plotStyle == PlotLines ||
Chris@6 170 m_plotStyle == PlotCurve) return true;
Chris@6 171
Chris@0 172 QPoint discard;
Chris@0 173 return !m_view->shouldIlluminateLocalFeatures(this, discard);
Chris@0 174 }
Chris@0 175
Chris@0 176 QRect
Chris@0 177 TimeValueLayer::getFeatureDescriptionRect(QPainter &paint, QPoint pos) const
Chris@0 178 {
Chris@0 179 return QRect(0, 0,
Chris@0 180 std::max(100, paint.fontMetrics().width(tr("No local points"))),
Chris@0 181 70); //!!!
Chris@0 182 }
Chris@0 183
Chris@0 184 //!!! too much in common with TimeInstantLayer
Chris@0 185
Chris@0 186 SparseTimeValueModel::PointList
Chris@0 187 TimeValueLayer::getLocalPoints(int x) const
Chris@0 188 {
Chris@0 189 if (!m_model) return SparseTimeValueModel::PointList();
Chris@0 190
Chris@0 191 long startFrame = m_view->getStartFrame();
Chris@0 192 long endFrame = m_view->getEndFrame();
Chris@0 193 int zoomLevel = m_view->getZoomLevel();
Chris@0 194 long frame = startFrame + x * zoomLevel;
Chris@0 195
Chris@0 196 SparseTimeValueModel::PointList onPoints =
Chris@0 197 m_model->getPoints(frame);
Chris@0 198
Chris@0 199 if (!onPoints.empty()) {
Chris@0 200 return onPoints;
Chris@0 201 }
Chris@0 202
Chris@0 203 SparseTimeValueModel::PointList prevPoints =
Chris@0 204 m_model->getPreviousPoints(frame);
Chris@0 205 SparseTimeValueModel::PointList nextPoints =
Chris@0 206 m_model->getNextPoints(frame);
Chris@0 207
Chris@0 208 SparseTimeValueModel::PointList usePoints = prevPoints;
Chris@0 209
Chris@0 210 if (prevPoints.empty()) {
Chris@0 211 usePoints = nextPoints;
Chris@0 212 } else if (prevPoints.begin()->frame < startFrame &&
Chris@0 213 !(nextPoints.begin()->frame > endFrame)) {
Chris@0 214 usePoints = nextPoints;
Chris@0 215 } else if (nextPoints.begin()->frame - frame <
Chris@0 216 frame - prevPoints.begin()->frame) {
Chris@0 217 usePoints = nextPoints;
Chris@0 218 }
Chris@0 219
Chris@0 220 return usePoints;
Chris@0 221 }
Chris@0 222
Chris@0 223 void
Chris@0 224 TimeValueLayer::paintLocalFeatureDescription(QPainter &paint, QRect rect,
Chris@0 225 QPoint pos) const
Chris@0 226 {
Chris@0 227 //!!! bleagh
Chris@0 228
Chris@0 229 int x = pos.x();
Chris@0 230
Chris@0 231 if (!m_model || !m_model->getSampleRate()) return;
Chris@0 232
Chris@0 233 SparseTimeValueModel::PointList points = getLocalPoints(x);
Chris@0 234
Chris@0 235 QFontMetrics metrics = paint.fontMetrics();
Chris@0 236 int xbase = rect.x() + 5;
Chris@0 237 int ybase = rect.y() + 5;
Chris@0 238
Chris@0 239 if (points.empty()) {
Chris@0 240 QString label = tr("No local points");
Chris@0 241 if (!m_model->isReady()) {
Chris@0 242 label = tr("In progress");
Chris@0 243 }
Chris@0 244 paint.drawText(xbase + 5, ybase + 5 + metrics.ascent(), label);
Chris@0 245 return;
Chris@0 246 }
Chris@0 247
Chris@0 248 long useFrame = points.begin()->frame;
Chris@0 249
Chris@0 250 RealTime rt = RealTime::frame2RealTime(useFrame, m_model->getSampleRate());
Chris@0 251 QString timeText = QString("%1").arg(rt.toText(true).c_str());
Chris@0 252 QString valueText = QString("%1").arg(points.begin()->value);
Chris@0 253
Chris@0 254 int timewidth = metrics.width(timeText);
Chris@0 255 int valuewidth = metrics.width(valueText);
Chris@0 256 int labelwidth = metrics.width(points.begin()->label);
Chris@0 257
Chris@0 258 int boxheight = metrics.height() * 3 + 4;
Chris@0 259 int boxwidth = std::max(std::max(timewidth, labelwidth), valuewidth);
Chris@0 260
Chris@0 261 paint.drawRect(xbase, ybase, boxwidth + 10,
Chris@0 262 boxheight + 10 - metrics.descent() + 1);
Chris@0 263
Chris@0 264 paint.drawText(xbase + 5, ybase + 5 + metrics.ascent(), timeText);
Chris@0 265 paint.drawText(xbase + 5, ybase + 7 + metrics.ascent() + metrics.height(),
Chris@0 266 valueText);
Chris@0 267 paint.drawText(xbase + 5, ybase + 9 + metrics.ascent() + 2*metrics.height(),
Chris@0 268 points.begin()->label);
Chris@0 269 }
Chris@0 270
Chris@0 271 void
Chris@0 272 TimeValueLayer::paint(QPainter &paint, QRect rect) const
Chris@0 273 {
Chris@0 274 if (!m_model || !m_model->isOK()) return;
Chris@0 275
Chris@0 276 int sampleRate = m_model->getSampleRate();
Chris@0 277 if (!sampleRate) return;
Chris@0 278
Chris@0 279 // Profiler profiler("TimeValueLayer::paint", true);
Chris@0 280
Chris@0 281 long startFrame = m_view->getStartFrame();
Chris@0 282 int zoomLevel = m_view->getZoomLevel();
Chris@0 283
Chris@0 284 int x0 = rect.left(), x1 = rect.right();
Chris@0 285 long frame0 = startFrame + x0 * zoomLevel;
Chris@0 286 long frame1 = startFrame + x1 * zoomLevel;
Chris@0 287
Chris@0 288 SparseTimeValueModel::PointList points(m_model->getPoints
Chris@0 289 (frame0, frame1));
Chris@0 290
Chris@0 291 paint.setPen(m_colour);
Chris@0 292
Chris@0 293 QColor brushColour(m_colour);
Chris@0 294 brushColour.setAlpha(80);
Chris@0 295 paint.setBrush(brushColour);
Chris@0 296
Chris@0 297 // std::cerr << "TimeValueLayer::paint: resolution is "
Chris@0 298 // << m_model->getResolution() << " frames" << std::endl;
Chris@0 299
Chris@0 300 float min = m_model->getValueMinimum();
Chris@0 301 float max = m_model->getValueMaximum();
Chris@0 302 if (max == min) max = min + 1.0;
Chris@0 303
Chris@0 304 int origin = int(nearbyint(m_view->height() -
Chris@0 305 (-min * m_view->height()) / (max - min)));
Chris@0 306
Chris@0 307 QPoint localPos;
Chris@0 308 long illuminateFrame = -1;
Chris@0 309
Chris@0 310 if (m_view->shouldIlluminateLocalFeatures(this, localPos)) {
Chris@0 311 SparseTimeValueModel::PointList localPoints =
Chris@0 312 getLocalPoints(localPos.x());
Chris@0 313 if (!localPoints.empty()) illuminateFrame = localPoints.begin()->frame;
Chris@0 314 }
Chris@6 315
Chris@7 316 int w = m_model->getResolution() / zoomLevel;
Chris@7 317
Chris@6 318 paint.save();
Chris@6 319
Chris@7 320 if (w > 1 &&
Chris@7 321 (m_plotStyle == PlotLines ||
Chris@7 322 m_plotStyle == PlotCurve)) {
Chris@6 323 paint.setRenderHint(QPainter::Antialiasing, true);
Chris@6 324 }
Chris@6 325 QPainterPath path;
Chris@6 326
Chris@0 327 for (SparseTimeValueModel::PointList::const_iterator i = points.begin();
Chris@0 328 i != points.end(); ++i) {
Chris@0 329
Chris@0 330 const SparseTimeValueModel::Point &p(*i);
Chris@0 331
Chris@0 332 int x = (p.frame - startFrame) / zoomLevel;
Chris@0 333 int y = int(nearbyint(m_view->height() -
Chris@0 334 ((p.value - min) * m_view->height()) /
Chris@0 335 (max - min)));
Chris@0 336
Chris@0 337 if (w < 1) w = 1;
Chris@0 338
Chris@6 339 if (m_plotStyle == PlotLines ||
Chris@6 340 m_plotStyle == PlotCurve) {
Chris@6 341 paint.setPen(m_colour);
Chris@6 342 paint.setBrush(Qt::NoBrush);
Chris@3 343 } else {
Chris@3 344 paint.setPen(m_colour);
Chris@6 345 paint.setBrush(brushColour);
Chris@3 346 }
Chris@0 347
Chris@0 348 if (m_plotStyle == PlotStems) {
Chris@0 349 paint.setPen(brushColour);
Chris@0 350 if (y < origin - 1) {
Chris@0 351 paint.drawRect(x + w/2, y + 1, 1, origin - y);
Chris@0 352 } else if (y > origin + 1) {
Chris@0 353 paint.drawRect(x + w/2, origin, 1, y - origin - 1);
Chris@0 354 }
Chris@0 355 paint.setPen(m_colour);
Chris@0 356 }
Chris@0 357
Chris@0 358 if (illuminateFrame == p.frame) {
Chris@6 359
Chris@0 360 //!!! aside from the problem of choosing a colour, it'd be
Chris@0 361 //better to save the highlighted rects and draw them at
Chris@0 362 //the end perhaps
Chris@6 363
Chris@6 364 //!!! not equipped to illuminate the right section in line
Chris@6 365 //or curve mode
Chris@6 366
Chris@6 367 if (m_plotStyle != PlotCurve &&
Chris@6 368 m_plotStyle != PlotLines) {
Chris@6 369 paint.setPen(Qt::black);//!!!
Chris@6 370 paint.setBrush(Qt::black);//!!!
Chris@6 371 }
Chris@0 372 }
Chris@0 373
Chris@6 374 if (m_plotStyle != PlotLines &&
Chris@6 375 m_plotStyle != PlotCurve) {
Chris@3 376 paint.drawRect(x, y - 1, w, 2);
Chris@3 377 }
Chris@0 378
Chris@6 379 if (m_plotStyle == PlotConnectedPoints ||
Chris@6 380 m_plotStyle == PlotLines ||
Chris@6 381 m_plotStyle == PlotCurve) {
Chris@0 382
Chris@0 383 SparseTimeValueModel::PointList::const_iterator j = i;
Chris@0 384 ++j;
Chris@3 385
Chris@0 386 if (j != points.end()) {
Chris@3 387
Chris@0 388 const SparseTimeValueModel::Point &q(*j);
Chris@0 389 int nx = (q.frame - startFrame) / zoomLevel;
Chris@0 390 int ny = int(nearbyint(m_view->height() -
Chris@0 391 ((q.value - min) * m_view->height()) /
Chris@0 392 (max - min)));
Chris@3 393
Chris@6 394 if (m_plotStyle == PlotConnectedPoints) {
Chris@6 395
Chris@3 396 paint.setPen(brushColour);
Chris@3 397 paint.drawLine(x + w, y, nx, ny);
Chris@6 398
Chris@6 399 } else if (m_plotStyle == PlotLines) {
Chris@6 400
Chris@6 401 paint.drawLine(x + w/2, y, nx + w/2, ny);
Chris@6 402
Chris@3 403 } else {
Chris@6 404
Chris@6 405 if (path.isEmpty()) {
Chris@6 406 path.moveTo(x + w/2, y);
Chris@6 407 }
Chris@6 408
Chris@6 409 if (nx - x > 5) {
Chris@6 410 path.cubicTo(x + w, y, nx, ny, nx + w/2, ny);
Chris@6 411 } else {
Chris@6 412 path.lineTo(nx + w/2, ny);
Chris@6 413 }
Chris@3 414 }
Chris@0 415 }
Chris@0 416 }
Chris@0 417
Chris@0 418 /// if (p.label != "") {
Chris@0 419 /// paint.drawText(x + 5, y - paint.fontMetrics().height() + paint.fontMetrics().ascent(), p.label);
Chris@0 420 /// }
Chris@0 421 }
Chris@6 422
Chris@6 423 if (m_plotStyle == PlotCurve && !path.isEmpty()) {
Chris@6 424 paint.drawPath(path);
Chris@6 425 }
Chris@6 426
Chris@6 427 paint.restore();
Chris@6 428
Chris@6 429 // looks like save/restore doesn't deal with this:
Chris@6 430 paint.setRenderHint(QPainter::Antialiasing, false);
Chris@6 431 }
Chris@6 432
Chris@6 433 QString
Chris@6 434 TimeValueLayer::toXmlString(QString indent, QString extraAttributes) const
Chris@6 435 {
Chris@6 436 return Layer::toXmlString(indent, extraAttributes +
Chris@6 437 QString(" colour=\"%1\" plotStyle=\"%2\"")
Chris@6 438 .arg(encodeColour(m_colour)).arg(m_plotStyle));
Chris@0 439 }
Chris@0 440
Chris@0 441
Chris@0 442 #ifdef INCLUDE_MOCFILES
Chris@0 443 #include "TimeValueLayer.moc.cpp"
Chris@0 444 #endif
Chris@0 445