While certainly not entirely complete yet, this implements most of the UI functionality of the graph editor.
148 lines
3.6 KiB
C++
148 lines
3.6 KiB
C++
#include "audiomonitor.h"
|
|
|
|
#include <QAudio>
|
|
#include <QDebug>
|
|
#include <QPainter>
|
|
|
|
#include "audio/audiomanager.h"
|
|
#include "common/qtversionabstraction.h"
|
|
|
|
const int kDecibelStep = 6;
|
|
const int kDecibelMinimum = -200;
|
|
const int kClearTimerInterval = 500;
|
|
|
|
AudioMonitor::AudioMonitor(QWidget *parent) :
|
|
QWidget(parent)
|
|
{
|
|
clear_timer_.setInterval(kClearTimerInterval);
|
|
|
|
connect(AudioManager::instance(), &AudioManager::SentSamples, this, &AudioMonitor::SetValues);
|
|
connect(&clear_timer_, &QTimer::timeout, this, &AudioMonitor::Clear);
|
|
connect(&clear_timer_, &QTimer::timeout, &clear_timer_, &QTimer::stop);
|
|
}
|
|
|
|
void AudioMonitor::SetValues(QVector<double> values)
|
|
{
|
|
values_ = values;
|
|
|
|
if (values_.size() != peaked_.size()) {
|
|
peaked_.resize(values_.size());
|
|
peaked_.fill(false);
|
|
}
|
|
|
|
clear_timer_.stop();
|
|
clear_timer_.start();
|
|
|
|
update();
|
|
}
|
|
|
|
void AudioMonitor::Clear()
|
|
{
|
|
values_.fill(0);
|
|
update();
|
|
}
|
|
|
|
void AudioMonitor::paintEvent(QPaintEvent *)
|
|
{
|
|
int channels = values_.size();
|
|
|
|
if (channels == 0) {
|
|
return;
|
|
}
|
|
|
|
QPainter p(this);
|
|
QFontMetrics fm = p.fontMetrics();
|
|
|
|
int peaks_y = 0;
|
|
int peaks_height = fm.height();
|
|
|
|
QRect db_labels_rect = rect();
|
|
db_labels_rect.setWidth(QFontMetricsWidth(p.fontMetrics(), "-00"));
|
|
db_labels_rect.adjust(0, peaks_height, 0, 0);
|
|
|
|
QRect full_meter_rect = rect();
|
|
full_meter_rect.adjust(db_labels_rect.width(), peaks_height, 0, 0);
|
|
|
|
// Draw decibel markings
|
|
QRect last_db_marking_rect;
|
|
|
|
for (int i=0;i>=kDecibelMinimum;i-=kDecibelStep) {
|
|
QString db_label;
|
|
|
|
if (i <= kDecibelMinimum) {
|
|
db_label = "-∞";
|
|
} else {
|
|
db_label = QStringLiteral("%1").arg(i);
|
|
}
|
|
|
|
qreal log_val = QAudio::convertVolume(i, QAudio::DecibelVolumeScale, QAudio::LogarithmicVolumeScale);
|
|
|
|
QRect db_marking_rect = db_labels_rect;
|
|
db_marking_rect.adjust(0, db_labels_rect.height() - qRound(log_val * db_labels_rect.height()), 0, 0);
|
|
db_marking_rect.setHeight(fm.height());
|
|
|
|
// Prevent any dB markings overlapping
|
|
if (i == 0 || !db_marking_rect.intersects(last_db_marking_rect)) {
|
|
p.drawText(db_marking_rect, Qt::AlignRight, db_label);
|
|
p.drawLine(db_marking_rect.topLeft(), db_marking_rect.topRight());
|
|
|
|
last_db_marking_rect = db_marking_rect;
|
|
}
|
|
}
|
|
|
|
QLinearGradient g(full_meter_rect.topLeft(), full_meter_rect.bottomLeft());
|
|
g.setStops({
|
|
QGradientStop(0.0, Qt::red),
|
|
QGradientStop(0.25, Qt::yellow),
|
|
QGradientStop(1.0, Qt::green)
|
|
});
|
|
|
|
int channel_width = full_meter_rect.width() / channels;
|
|
|
|
for (int i=0;i<channels;i++) {
|
|
int channel_x = full_meter_rect.x() + channel_width * i;
|
|
|
|
QRect peaks_rect(channel_x, peaks_y, channel_width, peaks_height);
|
|
|
|
QRect meter_rect = full_meter_rect;
|
|
meter_rect.setX(channel_x);
|
|
meter_rect.setWidth(channel_width);
|
|
|
|
p.setPen(Qt::black);
|
|
|
|
// Draw peak rects
|
|
p.setBrush(Qt::red);
|
|
p.drawRect(peaks_rect);
|
|
|
|
// Draw gradient meter
|
|
p.setBrush(g);
|
|
p.drawRect(meter_rect);
|
|
|
|
// Draw inverted semi-transparent black overlay depending on information
|
|
p.setPen(Qt::NoPen);
|
|
|
|
// Validate value and whether it peaked
|
|
double vol = values_.at(i);
|
|
if (vol > 1.0) {
|
|
peaked_[i] = true;
|
|
}
|
|
|
|
// Convert val to logarithmic scale
|
|
vol = QAudio::convertVolume(vol, QAudio::LinearVolumeScale, QAudio::LogarithmicVolumeScale);
|
|
|
|
p.setBrush(QColor(0, 0, 0, 128));
|
|
|
|
meter_rect.adjust(0, 0, 0, -qRound(meter_rect.height() * vol));
|
|
p.drawRect(meter_rect);
|
|
|
|
if (!peaked_.at(i))
|
|
p.drawRect(peaks_rect);
|
|
}
|
|
}
|
|
|
|
void AudioMonitor::mousePressEvent(QMouseEvent *)
|
|
{
|
|
peaked_.fill(false);
|
|
update();
|
|
}
|