annotate layer/PaintAssistant.cpp @ 1551:e79731086b0f

Fixes to NoteLayer, particularly to calculation of vertical scale when model unit is not Hz. To avoid inconsistency we now behave as if the unit is always Hz from the point of view of the external API and display, converting at the point where we obtain values from the events themselves. Also various fixes to editing.
author Chris Cannam
date Thu, 21 Nov 2019 14:02:57 +0000
parents f2525e6cbdf1
children
rev   line source
Chris@195 1 /* -*- c-basic-offset: 4 indent-tabs-mode: nil -*- vi:set ts=8 sts=4 sw=4: */
Chris@195 2
Chris@195 3 /*
Chris@195 4 Sonic Visualiser
Chris@195 5 An audio file viewer and annotation editor.
Chris@195 6 Centre for Digital Music, Queen Mary, University of London.
Chris@195 7 This file copyright 2006-2007 Chris Cannam and QMUL.
Chris@195 8
Chris@195 9 This program is free software; you can redistribute it and/or
Chris@195 10 modify it under the terms of the GNU General Public License as
Chris@195 11 published by the Free Software Foundation; either version 2 of the
Chris@195 12 License, or (at your option) any later version. See the file
Chris@195 13 COPYING included with this distribution for more information.
Chris@195 14 */
Chris@195 15
Chris@195 16 #include "PaintAssistant.h"
Chris@195 17
Chris@1078 18 #include "LayerGeometryProvider.h"
Chris@1078 19
Chris@195 20 #include "base/AudioLevel.h"
Chris@1147 21 #include "base/Strings.h"
Chris@1228 22 #include "base/Debug.h"
Chris@195 23
Chris@195 24 #include <QPaintDevice>
Chris@195 25 #include <QPainter>
Chris@195 26
Chris@198 27 #include <iostream>
Chris@230 28 #include <cmath>
Chris@198 29
Chris@195 30 void
Chris@195 31 PaintAssistant::paintVerticalLevelScale(QPainter &paint, QRect rect,
Chris@1266 32 double minVal, double maxVal,
Chris@220 33 Scale scale, int &mult,
Chris@220 34 std::vector<int> *vy)
Chris@195 35 {
Chris@905 36 static double meterdbs[] = { -40, -30, -20, -15, -10,
Chris@195 37 -5, -3, -2, -1, -0.5, 0 };
Chris@195 38
Chris@195 39 int h = rect.height(), w = rect.width();
Chris@195 40 int textHeight = paint.fontMetrics().height();
Chris@195 41 int toff = -textHeight/2 + paint.fontMetrics().ascent() + 1;
Chris@195 42
Chris@195 43 int lastLabelledY = -1;
Chris@195 44
Chris@195 45 int n = 10;
Chris@195 46 if (vy) vy->clear();
Chris@195 47
Chris@905 48 double step = 0;
Chris@220 49 mult = 1;
Chris@220 50 if (scale == LinearScale) {
Chris@220 51 step = (maxVal - minVal) / n;
Chris@220 52 int round = 0, limit = 10000000;
Chris@220 53 do {
Chris@220 54 round = int(minVal + step * mult);
Chris@220 55 mult *= 10;
Chris@220 56 } while (!round && mult < limit);
Chris@220 57 if (round) {
Chris@220 58 mult /= 10;
Chris@905 59 step = double(round) / mult;
Chris@905 60 n = int(lrint((maxVal - minVal) / step));
Chris@220 61 if (mult > 1) {
Chris@220 62 mult /= 10;
Chris@220 63 }
Chris@220 64 }
Chris@220 65 }
Chris@220 66
Chris@195 67 for (int i = 0; i <= n; ++i) {
Chris@195 68
Chris@905 69 double val = 0.0, nval = 0.0;
Chris@195 70 QString text = "";
Chris@195 71
Chris@195 72 switch (scale) {
Chris@195 73
Chris@195 74 case LinearScale:
Chris@220 75 val = (minVal + (i * step));
Chris@220 76 text = QString("%1").arg(mult * val);
Chris@195 77 break;
Chris@195 78
Chris@195 79 case MeterScale: // ... min, max
Chris@195 80 val = AudioLevel::dB_to_multiplier(meterdbs[i]);
Chris@195 81 text = QString("%1").arg(meterdbs[i]);
Chris@195 82 if (i == n) text = "0dB";
Chris@195 83 if (i == 0) {
Chris@1147 84 text = Strings::minus_infinity;
Chris@195 85 val = 0.0;
Chris@195 86 }
Chris@195 87 break;
Chris@195 88
Chris@195 89 case dBScale: // ... min, max
Chris@195 90 val = AudioLevel::dB_to_multiplier(-(10*n) + i * 10);
Chris@195 91 text = QString("%1").arg(-(10*n) + i * 10);
Chris@195 92 if (i == n) text = "0dB";
Chris@195 93 if (i == 0) {
Chris@1147 94 text = Strings::minus_infinity;
Chris@195 95 val = 0.0;
Chris@195 96 }
Chris@195 97 break;
Chris@195 98 }
Chris@195 99
Chris@195 100 if (val < minVal || val > maxVal) continue;
Chris@195 101
Chris@198 102 int y = getYForValue(scale, val, minVal, maxVal, rect.y(), h);
Chris@195 103
Chris@195 104 int ny = y;
Chris@195 105 if (nval != 0.0) {
Chris@198 106 ny = getYForValue(scale, nval, minVal, maxVal, rect.y(), h);
Chris@195 107 }
Chris@195 108
Chris@587 109 // SVDEBUG << "PaintAssistant::paintVerticalLevelScale: val = "
Chris@585 110 // << val << ", y = " << y << ", h = " << h << endl;
Chris@198 111
Chris@195 112 bool spaceForLabel = (i == 0 ||
Chris@195 113 abs(y - lastLabelledY) >= textHeight - 1);
Chris@195 114
Chris@195 115 if (spaceForLabel) {
Chris@195 116
Chris@1471 117 // Qt 5.13 deprecates QFontMetrics::width(), but its suggested
Chris@1471 118 // replacement (horizontalAdvance) was only added in Qt 5.11
Chris@1471 119 // which is too new for us
Chris@1471 120 #pragma GCC diagnostic ignored "-Wdeprecated-declarations"
Chris@1471 121
Chris@195 122 int tx = 3;
Chris@220 123 if (paint.fontMetrics().width(text) < w - 10) {
Chris@195 124 tx = w - 10 - paint.fontMetrics().width(text);
Chris@195 125 }
Chris@195 126
Chris@195 127 int ty = y;
Chris@198 128
Chris@195 129 if (ty < paint.fontMetrics().ascent()) {
Chris@195 130 ty = paint.fontMetrics().ascent();
Chris@195 131 } else {
Chris@195 132 ty += toff;
Chris@195 133 }
Chris@198 134
Chris@195 135 paint.drawText(tx, ty, text);
Chris@195 136
Chris@195 137 lastLabelledY = ty - toff;
Chris@1283 138
Chris@195 139 paint.drawLine(w - 7, y, w, y);
Chris@195 140 if (vy) vy->push_back(y);
Chris@195 141
Chris@195 142 if (ny != y) {
Chris@195 143 paint.drawLine(w - 7, ny, w, ny);
Chris@195 144 if (vy) vy->push_back(ny);
Chris@195 145 }
Chris@195 146
Chris@195 147 } else {
Chris@195 148
Chris@195 149 paint.drawLine(w - 4, y, w, y);
Chris@195 150 if (vy) vy->push_back(y);
Chris@195 151
Chris@195 152 if (ny != y) {
Chris@195 153 paint.drawLine(w - 4, ny, w, ny);
Chris@195 154 if (vy) vy->push_back(ny);
Chris@195 155 }
Chris@195 156 }
Chris@195 157 }
Chris@195 158 }
Chris@195 159
Chris@195 160 static int
Chris@905 161 dBscale(double sample, int m, double maxVal, double minVal)
Chris@195 162 {
Chris@195 163 if (sample < 0.0) return dBscale(-sample, m, maxVal, minVal);
Chris@905 164 double dB = AudioLevel::multiplier_to_dB(sample);
Chris@905 165 double mindB = AudioLevel::multiplier_to_dB(minVal);
Chris@905 166 double maxdB = AudioLevel::multiplier_to_dB(maxVal);
Chris@195 167 if (dB < mindB) return 0;
Chris@195 168 if (dB > 0.0) return m;
Chris@195 169 return int(((dB - mindB) * m) / (maxdB - mindB) + 0.1);
Chris@195 170 }
Chris@195 171
Chris@195 172 int
Chris@905 173 PaintAssistant::getYForValue(Scale scale, double value,
Chris@905 174 double minVal, double maxVal,
Chris@195 175 int minY, int height)
Chris@195 176 {
Chris@195 177 int vy = 0;
Chris@195 178
Chris@195 179 switch (scale) {
Chris@195 180
Chris@195 181 case LinearScale:
Chris@195 182 vy = minY + height - int(((value - minVal) / (maxVal - minVal)) * height);
Chris@195 183 break;
Chris@195 184
Chris@195 185 case MeterScale:
Chris@195 186 vy = minY + height - AudioLevel::multiplier_to_preview
Chris@195 187 ((value - minVal) / (maxVal - minVal), height);
Chris@195 188 break;
Chris@195 189
Chris@195 190 case dBScale:
Chris@195 191 vy = minY + height - dBscale(value, height, maxVal, minVal);
Chris@195 192 break;
Chris@195 193 }
Chris@195 194
Chris@195 195 return vy;
Chris@195 196 }
Chris@1078 197
Chris@1078 198 void
Chris@1078 199 PaintAssistant::drawVisibleText(const LayerGeometryProvider *v,
Chris@1078 200 QPainter &paint, int x, int y,
Chris@1078 201 QString text, TextStyle style)
Chris@1078 202 {
Chris@1078 203 if (style == OutlinedText || style == OutlinedItalicText) {
Chris@1078 204
Chris@1078 205 paint.save();
Chris@1078 206
Chris@1078 207 if (style == OutlinedItalicText) {
Chris@1078 208 QFont f(paint.font());
Chris@1078 209 f.setItalic(true);
Chris@1078 210 paint.setFont(f);
Chris@1078 211 }
Chris@1078 212
Chris@1078 213 QColor penColour, surroundColour, boxColour;
Chris@1078 214
Chris@1078 215 penColour = v->getForeground();
Chris@1078 216 surroundColour = v->getBackground();
Chris@1078 217 boxColour = surroundColour;
Chris@1078 218 boxColour.setAlpha(127);
Chris@1078 219
Chris@1078 220 paint.setPen(Qt::NoPen);
Chris@1078 221 paint.setBrush(boxColour);
Chris@1078 222
Chris@1078 223 QRect r = paint.fontMetrics().boundingRect(text);
Chris@1078 224 r.translate(QPoint(x, y));
Chris@1078 225 paint.drawRect(r);
Chris@1078 226 paint.setBrush(Qt::NoBrush);
Chris@1078 227
Chris@1266 228 paint.setPen(surroundColour);
Chris@1078 229
Chris@1266 230 for (int dx = -1; dx <= 1; ++dx) {
Chris@1266 231 for (int dy = -1; dy <= 1; ++dy) {
Chris@1266 232 if (!(dx || dy)) continue;
Chris@1266 233 paint.drawText(x + dx, y + dy, text);
Chris@1266 234 }
Chris@1266 235 }
Chris@1078 236
Chris@1266 237 paint.setPen(penColour);
Chris@1078 238
Chris@1266 239 paint.drawText(x, y, text);
Chris@1078 240
Chris@1078 241 paint.restore();
Chris@1078 242
Chris@1078 243 } else {
Chris@1078 244
Chris@1078 245 std::cerr << "ERROR: PaintAssistant::drawVisibleText: Boxed style not yet implemented!" << std::endl;
Chris@1078 246 }
Chris@1078 247 }
Chris@1228 248
Chris@1228 249