]> git.sesse.net Git - nageru/commitdiff
Make a non-linear fader for dB use. Not perfect yet, but will do.
authorSteinar H. Gunderson <sgunderson@bigfoot.com>
Thu, 18 Aug 2016 21:35:42 +0000 (23:35 +0200)
committerSteinar H. Gunderson <sgunderson@bigfoot.com>
Wed, 19 Oct 2016 22:55:44 +0000 (00:55 +0200)
Makefile
mainwindow.cpp
mainwindow.h
nonlinear_fader.cpp [new file with mode: 0644]
nonlinear_fader.h [new file with mode: 0644]
ui_audio_miniview.ui

index 1aae6cd4f94757215fe249dcb1ce814c39801e97..5cf71d709cda1e7a5a9e84ae4adaa2f18b140446 100644 (file)
--- a/Makefile
+++ b/Makefile
@@ -13,8 +13,8 @@ endif
 LDLIBS=$(shell pkg-config --libs $(PKG_MODULES)) -pthread -lva -lva-drm -lva-x11 -lX11 -lavformat -lavcodec -lavutil -lswscale -lavresample -lzita-resampler -lasound -ldl
 
 # Qt objects
-OBJS=glwidget.o main.o mainwindow.o vumeter.o lrameter.o vu_common.o correlation_meter.o aboutdialog.o input_mapping_dialog.o
-OBJS += glwidget.moc.o mainwindow.moc.o vumeter.moc.o lrameter.moc.o correlation_meter.moc.o aboutdialog.moc.o ellipsis_label.moc.o input_mapping_dialog.moc.o
+OBJS=glwidget.o main.o mainwindow.o vumeter.o lrameter.o vu_common.o correlation_meter.o aboutdialog.o input_mapping_dialog.o nonlinear_fader.o
+OBJS += glwidget.moc.o mainwindow.moc.o vumeter.moc.o lrameter.moc.o correlation_meter.moc.o aboutdialog.moc.o ellipsis_label.moc.o input_mapping_dialog.moc.o nonlinear_fader.moc.o
 
 # Mixer objects
 OBJS += mixer.o audio_mixer.o pbo_frame_allocator.o context.o ref_counted_frame.o theme.o resampling_queue.o httpd.o ebu_r128_proc.o flags.o image_input.o stereocompressor.o filter.o alsa_input.o alsa_output.o correlation_measurer.o disk_space_estimator.o
index c1a3836b2ff7a97b4e23c3b9acfd5c384a38cfcf..3f9873ffa76983866ed9c8c02ce164d6bc075206 100644 (file)
@@ -255,7 +255,7 @@ void MainWindow::setup_audio_miniview()
                // TODO: Set the fader position.
                ui->faders->addWidget(channel);
 
-               connect(ui_audio_miniview->fader, &QAbstractSlider::valueChanged,
+               connect(ui_audio_miniview->fader, &NonLinearFader::dbValueChanged,
                        bind(&MainWindow::mini_fader_changed, this, ui_audio_miniview, bus_index, _1));
        }
 }
@@ -381,13 +381,15 @@ void MainWindow::compressor_threshold_knob_changed(int value)
                QString::fromStdString(format_db(threshold_dbfs, DB_WITH_SIGN)));
 }
 
-void MainWindow::mini_fader_changed(Ui::AudioMiniView *ui, int channel, int value)
+void MainWindow::mini_fader_changed(Ui::AudioMiniView *ui, int channel, double volume_db)
 {
-       float volume_db = value * 0.1f;
-
        char buf[256];
-       snprintf(buf, sizeof(buf), "%+.1f dB", volume_db);
-       ui->fader_label->setText(buf);
+       if (isfinite(volume_db)) {
+               snprintf(buf, sizeof(buf), "%+.1f dB", volume_db);
+               ui->fader_label->setText(buf);
+       } else {
+               ui->fader_label->setText("-∞ dB");
+       }
 
        global_mixer->get_audio_mixer()->set_fader_volume(channel, volume_db);
 }
index b93470a4306fba0573291f2d6a46571a58e0b395..cfde53e1e086cf806dbcff7c36e54a6198c588a9 100644 (file)
@@ -51,7 +51,7 @@ public slots:
        void cutoff_knob_changed(int value);
        void limiter_threshold_knob_changed(int value);
        void compressor_threshold_knob_changed(int value);
-       void mini_fader_changed(Ui::AudioMiniView *ui, int channel, int value);
+       void mini_fader_changed(Ui::AudioMiniView *ui, int channel, double db_volume);
        void reset_meters_button_clicked();
        void relayout();
 
diff --git a/nonlinear_fader.cpp b/nonlinear_fader.cpp
new file mode 100644 (file)
index 0000000..7c26434
--- /dev/null
@@ -0,0 +1,150 @@
+#include <assert.h>
+#include <math.h>
+#include <stdio.h>
+#include <vector>
+#include <utility>
+
+#include <QResizeEvent>
+#include <QPainter>
+#include <QStyle>
+#include <QStyleOptionSlider>
+
+#include "nonlinear_fader.h"
+
+using namespace std;
+
+namespace {
+
+vector<pair<double, double>> fader_control_points = {
+       // The main area is from +6 to -12 dB (18 dB), and we use half the slider range for it.
+       { 6.0, 0.0 },
+       { -12.0, 0.5 },
+
+       // -12 to -21 is half the range (9 dB). Halve.
+       { -21.0, 0.625 },
+
+       // -21 to -30 (9 dB) gets the same range as the previous one.
+       { -30.0, 0.75 },
+
+       // -30 to -48 (18 dB) gets half of half.
+       { -48.0, 0.875 },
+
+       // -48 to -84 (36 dB) gets half of half of half.
+       { -84.0, 1.0 },
+};
+
+double slider_fraction_to_db(double db)
+{
+       if (db <= fader_control_points[0].second) {
+               return fader_control_points[0].first;
+       }
+       if (db >= fader_control_points.back().second) {
+               return fader_control_points.back().first;
+       }
+       for (unsigned i = 1; i < fader_control_points.size(); ++i) {
+               const double x0 = fader_control_points[i - 1].second;
+               const double x1 = fader_control_points[i].second;
+               const double y0 = fader_control_points[i - 1].first;
+               const double y1 = fader_control_points[i].first;
+               if (db >= x0 && db <= x1) {
+                       const double t = (db - x0) / (x1 - x0);
+                       return y0 + t * (y1 - y0);
+               }
+       }
+       assert(false);
+}
+
+double db_to_slider_fraction(double x)
+{
+       if (x >= fader_control_points[0].first) {
+               return fader_control_points[0].second;
+       }
+       if (x <= fader_control_points.back().first) {
+               return fader_control_points.back().second;
+       }
+       for (unsigned i = 1; i < fader_control_points.size(); ++i) {
+               const double x0 = fader_control_points[i].first;
+               const double x1 = fader_control_points[i - 1].first;
+               const double y0 = fader_control_points[i].second;
+               const double y1 = fader_control_points[i - 1].second;
+               if (x >= x0 && x <= x1) {
+                       const double t = (x - x0) / (x1 - x0);
+                       return y0 + t * (y1 - y0);
+               }
+       }
+       assert(false);
+}
+
+}  // namespace
+
+NonLinearFader::NonLinearFader(QWidget *parent)
+       : QSlider(parent)
+{
+       update_slider_position();
+}
+
+void NonLinearFader::setDbValue(double db)
+{
+       db_value = db;
+       update_slider_position();
+       emit dbValueChanged(db);
+}
+
+void NonLinearFader::paintEvent(QPaintEvent *event)
+{
+       QStyleOptionSlider opt;
+       this->initStyleOption(&opt);
+       QRect gr = this->style()->subControlRect(QStyle::CC_Slider, &opt, QStyle::SC_SliderGroove, this);
+       QRect sr = this->style()->subControlRect(QStyle::CC_Slider, &opt, QStyle::SC_SliderHandle, this);
+
+       // FIXME: Where does the slider_length / 2 come from? I can't really find it
+       // in the Qt code, but it seems to match up with reality.
+       int slider_length = sr.height();
+       int slider_min = gr.top() + (slider_length / 2);
+       int slider_max = gr.bottom() + (slider_length / 2) - slider_length + 1;
+
+       QPainter p(this);
+
+       // Draw some ticks every 6 dB.
+       // FIXME: Find a way to make the slider wider, so that we have more space for tickmarks
+       // and some dB numbering.
+       int x_margin = 5;
+       p.setPen(Qt::darkGray);
+       for (int db = -84; db <= 6; db += 6) {
+               int y = slider_min + lrint(db_to_slider_fraction(db) * (slider_max - slider_min));
+               p.drawLine(QPoint(0, y), QPoint(gr.left() - x_margin, y));
+               p.drawLine(QPoint(gr.right() + x_margin, y), QPoint(width() - 1, y));
+       }
+
+       QSlider::paintEvent(event);
+}
+
+void NonLinearFader::resizeEvent(QResizeEvent *event)
+{
+       QSlider::resizeEvent(event);
+       inhibit_updates = true;
+       setRange(0, event->size().height() - 1);
+       inhibit_updates = false;
+       update_slider_position();
+}
+
+void NonLinearFader::sliderChange(SliderChange change)
+{
+       QSlider::sliderChange(change);
+       if (change == QAbstractSlider::SliderValueChange && !inhibit_updates) {
+               if (value() == 0) {
+                       db_value = -HUGE_VAL;
+               } else {
+                       double frac = 1.0 - value() / (height() - 1.0);
+                       db_value = slider_fraction_to_db(frac);
+               }
+               emit dbValueChanged(db_value);
+       }
+}
+
+void NonLinearFader::update_slider_position()
+{
+       inhibit_updates = true;
+       setValue(lrint((1.0 - db_to_slider_fraction(db_value)) * (height() - 1.0)));
+       inhibit_updates = false;
+}
diff --git a/nonlinear_fader.h b/nonlinear_fader.h
new file mode 100644 (file)
index 0000000..4126fbd
--- /dev/null
@@ -0,0 +1,28 @@
+#ifndef _NONLINEAR_FADER_H
+#define _NONLINEAR_FADER_H 1
+
+#include <QSlider>
+
+class NonLinearFader : public QSlider {
+       Q_OBJECT
+
+public:
+       NonLinearFader(QWidget *parent);
+       void setDbValue(double db);
+
+signals:
+       void dbValueChanged(double db);
+
+protected:
+       void paintEvent(QPaintEvent *event) override;
+       void resizeEvent(QResizeEvent *event) override;
+       void sliderChange(SliderChange change) override;
+
+private:
+       void update_slider_position();
+
+       bool inhibit_updates = false;
+       double db_value = 0.0;
+};
+
+#endif  // !defined(_NONLINEAR_FADER_H)
index d7149c912c81a513875d3fc2dbfd85f95bf408a5..a261e889214b2fccbaa6d9f324060833fc06459c 100644 (file)
                  <number>0</number>
                 </property>
                 <item>
-                 <widget class="QSlider" name="fader">
+                 <widget class="NonLinearFader" name="fader">
                   <property name="minimum">
                    <number>-390</number>
                   </property>
                    <enum>Qt::Vertical</enum>
                   </property>
                   <property name="tickPosition">
-                   <enum>QSlider::TicksBothSides</enum>
+                   <enum>QSlider::NoTicks</enum>
                   </property>
                   <property name="tickInterval">
                    <number>30</number>
    <extends>QLabel</extends>
    <header>ellipsis_label.h</header>
   </customwidget>
+  <customwidget>
+   <class>NonLinearFader</class>
+   <extends>QSlider</extends>
+   <header>nonlinear_fader.h</header>
+  </customwidget>
  </customwidgets>
  <resources/>
  <connections/>