mirror of
https://github.com/WarmUpTill/SceneSwitcher.git
synced 2026-07-02 00:22:10 -05:00
Rebase volume control widgets to new OBS version
This commit is contained in:
parent
c9b5bbfc14
commit
66ddf3b4ef
|
|
@ -5,23 +5,24 @@
|
|||
#include <QPushButton>
|
||||
#include <QLabel>
|
||||
#include <QPainter>
|
||||
#include <QStyle>
|
||||
#include <QStyleFactory>
|
||||
#include <QSlider>
|
||||
|
||||
#include <util/platform.h>
|
||||
|
||||
using namespace std;
|
||||
|
||||
#define CLAMP(x, min, max) ((x) < (min) ? (min) : ((x) > (max) ? (max) : (x)))
|
||||
#define FADER_PRECISION 4096.0
|
||||
#define FADER_PRECISION 100
|
||||
|
||||
// Size of the audio indicator in pixels
|
||||
#define INDICATOR_THICKNESS 3
|
||||
|
||||
// Padding on top and bottom of vertical meters
|
||||
#define METER_PADDING 1
|
||||
|
||||
QWeakPointer<VolumeMeterTimer> VolumeMeter::updateTimer;
|
||||
|
||||
void VolControl::OBSVolumeChanged(void *data, float db)
|
||||
{
|
||||
Q_UNUSED(db);
|
||||
Q_UNUSED(data);
|
||||
}
|
||||
void VolControl::OBSVolumeChanged(void *, float) {}
|
||||
|
||||
void VolControl::OBSVolumeLevel(void *data,
|
||||
const float magnitude[MAX_AUDIO_CHANNELS],
|
||||
|
|
@ -33,9 +34,19 @@ void VolControl::OBSVolumeLevel(void *data,
|
|||
volControl->volMeter->setLevels(magnitude, peak, inputPeak);
|
||||
}
|
||||
|
||||
void VolControl::SliderChanged(int vol)
|
||||
void VolControl::VolumeChanged()
|
||||
{
|
||||
Q_UNUSED(vol);
|
||||
slider->blockSignals(true);
|
||||
slider->setValue(
|
||||
(int)(obs_fader_get_deflection(obs_fader) * FADER_PRECISION));
|
||||
slider->blockSignals(false);
|
||||
|
||||
updateText();
|
||||
}
|
||||
|
||||
void VolControl::SliderChanged(int)
|
||||
{
|
||||
float prev = obs_source_get_volume(source);
|
||||
updateText();
|
||||
}
|
||||
|
||||
|
|
@ -61,6 +72,12 @@ void VolControl::SetName(const QString &newName)
|
|||
{
|
||||
nameLabel->setText(newName);
|
||||
}
|
||||
|
||||
void VolControl::EmitConfigClicked()
|
||||
{
|
||||
emit ConfigClicked();
|
||||
}
|
||||
|
||||
void VolControl::SetMeterDecayRate(qreal q)
|
||||
{
|
||||
volMeter->setPeakDecayRate(q);
|
||||
|
|
@ -71,23 +88,46 @@ void VolControl::setPeakMeterType(enum obs_peak_meter_type peakMeterType)
|
|||
volMeter->setPeakMeterType(peakMeterType);
|
||||
}
|
||||
|
||||
VolControl::VolControl(OBSSource source_, bool vertical)
|
||||
VolumeSlider::VolumeSlider(obs_fader_t *fader, QWidget *parent)
|
||||
: QSlider(parent)
|
||||
{
|
||||
fad = fader;
|
||||
}
|
||||
|
||||
VolumeSlider::VolumeSlider(obs_fader_t *fader, Qt::Orientation orientation,
|
||||
QWidget *parent)
|
||||
: QSlider(orientation, parent)
|
||||
{
|
||||
fad = fader;
|
||||
}
|
||||
|
||||
VolControl::VolControl(OBSSource source_, bool showConfig, bool vertical)
|
||||
: source(std::move(source_)),
|
||||
levelTotal(0.0f),
|
||||
levelCount(0.0f),
|
||||
obs_fader(obs_fader_create(OBS_FADER_LOG)),
|
||||
obs_volmeter(obs_volmeter_create(OBS_FADER_LOG)),
|
||||
vertical(vertical)
|
||||
vertical(vertical),
|
||||
contextMenu(nullptr)
|
||||
{
|
||||
nameLabel = new QLabel();
|
||||
volLabel = new QLabel();
|
||||
|
||||
QString sourceName = "";
|
||||
if (source) {
|
||||
obs_source_get_name(source);
|
||||
}
|
||||
QString sourceName = obs_source_get_name(source);
|
||||
setObjectName(sourceName);
|
||||
|
||||
if (showConfig) {
|
||||
config = new QPushButton(this);
|
||||
config->setProperty("themeID", "menuIconSmall");
|
||||
config->setSizePolicy(QSizePolicy::Maximum,
|
||||
QSizePolicy::Maximum);
|
||||
config->setMaximumSize(22, 22);
|
||||
config->setAutoDefault(false);
|
||||
|
||||
connect(config, &QAbstractButton::clicked, this,
|
||||
&VolControl::EmitConfigClicked);
|
||||
}
|
||||
|
||||
QVBoxLayout *mainLayout = new QVBoxLayout;
|
||||
mainLayout->setContentsMargins(4, 4, 4, 4);
|
||||
mainLayout->setSpacing(2);
|
||||
|
|
@ -99,7 +139,7 @@ VolControl::VolControl(OBSSource source_, bool vertical)
|
|||
QHBoxLayout *meterLayout = new QHBoxLayout;
|
||||
|
||||
volMeter = new VolumeMeter(nullptr, obs_volmeter, true);
|
||||
slider = new QSlider(Qt::Vertical);
|
||||
slider = new VolumeSlider(obs_fader, Qt::Vertical);
|
||||
|
||||
nameLayout->setAlignment(Qt::AlignCenter);
|
||||
meterLayout->setAlignment(Qt::AlignCenter);
|
||||
|
|
@ -113,7 +153,11 @@ VolControl::VolControl(OBSSource source_, bool vertical)
|
|||
controlLayout->setContentsMargins(0, 0, 0, 0);
|
||||
controlLayout->setSpacing(0);
|
||||
|
||||
if (showConfig)
|
||||
controlLayout->addWidget(config);
|
||||
|
||||
controlLayout->addItem(new QSpacerItem(3, 0));
|
||||
// Add Headphone (audio monitoring) widget here
|
||||
|
||||
meterLayout->setContentsMargins(0, 0, 0, 0);
|
||||
meterLayout->setSpacing(0);
|
||||
|
|
@ -131,6 +175,12 @@ VolControl::VolControl(OBSSource source_, bool vertical)
|
|||
|
||||
volMeter->setFocusProxy(slider);
|
||||
|
||||
// Default size can cause clipping of long names in vertical layout.
|
||||
QFont font = nameLabel->font();
|
||||
QFontInfo info(font);
|
||||
font.setPointSizeF(0.8 * info.pointSizeF());
|
||||
nameLabel->setFont(font);
|
||||
|
||||
setMaximumWidth(110);
|
||||
} else {
|
||||
QHBoxLayout *volLayout = new QHBoxLayout;
|
||||
|
|
@ -138,7 +188,7 @@ VolControl::VolControl(OBSSource source_, bool vertical)
|
|||
QHBoxLayout *botLayout = new QHBoxLayout;
|
||||
|
||||
volMeter = new VolumeMeter(nullptr, obs_volmeter, false);
|
||||
slider = new QSlider(Qt::Horizontal);
|
||||
slider = new VolumeSlider(obs_fader, Qt::Horizontal);
|
||||
|
||||
textLayout->setContentsMargins(0, 0, 0, 0);
|
||||
textLayout->addWidget(nameLabel);
|
||||
|
|
@ -153,6 +203,9 @@ VolControl::VolControl(OBSSource source_, bool vertical)
|
|||
botLayout->setSpacing(0);
|
||||
botLayout->addLayout(volLayout);
|
||||
|
||||
if (showConfig)
|
||||
botLayout->addWidget(config);
|
||||
|
||||
mainLayout->addItem(textLayout);
|
||||
mainLayout->addWidget(volMeter);
|
||||
mainLayout->addItem(botLayout);
|
||||
|
|
@ -162,15 +215,10 @@ VolControl::VolControl(OBSSource source_, bool vertical)
|
|||
|
||||
setLayout(mainLayout);
|
||||
|
||||
QFont font = nameLabel->font();
|
||||
font.setPointSize(font.pointSize() - 1);
|
||||
|
||||
nameLabel->setText(sourceName);
|
||||
nameLabel->setFont(font);
|
||||
volLabel->setFont(font);
|
||||
|
||||
slider->setMinimum(0);
|
||||
slider->setMaximum(100);
|
||||
slider->setMaximum(int(FADER_PRECISION));
|
||||
|
||||
obs_fader_add_callback(obs_fader, OBSVolumeChanged, this);
|
||||
obs_volmeter_add_callback(obs_volmeter, OBSVolumeLevel, this);
|
||||
|
|
@ -180,6 +228,9 @@ VolControl::VolControl(OBSSource source_, bool vertical)
|
|||
|
||||
obs_fader_attach_source(obs_fader, source);
|
||||
obs_volmeter_attach_source(obs_volmeter, source);
|
||||
|
||||
/* Call volume changed once to init the slider position and label */
|
||||
VolumeChanged();
|
||||
}
|
||||
|
||||
void VolControl::EnableSlider(bool enable)
|
||||
|
|
@ -187,11 +238,6 @@ void VolControl::EnableSlider(bool enable)
|
|||
slider->setEnabled(enable);
|
||||
}
|
||||
|
||||
QSlider *VolControl::GetSlider() const
|
||||
{
|
||||
return slider;
|
||||
}
|
||||
|
||||
VolControl::~VolControl()
|
||||
{
|
||||
obs_fader_remove_callback(obs_fader, OBSVolumeChanged, this);
|
||||
|
|
@ -199,66 +245,143 @@ VolControl::~VolControl()
|
|||
|
||||
obs_fader_destroy(obs_fader);
|
||||
obs_volmeter_destroy(obs_volmeter);
|
||||
if (contextMenu)
|
||||
contextMenu->close();
|
||||
}
|
||||
|
||||
static inline QColor color_from_int(long long val)
|
||||
{
|
||||
QColor color(val & 0xff, (val >> 8) & 0xff, (val >> 16) & 0xff,
|
||||
(val >> 24) & 0xff);
|
||||
color.setAlpha(255);
|
||||
|
||||
return color;
|
||||
}
|
||||
|
||||
QColor VolumeMeter::getBackgroundNominalColor() const
|
||||
{
|
||||
return backgroundNominalColor;
|
||||
return p_backgroundNominalColor;
|
||||
}
|
||||
|
||||
QColor VolumeMeter::getBackgroundNominalColorDisabled() const
|
||||
{
|
||||
return backgroundNominalColorDisabled;
|
||||
}
|
||||
|
||||
void VolumeMeter::setBackgroundNominalColor(QColor c)
|
||||
{
|
||||
backgroundNominalColor = std::move(c);
|
||||
p_backgroundNominalColor = std::move(c);
|
||||
backgroundNominalColor = p_backgroundNominalColor;
|
||||
}
|
||||
|
||||
void VolumeMeter::setBackgroundNominalColorDisabled(QColor c)
|
||||
{
|
||||
backgroundNominalColorDisabled = std::move(c);
|
||||
}
|
||||
|
||||
QColor VolumeMeter::getBackgroundWarningColor() const
|
||||
{
|
||||
return backgroundWarningColor;
|
||||
return p_backgroundWarningColor;
|
||||
}
|
||||
|
||||
QColor VolumeMeter::getBackgroundWarningColorDisabled() const
|
||||
{
|
||||
return backgroundWarningColorDisabled;
|
||||
}
|
||||
|
||||
void VolumeMeter::setBackgroundWarningColor(QColor c)
|
||||
{
|
||||
backgroundWarningColor = std::move(c);
|
||||
p_backgroundWarningColor = std::move(c);
|
||||
backgroundWarningColor = p_backgroundWarningColor;
|
||||
}
|
||||
|
||||
void VolumeMeter::setBackgroundWarningColorDisabled(QColor c)
|
||||
{
|
||||
backgroundWarningColorDisabled = std::move(c);
|
||||
}
|
||||
|
||||
QColor VolumeMeter::getBackgroundErrorColor() const
|
||||
{
|
||||
return backgroundErrorColor;
|
||||
return p_backgroundErrorColor;
|
||||
}
|
||||
|
||||
QColor VolumeMeter::getBackgroundErrorColorDisabled() const
|
||||
{
|
||||
return backgroundErrorColorDisabled;
|
||||
}
|
||||
|
||||
void VolumeMeter::setBackgroundErrorColor(QColor c)
|
||||
{
|
||||
backgroundErrorColor = std::move(c);
|
||||
p_backgroundErrorColor = std::move(c);
|
||||
backgroundErrorColor = p_backgroundErrorColor;
|
||||
}
|
||||
|
||||
void VolumeMeter::setBackgroundErrorColorDisabled(QColor c)
|
||||
{
|
||||
backgroundErrorColorDisabled = std::move(c);
|
||||
}
|
||||
|
||||
QColor VolumeMeter::getForegroundNominalColor() const
|
||||
{
|
||||
return foregroundNominalColor;
|
||||
return p_foregroundNominalColor;
|
||||
}
|
||||
|
||||
QColor VolumeMeter::getForegroundNominalColorDisabled() const
|
||||
{
|
||||
return foregroundNominalColorDisabled;
|
||||
}
|
||||
|
||||
void VolumeMeter::setForegroundNominalColor(QColor c)
|
||||
{
|
||||
foregroundNominalColor = std::move(c);
|
||||
p_foregroundNominalColor = std::move(c);
|
||||
foregroundNominalColor = p_foregroundNominalColor;
|
||||
}
|
||||
|
||||
void VolumeMeter::setForegroundNominalColorDisabled(QColor c)
|
||||
{
|
||||
foregroundNominalColorDisabled = std::move(c);
|
||||
}
|
||||
|
||||
QColor VolumeMeter::getForegroundWarningColor() const
|
||||
{
|
||||
return foregroundWarningColor;
|
||||
return p_foregroundWarningColor;
|
||||
}
|
||||
|
||||
QColor VolumeMeter::getForegroundWarningColorDisabled() const
|
||||
{
|
||||
return foregroundWarningColorDisabled;
|
||||
}
|
||||
|
||||
void VolumeMeter::setForegroundWarningColor(QColor c)
|
||||
{
|
||||
foregroundWarningColor = std::move(c);
|
||||
p_foregroundWarningColor = std::move(c);
|
||||
foregroundWarningColor = p_foregroundWarningColor;
|
||||
}
|
||||
|
||||
void VolumeMeter::setForegroundWarningColorDisabled(QColor c)
|
||||
{
|
||||
foregroundWarningColorDisabled = std::move(c);
|
||||
}
|
||||
|
||||
QColor VolumeMeter::getForegroundErrorColor() const
|
||||
{
|
||||
return foregroundErrorColor;
|
||||
return p_foregroundErrorColor;
|
||||
}
|
||||
|
||||
QColor VolumeMeter::getForegroundErrorColorDisabled() const
|
||||
{
|
||||
return foregroundErrorColorDisabled;
|
||||
}
|
||||
|
||||
void VolumeMeter::setForegroundErrorColor(QColor c)
|
||||
{
|
||||
foregroundErrorColor = std::move(c);
|
||||
p_foregroundErrorColor = std::move(c);
|
||||
foregroundErrorColor = p_foregroundErrorColor;
|
||||
}
|
||||
|
||||
void VolumeMeter::setForegroundErrorColorDisabled(QColor c)
|
||||
{
|
||||
foregroundErrorColorDisabled = std::move(c);
|
||||
}
|
||||
|
||||
QColor VolumeMeter::getClipColor() const
|
||||
|
|
@ -301,6 +424,42 @@ void VolumeMeter::setMinorTickColor(QColor c)
|
|||
minorTickColor = std::move(c);
|
||||
}
|
||||
|
||||
int VolumeMeter::getMeterThickness() const
|
||||
{
|
||||
return meterThickness;
|
||||
}
|
||||
|
||||
void VolumeMeter::setMeterThickness(int v)
|
||||
{
|
||||
meterThickness = v;
|
||||
recalculateLayout = true;
|
||||
}
|
||||
|
||||
qreal VolumeMeter::getMeterFontScaling() const
|
||||
{
|
||||
return meterFontScaling;
|
||||
}
|
||||
|
||||
void VolumeMeter::setMeterFontScaling(qreal v)
|
||||
{
|
||||
meterFontScaling = v;
|
||||
recalculateLayout = true;
|
||||
}
|
||||
|
||||
void VolControl::refreshColors()
|
||||
{
|
||||
volMeter->setBackgroundNominalColor(
|
||||
volMeter->getBackgroundNominalColor());
|
||||
volMeter->setBackgroundWarningColor(
|
||||
volMeter->getBackgroundWarningColor());
|
||||
volMeter->setBackgroundErrorColor(volMeter->getBackgroundErrorColor());
|
||||
volMeter->setForegroundNominalColor(
|
||||
volMeter->getForegroundNominalColor());
|
||||
volMeter->setForegroundWarningColor(
|
||||
volMeter->getForegroundWarningColor());
|
||||
volMeter->setForegroundErrorColor(volMeter->getForegroundErrorColor());
|
||||
}
|
||||
|
||||
qreal VolumeMeter::getMinimumLevel() const
|
||||
{
|
||||
return minimumLevel;
|
||||
|
|
@ -434,10 +593,9 @@ VolumeMeter::VolumeMeter(QWidget *parent, obs_volmeter_t *obs_volmeter,
|
|||
bool vertical)
|
||||
: QWidget(parent), obs_volmeter(obs_volmeter), vertical(vertical)
|
||||
{
|
||||
// Use a font that can be rendered small.
|
||||
tickFont = QFont("Arial");
|
||||
tickFont.setPixelSize(7);
|
||||
// Default meter color settings, they only show if
|
||||
setAttribute(Qt::WA_OpaquePaintEvent, true);
|
||||
|
||||
// Default meter settings, they only show if
|
||||
// there is no stylesheet, do not remove.
|
||||
backgroundNominalColor.setRgb(0x26, 0x7f, 0x26); // Dark green
|
||||
backgroundWarningColor.setRgb(0x7f, 0x7f, 0x26); // Dark yellow
|
||||
|
|
@ -445,27 +603,38 @@ VolumeMeter::VolumeMeter(QWidget *parent, obs_volmeter_t *obs_volmeter,
|
|||
foregroundNominalColor.setRgb(0x4c, 0xff, 0x4c); // Bright green
|
||||
foregroundWarningColor.setRgb(0xff, 0xff, 0x4c); // Bright yellow
|
||||
foregroundErrorColor.setRgb(0xff, 0x4c, 0x4c); // Bright red
|
||||
clipColor.setRgb(0xff, 0xff, 0xff); // Bright white
|
||||
magnitudeColor.setRgb(0x00, 0x00, 0x00); // Black
|
||||
majorTickColor.setRgb(0xff, 0xff, 0xff); // Black
|
||||
minorTickColor.setRgb(0xcc, 0xcc, 0xcc); // Black
|
||||
minimumLevel = -60.0; // -60 dB
|
||||
warningLevel = -20.0; // -20 dB
|
||||
errorLevel = -9.0; // -9 dB
|
||||
clipLevel = -0.5; // -0.5 dB
|
||||
minimumInputLevel = -50.0; // -50 dB
|
||||
peakDecayRate = 11.76; // 20 dB / 1.7 sec
|
||||
magnitudeIntegrationTime = 0.3; // 99% in 300 ms
|
||||
peakHoldDuration = 20.0; // 20 seconds
|
||||
inputPeakHoldDuration = 1.0; // 1 second
|
||||
|
||||
backgroundNominalColorDisabled.setRgb(90, 90, 90);
|
||||
backgroundWarningColorDisabled.setRgb(117, 117, 117);
|
||||
backgroundErrorColorDisabled.setRgb(65, 65, 65);
|
||||
foregroundNominalColorDisabled.setRgb(163, 163, 163);
|
||||
foregroundWarningColorDisabled.setRgb(217, 217, 217);
|
||||
foregroundErrorColorDisabled.setRgb(113, 113, 113);
|
||||
|
||||
clipColor.setRgb(0xff, 0xff, 0xff); // Bright white
|
||||
magnitudeColor.setRgb(0x00, 0x00, 0x00); // Black
|
||||
majorTickColor.setRgb(0xff, 0xff, 0xff); // Black
|
||||
minorTickColor.setRgb(0xcc, 0xcc, 0xcc); // Black
|
||||
minimumLevel = -60.0; // -60 dB
|
||||
warningLevel = -20.0; // -20 dB
|
||||
errorLevel = -9.0; // -9 dB
|
||||
clipLevel = -0.5; // -0.5 dB
|
||||
minimumInputLevel = -50.0; // -50 dB
|
||||
peakDecayRate = 11.76; // 20 dB / 1.7 sec
|
||||
magnitudeIntegrationTime = 0.3; // 99% in 300 ms
|
||||
peakHoldDuration = 20.0; // 20 seconds
|
||||
inputPeakHoldDuration = 1.0; // 1 second
|
||||
meterThickness = 3; // Bar thickness in pixels
|
||||
meterFontScaling =
|
||||
0.7; // Font size for numbers is 70% of Widget's font size
|
||||
channels = (int)audio_output_get_channels(obs_get_audio());
|
||||
|
||||
handleChannelCofigurationChange();
|
||||
doLayout();
|
||||
updateTimerRef = updateTimer.toStrongRef();
|
||||
if (!updateTimerRef) {
|
||||
updateTimerRef = QSharedPointer<VolumeMeterTimer>::create();
|
||||
updateTimerRef->start(34);
|
||||
updateTimerRef->setTimerType(Qt::PreciseTimer);
|
||||
updateTimerRef->start(16);
|
||||
updateTimer = updateTimerRef;
|
||||
}
|
||||
|
||||
|
|
@ -475,7 +644,6 @@ VolumeMeter::VolumeMeter(QWidget *parent, obs_volmeter_t *obs_volmeter,
|
|||
VolumeMeter::~VolumeMeter()
|
||||
{
|
||||
updateTimerRef->RemoveVolControl(this);
|
||||
delete tickPaintCache;
|
||||
}
|
||||
|
||||
void VolumeMeter::setLevels(const float magnitude[MAX_AUDIO_CHANNELS],
|
||||
|
|
@ -515,23 +683,58 @@ inline void VolumeMeter::resetLevels()
|
|||
}
|
||||
}
|
||||
|
||||
inline void VolumeMeter::handleChannelCofigurationChange()
|
||||
bool VolumeMeter::needLayoutChange()
|
||||
{
|
||||
int currentNrAudioChannels = obs_volmeter_get_nr_channels(obs_volmeter);
|
||||
|
||||
if (!currentNrAudioChannels) {
|
||||
struct obs_audio_info oai;
|
||||
obs_get_audio_info(&oai);
|
||||
currentNrAudioChannels = (oai.speakers == SPEAKERS_MONO) ? 1
|
||||
: 2;
|
||||
}
|
||||
|
||||
if (displayNrAudioChannels != currentNrAudioChannels) {
|
||||
displayNrAudioChannels = currentNrAudioChannels;
|
||||
recalculateLayout = true;
|
||||
}
|
||||
|
||||
return recalculateLayout;
|
||||
}
|
||||
|
||||
// When this is called from the constructor, obs_volmeter_get_nr_channels has not
|
||||
// yet been called and Q_PROPERTY settings have not yet been read from the
|
||||
// stylesheet.
|
||||
inline void VolumeMeter::doLayout()
|
||||
{
|
||||
QMutexLocker locker(&dataMutex);
|
||||
|
||||
int currentNrAudioChannels = obs_volmeter_get_nr_channels(obs_volmeter);
|
||||
if (displayNrAudioChannels != currentNrAudioChannels) {
|
||||
displayNrAudioChannels = currentNrAudioChannels;
|
||||
recalculateLayout = false;
|
||||
|
||||
// Make room for 3 pixels meter, with one pixel between each.
|
||||
// Then 9/13 pixels for ticks and numbers.
|
||||
if (vertical)
|
||||
setMinimumSize(displayNrAudioChannels * 4 + 14, 130);
|
||||
else
|
||||
setMinimumSize(130, displayNrAudioChannels * 4 + 8);
|
||||
|
||||
resetLevels();
|
||||
tickFont = font();
|
||||
QFontInfo info(tickFont);
|
||||
tickFont.setPointSizeF(info.pointSizeF() * meterFontScaling);
|
||||
QFontMetrics metrics(tickFont);
|
||||
if (vertical) {
|
||||
// Each meter channel is meterThickness pixels wide, plus one pixel
|
||||
// between channels, but not after the last.
|
||||
// Add 4 pixels for ticks, space to hold our longest label in this font,
|
||||
// and a few pixels before the fader.
|
||||
QRect scaleBounds = metrics.boundingRect("-88");
|
||||
setMinimumSize(displayNrAudioChannels * (meterThickness + 1) -
|
||||
1 + 4 + scaleBounds.width() + 2,
|
||||
130);
|
||||
} else {
|
||||
// Each meter channel is meterThickness pixels high, plus one pixel
|
||||
// between channels, but not after the last.
|
||||
// Add 4 pixels for ticks, and space high enough to hold our label in
|
||||
// this font, presuming that digits don't have descenders.
|
||||
setMinimumSize(130,
|
||||
displayNrAudioChannels * (meterThickness + 1) -
|
||||
1 + 4 + metrics.capHeight());
|
||||
}
|
||||
|
||||
resetLevels();
|
||||
}
|
||||
|
||||
inline bool VolumeMeter::detectIdle(uint64_t ts)
|
||||
|
|
@ -649,12 +852,12 @@ void VolumeMeter::paintInputMeter(QPainter &painter, int x, int y, int width,
|
|||
painter.fillRect(x, y, width, height, color);
|
||||
}
|
||||
|
||||
void VolumeMeter::paintHTicks(QPainter &painter, int x, int y, int width,
|
||||
int height)
|
||||
void VolumeMeter::paintHTicks(QPainter &painter, int x, int y, int width)
|
||||
{
|
||||
qreal scale = width / minimumLevel;
|
||||
|
||||
painter.setFont(tickFont);
|
||||
QFontMetrics metrics(tickFont);
|
||||
painter.setPen(majorTickColor);
|
||||
|
||||
// Draw major tick lines and numeric indicators.
|
||||
|
|
@ -662,10 +865,18 @@ void VolumeMeter::paintHTicks(QPainter &painter, int x, int y, int width,
|
|||
int position = int(x + width - (i * scale) - 1);
|
||||
QString str = QString::number(i);
|
||||
|
||||
if (i == 0 || i == -5)
|
||||
painter.drawText(position - 3, height, str);
|
||||
else
|
||||
painter.drawText(position - 5, height, str);
|
||||
// Center the number on the tick, but don't overflow
|
||||
QRect textBounds = metrics.boundingRect(str);
|
||||
int pos;
|
||||
if (i == 0) {
|
||||
pos = position - textBounds.width();
|
||||
} else {
|
||||
pos = position - (textBounds.width() / 2);
|
||||
if (pos < 0)
|
||||
pos = 0;
|
||||
}
|
||||
painter.drawText(pos, y + 4 + metrics.capHeight(), str);
|
||||
|
||||
painter.drawLine(position, y, position, y + 2);
|
||||
}
|
||||
|
||||
|
|
@ -683,26 +894,31 @@ void VolumeMeter::paintVTicks(QPainter &painter, int x, int y, int height)
|
|||
qreal scale = height / minimumLevel;
|
||||
|
||||
painter.setFont(tickFont);
|
||||
QFontMetrics metrics(tickFont);
|
||||
painter.setPen(majorTickColor);
|
||||
|
||||
// Draw major tick lines and numeric indicators.
|
||||
for (int i = 0; i >= minimumLevel; i -= 5) {
|
||||
int position = y + int((i * scale) - 1);
|
||||
int position = y + int(i * scale) + METER_PADDING;
|
||||
QString str = QString::number(i);
|
||||
|
||||
if (i == 0)
|
||||
painter.drawText(x + 5, position + 4, str);
|
||||
else if (i == -60)
|
||||
painter.drawText(x + 4, position, str);
|
||||
else
|
||||
painter.drawText(x + 4, position + 2, str);
|
||||
// Center the number on the tick, but don't overflow
|
||||
if (i == 0) {
|
||||
painter.drawText(x + 6, position + metrics.capHeight(),
|
||||
str);
|
||||
} else {
|
||||
painter.drawText(x + 4,
|
||||
position + (metrics.capHeight() / 2),
|
||||
str);
|
||||
}
|
||||
|
||||
painter.drawLine(x, position, x + 2, position);
|
||||
}
|
||||
|
||||
// Draw minor tick lines.
|
||||
painter.setPen(minorTickColor);
|
||||
for (int i = 0; i >= minimumLevel; i--) {
|
||||
int position = y + int((i * scale) - 1);
|
||||
int position = y + int(i * scale) + METER_PADDING;
|
||||
if (i % 5 != 0)
|
||||
painter.drawLine(x, position, x + 1, position);
|
||||
}
|
||||
|
|
@ -777,7 +993,7 @@ void VolumeMeter::paintHMeter(QPainter &painter, int x, int y, int width,
|
|||
painter.fillRect(peakPosition, y,
|
||||
maximumPosition - peakPosition, height,
|
||||
backgroundErrorColor);
|
||||
} else {
|
||||
} else if (int(magnitude) != 0) {
|
||||
if (!clipping) {
|
||||
QTimer::singleShot(CLIP_FLASH_DURATION_MS, this,
|
||||
SLOT(ClipEnding()));
|
||||
|
|
@ -879,7 +1095,7 @@ void VolumeMeter::paintVMeter(QPainter &painter, int x, int y, int width,
|
|||
|
||||
int end = errorLength + warningLength + nominalLength;
|
||||
painter.fillRect(x, minimumPosition, width, end,
|
||||
QBrush(foregroundErrorColor));
|
||||
foregroundErrorColor);
|
||||
}
|
||||
|
||||
if (peakHoldPosition - 3 < minimumPosition)
|
||||
|
|
@ -903,53 +1119,48 @@ void VolumeMeter::paintEvent(QPaintEvent *event)
|
|||
{
|
||||
uint64_t ts = os_gettime_ns();
|
||||
qreal timeSinceLastRedraw = (ts - lastRedrawTime) * 0.000000001;
|
||||
|
||||
const QRect rect = event->region().boundingRect();
|
||||
int width = rect.width();
|
||||
int height = rect.height();
|
||||
|
||||
handleChannelCofigurationChange();
|
||||
calculateBallistics(ts, timeSinceLastRedraw);
|
||||
bool idle = detectIdle(ts);
|
||||
|
||||
// Draw the ticks in a off-screen buffer when the widget changes size.
|
||||
QSize tickPaintCacheSize;
|
||||
QRect widgetRect = rect();
|
||||
int width = widgetRect.width();
|
||||
int height = widgetRect.height();
|
||||
|
||||
QPainter painter(this);
|
||||
|
||||
if (vertical)
|
||||
tickPaintCacheSize = QSize(14, height);
|
||||
else
|
||||
tickPaintCacheSize = QSize(width, 9);
|
||||
if (tickPaintCache == nullptr ||
|
||||
tickPaintCache->size() != tickPaintCacheSize) {
|
||||
delete tickPaintCache;
|
||||
tickPaintCache = new QPixmap(tickPaintCacheSize);
|
||||
height -= METER_PADDING * 2;
|
||||
|
||||
QColor clearColor(0, 0, 0, 0);
|
||||
tickPaintCache->fill(clearColor);
|
||||
// timerEvent requests update of the bar(s) only, so we can avoid the
|
||||
// overhead of repainting the scale and labels.
|
||||
if (event->region().boundingRect() != getBarRect()) {
|
||||
if (needLayoutChange())
|
||||
doLayout();
|
||||
|
||||
// Paint window background color (as widget is opaque)
|
||||
QColor background =
|
||||
palette().color(QPalette::ColorRole::Window);
|
||||
painter.fillRect(widgetRect, background);
|
||||
|
||||
QPainter tickPainter(tickPaintCache);
|
||||
if (vertical) {
|
||||
tickPainter.translate(0, height);
|
||||
tickPainter.scale(1, -1);
|
||||
paintVTicks(tickPainter, 0, 11,
|
||||
tickPaintCacheSize.height() - 11);
|
||||
paintVTicks(painter,
|
||||
displayNrAudioChannels *
|
||||
(meterThickness + 1) -
|
||||
1,
|
||||
0, height - (INDICATOR_THICKNESS + 3));
|
||||
} else {
|
||||
paintHTicks(tickPainter, 6, 0,
|
||||
tickPaintCacheSize.width() - 6,
|
||||
tickPaintCacheSize.height());
|
||||
paintHTicks(painter, INDICATOR_THICKNESS + 3,
|
||||
displayNrAudioChannels *
|
||||
(meterThickness + 1) -
|
||||
1,
|
||||
width - (INDICATOR_THICKNESS + 3));
|
||||
}
|
||||
tickPainter.end();
|
||||
}
|
||||
|
||||
// Actual painting of the widget starts here.
|
||||
QPainter painter(this);
|
||||
if (vertical) {
|
||||
// Invert the Y axis to ease the math
|
||||
painter.translate(0, height);
|
||||
painter.translate(0, height + METER_PADDING);
|
||||
painter.scale(1, -1);
|
||||
painter.drawPixmap(displayNrAudioChannels * 4 - 1, 7,
|
||||
*tickPaintCache);
|
||||
} else {
|
||||
painter.drawPixmap(0, height - 9, *tickPaintCache);
|
||||
}
|
||||
|
||||
for (int channelNr = 0; channelNr < displayNrAudioChannels;
|
||||
|
|
@ -961,12 +1172,17 @@ void VolumeMeter::paintEvent(QPaintEvent *event)
|
|||
: channelNr;
|
||||
|
||||
if (vertical)
|
||||
paintVMeter(painter, channelNr * 4, 8, 3, height - 10,
|
||||
paintVMeter(painter, channelNr * (meterThickness + 1),
|
||||
INDICATOR_THICKNESS + 2, meterThickness,
|
||||
height - (INDICATOR_THICKNESS + 2),
|
||||
displayMagnitude[channelNrFixed],
|
||||
displayPeak[channelNrFixed],
|
||||
displayPeakHold[channelNrFixed]);
|
||||
else
|
||||
paintHMeter(painter, 5, channelNr * 4, width - 5, 3,
|
||||
paintHMeter(painter, INDICATOR_THICKNESS + 2,
|
||||
channelNr * (meterThickness + 1),
|
||||
width - (INDICATOR_THICKNESS + 2),
|
||||
meterThickness,
|
||||
displayMagnitude[channelNrFixed],
|
||||
displayPeak[channelNrFixed],
|
||||
displayPeakHold[channelNrFixed]);
|
||||
|
|
@ -978,16 +1194,40 @@ void VolumeMeter::paintEvent(QPaintEvent *event)
|
|||
// see that the audio stream has been stopped, without
|
||||
// having too much visual impact.
|
||||
if (vertical)
|
||||
paintInputMeter(painter, channelNr * 4, 3, 3, 3,
|
||||
paintInputMeter(painter,
|
||||
channelNr * (meterThickness + 1), 0,
|
||||
meterThickness, INDICATOR_THICKNESS,
|
||||
displayInputPeakHold[channelNrFixed]);
|
||||
else
|
||||
paintInputMeter(painter, 0, channelNr * 4, 3, 3,
|
||||
paintInputMeter(painter, 0,
|
||||
channelNr * (meterThickness + 1),
|
||||
INDICATOR_THICKNESS, meterThickness,
|
||||
displayInputPeakHold[channelNrFixed]);
|
||||
}
|
||||
|
||||
lastRedrawTime = ts;
|
||||
}
|
||||
|
||||
QRect VolumeMeter::getBarRect() const
|
||||
{
|
||||
QRect rec = rect();
|
||||
if (vertical)
|
||||
rec.setWidth(displayNrAudioChannels * (meterThickness + 1) - 1);
|
||||
else
|
||||
rec.setHeight(displayNrAudioChannels * (meterThickness + 1) -
|
||||
1);
|
||||
|
||||
return rec;
|
||||
}
|
||||
|
||||
void VolumeMeter::changeEvent(QEvent *e)
|
||||
{
|
||||
if (e->type() == QEvent::StyleChange)
|
||||
recalculateLayout = true;
|
||||
|
||||
QWidget::changeEvent(e);
|
||||
}
|
||||
|
||||
void VolumeMeterTimer::AddVolControl(VolumeMeter *meter)
|
||||
{
|
||||
volumeMeters.push_back(meter);
|
||||
|
|
@ -1000,6 +1240,13 @@ void VolumeMeterTimer::RemoveVolControl(VolumeMeter *meter)
|
|||
|
||||
void VolumeMeterTimer::timerEvent(QTimerEvent *)
|
||||
{
|
||||
for (VolumeMeter *meter : volumeMeters)
|
||||
meter->update();
|
||||
for (VolumeMeter *meter : volumeMeters) {
|
||||
if (meter->needLayoutChange()) {
|
||||
// Tell paintEvent to update layout and paint everything
|
||||
meter->update();
|
||||
} else {
|
||||
// Tell paintEvent to paint only the bars
|
||||
meter->update(meter->getBarRect());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -7,6 +7,8 @@
|
|||
#include <QTimer>
|
||||
#include <QMutex>
|
||||
#include <QList>
|
||||
#include <QSlider>
|
||||
#include <QMenu>
|
||||
|
||||
class QPushButton;
|
||||
class VolumeMeterTimer;
|
||||
|
|
@ -25,6 +27,32 @@ class VolumeMeter : public QWidget {
|
|||
WRITE setForegroundWarningColor DESIGNABLE true)
|
||||
Q_PROPERTY(QColor foregroundErrorColor READ getForegroundErrorColor
|
||||
WRITE setForegroundErrorColor DESIGNABLE true)
|
||||
|
||||
Q_PROPERTY(QColor backgroundNominalColorDisabled READ
|
||||
getBackgroundNominalColorDisabled WRITE
|
||||
setBackgroundNominalColorDisabled
|
||||
DESIGNABLE true)
|
||||
Q_PROPERTY(QColor backgroundWarningColorDisabled READ
|
||||
getBackgroundWarningColorDisabled WRITE
|
||||
setBackgroundWarningColorDisabled
|
||||
DESIGNABLE true)
|
||||
Q_PROPERTY(
|
||||
QColor backgroundErrorColorDisabled READ
|
||||
getBackgroundErrorColorDisabled WRITE
|
||||
setBackgroundErrorColorDisabled DESIGNABLE true)
|
||||
Q_PROPERTY(QColor foregroundNominalColorDisabled READ
|
||||
getForegroundNominalColorDisabled WRITE
|
||||
setForegroundNominalColorDisabled
|
||||
DESIGNABLE true)
|
||||
Q_PROPERTY(QColor foregroundWarningColorDisabled READ
|
||||
getForegroundWarningColorDisabled WRITE
|
||||
setForegroundWarningColorDisabled
|
||||
DESIGNABLE true)
|
||||
Q_PROPERTY(
|
||||
QColor foregroundErrorColorDisabled READ
|
||||
getForegroundErrorColorDisabled WRITE
|
||||
setForegroundErrorColorDisabled DESIGNABLE true)
|
||||
|
||||
Q_PROPERTY(QColor clipColor READ getClipColor WRITE setClipColor
|
||||
DESIGNABLE true)
|
||||
Q_PROPERTY(QColor magnitudeColor READ getMagnitudeColor WRITE
|
||||
|
|
@ -33,6 +61,10 @@ class VolumeMeter : public QWidget {
|
|||
setMajorTickColor DESIGNABLE true)
|
||||
Q_PROPERTY(QColor minorTickColor READ getMinorTickColor WRITE
|
||||
setMinorTickColor DESIGNABLE true)
|
||||
Q_PROPERTY(int meterThickness READ getMeterThickness WRITE
|
||||
setMeterThickness DESIGNABLE true)
|
||||
Q_PROPERTY(qreal meterFontScaling READ getMeterFontScaling WRITE
|
||||
setMeterFontScaling DESIGNABLE true)
|
||||
|
||||
// Levels are denoted in dBFS.
|
||||
Q_PROPERTY(qreal minimumLevel READ getMinimumLevel WRITE setMinimumLevel
|
||||
|
|
@ -61,6 +93,8 @@ class VolumeMeter : public QWidget {
|
|||
Q_PROPERTY(qreal inputPeakHoldDuration READ getInputPeakHoldDuration
|
||||
WRITE setInputPeakHoldDuration DESIGNABLE true)
|
||||
|
||||
friend class VolControl;
|
||||
|
||||
private slots:
|
||||
void ClipEnding();
|
||||
|
||||
|
|
@ -70,7 +104,7 @@ private:
|
|||
QSharedPointer<VolumeMeterTimer> updateTimerRef;
|
||||
|
||||
inline void resetLevels();
|
||||
inline void handleChannelCofigurationChange();
|
||||
inline void doLayout();
|
||||
inline bool detectIdle(uint64_t ts);
|
||||
inline void calculateBallistics(uint64_t ts,
|
||||
qreal timeSinceLastRedraw = 0.0);
|
||||
|
|
@ -81,20 +115,19 @@ private:
|
|||
int height, float peakHold);
|
||||
void paintHMeter(QPainter &painter, int x, int y, int width, int height,
|
||||
float magnitude, float peak, float peakHold);
|
||||
void paintHTicks(QPainter &painter, int x, int y, int width,
|
||||
int height);
|
||||
void paintHTicks(QPainter &painter, int x, int y, int width);
|
||||
void paintVMeter(QPainter &painter, int x, int y, int width, int height,
|
||||
float magnitude, float peak, float peakHold);
|
||||
void paintVTicks(QPainter &painter, int x, int y, int height);
|
||||
|
||||
QMutex dataMutex;
|
||||
|
||||
bool recalculateLayout = true;
|
||||
uint64_t currentLastUpdateTime = 0;
|
||||
float currentMagnitude[MAX_AUDIO_CHANNELS];
|
||||
float currentPeak[MAX_AUDIO_CHANNELS];
|
||||
float currentInputPeak[MAX_AUDIO_CHANNELS];
|
||||
|
||||
QPixmap *tickPaintCache = nullptr;
|
||||
int displayNrAudioChannels = 0;
|
||||
float displayMagnitude[MAX_AUDIO_CHANNELS];
|
||||
float displayPeak[MAX_AUDIO_CHANNELS];
|
||||
|
|
@ -110,10 +143,22 @@ private:
|
|||
QColor foregroundNominalColor;
|
||||
QColor foregroundWarningColor;
|
||||
QColor foregroundErrorColor;
|
||||
|
||||
QColor backgroundNominalColorDisabled;
|
||||
QColor backgroundWarningColorDisabled;
|
||||
QColor backgroundErrorColorDisabled;
|
||||
QColor foregroundNominalColorDisabled;
|
||||
QColor foregroundWarningColorDisabled;
|
||||
QColor foregroundErrorColorDisabled;
|
||||
|
||||
QColor clipColor;
|
||||
QColor magnitudeColor;
|
||||
QColor majorTickColor;
|
||||
QColor minorTickColor;
|
||||
|
||||
int meterThickness;
|
||||
qreal meterFontScaling;
|
||||
|
||||
qreal minimumLevel;
|
||||
qreal warningLevel;
|
||||
qreal errorLevel;
|
||||
|
|
@ -124,6 +169,13 @@ private:
|
|||
qreal peakHoldDuration;
|
||||
qreal inputPeakHoldDuration;
|
||||
|
||||
QColor p_backgroundNominalColor;
|
||||
QColor p_backgroundWarningColor;
|
||||
QColor p_backgroundErrorColor;
|
||||
QColor p_foregroundNominalColor;
|
||||
QColor p_foregroundWarningColor;
|
||||
QColor p_foregroundErrorColor;
|
||||
|
||||
uint64_t lastRedrawTime = 0;
|
||||
int channels = 0;
|
||||
bool clipping = false;
|
||||
|
|
@ -138,6 +190,8 @@ public:
|
|||
void setLevels(const float magnitude[MAX_AUDIO_CHANNELS],
|
||||
const float peak[MAX_AUDIO_CHANNELS],
|
||||
const float inputPeak[MAX_AUDIO_CHANNELS]);
|
||||
QRect getBarRect() const;
|
||||
bool needLayoutChange();
|
||||
|
||||
QColor getBackgroundNominalColor() const;
|
||||
void setBackgroundNominalColor(QColor c);
|
||||
|
|
@ -151,6 +205,20 @@ public:
|
|||
void setForegroundWarningColor(QColor c);
|
||||
QColor getForegroundErrorColor() const;
|
||||
void setForegroundErrorColor(QColor c);
|
||||
|
||||
QColor getBackgroundNominalColorDisabled() const;
|
||||
void setBackgroundNominalColorDisabled(QColor c);
|
||||
QColor getBackgroundWarningColorDisabled() const;
|
||||
void setBackgroundWarningColorDisabled(QColor c);
|
||||
QColor getBackgroundErrorColorDisabled() const;
|
||||
void setBackgroundErrorColorDisabled(QColor c);
|
||||
QColor getForegroundNominalColorDisabled() const;
|
||||
void setForegroundNominalColorDisabled(QColor c);
|
||||
QColor getForegroundWarningColorDisabled() const;
|
||||
void setForegroundWarningColorDisabled(QColor c);
|
||||
QColor getForegroundErrorColorDisabled() const;
|
||||
void setForegroundErrorColorDisabled(QColor c);
|
||||
|
||||
QColor getClipColor() const;
|
||||
void setClipColor(QColor c);
|
||||
QColor getMagnitudeColor() const;
|
||||
|
|
@ -159,6 +227,10 @@ public:
|
|||
void setMajorTickColor(QColor c);
|
||||
QColor getMinorTickColor() const;
|
||||
void setMinorTickColor(QColor c);
|
||||
int getMeterThickness() const;
|
||||
void setMeterThickness(int v);
|
||||
qreal getMeterFontScaling() const;
|
||||
void setMeterFontScaling(qreal v);
|
||||
qreal getMinimumLevel() const;
|
||||
void setMinimumLevel(qreal v);
|
||||
qreal getWarningLevel() const;
|
||||
|
|
@ -182,6 +254,7 @@ public:
|
|||
|
||||
protected:
|
||||
void paintEvent(QPaintEvent *event) override;
|
||||
void changeEvent(QEvent *e) override;
|
||||
};
|
||||
|
||||
class VolumeMeterTimer : public QTimer {
|
||||
|
|
@ -200,7 +273,6 @@ protected:
|
|||
|
||||
class QLabel;
|
||||
class QSlider;
|
||||
class MuteCheckBox;
|
||||
|
||||
class VolControl : public QWidget {
|
||||
Q_OBJECT
|
||||
|
|
@ -211,11 +283,13 @@ private:
|
|||
QLabel *volLabel;
|
||||
VolumeMeter *volMeter;
|
||||
QSlider *slider;
|
||||
QPushButton *config = nullptr;
|
||||
float levelTotal;
|
||||
float levelCount;
|
||||
obs_fader_t *obs_fader;
|
||||
obs_volmeter_t *obs_volmeter;
|
||||
bool vertical;
|
||||
QMenu *contextMenu;
|
||||
|
||||
static void OBSVolumeChanged(void *param, float db);
|
||||
static void OBSVolumeLevel(void *data,
|
||||
|
|
@ -223,12 +297,20 @@ private:
|
|||
const float peak[MAX_AUDIO_CHANNELS],
|
||||
const float inputPeak[MAX_AUDIO_CHANNELS]);
|
||||
|
||||
void EmitConfigClicked();
|
||||
|
||||
private slots:
|
||||
void VolumeChanged();
|
||||
|
||||
void SliderChanged(int vol);
|
||||
void updateText();
|
||||
|
||||
signals:
|
||||
void ConfigClicked();
|
||||
|
||||
public:
|
||||
explicit VolControl(OBSSource source, bool vertical = false);
|
||||
explicit VolControl(OBSSource source, bool showConfig = false,
|
||||
bool vertical = false);
|
||||
~VolControl();
|
||||
|
||||
inline obs_source_t *GetSource() const { return source; }
|
||||
|
|
@ -240,6 +322,19 @@ public:
|
|||
void setPeakMeterType(enum obs_peak_meter_type peakMeterType);
|
||||
|
||||
void EnableSlider(bool enable);
|
||||
inline void SetContextMenu(QMenu *cm) { contextMenu = cm; }
|
||||
|
||||
QSlider *GetSlider() const;
|
||||
void refreshColors();
|
||||
QSlider *GetSlider() const { return slider; }
|
||||
};
|
||||
|
||||
class VolumeSlider : public QSlider {
|
||||
Q_OBJECT
|
||||
|
||||
public:
|
||||
obs_fader_t *fad;
|
||||
|
||||
VolumeSlider(obs_fader_t *fader, QWidget *parent = nullptr);
|
||||
VolumeSlider(obs_fader_t *fader, Qt::Orientation orientation,
|
||||
QWidget *parent = nullptr);
|
||||
};
|
||||
|
|
|
|||
Loading…
Reference in New Issue
Block a user