mirror of
https://github.com/obsproject/obs-studio.git
synced 2026-02-01 10:02:11 -05:00
This replaces the code for volume control with a newer version that uses the new fader implementation in order to control the fader/slider position and source volume. The volume label will now indicate the gain in dB instead of percent, mainly because it looks cool and is easy to do. Due to libobs saving the multiplier option for the source instead of the slider/fader positon, existing volume levels will (mostly) stay the same with only the slider beeing at a different position. This is of course within the resolution of the slider (100 steps).
245 lines
5.7 KiB
C++
245 lines
5.7 KiB
C++
#include "volume-control.hpp"
|
|
#include "qt-wrappers.hpp"
|
|
#include <util/platform.h>
|
|
#include <QHBoxLayout>
|
|
#include <QVBoxLayout>
|
|
#include <QSlider>
|
|
#include <QLabel>
|
|
#include <QPainter>
|
|
#include <QTimer>
|
|
#include <string>
|
|
#include <math.h>
|
|
|
|
using namespace std;
|
|
|
|
#define VOL_MIN -96.0f
|
|
#define VOL_MAX 0.0f
|
|
|
|
/*
|
|
VOL_MIN_LOG = DBToLog(VOL_MIN)
|
|
VOL_MAX_LOG = DBToLog(VOL_MAX)
|
|
... just in case someone wants to use a smaller scale
|
|
*/
|
|
|
|
#define VOL_MIN_LOG -2.0086001717619175
|
|
#define VOL_MAX_LOG -0.77815125038364363
|
|
|
|
#define UPDATE_INTERVAL_MS 50
|
|
|
|
static inline float DBToLog(float db)
|
|
{
|
|
return -log10f(0.0f - (db - 6.0f));
|
|
}
|
|
|
|
static inline float DBToLinear(float db_full)
|
|
{
|
|
float db = fmaxf(fminf(db_full, VOL_MAX), VOL_MIN);
|
|
return (DBToLog(db) - VOL_MIN_LOG) / (VOL_MAX_LOG - VOL_MIN_LOG);
|
|
}
|
|
|
|
void VolControl::OBSVolumeChanged(void *data, calldata_t *calldata)
|
|
{
|
|
Q_UNUSED(calldata);
|
|
VolControl *volControl = static_cast<VolControl*>(data);
|
|
|
|
QMetaObject::invokeMethod(volControl, "VolumeChanged");
|
|
}
|
|
|
|
void VolControl::OBSVolumeLevel(void *data, calldata_t *calldata)
|
|
{
|
|
VolControl *volControl = static_cast<VolControl*>(data);
|
|
float peak = calldata_float(calldata, "level");
|
|
float mag = calldata_float(calldata, "magnitude");
|
|
float peakHold = calldata_float(calldata, "peak");
|
|
|
|
QMetaObject::invokeMethod(volControl, "VolumeLevel",
|
|
Q_ARG(float, mag),
|
|
Q_ARG(float, peak),
|
|
Q_ARG(float, peakHold));
|
|
}
|
|
|
|
void VolControl::VolumeChanged()
|
|
{
|
|
slider->setValue((int) (obs_fader_get_deflection(obs_fader) * 100.0f));
|
|
}
|
|
|
|
void VolControl::VolumeLevel(float mag, float peak, float peakHold)
|
|
{
|
|
uint64_t curMeterTime = os_gettime_ns() / 1000000;
|
|
|
|
/*
|
|
Add again peak averaging?
|
|
*/
|
|
|
|
/* only update after a certain amount of time */
|
|
if ((curMeterTime - lastMeterTime) > UPDATE_INTERVAL_MS) {
|
|
float vol = (float)slider->value() * 0.01f;
|
|
lastMeterTime = curMeterTime;
|
|
volMeter->setLevels(DBToLinear(mag) * vol,
|
|
DBToLinear(peak) * vol,
|
|
DBToLinear(peakHold) * vol);
|
|
}
|
|
}
|
|
|
|
void VolControl::SliderChanged(int vol)
|
|
{
|
|
obs_fader_set_deflection(obs_fader, float(vol) * 0.01f);
|
|
volLabel->setText(QString::number(obs_fader_get_db(obs_fader), 'f', 1)
|
|
.append(" dB"));
|
|
}
|
|
|
|
QString VolControl::GetName() const
|
|
{
|
|
return nameLabel->text();
|
|
}
|
|
|
|
void VolControl::SetName(const QString &newName)
|
|
{
|
|
nameLabel->setText(newName);
|
|
}
|
|
|
|
VolControl::VolControl(OBSSource source_)
|
|
: source (source_),
|
|
lastMeterTime (0),
|
|
levelTotal (0.0f),
|
|
levelCount (0.0f),
|
|
obs_fader (obs_fader_create(OBS_FADER_CUBIC))
|
|
{
|
|
QVBoxLayout *mainLayout = new QVBoxLayout();
|
|
QHBoxLayout *textLayout = new QHBoxLayout();
|
|
|
|
nameLabel = new QLabel();
|
|
volLabel = new QLabel();
|
|
volMeter = new VolumeMeter();
|
|
slider = new QSlider(Qt::Horizontal);
|
|
|
|
QFont font = nameLabel->font();
|
|
font.setPointSize(font.pointSize()-1);
|
|
|
|
nameLabel->setText(obs_source_get_name(source));
|
|
nameLabel->setFont(font);
|
|
volLabel->setFont(font);
|
|
slider->setMinimum(0);
|
|
slider->setMaximum(100);
|
|
|
|
// slider->setMaximumHeight(13);
|
|
|
|
textLayout->setContentsMargins(0, 0, 0, 0);
|
|
textLayout->addWidget(nameLabel);
|
|
textLayout->addWidget(volLabel);
|
|
textLayout->setAlignment(nameLabel, Qt::AlignLeft);
|
|
textLayout->setAlignment(volLabel, Qt::AlignRight);
|
|
|
|
mainLayout->setContentsMargins(4, 4, 4, 4);
|
|
mainLayout->setSpacing(2);
|
|
mainLayout->addItem(textLayout);
|
|
mainLayout->addWidget(volMeter);
|
|
mainLayout->addWidget(slider);
|
|
|
|
setLayout(mainLayout);
|
|
|
|
signal_handler_connect(obs_fader_get_signal_handler(obs_fader),
|
|
"volume_changed", OBSVolumeChanged, this);
|
|
|
|
signal_handler_connect(obs_source_get_signal_handler(source),
|
|
"volume_level", OBSVolumeLevel, this);
|
|
|
|
QWidget::connect(slider, SIGNAL(valueChanged(int)),
|
|
this, SLOT(SliderChanged(int)));
|
|
|
|
obs_fader_attach_source(obs_fader, source);
|
|
/* Call volume changed once to init the slider position and label */
|
|
VolumeChanged();
|
|
}
|
|
|
|
VolControl::~VolControl()
|
|
{
|
|
signal_handler_disconnect(obs_fader_get_signal_handler(obs_fader),
|
|
"volume_changed", OBSVolumeChanged, this);
|
|
|
|
signal_handler_disconnect(obs_source_get_signal_handler(source),
|
|
"volume_level", OBSVolumeLevel, this);
|
|
|
|
obs_fader_destroy(obs_fader);
|
|
}
|
|
|
|
VolumeMeter::VolumeMeter(QWidget *parent)
|
|
: QWidget(parent)
|
|
{
|
|
setMinimumSize(1, 3);
|
|
|
|
bkColor.setRgb(0xDD, 0xDD, 0xDD);
|
|
magColor.setRgb(0x20, 0x7D, 0x17);
|
|
peakColor.setRgb(0x3E, 0xF1, 0x2B);
|
|
peakHoldColor.setRgb(0x00, 0x00, 0x00);
|
|
|
|
resetTimer = new QTimer(this);
|
|
connect(resetTimer, SIGNAL(timeout()), this, SLOT(resetState()));
|
|
|
|
resetState();
|
|
}
|
|
|
|
void VolumeMeter::resetState(void)
|
|
{
|
|
setLevels(0.0f, 0.0f, 0.0f);
|
|
if (resetTimer->isActive())
|
|
resetTimer->stop();
|
|
}
|
|
|
|
void VolumeMeter::setLevels(float nmag, float npeak, float npeakHold)
|
|
{
|
|
mag = nmag;
|
|
peak = npeak;
|
|
peakHold = npeakHold;
|
|
|
|
update();
|
|
|
|
if (resetTimer->isActive())
|
|
resetTimer->stop();
|
|
resetTimer->start(1000);
|
|
}
|
|
|
|
void VolumeMeter::paintEvent(QPaintEvent *event)
|
|
{
|
|
UNUSED_PARAMETER(event);
|
|
|
|
QPainter painter(this);
|
|
QLinearGradient gradient;
|
|
|
|
int width = size().width();
|
|
int height = size().height();
|
|
|
|
int scaledMag = int((float)width * mag);
|
|
int scaledPeak = int((float)width * peak);
|
|
int scaledPeakHold = int((float)width * peakHold);
|
|
|
|
gradient.setStart(qreal(scaledMag), 0);
|
|
gradient.setFinalStop(qreal(scaledPeak), 0);
|
|
gradient.setColorAt(0, magColor);
|
|
gradient.setColorAt(1, peakColor);
|
|
|
|
// RMS
|
|
painter.fillRect(0, 0,
|
|
scaledMag, height,
|
|
magColor);
|
|
|
|
// RMS - Peak gradient
|
|
painter.fillRect(scaledMag, 0,
|
|
scaledPeak - scaledMag + 1, height,
|
|
QBrush(gradient));
|
|
|
|
// Background
|
|
painter.fillRect(scaledPeak, 0,
|
|
width - scaledPeak, height,
|
|
bkColor);
|
|
|
|
// Peak hold
|
|
if (peakHold == 1.0f)
|
|
scaledPeakHold--;
|
|
|
|
painter.setPen(peakHoldColor);
|
|
painter.drawLine(scaledPeakHold, 0,
|
|
scaledPeakHold, height);
|
|
|
|
}
|