/***
Olive - Non-Linear Video Editor
Copyright (C) 2019 Olive Team
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program. If not, see .
***/
#include "audiomonitor.h"
#include
#include
#include
#include "audio/audiomanager.h"
#include "common/qtutils.h"
OLIVE_NAMESPACE_ENTER
const int kDecibelStep = 6;
const int kDecibelMinimum = -200;
const int kMaximumSmoothness = 4;
AudioMonitor::AudioMonitor(QWidget *parent) :
QWidget(parent),
cached_channels_(0)
{
update_timer_.setInterval(1000 / 60); // Hardcoded to 60 FPS
connect(&update_timer_, &QTimer::timeout, this, static_cast(&AudioMonitor::update));
update_timer_.start();
values_.resize(kMaximumSmoothness);
connect(AudioManager::instance(), &AudioManager::OutputDeviceStarted, this, &AudioMonitor::OutputDeviceSet);
connect(AudioManager::instance(), &AudioManager::AudioParamsChanged, this, &AudioMonitor::SetParams);
connect(AudioManager::instance(), &AudioManager::Stopped, this, &AudioMonitor::Stop);
}
AudioMonitor::~AudioMonitor()
{
Stop();
}
void AudioMonitor::SetParams(const AudioRenderingParams ¶ms)
{
params_ = params;
for (int i=0;i values)
{
values_ = values;
if (values_.size() != peaked_.size()) {
peaked_.resize(values_.size());
peaked_.fill(false);
}
clear_timer_.stop();
clear_timer_.start();
update();
}
*/
void AudioMonitor::paintEvent(QPaintEvent *)
{
if (!params_.channel_count()) {
return;
}
QPainter p(this);
QFontMetrics fm = p.fontMetrics();
int peaks_y = 0;
int font_height = fm.height();
// Create rect where decibel markings will go on the side
QRect db_labels_rect = rect();
db_labels_rect.setWidth(QFontMetricsWidth(p.fontMetrics(), "-00"));
db_labels_rect.adjust(0, font_height, 0, 0);
// Determine rect where the main meter will go
QRect full_meter_rect = rect();
full_meter_rect.adjust(db_labels_rect.width(), font_height, 0, 0);
// Width of each channel in the meter
int channel_width = full_meter_rect.width() / params_.channel_count();
if (cached_background_.size() != size()
|| cached_channels_ != params_.channel_count()) {
cached_channels_ = params_.channel_count();
// Generate new background
cached_background_ = QPixmap(size());
cached_background_.fill(Qt::transparent);
QPainter cached_painter(&cached_background_);
{
// Draw decibel markings
QRect last_db_marking_rect;
cached_painter.setPen(palette().text().color());
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)) {
cached_painter.drawText(db_marking_rect, Qt::AlignRight, db_label);
cached_painter.drawLine(db_marking_rect.topLeft(), db_marking_rect.topRight());
last_db_marking_rect = db_marking_rect;
}
}
}
{
// Draw bars
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)
});
cached_painter.setPen(Qt::black);
for (int i=0;i v(params_.channel_count(), 0);
if (file_.isOpen()) {
UpdateValuesFromFile(v);
}
PushValue(v);
QVector vals = GetAverages();
p.setBrush(QColor(0, 0, 0, 128));
p.setPen(Qt::NoPen);
for (int i=0;i 1.0) {
peaked_[i] = true;
}
// Convert val to logarithmic scale
vol = QAudio::convertVolume(vol, QAudio::LinearVolumeScale, QAudio::LogarithmicVolumeScale);
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();
}
void AudioMonitor::UpdateValuesFromFile(QVector& v)
{
// Determines how many milliseconds have passed since last update
qint64 current_time = QDateTime::currentMSecsSinceEpoch();
qint64 time_passed = current_time - last_time_;
// Determine how many bytes this is
int bytes_to_read = params_.time_to_bytes(static_cast(time_passed) * 0.001);
QByteArray b = file_.read(bytes_to_read);
const float* samples = reinterpret_cast(b.constData());
int nb_samples = b.size() / sizeof(float);
for (int i=0;i v.at(channel)) {
v.replace(channel, abs_sample);
}
}
last_time_ = current_time;
}
void AudioMonitor::PushValue(const QVector &v)
{
values_.removeFirst();
values_.append(v);
}
QVector AudioMonitor::GetAverages() const
{
QVector v(params_.channel_count(), 0);
for (int i=0;i(values_.size());
}
return v;
}
OLIVE_NAMESPACE_EXIT