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 midi_mapping_dialog.o nonlinear_fader.o piecewise_interpolator.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 midi_mapping_dialog.moc.o nonlinear_fader.moc.o clickable_label.moc.o
+OBJS=glwidget.o main.o mainwindow.o vumeter.o lrameter.o compression_reduction_meter.o vu_common.o correlation_meter.o aboutdialog.o input_mapping_dialog.o midi_mapping_dialog.o nonlinear_fader.o piecewise_interpolator.o
+OBJS += glwidget.moc.o mainwindow.moc.o vumeter.moc.o lrameter.moc.o compression_reduction_meter.moc.o correlation_meter.moc.o aboutdialog.moc.o ellipsis_label.moc.o input_mapping_dialog.moc.o midi_mapping_dialog.moc.o nonlinear_fader.moc.o clickable_label.moc.o
OBJS += midi_mapper.o midi_mapping.pb.o
# Mixer objects
--- /dev/null
+#include "compression_reduction_meter.h"
+
+#include <QPainter>
+#include <QRect>
+#include "piecewise_interpolator.h"
+#include "vu_common.h"
+
+class QPaintEvent;
+class QResizeEvent;
+
+using namespace std;
+
+namespace {
+
+vector<PiecewiseInterpolator::ControlPoint> control_points = {
+ { 60.0f, 6.0f },
+ { 30.0f, 5.0f },
+ { 18.0f, 4.0f },
+ { 12.0f, 3.0f },
+ { 6.0f, 2.0f },
+ { 3.0f, 1.0f },
+ { 0.0f, 0.0f }
+};
+PiecewiseInterpolator interpolator(control_points);
+
+} // namespace
+
+CompressionReductionMeter::CompressionReductionMeter(QWidget *parent)
+ : QWidget(parent)
+{
+}
+
+void CompressionReductionMeter::resizeEvent(QResizeEvent *event)
+{
+ recalculate_pixmaps();
+}
+
+void CompressionReductionMeter::paintEvent(QPaintEvent *event)
+{
+ QPainter painter(this);
+
+ float level_db;
+ {
+ unique_lock<mutex> lock(level_mutex);
+ level_db = this->level_db;
+ }
+
+ int on_pos = lrint(db_to_pos(level_db));
+
+ QRect on_rect(0, 0, width(), on_pos);
+ QRect off_rect(0, on_pos, width(), height());
+
+ painter.drawPixmap(on_rect, on_pixmap, on_rect);
+ painter.drawPixmap(off_rect, off_pixmap, off_rect);
+}
+
+void CompressionReductionMeter::recalculate_pixmaps()
+{
+ constexpr int y_offset = text_box_height / 2;
+ constexpr int text_margin = 5;
+ float margin = 0.5 * (width() - meter_width);
+
+ on_pixmap = QPixmap(width(), height());
+ QPainter on_painter(&on_pixmap);
+ on_painter.fillRect(0, 0, width(), height(), parentWidget()->palette().window());
+ draw_vu_meter(on_painter, width(), meter_height(), margin, 2.0, true, min_level, max_level, /*flip=*/true, y_offset);
+ draw_scale(&on_painter, 0.5 * width() + 0.5 * meter_width + text_margin);
+
+ off_pixmap = QPixmap(width(), height());
+ QPainter off_painter(&off_pixmap);
+ off_painter.fillRect(0, 0, width(), height(), parentWidget()->palette().window());
+ draw_vu_meter(off_painter, width(), meter_height(), margin, 2.0, false, min_level, max_level, /*flip=*/true, y_offset);
+ draw_scale(&off_painter, 0.5 * width() + 0.5 * meter_width + text_margin);
+}
+
+void CompressionReductionMeter::draw_scale(QPainter *painter, int x_pos)
+{
+ QFont font;
+ font.setPointSize(8);
+ painter->setPen(Qt::black);
+ painter->setFont(font);
+ for (size_t i = 0; i < control_points.size(); ++i) {
+ char buf[256];
+ snprintf(buf, 256, "%.0f", control_points[i].db_value);
+ double y = db_to_pos(control_points[i].db_value);
+ painter->drawText(QRect(x_pos, y - text_box_height / 2, text_box_width, text_box_height),
+ Qt::AlignCenter | Qt::AlignVCenter, buf);
+ }
+}
+
+double CompressionReductionMeter::db_to_pos(double level_db) const
+{
+ float value = interpolator.db_to_fraction(level_db);
+ return height() - lufs_to_pos(value, meter_height(), min_level, max_level) - text_box_height / 2;
+}
+
+int CompressionReductionMeter::meter_height() const
+{
+ return height() - text_box_height;
+}
--- /dev/null
+#ifndef COMPRESSION_REDUCTION_METER_H
+#define COMPRESSION_REDUCTION_METER_H
+
+// A meter that goes downwards instead of upwards, and has a non-linear scale.
+
+#include <math.h>
+#include <QPixmap>
+#include <QString>
+#include <QWidget>
+#include <mutex>
+
+#include "piecewise_interpolator.h"
+
+class QObject;
+class QPaintEvent;
+class QResizeEvent;
+
+class CompressionReductionMeter : public QWidget
+{
+ Q_OBJECT
+
+public:
+ CompressionReductionMeter(QWidget *parent);
+
+ void set_reduction_db(float level_db) {
+ std::unique_lock<std::mutex> lock(level_mutex);
+ this->level_db = level_db;
+ QMetaObject::invokeMethod(this, "update", Qt::AutoConnection);
+ }
+
+private:
+ void resizeEvent(QResizeEvent *event) override;
+ void paintEvent(QPaintEvent *event) override;
+ void recalculate_pixmaps();
+ void draw_scale(QPainter *painter, int x_pos);
+ double db_to_pos(double db) const;
+ int meter_height() const;
+
+ std::mutex level_mutex;
+ float level_db = 0.0f;
+
+ static constexpr float min_level = 0.0f; // Must match control_points (in the .cpp file).
+ static constexpr float max_level = 6.0f; // Same.
+ static constexpr int meter_width = 20;
+
+ // Size of the text box. The meter will be shrunk to make room for the text box
+ // (half the height) on both sides.
+ static constexpr int text_box_width = 15;
+ static constexpr int text_box_height = 10;
+
+ QPixmap on_pixmap, off_pixmap;
+};
+
+#endif
global_audio_mixer->reset_peak(bus_index);
midi_mapper.refresh_lights();
});
-
- // Set up the compression attenuation meter.
- VUMeter *reduction_meter = ui_audio_expanded_view->reduction_meter;
- reduction_meter->set_min_level(0.0f);
- reduction_meter->set_max_level(10.0f);
- reduction_meter->set_ref_level(0.0f);
- reduction_meter->set_flip(true);
}
update_cutoff_labels(global_audio_mixer->get_locut_cutoff());
level.current_level_dbfs[0], level.current_level_dbfs[1]);
view->peak_meter->set_peak(
level.peak_level_dbfs[0], level.peak_level_dbfs[1]);
- view->reduction_meter->set_level(level.compressor_attenuation_db);
+ view->reduction_meter->set_reduction_db(level.compressor_attenuation_db);
view->gainstaging_knob->blockSignals(true);
view->gainstaging_knob->setValue(lrintf(level.gain_staging_db * 10.0f));
view->gainstaging_knob->blockSignals(false);
</item>
<item>
<layout class="QVBoxLayout" name="reduction_layout" stretch="0,1">
+ <property name="spacing">
+ <number>0</number>
+ </property>
<item>
<widget class="QLabel" name="reduction_header">
<property name="text">
<item>
<layout class="QHBoxLayout" name="reduction_meter_centerer">
<item>
- <widget class="VUMeter" name="reduction_meter" native="true">
+ <widget class="CompressionReductionMeter" name="reduction_meter" native="true">
<property name="maximumSize">
<size>
- <width>20</width>
+ <width>16777215</width>
<height>16777215</height>
</size>
</property>
<extends>QLabel</extends>
<header>ellipsis_label.h</header>
</customwidget>
+ <customwidget>
+ <class>CompressionReductionMeter</class>
+ <extends>QWidget</extends>
+ <header>compression_reduction_meter.h</header>
+ <container>1</container>
+ </customwidget>
</customwidgets>
<resources/>
<connections/>
return y;
}
-void draw_vu_meter(QPainter &painter, int width, int height, int horizontal_margin, double segment_margin, bool is_on, float min_level, float max_level, bool flip)
+void draw_vu_meter(QPainter &painter, int width, int height, int horizontal_margin, double segment_margin, bool is_on, float min_level, float max_level, bool flip, int y_offset)
{
- painter.fillRect(horizontal_margin, 0, width - 2 * horizontal_margin, height, Qt::black);
+ painter.fillRect(horizontal_margin, y_offset, width - 2 * horizontal_margin, height, Qt::black);
for (int y = 0; y < height; ++y) {
// Find coverage of “on” rectangles in this pixel row.
int g = lrintf(255 * pow(on_g * coverage, 1.0 / 2.2));
int b = lrintf(255 * pow(on_b * coverage, 1.0 / 2.2));
int draw_y = flip ? (height - y - 1) : y;
- painter.fillRect(horizontal_margin, draw_y, width - 2 * horizontal_margin, 1, QColor(r, g, b));
+ painter.fillRect(horizontal_margin, draw_y + y_offset, width - 2 * horizontal_margin, 1, QColor(r, g, b));
}
}
double lufs_to_pos(float level_lu, int height, float min_level, float max_level);
-void draw_vu_meter(QPainter &painter, int width, int height, int horizontal_margin, double segment_margin, bool is_on, float min_level, float max_level, bool flip);
+void draw_vu_meter(QPainter &painter, int width, int height, int horizontal_margin, double segment_margin, bool is_on, float min_level, float max_level, bool flip, int y_offset = 0);
#endif // !defined(_VU_COMMON_H)
float level_lu = level_lufs[channel] - ref_level_lufs;
int on_pos = lrint(lufs_to_pos(level_lu, height()));
- if (flip) {
- QRect on_rect(left, 0, right - left, height() - on_pos);
- QRect off_rect(left, height() - on_pos, right - left, height());
-
- painter.drawPixmap(on_rect, on_pixmap, on_rect);
- painter.drawPixmap(off_rect, off_pixmap, off_rect);
- } else {
- QRect off_rect(left, 0, right - left, on_pos);
- QRect on_rect(left, on_pos, right - left, height() - on_pos);
-
- painter.drawPixmap(off_rect, off_pixmap, off_rect);
- painter.drawPixmap(on_rect, on_pixmap, on_rect);
- }
+ QRect off_rect(left, 0, right - left, on_pos);
+ QRect on_rect(left, on_pos, right - left, height() - on_pos);
+
+ painter.drawPixmap(off_rect, off_pixmap, off_rect);
+ painter.drawPixmap(on_rect, on_pixmap, on_rect);
float peak_lu = peak_lufs[channel] - ref_level_lufs;
if (peak_lu >= min_level && peak_lu <= max_level) {
{
full_on_pixmap = QPixmap(width(), height());
QPainter full_on_painter(&full_on_pixmap);
- draw_vu_meter(full_on_painter, width(), height(), 0, 0.0, true, min_level, max_level, flip);
+ full_on_painter.fillRect(0, 0, width(), height(), parentWidget()->palette().window());
+ draw_vu_meter(full_on_painter, width(), height(), 0, 0.0, true, min_level, max_level, /*flip=*/false);
+
+ float margin = 0.5 * (width() - 20);
on_pixmap = QPixmap(width(), height());
QPainter on_painter(&on_pixmap);
- draw_vu_meter(on_painter, width(), height(), 0, 2.0, true, min_level, max_level, flip);
+ on_painter.fillRect(0, 0, width(), height(), parentWidget()->palette().window());
+ draw_vu_meter(on_painter, width(), height(), margin, 2.0, true, min_level, max_level, /*flip=*/false);
off_pixmap = QPixmap(width(), height());
QPainter off_painter(&off_pixmap);
- draw_vu_meter(off_painter, width(), height(), 0, 2.0, false, min_level, max_level, flip);
+ off_painter.fillRect(0, 0, width(), height(), parentWidget()->palette().window());
+ draw_vu_meter(off_painter, width(), height(), margin, 2.0, false, min_level, max_level, /*flip=*/false);
}
this->ref_level_lufs = ref_level_lufs;
}
- void set_flip(bool flip)
- {
- this->flip = flip;
- recalculate_pixmaps();
- }
-
private:
void resizeEvent(QResizeEvent *event) override;
void paintEvent(QPaintEvent *event) override;
float level_lufs[2] { -HUGE_VALF, -HUGE_VALF };
float peak_lufs[2] { -HUGE_VALF, -HUGE_VALF };
float min_level = -18.0f, max_level = 9.0f, ref_level_lufs = -23.0f;
- bool flip = false;
QPixmap full_on_pixmap, on_pixmap, off_pixmap;
};