]> git.sesse.net Git - nageru/blob - nageru/nonlinear_fader.cpp
Fix a dangling reference (found by GCC 14).
[nageru] / nageru / nonlinear_fader.cpp
1 #include "nonlinear_fader.h"
2
3 #include <assert.h>
4 #include <math.h>
5 #include <QAbstractSlider>
6 #include <QPaintDevice>
7 #include <QPainter>
8 #include <QPoint>
9 #include <QRect>
10 #include <QSlider>
11 #include <QStyle>
12 #include <QStyleOption>
13 #include <vector>
14
15 #include "piecewise_interpolator.h"
16
17 class QPaintEvent;
18 class QWidget;
19
20 using namespace std;
21
22 namespace {
23
24 PiecewiseInterpolator interpolator({
25         // The main area is from +6 to -12 dB (18 dB), and we use half the slider range for it.
26         // Adjust slightly so that the MIDI controller value of 106 becomes exactly 0.0 dB
27         // (cf. map_controller_to_float()); otherwise, we'd miss ever so slightly, which is
28         // really frustrating.
29         { 6.0, 1.0 },
30         { -12.0, 1.0 - (1.0 - 106.5/127.0) * 3.0 },  // About 0.492.
31
32         // -12 to -21 is half the range (9 dB). Halve.
33         { -21.0, 0.325 },
34
35         // -21 to -30 (9 dB) gets the same range as the previous one.
36         { -30.0, 0.25 },
37
38         // -30 to -48 (18 dB) gets half of half.
39         { -48.0, 0.125 },
40
41         // -48 to -84 (36 dB) gets half of half of half.
42         { -84.0, 0.0 },
43 });
44
45 }  // namespace
46
47 NonLinearFader::NonLinearFader(QWidget *parent)
48         : QSlider(parent)
49 {
50         update_slider_position();
51 }
52
53 void NonLinearFader::setDbValue(double db)
54 {
55         db_value = db;
56         update_slider_position();
57         emit dbValueChanged(db);
58 }
59
60 void NonLinearFader::paintEvent(QPaintEvent *event)
61 {
62         QStyleOptionSlider opt;
63         this->initStyleOption(&opt);
64         QRect gr = this->style()->subControlRect(QStyle::CC_Slider, &opt, QStyle::SC_SliderGroove, this);
65         QRect sr = this->style()->subControlRect(QStyle::CC_Slider, &opt, QStyle::SC_SliderHandle, this);
66
67         // FIXME: Where does the slider_length / 2 come from? I can't really find it
68         // in the Qt code, but it seems to match up with reality.
69         int slider_length = sr.height();
70         int slider_max = gr.top() + (slider_length / 2);
71         int slider_min = gr.bottom() + (slider_length / 2) - slider_length + 1;
72
73         QPainter p(this);
74
75         // Draw some ticks every 6 dB.
76         // FIXME: Find a way to make the slider wider, so that we have more space for tickmarks
77         // and some dB numbering.
78         int x_margin = 5;
79         p.setPen(Qt::darkGray);
80         for (int db = -84; db <= 6; db += 6) {
81                 int y = slider_min + lrint(interpolator.db_to_fraction(db) * (slider_max - slider_min));
82                 p.drawLine(QPoint(0, y), QPoint(gr.left() - x_margin, y));
83                 p.drawLine(QPoint(gr.right() + x_margin, y), QPoint(width() - 1, y));
84         }
85
86         QSlider::paintEvent(event);
87 }
88
89 void NonLinearFader::sliderChange(SliderChange change)
90 {
91         QSlider::sliderChange(change);
92         if (change == QAbstractSlider::SliderValueChange && !inhibit_updates) {
93                 if (value() == 0) {
94                         db_value = -HUGE_VAL;
95                 } else {
96                         double frac = double(value() - minimum()) / (maximum() - minimum());
97                         db_value = interpolator.fraction_to_db(frac);
98                 }
99                 emit dbValueChanged(db_value);
100         }
101 }
102
103 void NonLinearFader::update_slider_position()
104 {
105         inhibit_updates = true;
106         double val = interpolator.db_to_fraction(db_value) * (maximum() - minimum()) + minimum();
107         setValue(lrint(val));
108         inhibit_updates = false;
109 }