obs-studio/obs/volume-control.cpp
jp9000 c9df41c1e2 (API Change) Remove pointers from all typedefs
Typedef pointers are unsafe.  If you do:
typedef struct bla *bla_t;
then you cannot use it as a constant, such as: const bla_t, because
that constant will be to the pointer itself rather than to the
underlying data.  I admit this was a fundamental mistake that must
be corrected.

All typedefs that were pointer types will now have their pointers
removed from the type itself, and the pointers will be used when they
are actually used as variables/parameters/returns instead.

This does not break ABI though, which is pretty nice.
2014-09-25 21:48:11 -07:00

251 lines
5.9 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)
{
VolControl *volControl = static_cast<VolControl*>(data);
int vol = (int)(calldata_float(calldata, "volume") * 100.0f + 0.5f);
QMetaObject::invokeMethod(volControl, "VolumeChanged", Q_ARG(int, vol));
}
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(int vol)
{
signalChanged = false;
slider->setValue(vol);
signalChanged = true;
}
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)
{
if (signalChanged) {
signal_handler_disconnect(obs_source_get_signal_handler(source),
"volume", OBSVolumeChanged, this);
obs_source_set_volume(source, float(vol)*0.01f);
signal_handler_connect(obs_source_get_signal_handler(source),
"volume", OBSVolumeChanged, this);
}
volLabel->setText(QString::number(vol));
}
QString VolControl::GetName() const
{
return nameLabel->text();
}
void VolControl::SetName(const QString &newName)
{
nameLabel->setText(newName);
}
VolControl::VolControl(OBSSource source_)
: source (source_),
signalChanged (true),
lastMeterTime (0),
levelTotal (0.0f),
levelCount (0.0f)
{
QVBoxLayout *mainLayout = new QVBoxLayout();
QHBoxLayout *textLayout = new QHBoxLayout();
int vol = int(obs_source_get_volume(source) * 100.0f);
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->setText(QString::number(vol));
volLabel->setFont(font);
slider->setMinimum(0);
slider->setMaximum(100);
slider->setValue(vol);
// 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_source_get_signal_handler(source),
"volume", 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)));
}
VolControl::~VolControl()
{
signal_handler_disconnect(obs_source_get_signal_handler(source),
"volume", OBSVolumeChanged, this);
signal_handler_disconnect(obs_source_get_signal_handler(source),
"volume_level", OBSVolumeLevel, this);
}
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);
}