Chris@195: /* -*- c-basic-offset: 4 indent-tabs-mode: nil -*- vi:set ts=8 sts=4 sw=4: */ Chris@195: Chris@195: /* Chris@195: Sonic Visualiser Chris@195: An audio file viewer and annotation editor. Chris@195: Centre for Digital Music, Queen Mary, University of London. Chris@195: This file copyright 2006-2007 Chris Cannam and QMUL. Chris@195: Chris@195: This program is free software; you can redistribute it and/or Chris@195: modify it under the terms of the GNU General Public License as Chris@195: published by the Free Software Foundation; either version 2 of the Chris@195: License, or (at your option) any later version. See the file Chris@195: COPYING included with this distribution for more information. Chris@195: */ Chris@195: Chris@195: #include "PaintAssistant.h" Chris@195: Chris@195: #include "base/AudioLevel.h" Chris@195: Chris@195: #include Chris@195: #include Chris@195: Chris@198: #include Chris@230: #include Chris@198: Chris@195: void Chris@195: PaintAssistant::paintVerticalLevelScale(QPainter &paint, QRect rect, Chris@195: float minVal, float maxVal, Chris@220: Scale scale, int &mult, Chris@220: std::vector *vy) Chris@195: { Chris@195: static float meterdbs[] = { -40, -30, -20, -15, -10, Chris@195: -5, -3, -2, -1, -0.5, 0 }; Chris@195: Chris@195: int h = rect.height(), w = rect.width(); Chris@195: int textHeight = paint.fontMetrics().height(); Chris@195: int toff = -textHeight/2 + paint.fontMetrics().ascent() + 1; Chris@195: Chris@195: int lastLabelledY = -1; Chris@195: Chris@195: int n = 10; Chris@195: if (vy) vy->clear(); Chris@195: Chris@220: float step = 0; Chris@220: mult = 1; Chris@220: if (scale == LinearScale) { Chris@220: step = (maxVal - minVal) / n; Chris@220: int round = 0, limit = 10000000; Chris@220: do { Chris@220: round = int(minVal + step * mult); Chris@220: mult *= 10; Chris@220: } while (!round && mult < limit); Chris@220: if (round) { Chris@220: mult /= 10; Chris@220: // std::cerr << "\n\nstep goes from " << step; Chris@220: step = float(round) / mult; Chris@220: n = lrintf((maxVal - minVal) / step); Chris@220: if (mult > 1) { Chris@220: mult /= 10; Chris@220: } Chris@220: // std::cerr << " to " << step << " (n = " << n << ")" << std::endl; Chris@220: } Chris@220: } Chris@220: Chris@195: for (int i = 0; i <= n; ++i) { Chris@195: Chris@195: float val = 0.0, nval = 0.0; Chris@195: QString text = ""; Chris@195: Chris@195: switch (scale) { Chris@195: Chris@195: case LinearScale: Chris@220: val = (minVal + (i * step)); Chris@220: text = QString("%1").arg(mult * val); Chris@195: break; Chris@195: Chris@195: case MeterScale: // ... min, max Chris@195: val = AudioLevel::dB_to_multiplier(meterdbs[i]); Chris@195: text = QString("%1").arg(meterdbs[i]); Chris@195: if (i == n) text = "0dB"; Chris@195: if (i == 0) { Chris@195: text = "-Inf"; Chris@195: val = 0.0; Chris@195: } Chris@195: break; Chris@195: Chris@195: case dBScale: // ... min, max Chris@195: val = AudioLevel::dB_to_multiplier(-(10*n) + i * 10); Chris@195: text = QString("%1").arg(-(10*n) + i * 10); Chris@195: if (i == n) text = "0dB"; Chris@195: if (i == 0) { Chris@195: text = "-Inf"; Chris@195: val = 0.0; Chris@195: } Chris@195: break; Chris@195: } Chris@195: Chris@195: if (val < minVal || val > maxVal) continue; Chris@195: Chris@198: int y = getYForValue(scale, val, minVal, maxVal, rect.y(), h); Chris@195: Chris@195: int ny = y; Chris@195: if (nval != 0.0) { Chris@198: ny = getYForValue(scale, nval, minVal, maxVal, rect.y(), h); Chris@195: } Chris@195: Chris@198: // std::cerr << "PaintAssistant::paintVerticalLevelScale: val = " Chris@198: // << val << ", y = " << y << ", h = " << h << std::endl; Chris@198: Chris@195: bool spaceForLabel = (i == 0 || Chris@195: abs(y - lastLabelledY) >= textHeight - 1); Chris@195: Chris@195: if (spaceForLabel) { Chris@195: Chris@195: int tx = 3; Chris@220: // if (scale != LinearScale) { Chris@220: if (paint.fontMetrics().width(text) < w - 10) { Chris@195: tx = w - 10 - paint.fontMetrics().width(text); Chris@195: } Chris@195: Chris@195: int ty = y; Chris@198: Chris@195: if (ty < paint.fontMetrics().ascent()) { Chris@195: ty = paint.fontMetrics().ascent(); Chris@198: // } else if (ty > rect.y() + h - paint.fontMetrics().descent()) { Chris@198: // ty = rect.y() + h - paint.fontMetrics().descent(); Chris@195: } else { Chris@195: ty += toff; Chris@195: } Chris@198: Chris@195: paint.drawText(tx, ty, text); Chris@195: Chris@195: lastLabelledY = ty - toff; Chris@195: /* Chris@195: if (ny != y) { Chris@195: ty = ny; Chris@195: if (ty < paint.fontMetrics().ascent()) { Chris@195: ty = paint.fontMetrics().ascent(); Chris@195: } else if (ty > h - paint.fontMetrics().descent()) { Chris@195: ty = h - paint.fontMetrics().descent(); Chris@195: } else { Chris@195: ty += toff; Chris@195: } Chris@195: paint.drawText(tx, ty, text); Chris@195: } Chris@195: */ Chris@195: paint.drawLine(w - 7, y, w, y); Chris@195: if (vy) vy->push_back(y); Chris@195: Chris@195: if (ny != y) { Chris@195: paint.drawLine(w - 7, ny, w, ny); Chris@195: if (vy) vy->push_back(ny); Chris@195: } Chris@195: Chris@195: } else { Chris@195: Chris@195: paint.drawLine(w - 4, y, w, y); Chris@195: if (vy) vy->push_back(y); Chris@195: Chris@195: if (ny != y) { Chris@195: paint.drawLine(w - 4, ny, w, ny); Chris@195: if (vy) vy->push_back(ny); Chris@195: } Chris@195: } Chris@195: } Chris@195: } Chris@195: Chris@195: static int Chris@195: dBscale(float sample, int m, float maxVal, float minVal) Chris@195: { Chris@195: if (sample < 0.0) return dBscale(-sample, m, maxVal, minVal); Chris@195: float dB = AudioLevel::multiplier_to_dB(sample); Chris@195: float mindB = AudioLevel::multiplier_to_dB(minVal); Chris@195: float maxdB = AudioLevel::multiplier_to_dB(maxVal); Chris@195: if (dB < mindB) return 0; Chris@195: if (dB > 0.0) return m; Chris@195: return int(((dB - mindB) * m) / (maxdB - mindB) + 0.1); Chris@195: } Chris@195: Chris@195: int Chris@195: PaintAssistant::getYForValue(Scale scale, float value, Chris@195: float minVal, float maxVal, Chris@195: int minY, int height) Chris@195: { Chris@195: int vy = 0; Chris@195: Chris@195: // int m = height/2; Chris@195: // int my = minY + m; Chris@195: Chris@195: switch (scale) { Chris@195: Chris@195: case LinearScale: Chris@195: // vy = my - int(m * value); Chris@195: vy = minY + height - int(((value - minVal) / (maxVal - minVal)) * height); Chris@195: break; Chris@195: Chris@195: case MeterScale: Chris@195: // vy = my - AudioLevel::multiplier_to_preview(value, m); Chris@195: vy = minY + height - AudioLevel::multiplier_to_preview Chris@195: ((value - minVal) / (maxVal - minVal), height); Chris@195: break; Chris@195: Chris@195: case dBScale: Chris@195: vy = minY + height - dBscale(value, height, maxVal, minVal); Chris@195: break; Chris@195: } Chris@195: Chris@195: return vy; Chris@195: }