annotate layer/PaintAssistant.cpp @ 1127:9fb8dfd7ce4c spectrogram-minor-refactor

Fix threshold in spectrogram -- it wasn't working in the last release. There is a new protocol for this. Formerly the threshold parameter had a range from -50dB to 0 with the default at -50, and -50 treated internally as "no threshold". However, there was a hardcoded, hidden internal threshold for spectrogram colour mapping at -80dB with anything below this being rounded to zero. Now the threshold parameter has range -81 to -1 with the default at -80, -81 is treated internally as "no threshold", and there is no hidden internal threshold. So the default behaviour is the same as before, an effective -80dB threshold, but it is now possible to change this in both directions. Sessions reloaded from prior versions may look slightly different because, if the session says there should be no threshold, there will now actually be no threshold instead of having the hidden internal one. Still need to do something in the UI to make it apparent that the -81dB setting removes the threshold entirely. This is at least no worse than the previous, also obscured, magic -50dB setting.
author Chris Cannam
date Mon, 01 Aug 2016 16:21:01 +0100
parents ee01a4062747
children 1badacff7ab2
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@195 21
Chris@195 22 #include <QPaintDevice>
Chris@195 23 #include <QPainter>
Chris@195 24
Chris@198 25 #include <iostream>
Chris@230 26 #include <cmath>
Chris@198 27
Chris@195 28 void
Chris@195 29 PaintAssistant::paintVerticalLevelScale(QPainter &paint, QRect rect,
Chris@905 30 double minVal, double maxVal,
Chris@220 31 Scale scale, int &mult,
Chris@220 32 std::vector<int> *vy)
Chris@195 33 {
Chris@905 34 static double meterdbs[] = { -40, -30, -20, -15, -10,
Chris@195 35 -5, -3, -2, -1, -0.5, 0 };
Chris@195 36
Chris@195 37 int h = rect.height(), w = rect.width();
Chris@195 38 int textHeight = paint.fontMetrics().height();
Chris@195 39 int toff = -textHeight/2 + paint.fontMetrics().ascent() + 1;
Chris@195 40
Chris@195 41 int lastLabelledY = -1;
Chris@195 42
Chris@195 43 int n = 10;
Chris@195 44 if (vy) vy->clear();
Chris@195 45
Chris@905 46 double step = 0;
Chris@220 47 mult = 1;
Chris@220 48 if (scale == LinearScale) {
Chris@220 49 step = (maxVal - minVal) / n;
Chris@220 50 int round = 0, limit = 10000000;
Chris@220 51 do {
Chris@220 52 round = int(minVal + step * mult);
Chris@220 53 mult *= 10;
Chris@220 54 } while (!round && mult < limit);
Chris@220 55 if (round) {
Chris@220 56 mult /= 10;
Chris@682 57 // cerr << "\n\nstep goes from " << step;
Chris@905 58 step = double(round) / mult;
Chris@905 59 n = int(lrint((maxVal - minVal) / step));
Chris@220 60 if (mult > 1) {
Chris@220 61 mult /= 10;
Chris@220 62 }
Chris@682 63 // cerr << " to " << step << " (n = " << n << ")" << endl;
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@195 84 text = "-Inf";
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@195 94 text = "-Inf";
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@195 117 int tx = 3;
Chris@220 118 // if (scale != LinearScale) {
Chris@220 119 if (paint.fontMetrics().width(text) < w - 10) {
Chris@195 120 tx = w - 10 - paint.fontMetrics().width(text);
Chris@195 121 }
Chris@195 122
Chris@195 123 int ty = y;
Chris@198 124
Chris@195 125 if (ty < paint.fontMetrics().ascent()) {
Chris@195 126 ty = paint.fontMetrics().ascent();
Chris@198 127 // } else if (ty > rect.y() + h - paint.fontMetrics().descent()) {
Chris@198 128 // ty = rect.y() + h - paint.fontMetrics().descent();
Chris@195 129 } else {
Chris@195 130 ty += toff;
Chris@195 131 }
Chris@198 132
Chris@195 133 paint.drawText(tx, ty, text);
Chris@195 134
Chris@195 135 lastLabelledY = ty - toff;
Chris@195 136 /*
Chris@195 137 if (ny != y) {
Chris@195 138 ty = ny;
Chris@195 139 if (ty < paint.fontMetrics().ascent()) {
Chris@195 140 ty = paint.fontMetrics().ascent();
Chris@195 141 } else if (ty > h - paint.fontMetrics().descent()) {
Chris@195 142 ty = h - paint.fontMetrics().descent();
Chris@195 143 } else {
Chris@195 144 ty += toff;
Chris@195 145 }
Chris@195 146 paint.drawText(tx, ty, text);
Chris@195 147 }
Chris@195 148 */
Chris@195 149 paint.drawLine(w - 7, 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 - 7, ny, w, ny);
Chris@195 154 if (vy) vy->push_back(ny);
Chris@195 155 }
Chris@195 156
Chris@195 157 } else {
Chris@195 158
Chris@195 159 paint.drawLine(w - 4, y, w, y);
Chris@195 160 if (vy) vy->push_back(y);
Chris@195 161
Chris@195 162 if (ny != y) {
Chris@195 163 paint.drawLine(w - 4, ny, w, ny);
Chris@195 164 if (vy) vy->push_back(ny);
Chris@195 165 }
Chris@195 166 }
Chris@195 167 }
Chris@195 168 }
Chris@195 169
Chris@195 170 static int
Chris@905 171 dBscale(double sample, int m, double maxVal, double minVal)
Chris@195 172 {
Chris@195 173 if (sample < 0.0) return dBscale(-sample, m, maxVal, minVal);
Chris@905 174 double dB = AudioLevel::multiplier_to_dB(sample);
Chris@905 175 double mindB = AudioLevel::multiplier_to_dB(minVal);
Chris@905 176 double maxdB = AudioLevel::multiplier_to_dB(maxVal);
Chris@195 177 if (dB < mindB) return 0;
Chris@195 178 if (dB > 0.0) return m;
Chris@195 179 return int(((dB - mindB) * m) / (maxdB - mindB) + 0.1);
Chris@195 180 }
Chris@195 181
Chris@195 182 int
Chris@905 183 PaintAssistant::getYForValue(Scale scale, double value,
Chris@905 184 double minVal, double maxVal,
Chris@195 185 int minY, int height)
Chris@195 186 {
Chris@195 187 int vy = 0;
Chris@195 188
Chris@195 189 // int m = height/2;
Chris@195 190 // int my = minY + m;
Chris@195 191
Chris@195 192 switch (scale) {
Chris@195 193
Chris@195 194 case LinearScale:
Chris@195 195 // vy = my - int(m * value);
Chris@195 196 vy = minY + height - int(((value - minVal) / (maxVal - minVal)) * height);
Chris@195 197 break;
Chris@195 198
Chris@195 199 case MeterScale:
Chris@195 200 // vy = my - AudioLevel::multiplier_to_preview(value, m);
Chris@195 201 vy = minY + height - AudioLevel::multiplier_to_preview
Chris@195 202 ((value - minVal) / (maxVal - minVal), height);
Chris@195 203 break;
Chris@195 204
Chris@195 205 case dBScale:
Chris@195 206 vy = minY + height - dBscale(value, height, maxVal, minVal);
Chris@195 207 break;
Chris@195 208 }
Chris@195 209
Chris@195 210 return vy;
Chris@195 211 }
Chris@1078 212
Chris@1078 213 void
Chris@1078 214 PaintAssistant::drawVisibleText(const LayerGeometryProvider *v,
Chris@1078 215 QPainter &paint, int x, int y,
Chris@1078 216 QString text, TextStyle style)
Chris@1078 217 {
Chris@1078 218 if (style == OutlinedText || style == OutlinedItalicText) {
Chris@1078 219
Chris@1078 220 paint.save();
Chris@1078 221
Chris@1078 222 if (style == OutlinedItalicText) {
Chris@1078 223 QFont f(paint.font());
Chris@1078 224 f.setItalic(true);
Chris@1078 225 paint.setFont(f);
Chris@1078 226 }
Chris@1078 227
Chris@1078 228 QColor penColour, surroundColour, boxColour;
Chris@1078 229
Chris@1078 230 penColour = v->getForeground();
Chris@1078 231 surroundColour = v->getBackground();
Chris@1078 232 boxColour = surroundColour;
Chris@1078 233 boxColour.setAlpha(127);
Chris@1078 234
Chris@1078 235 paint.setPen(Qt::NoPen);
Chris@1078 236 paint.setBrush(boxColour);
Chris@1078 237
Chris@1078 238 QRect r = paint.fontMetrics().boundingRect(text);
Chris@1078 239 r.translate(QPoint(x, y));
Chris@1078 240 // cerr << "drawVisibleText: r = " << r.x() << "," <<r.y() << " " << r.width() << "x" << r.height() << endl;
Chris@1078 241 paint.drawRect(r);
Chris@1078 242 paint.setBrush(Qt::NoBrush);
Chris@1078 243
Chris@1078 244 paint.setPen(surroundColour);
Chris@1078 245
Chris@1078 246 for (int dx = -1; dx <= 1; ++dx) {
Chris@1078 247 for (int dy = -1; dy <= 1; ++dy) {
Chris@1078 248 if (!(dx || dy)) continue;
Chris@1078 249 paint.drawText(x + dx, y + dy, text);
Chris@1078 250 }
Chris@1078 251 }
Chris@1078 252
Chris@1078 253 paint.setPen(penColour);
Chris@1078 254
Chris@1078 255 paint.drawText(x, y, text);
Chris@1078 256
Chris@1078 257 paint.restore();
Chris@1078 258
Chris@1078 259 } else {
Chris@1078 260
Chris@1078 261 std::cerr << "ERROR: PaintAssistant::drawVisibleText: Boxed style not yet implemented!" << std::endl;
Chris@1078 262 }
Chris@1078 263 }