New upstream version 22.0.3+dfsg1
This commit is contained in:
parent
665f64a933
commit
cdc9a9fc87
334 changed files with 14525 additions and 2639 deletions
|
|
@ -3,24 +3,17 @@
|
|||
#include "obs-app.hpp"
|
||||
#include "mute-checkbox.hpp"
|
||||
#include "slider-absoluteset-style.hpp"
|
||||
#include <obs-audio-controls.h>
|
||||
#include <util/platform.h>
|
||||
#include <util/threading.h>
|
||||
#include <QFontDatabase>
|
||||
#include <QHBoxLayout>
|
||||
#include <QVBoxLayout>
|
||||
#include <QPushButton>
|
||||
#include <QVariant>
|
||||
#include <QSlider>
|
||||
#include <QLabel>
|
||||
#include <QPainter>
|
||||
#include <QTimer>
|
||||
#include <string>
|
||||
#include <math.h>
|
||||
#include <QStyleFactory>
|
||||
|
||||
using namespace std;
|
||||
|
||||
#define CLAMP(x, min, max) ((x) < min ? min : ((x) > max ? max : (x)))
|
||||
#define CLAMP(x, min, max) ((x) < (min) ? (min) : ((x) > (max) ? (max) : (x)))
|
||||
|
||||
QWeakPointer<VolumeMeterTimer> VolumeMeter::updateTimer;
|
||||
|
||||
|
|
@ -33,9 +26,9 @@ void VolControl::OBSVolumeChanged(void *data, float db)
|
|||
}
|
||||
|
||||
void VolControl::OBSVolumeLevel(void *data,
|
||||
const float magnitude[MAX_AUDIO_CHANNELS],
|
||||
const float peak[MAX_AUDIO_CHANNELS],
|
||||
const float inputPeak[MAX_AUDIO_CHANNELS])
|
||||
const float magnitude[MAX_AUDIO_CHANNELS],
|
||||
const float peak[MAX_AUDIO_CHANNELS],
|
||||
const float inputPeak[MAX_AUDIO_CHANNELS])
|
||||
{
|
||||
VolControl *volControl = static_cast<VolControl*>(data);
|
||||
|
||||
|
|
@ -114,55 +107,24 @@ void VolControl::SetMeterDecayRate(qreal q)
|
|||
volMeter->setPeakDecayRate(q);
|
||||
}
|
||||
|
||||
VolControl::VolControl(OBSSource source_, bool showConfig)
|
||||
: source (source_),
|
||||
levelTotal (0.0f),
|
||||
levelCount (0.0f),
|
||||
obs_fader (obs_fader_create(OBS_FADER_CUBIC)),
|
||||
obs_volmeter (obs_volmeter_create(OBS_FADER_LOG))
|
||||
void VolControl::setPeakMeterType(enum obs_peak_meter_type peakMeterType)
|
||||
{
|
||||
QHBoxLayout *volLayout = new QHBoxLayout();
|
||||
QVBoxLayout *mainLayout = new QVBoxLayout();
|
||||
QHBoxLayout *textLayout = new QHBoxLayout();
|
||||
QHBoxLayout *botLayout = new QHBoxLayout();
|
||||
volMeter->setPeakMeterType(peakMeterType);
|
||||
}
|
||||
|
||||
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_CUBIC)),
|
||||
obs_volmeter (obs_volmeter_create(OBS_FADER_LOG)),
|
||||
vertical (vertical)
|
||||
{
|
||||
nameLabel = new QLabel();
|
||||
volLabel = new QLabel();
|
||||
volMeter = new VolumeMeter(0, obs_volmeter);
|
||||
mute = new MuteCheckBox();
|
||||
slider = new QSlider(Qt::Horizontal);
|
||||
|
||||
QFont font = nameLabel->font();
|
||||
font.setPointSize(font.pointSize()-1);
|
||||
|
||||
QString sourceName = obs_source_get_name(source);
|
||||
|
||||
nameLabel->setText(sourceName);
|
||||
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);
|
||||
|
||||
bool muted = obs_source_muted(source);
|
||||
mute->setChecked(muted);
|
||||
mute->setAccessibleName(
|
||||
QTStr("VolControl.Mute").arg(sourceName));
|
||||
|
||||
volLayout->addWidget(slider);
|
||||
volLayout->addWidget(mute);
|
||||
volLayout->setSpacing(5);
|
||||
|
||||
botLayout->setContentsMargins(0, 0, 0, 0);
|
||||
botLayout->setSpacing(0);
|
||||
botLayout->addLayout(volLayout);
|
||||
setObjectName(sourceName);
|
||||
|
||||
if (showConfig) {
|
||||
config = new QPushButton(this);
|
||||
|
|
@ -178,18 +140,99 @@ VolControl::VolControl(OBSSource source_, bool showConfig)
|
|||
|
||||
connect(config, &QAbstractButton::clicked,
|
||||
this, &VolControl::EmitConfigClicked);
|
||||
|
||||
botLayout->addWidget(config);
|
||||
}
|
||||
|
||||
QVBoxLayout *mainLayout = new QVBoxLayout;
|
||||
mainLayout->setContentsMargins(4, 4, 4, 4);
|
||||
mainLayout->setSpacing(2);
|
||||
mainLayout->addItem(textLayout);
|
||||
mainLayout->addWidget(volMeter);
|
||||
mainLayout->addItem(botLayout);
|
||||
|
||||
if (vertical) {
|
||||
QHBoxLayout *nameLayout = new QHBoxLayout;
|
||||
QHBoxLayout *controlLayout = new QHBoxLayout;
|
||||
QHBoxLayout *volLayout = new QHBoxLayout;
|
||||
QHBoxLayout *meterLayout = new QHBoxLayout;
|
||||
|
||||
volMeter = new VolumeMeter(nullptr, obs_volmeter, true);
|
||||
slider = new QSlider(Qt::Vertical);
|
||||
|
||||
nameLayout->setAlignment(Qt::AlignCenter);
|
||||
meterLayout->setAlignment(Qt::AlignCenter);
|
||||
controlLayout->setAlignment(Qt::AlignCenter);
|
||||
volLayout->setAlignment(Qt::AlignCenter);
|
||||
|
||||
nameLayout->setContentsMargins(0, 0, 0, 0);
|
||||
nameLayout->setSpacing(0);
|
||||
nameLayout->addWidget(nameLabel);
|
||||
|
||||
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
|
||||
controlLayout->addWidget(mute);
|
||||
|
||||
meterLayout->setContentsMargins(0, 0, 0, 0);
|
||||
meterLayout->setSpacing(0);
|
||||
meterLayout->addWidget(volMeter);
|
||||
meterLayout->addWidget(slider);
|
||||
|
||||
volLayout->setContentsMargins(0, 0, 0, 0);
|
||||
volLayout->setSpacing(0);
|
||||
volLayout->addWidget(volLabel);
|
||||
|
||||
mainLayout->addItem(nameLayout);
|
||||
mainLayout->addItem(volLayout);
|
||||
mainLayout->addItem(meterLayout);
|
||||
mainLayout->addItem(controlLayout);
|
||||
|
||||
setMaximumWidth(110);
|
||||
} else {
|
||||
QHBoxLayout *volLayout = new QHBoxLayout;
|
||||
QHBoxLayout *textLayout = new QHBoxLayout;
|
||||
QHBoxLayout *botLayout = new QHBoxLayout;
|
||||
|
||||
volMeter = new VolumeMeter(nullptr, obs_volmeter, false);
|
||||
slider = new QSlider(Qt::Horizontal);
|
||||
|
||||
textLayout->setContentsMargins(0, 0, 0, 0);
|
||||
textLayout->addWidget(nameLabel);
|
||||
textLayout->addWidget(volLabel);
|
||||
textLayout->setAlignment(nameLabel, Qt::AlignLeft);
|
||||
textLayout->setAlignment(volLabel, Qt::AlignRight);
|
||||
|
||||
volLayout->addWidget(slider);
|
||||
volLayout->addWidget(mute);
|
||||
volLayout->setSpacing(5);
|
||||
|
||||
botLayout->setContentsMargins(0, 0, 0, 0);
|
||||
botLayout->setSpacing(0);
|
||||
botLayout->addLayout(volLayout);
|
||||
|
||||
if (showConfig)
|
||||
botLayout->addWidget(config);
|
||||
|
||||
mainLayout->addItem(textLayout);
|
||||
mainLayout->addWidget(volMeter);
|
||||
mainLayout->addItem(botLayout);
|
||||
}
|
||||
|
||||
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);
|
||||
|
||||
bool muted = obs_source_muted(source);
|
||||
mute->setChecked(muted);
|
||||
mute->setAccessibleName(QTStr("VolControl.Mute").arg(sourceName));
|
||||
obs_fader_add_callback(obs_fader, OBSVolumeChanged, this);
|
||||
obs_volmeter_add_callback(obs_volmeter, OBSVolumeLevel, this);
|
||||
|
||||
|
|
@ -204,7 +247,17 @@ VolControl::VolControl(OBSSource source_, bool showConfig)
|
|||
obs_fader_attach_source(obs_fader, source);
|
||||
obs_volmeter_attach_source(obs_volmeter, source);
|
||||
|
||||
slider->setStyle(new SliderAbsoluteSetStyle(slider->style()));
|
||||
QString styleName = slider->style()->objectName();
|
||||
QStyle *style;
|
||||
style = QStyleFactory::create(styleName);
|
||||
if (!style) {
|
||||
style = new SliderAbsoluteSetStyle();
|
||||
} else {
|
||||
style = new SliderAbsoluteSetStyle(style);
|
||||
}
|
||||
|
||||
style->setParent(slider);
|
||||
slider->setStyle(style);
|
||||
|
||||
/* Call volume changed once to init the slider position and label */
|
||||
VolumeChanged();
|
||||
|
|
@ -229,7 +282,7 @@ QColor VolumeMeter::getBackgroundNominalColor() const
|
|||
|
||||
void VolumeMeter::setBackgroundNominalColor(QColor c)
|
||||
{
|
||||
backgroundNominalColor = c;
|
||||
backgroundNominalColor = std::move(c);
|
||||
}
|
||||
|
||||
QColor VolumeMeter::getBackgroundWarningColor() const
|
||||
|
|
@ -239,7 +292,7 @@ QColor VolumeMeter::getBackgroundWarningColor() const
|
|||
|
||||
void VolumeMeter::setBackgroundWarningColor(QColor c)
|
||||
{
|
||||
backgroundWarningColor = c;
|
||||
backgroundWarningColor = std::move(c);
|
||||
}
|
||||
|
||||
QColor VolumeMeter::getBackgroundErrorColor() const
|
||||
|
|
@ -249,7 +302,7 @@ QColor VolumeMeter::getBackgroundErrorColor() const
|
|||
|
||||
void VolumeMeter::setBackgroundErrorColor(QColor c)
|
||||
{
|
||||
backgroundErrorColor = c;
|
||||
backgroundErrorColor = std::move(c);
|
||||
}
|
||||
|
||||
QColor VolumeMeter::getForegroundNominalColor() const
|
||||
|
|
@ -259,7 +312,7 @@ QColor VolumeMeter::getForegroundNominalColor() const
|
|||
|
||||
void VolumeMeter::setForegroundNominalColor(QColor c)
|
||||
{
|
||||
foregroundNominalColor = c;
|
||||
foregroundNominalColor = std::move(c);
|
||||
}
|
||||
|
||||
QColor VolumeMeter::getForegroundWarningColor() const
|
||||
|
|
@ -269,7 +322,7 @@ QColor VolumeMeter::getForegroundWarningColor() const
|
|||
|
||||
void VolumeMeter::setForegroundWarningColor(QColor c)
|
||||
{
|
||||
foregroundWarningColor = c;
|
||||
foregroundWarningColor = std::move(c);
|
||||
}
|
||||
|
||||
QColor VolumeMeter::getForegroundErrorColor() const
|
||||
|
|
@ -279,7 +332,7 @@ QColor VolumeMeter::getForegroundErrorColor() const
|
|||
|
||||
void VolumeMeter::setForegroundErrorColor(QColor c)
|
||||
{
|
||||
foregroundErrorColor = c;
|
||||
foregroundErrorColor = std::move(c);
|
||||
}
|
||||
|
||||
QColor VolumeMeter::getClipColor() const
|
||||
|
|
@ -289,7 +342,7 @@ QColor VolumeMeter::getClipColor() const
|
|||
|
||||
void VolumeMeter::setClipColor(QColor c)
|
||||
{
|
||||
clipColor = c;
|
||||
clipColor = std::move(c);
|
||||
}
|
||||
|
||||
QColor VolumeMeter::getMagnitudeColor() const
|
||||
|
|
@ -299,7 +352,7 @@ QColor VolumeMeter::getMagnitudeColor() const
|
|||
|
||||
void VolumeMeter::setMagnitudeColor(QColor c)
|
||||
{
|
||||
magnitudeColor = c;
|
||||
magnitudeColor = std::move(c);
|
||||
}
|
||||
|
||||
QColor VolumeMeter::getMajorTickColor() const
|
||||
|
|
@ -309,7 +362,7 @@ QColor VolumeMeter::getMajorTickColor() const
|
|||
|
||||
void VolumeMeter::setMajorTickColor(QColor c)
|
||||
{
|
||||
majorTickColor = c;
|
||||
majorTickColor = std::move(c);
|
||||
}
|
||||
|
||||
QColor VolumeMeter::getMinorTickColor() const
|
||||
|
|
@ -319,7 +372,7 @@ QColor VolumeMeter::getMinorTickColor() const
|
|||
|
||||
void VolumeMeter::setMinorTickColor(QColor c)
|
||||
{
|
||||
minorTickColor = c;
|
||||
minorTickColor = std::move(c);
|
||||
}
|
||||
|
||||
qreal VolumeMeter::getMinimumLevel() const
|
||||
|
|
@ -412,8 +465,43 @@ void VolumeMeter::setInputPeakHoldDuration(qreal v)
|
|||
inputPeakHoldDuration = v;
|
||||
}
|
||||
|
||||
VolumeMeter::VolumeMeter(QWidget *parent, obs_volmeter_t *obs_volmeter)
|
||||
: QWidget(parent), obs_volmeter(obs_volmeter)
|
||||
void VolumeMeter::setPeakMeterType(enum obs_peak_meter_type peakMeterType)
|
||||
{
|
||||
obs_volmeter_set_peak_meter_type(obs_volmeter, peakMeterType);
|
||||
switch (peakMeterType) {
|
||||
case TRUE_PEAK_METER:
|
||||
// For true-peak meters EBU has defined the Permitted Maximum,
|
||||
// taking into account the accuracy of the meter and further
|
||||
// processing required by lossy audio compression.
|
||||
//
|
||||
// The alignment level was not specified, but I've adjusted
|
||||
// it compared to a sample-peak meter. Incidently Youtube
|
||||
// uses this new Alignment Level as the maximum integrated
|
||||
// loudness of a video.
|
||||
//
|
||||
// * Permitted Maximum Level (PML) = -2.0 dBTP
|
||||
// * Alignment Level (AL) = -13 dBTP
|
||||
setErrorLevel(-2.0);
|
||||
setWarningLevel(-13.0);
|
||||
break;
|
||||
|
||||
case SAMPLE_PEAK_METER:
|
||||
default:
|
||||
// For a sample Peak Meter EBU has the following level
|
||||
// definitions, taking into account inaccuracies of this meter:
|
||||
//
|
||||
// * Permitted Maximum Level (PML) = -9.0 dBFS
|
||||
// * Alignment Level (AL) = -20.0 dBFS
|
||||
setErrorLevel(-9.0);
|
||||
setWarningLevel(-20.0);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
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");
|
||||
|
|
@ -454,12 +542,12 @@ 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],
|
||||
const float peak[MAX_AUDIO_CHANNELS],
|
||||
const float inputPeak[MAX_AUDIO_CHANNELS])
|
||||
void VolumeMeter::setLevels(const float magnitude[MAX_AUDIO_CHANNELS],
|
||||
const float peak[MAX_AUDIO_CHANNELS],
|
||||
const float inputPeak[MAX_AUDIO_CHANNELS])
|
||||
{
|
||||
uint64_t ts = os_gettime_ns();
|
||||
QMutexLocker locker(&dataMutex);
|
||||
|
|
@ -502,9 +590,12 @@ inline void VolumeMeter::handleChannelCofigurationChange()
|
|||
if (displayNrAudioChannels != currentNrAudioChannels) {
|
||||
displayNrAudioChannels = currentNrAudioChannels;
|
||||
|
||||
// Make room for 3 pixels high meter, with one pixel between
|
||||
// each. Then 9 pixels below it for ticks and numbers.
|
||||
setMinimumSize(130, displayNrAudioChannels * 4 + 8);
|
||||
// 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();
|
||||
}
|
||||
|
|
@ -512,7 +603,7 @@ inline void VolumeMeter::handleChannelCofigurationChange()
|
|||
|
||||
inline bool VolumeMeter::detectIdle(uint64_t ts)
|
||||
{
|
||||
float timeSinceLastUpdate = (ts - currentLastUpdateTime) * 0.000000001;
|
||||
double timeSinceLastUpdate = (ts - currentLastUpdateTime) * 0.000000001;
|
||||
if (timeSinceLastUpdate > 0.5) {
|
||||
resetLevels();
|
||||
return true;
|
||||
|
|
@ -522,7 +613,7 @@ inline bool VolumeMeter::detectIdle(uint64_t ts)
|
|||
}
|
||||
|
||||
inline void VolumeMeter::calculateBallisticsForChannel(int channelNr,
|
||||
uint64_t ts, qreal timeSinceLastRedraw)
|
||||
uint64_t ts, qreal timeSinceLastRedraw)
|
||||
{
|
||||
if (currentPeak[channelNr] >= displayPeak[channelNr] ||
|
||||
isnan(displayPeak[channelNr])) {
|
||||
|
|
@ -532,7 +623,7 @@ inline void VolumeMeter::calculateBallisticsForChannel(int channelNr,
|
|||
// Decay of peak is 40 dB / 1.7 seconds for Fast Profile
|
||||
// 20 dB / 1.7 seconds for Medium Profile (Type I PPM)
|
||||
// 24 dB / 2.8 seconds for Slow Profile (Type II PPM)
|
||||
qreal decay = peakDecayRate * timeSinceLastRedraw;
|
||||
float decay = float(peakDecayRate * timeSinceLastRedraw);
|
||||
displayPeak[channelNr] = CLAMP(displayPeak[channelNr] - decay,
|
||||
currentPeak[channelNr], 0);
|
||||
}
|
||||
|
|
@ -576,53 +667,52 @@ inline void VolumeMeter::calculateBallisticsForChannel(int channelNr,
|
|||
if (!isfinite(displayMagnitude[channelNr])) {
|
||||
// The statements in the else-leg do not work with
|
||||
// NaN and infinite displayMagnitude.
|
||||
displayMagnitude[channelNr] =
|
||||
currentMagnitude[channelNr];
|
||||
displayMagnitude[channelNr] = currentMagnitude[channelNr];
|
||||
} else {
|
||||
// A VU meter will integrate to the new value to 99% in 300 ms.
|
||||
// The calculation here is very simplified and is more accurate
|
||||
// with higher frame-rate.
|
||||
qreal attack = (currentMagnitude[channelNr] -
|
||||
float attack = float((currentMagnitude[channelNr] -
|
||||
displayMagnitude[channelNr]) *
|
||||
(timeSinceLastRedraw /
|
||||
magnitudeIntegrationTime) * 0.99;
|
||||
displayMagnitude[channelNr] = CLAMP(
|
||||
displayMagnitude[channelNr] + attack,
|
||||
minimumLevel, 0);
|
||||
magnitudeIntegrationTime) * 0.99);
|
||||
displayMagnitude[channelNr] = CLAMP(displayMagnitude[channelNr]
|
||||
+ attack, (float)minimumLevel, 0);
|
||||
}
|
||||
}
|
||||
|
||||
inline void VolumeMeter::calculateBallistics(uint64_t ts,
|
||||
qreal timeSinceLastRedraw)
|
||||
qreal timeSinceLastRedraw)
|
||||
{
|
||||
QMutexLocker locker(&dataMutex);
|
||||
|
||||
for (int channelNr = 0; channelNr < MAX_AUDIO_CHANNELS; channelNr++) {
|
||||
for (int channelNr = 0; channelNr < MAX_AUDIO_CHANNELS; channelNr++)
|
||||
calculateBallisticsForChannel(channelNr, ts,
|
||||
timeSinceLastRedraw);
|
||||
}
|
||||
timeSinceLastRedraw);
|
||||
}
|
||||
|
||||
void VolumeMeter::paintInputMeter(QPainter &painter, int x, int y,
|
||||
int width, int height, float peakHold)
|
||||
void VolumeMeter::paintInputMeter(QPainter &painter, int x, int y, int width,
|
||||
int height, float peakHold)
|
||||
{
|
||||
QMutexLocker locker(&dataMutex);
|
||||
QColor color;
|
||||
|
||||
if (peakHold < minimumInputLevel) {
|
||||
painter.fillRect(x, y, width, height, backgroundNominalColor);
|
||||
} else if (peakHold < warningLevel) {
|
||||
painter.fillRect(x, y, width, height, foregroundNominalColor);
|
||||
} else if (peakHold < errorLevel) {
|
||||
painter.fillRect(x, y, width, height, foregroundWarningColor);
|
||||
} else if (peakHold <= clipLevel) {
|
||||
painter.fillRect(x, y, width, height, foregroundErrorColor);
|
||||
} else {
|
||||
painter.fillRect(x, y, width, height, clipColor);
|
||||
}
|
||||
if (peakHold < minimumInputLevel)
|
||||
color = backgroundNominalColor;
|
||||
else if (peakHold < warningLevel)
|
||||
color = foregroundNominalColor;
|
||||
else if (peakHold < errorLevel)
|
||||
color = foregroundWarningColor;
|
||||
else if (peakHold <= clipLevel)
|
||||
color = foregroundErrorColor;
|
||||
else
|
||||
color = clipColor;
|
||||
|
||||
painter.fillRect(x, y, width, height, color);
|
||||
}
|
||||
|
||||
void VolumeMeter::paintTicks(QPainter &painter, int x, int y,
|
||||
int width, int height)
|
||||
void VolumeMeter::paintHTicks(QPainter &painter, int x, int y, int width,
|
||||
int height)
|
||||
{
|
||||
qreal scale = width / minimumLevel;
|
||||
|
||||
|
|
@ -631,184 +721,257 @@ void VolumeMeter::paintTicks(QPainter &painter, int x, int y,
|
|||
|
||||
// Draw major tick lines and numeric indicators.
|
||||
for (int i = 0; i >= minimumLevel; i-= 5) {
|
||||
int position = x + width - (i * scale) - 1;
|
||||
int position = int(x + width - (i * scale) - 1);
|
||||
QString str = QString::number(i);
|
||||
|
||||
if (i == 0 || i == -5) {
|
||||
if (i == 0 || i == -5)
|
||||
painter.drawText(position - 3, height, str);
|
||||
} else {
|
||||
else
|
||||
painter.drawText(position - 5, height, str);
|
||||
}
|
||||
painter.drawLine(position, y, position, y + 2);
|
||||
}
|
||||
|
||||
// Draw minor tick lines.
|
||||
painter.setPen(minorTickColor);
|
||||
for (int i = 0; i >= minimumLevel; i--) {
|
||||
int position = x + width - (i * scale) - 1;
|
||||
|
||||
if (i % 5 != 0) {
|
||||
int position = int(x + width - (i * scale) - 1);
|
||||
if (i % 5 != 0)
|
||||
painter.drawLine(position, y, position, y + 1);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void VolumeMeter::paintMeter(QPainter &painter, int x, int y,
|
||||
int width, int height, float magnitude, float peak, float peakHold)
|
||||
void VolumeMeter::paintVTicks(QPainter &painter, int x, int y, int height)
|
||||
{
|
||||
qreal scale = height / minimumLevel;
|
||||
|
||||
painter.setFont(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);
|
||||
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);
|
||||
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);
|
||||
if (i % 5 != 0)
|
||||
painter.drawLine(x, position, x + 1, position);
|
||||
}
|
||||
}
|
||||
|
||||
#define CLIP_FLASH_DURATION_MS 1000
|
||||
|
||||
void VolumeMeter::ClipEnding()
|
||||
{
|
||||
clipping = false;
|
||||
}
|
||||
|
||||
void VolumeMeter::paintHMeter(QPainter &painter, int x, int y, int width,
|
||||
int height, float magnitude, float peak, float peakHold)
|
||||
{
|
||||
qreal scale = width / minimumLevel;
|
||||
|
||||
QMutexLocker locker(&dataMutex);
|
||||
int minimumPosition = x + 0;
|
||||
int maximumPosition = x + width;
|
||||
int magnitudePosition = x + width - (magnitude * scale);
|
||||
int peakPosition = x + width - (peak * scale);
|
||||
int peakHoldPosition = x + width - (peakHold * scale);
|
||||
int warningPosition = x + width - (warningLevel * scale);
|
||||
int errorPosition = x + width - (errorLevel * scale);
|
||||
int magnitudePosition = int(x + width - (magnitude * scale));
|
||||
int peakPosition = int(x + width - (peak * scale));
|
||||
int peakHoldPosition = int(x + width - (peakHold * scale));
|
||||
int warningPosition = int(x + width - (warningLevel * scale));
|
||||
int errorPosition = int(x + width - (errorLevel * scale));
|
||||
|
||||
int nominalLength = warningPosition - minimumPosition;
|
||||
int warningLength = errorPosition - warningPosition;
|
||||
int errorLength = maximumPosition - errorPosition;
|
||||
locker.unlock();
|
||||
|
||||
if (clipping) {
|
||||
peakPosition = maximumPosition;
|
||||
}
|
||||
|
||||
if (peakPosition < minimumPosition) {
|
||||
painter.fillRect(
|
||||
minimumPosition, y,
|
||||
nominalLength, height,
|
||||
backgroundNominalColor);
|
||||
painter.fillRect(
|
||||
warningPosition, y,
|
||||
warningLength, height,
|
||||
backgroundWarningColor);
|
||||
painter.fillRect(
|
||||
errorPosition, y,
|
||||
errorLength, height,
|
||||
backgroundErrorColor);
|
||||
|
||||
painter.fillRect(minimumPosition, y, nominalLength, height,
|
||||
backgroundNominalColor);
|
||||
painter.fillRect(warningPosition, y, warningLength, height,
|
||||
backgroundWarningColor);
|
||||
painter.fillRect(errorPosition, y, errorLength, height,
|
||||
backgroundErrorColor);
|
||||
} else if (peakPosition < warningPosition) {
|
||||
painter.fillRect(
|
||||
minimumPosition, y,
|
||||
peakPosition - minimumPosition, height,
|
||||
foregroundNominalColor);
|
||||
painter.fillRect(
|
||||
peakPosition, y,
|
||||
warningPosition - peakPosition, height,
|
||||
backgroundNominalColor);
|
||||
painter.fillRect(
|
||||
warningPosition, y,
|
||||
warningLength, height,
|
||||
backgroundWarningColor);
|
||||
painter.fillRect(errorPosition, y,
|
||||
errorLength, height,
|
||||
backgroundErrorColor);
|
||||
|
||||
painter.fillRect(minimumPosition, y, peakPosition -
|
||||
minimumPosition, height,
|
||||
foregroundNominalColor);
|
||||
painter.fillRect(peakPosition, y, warningPosition -
|
||||
peakPosition, height, backgroundNominalColor);
|
||||
painter.fillRect(warningPosition, y, warningLength, height,
|
||||
backgroundWarningColor);
|
||||
painter.fillRect(errorPosition, y, errorLength, height,
|
||||
backgroundErrorColor);
|
||||
} else if (peakPosition < errorPosition) {
|
||||
painter.fillRect(
|
||||
minimumPosition, y,
|
||||
nominalLength, height,
|
||||
foregroundNominalColor);
|
||||
painter.fillRect(
|
||||
warningPosition, y,
|
||||
peakPosition - warningPosition, height,
|
||||
foregroundWarningColor);
|
||||
painter.fillRect(
|
||||
peakPosition, y,
|
||||
errorPosition - peakPosition, height,
|
||||
backgroundWarningColor);
|
||||
painter.fillRect(
|
||||
errorPosition, y,
|
||||
errorLength, height,
|
||||
backgroundErrorColor);
|
||||
|
||||
painter.fillRect(minimumPosition, y, nominalLength, height,
|
||||
foregroundNominalColor);
|
||||
painter.fillRect(warningPosition, y,
|
||||
peakPosition - warningPosition, height,
|
||||
foregroundWarningColor);
|
||||
painter.fillRect(peakPosition, y, errorPosition -
|
||||
peakPosition, height, backgroundWarningColor);
|
||||
painter.fillRect(errorPosition, y, errorLength, height,
|
||||
backgroundErrorColor);
|
||||
} else if (peakPosition < maximumPosition) {
|
||||
painter.fillRect(
|
||||
minimumPosition, y,
|
||||
nominalLength, height,
|
||||
foregroundNominalColor);
|
||||
painter.fillRect(
|
||||
warningPosition, y,
|
||||
warningLength, height,
|
||||
foregroundWarningColor);
|
||||
painter.fillRect(
|
||||
errorPosition, y,
|
||||
peakPosition - errorPosition, height,
|
||||
foregroundErrorColor);
|
||||
painter.fillRect(
|
||||
peakPosition, y,
|
||||
maximumPosition - peakPosition, height,
|
||||
backgroundErrorColor);
|
||||
|
||||
painter.fillRect(minimumPosition, y, nominalLength, height,
|
||||
foregroundNominalColor);
|
||||
painter.fillRect(warningPosition, y, warningLength, height,
|
||||
foregroundWarningColor);
|
||||
painter.fillRect(errorPosition, y, peakPosition - errorPosition,
|
||||
height, foregroundErrorColor);
|
||||
painter.fillRect(peakPosition, y,
|
||||
maximumPosition - peakPosition, height,
|
||||
backgroundErrorColor);
|
||||
} else {
|
||||
qreal end = errorLength + warningLength + nominalLength;
|
||||
painter.fillRect(
|
||||
minimumPosition, y,
|
||||
end, height,
|
||||
QBrush(foregroundErrorColor));
|
||||
if (!clipping) {
|
||||
QTimer::singleShot(CLIP_FLASH_DURATION_MS, this,
|
||||
SLOT(ClipEnding()));
|
||||
clipping = true;
|
||||
}
|
||||
|
||||
int end = errorLength + warningLength + nominalLength;
|
||||
painter.fillRect(minimumPosition, y, end, height,
|
||||
QBrush(foregroundErrorColor));
|
||||
}
|
||||
|
||||
if (peakHoldPosition - 3 < minimumPosition) {
|
||||
// Peak-hold below minimum, no drawing.
|
||||
if (peakHoldPosition - 3 < minimumPosition)
|
||||
;// Peak-hold below minimum, no drawing.
|
||||
else if (peakHoldPosition < warningPosition)
|
||||
painter.fillRect(peakHoldPosition - 3, y, 3, height,
|
||||
foregroundNominalColor);
|
||||
else if (peakHoldPosition < errorPosition)
|
||||
painter.fillRect(peakHoldPosition - 3, y, 3, height,
|
||||
foregroundWarningColor);
|
||||
else
|
||||
painter.fillRect(peakHoldPosition - 3, y, 3, height,
|
||||
foregroundErrorColor);
|
||||
|
||||
} else if (peakHoldPosition < warningPosition) {
|
||||
painter.fillRect(
|
||||
peakHoldPosition - 3, y,
|
||||
3, height,
|
||||
foregroundNominalColor);
|
||||
if (magnitudePosition - 3 >= minimumPosition)
|
||||
painter.fillRect(magnitudePosition - 3, y, 3, height,
|
||||
magnitudeColor);
|
||||
}
|
||||
|
||||
} else if (peakHoldPosition < errorPosition) {
|
||||
painter.fillRect(
|
||||
peakHoldPosition - 3, y,
|
||||
3, height,
|
||||
foregroundWarningColor);
|
||||
void VolumeMeter::paintVMeter(QPainter &painter, int x, int y, int width,
|
||||
int height, float magnitude, float peak, float peakHold)
|
||||
{
|
||||
qreal scale = height / minimumLevel;
|
||||
|
||||
} else {
|
||||
painter.fillRect(
|
||||
peakHoldPosition - 3, y,
|
||||
3, height,
|
||||
foregroundErrorColor);
|
||||
QMutexLocker locker(&dataMutex);
|
||||
int minimumPosition = y + 0;
|
||||
int maximumPosition = y + height;
|
||||
int magnitudePosition = int(y + height - (magnitude * scale));
|
||||
int peakPosition = int(y + height - (peak * scale));
|
||||
int peakHoldPosition = int(y + height - (peakHold * scale));
|
||||
int warningPosition = int(y + height - (warningLevel * scale));
|
||||
int errorPosition = int(y + height - (errorLevel * scale));
|
||||
|
||||
int nominalLength = warningPosition - minimumPosition;
|
||||
int warningLength = errorPosition - warningPosition;
|
||||
int errorLength = maximumPosition - errorPosition;
|
||||
locker.unlock();
|
||||
|
||||
if (clipping) {
|
||||
peakPosition = maximumPosition;
|
||||
}
|
||||
|
||||
if (magnitudePosition - 3 < minimumPosition) {
|
||||
// Magnitude below minimum, no drawing.
|
||||
|
||||
} else if (magnitudePosition < warningPosition) {
|
||||
painter.fillRect(
|
||||
magnitudePosition - 3, y,
|
||||
3, height,
|
||||
magnitudeColor);
|
||||
|
||||
} else if (magnitudePosition < errorPosition) {
|
||||
painter.fillRect(
|
||||
magnitudePosition - 3, y,
|
||||
3, height,
|
||||
magnitudeColor);
|
||||
|
||||
if (peakPosition < minimumPosition) {
|
||||
painter.fillRect(x, minimumPosition, width, nominalLength,
|
||||
backgroundNominalColor);
|
||||
painter.fillRect(x, warningPosition, width, warningLength,
|
||||
backgroundWarningColor);
|
||||
painter.fillRect(x, errorPosition, width, errorLength,
|
||||
backgroundErrorColor);
|
||||
} else if (peakPosition < warningPosition) {
|
||||
painter.fillRect(x, minimumPosition, width, peakPosition -
|
||||
minimumPosition, foregroundNominalColor);
|
||||
painter.fillRect(x, peakPosition, width, warningPosition -
|
||||
peakPosition, backgroundNominalColor);
|
||||
painter.fillRect(x, warningPosition, width, warningLength,
|
||||
backgroundWarningColor);
|
||||
painter.fillRect(x, errorPosition, width, errorLength,
|
||||
backgroundErrorColor);
|
||||
} else if (peakPosition < errorPosition) {
|
||||
painter.fillRect(x,minimumPosition, width, nominalLength,
|
||||
foregroundNominalColor);
|
||||
painter.fillRect(x, warningPosition, width, peakPosition -
|
||||
warningPosition, foregroundWarningColor);
|
||||
painter.fillRect(x, peakPosition, width, errorPosition -
|
||||
peakPosition, backgroundWarningColor);
|
||||
painter.fillRect(x, errorPosition, width, errorLength,
|
||||
backgroundErrorColor);
|
||||
} else if (peakPosition < maximumPosition) {
|
||||
painter.fillRect(x, minimumPosition, width, nominalLength,
|
||||
foregroundNominalColor);
|
||||
painter.fillRect(x, warningPosition, width, warningLength,
|
||||
foregroundWarningColor);
|
||||
painter.fillRect(x, errorPosition, width, peakPosition -
|
||||
errorPosition, foregroundErrorColor);
|
||||
painter.fillRect(x, peakPosition, width, maximumPosition -
|
||||
peakPosition, backgroundErrorColor);
|
||||
} else {
|
||||
painter.fillRect(
|
||||
magnitudePosition - 3, y,
|
||||
3, height,
|
||||
magnitudeColor);
|
||||
if (!clipping) {
|
||||
QTimer::singleShot(CLIP_FLASH_DURATION_MS, this,
|
||||
SLOT(ClipEnding()));
|
||||
clipping = true;
|
||||
}
|
||||
|
||||
int end = errorLength + warningLength + nominalLength;
|
||||
painter.fillRect(x, minimumPosition, width, end,
|
||||
QBrush(foregroundErrorColor));
|
||||
}
|
||||
|
||||
if (peakHoldPosition - 3 < minimumPosition)
|
||||
;// Peak-hold below minimum, no drawing.
|
||||
else if (peakHoldPosition < warningPosition)
|
||||
painter.fillRect(x, peakHoldPosition - 3, width, 3,
|
||||
foregroundNominalColor);
|
||||
else if (peakHoldPosition < errorPosition)
|
||||
painter.fillRect(x, peakHoldPosition - 3, width, 3,
|
||||
foregroundWarningColor);
|
||||
else
|
||||
painter.fillRect(x, peakHoldPosition - 3, width, 3,
|
||||
foregroundErrorColor);
|
||||
|
||||
if (magnitudePosition - 3 >= minimumPosition)
|
||||
painter.fillRect(x, magnitudePosition - 3, width, 3,
|
||||
magnitudeColor);
|
||||
}
|
||||
|
||||
void VolumeMeter::paintEvent(QPaintEvent *event)
|
||||
{
|
||||
UNUSED_PARAMETER(event);
|
||||
|
||||
uint64_t ts = os_gettime_ns();
|
||||
qreal timeSinceLastRedraw = (ts - lastRedrawTime) * 0.000000001;
|
||||
|
||||
int width = size().width();
|
||||
int height = size().height();
|
||||
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 = QSize(width, 9);
|
||||
if (tickPaintCache == NULL ||
|
||||
QSize tickPaintCacheSize;
|
||||
if (vertical)
|
||||
tickPaintCacheSize = QSize(14, height);
|
||||
else
|
||||
tickPaintCacheSize = QSize(width, 9);
|
||||
if (tickPaintCache == nullptr ||
|
||||
tickPaintCache->size() != tickPaintCacheSize) {
|
||||
delete tickPaintCache;
|
||||
tickPaintCache = new QPixmap(tickPaintCacheSize);
|
||||
|
|
@ -817,30 +980,56 @@ void VolumeMeter::paintEvent(QPaintEvent *event)
|
|||
tickPaintCache->fill(clearColor);
|
||||
|
||||
QPainter tickPainter(tickPaintCache);
|
||||
paintTicks(tickPainter, 6, 0, tickPaintCacheSize.width() - 6,
|
||||
tickPaintCacheSize.height());
|
||||
if (vertical) {
|
||||
tickPainter.translate(0, height);
|
||||
tickPainter.scale(1, -1);
|
||||
paintVTicks(tickPainter, 0, 11,
|
||||
tickPaintCacheSize.height() - 11);
|
||||
} else {
|
||||
paintHTicks(tickPainter, 6, 0,
|
||||
tickPaintCacheSize.width() - 6,
|
||||
tickPaintCacheSize.height());
|
||||
}
|
||||
tickPainter.end();
|
||||
}
|
||||
|
||||
// Actual painting of the widget starts here.
|
||||
QPainter painter(this);
|
||||
painter.drawPixmap(0, height - 9, *tickPaintCache);
|
||||
if (vertical) {
|
||||
// Invert the Y axis to ease the math
|
||||
painter.translate(0, height);
|
||||
painter.scale(1, -1);
|
||||
painter.drawPixmap(displayNrAudioChannels * 4 - 1, 7,
|
||||
*tickPaintCache);
|
||||
} else {
|
||||
painter.drawPixmap(0, height - 9, *tickPaintCache);
|
||||
}
|
||||
|
||||
for (int channelNr = 0; channelNr < displayNrAudioChannels;
|
||||
channelNr++) {
|
||||
paintMeter(painter,
|
||||
5, channelNr * 4, width - 5, 3,
|
||||
displayMagnitude[channelNr], displayPeak[channelNr],
|
||||
displayPeakHold[channelNr]);
|
||||
if (vertical)
|
||||
paintVMeter(painter, channelNr * 4, 8, 3, height - 10,
|
||||
displayMagnitude[channelNr],
|
||||
displayPeak[channelNr],
|
||||
displayPeakHold[channelNr]);
|
||||
else
|
||||
paintHMeter(painter, 5, channelNr * 4, width - 5, 3,
|
||||
displayMagnitude[channelNr],
|
||||
displayPeak[channelNr],
|
||||
displayPeakHold[channelNr]);
|
||||
|
||||
if (!idle) {
|
||||
// By not drawing the input meter boxes the user can
|
||||
// see that the audio stream has been stopped, without
|
||||
// having too much visual impact.
|
||||
paintInputMeter(painter,
|
||||
0, channelNr * 4, 3, 3,
|
||||
displayInputPeakHold[channelNr]);
|
||||
}
|
||||
if (idle)
|
||||
continue;
|
||||
|
||||
// By not drawing the input meter boxes the user can
|
||||
// see that the audio stream has been stopped, without
|
||||
// having too much visual impact.
|
||||
if (vertical)
|
||||
paintInputMeter(painter, channelNr * 4, 3, 3, 3,
|
||||
displayInputPeakHold[channelNr]);
|
||||
else
|
||||
paintInputMeter(painter, 0, channelNr * 4, 3, 3,
|
||||
displayInputPeakHold[channelNr]);
|
||||
}
|
||||
|
||||
lastRedrawTime = ts;
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue