161 lines
4.5 KiB
C++
161 lines
4.5 KiB
C++
#include "quicklevel.h"
|
|
using Xybrid::Gadgets::QuickLevel;
|
|
using namespace Xybrid::Data;
|
|
|
|
#include <cmath>
|
|
#include <iostream>
|
|
|
|
#include <QPainter>
|
|
#include <QGraphicsScene>
|
|
#include <QStyleOptionGraphicsItem>
|
|
|
|
#include "util/strings.h"
|
|
|
|
#include "config/pluginregistry.h"
|
|
using namespace Xybrid::Config;
|
|
|
|
#include "audio/audioengine.h"
|
|
using namespace Xybrid::Audio;
|
|
|
|
#include "ui/patchboard/nodeobject.h"
|
|
using namespace Xybrid::UI;
|
|
|
|
#include "data/audioframe.h"
|
|
#include "data/porttypes.h"
|
|
|
|
// clazy:excludeall=non-pod-global-static
|
|
RegisterPlugin(QuickLevel, {
|
|
i->id = "gadget:quicklevel";
|
|
i->displayName = "Quick Level";
|
|
i->category = "Gadget";
|
|
})
|
|
|
|
QuickLevel::QuickLevel() {
|
|
|
|
}
|
|
|
|
void QuickLevel::init() {
|
|
auto in = addPort(Port::Input, Port::Audio, 0);
|
|
auto out = addPort(Port::Output, Port::Audio, 0);
|
|
out->passthroughTo = in;
|
|
|
|
lv = { };
|
|
}
|
|
|
|
void QuickLevel::reset() {
|
|
release();
|
|
auto sr = audioEngine->curSampleRate();
|
|
buf.setCapacity(static_cast<int>(sr * SPAN_TIME)); // fixed timestep
|
|
}
|
|
|
|
void QuickLevel::release() {
|
|
buf.clear();
|
|
buf.setCapacity(0);
|
|
|
|
lv = { };
|
|
|
|
if (obj) QMetaObject::invokeMethod(obj->scene(), "update", Qt::QueuedConnection);
|
|
}
|
|
|
|
void QuickLevel::process() {
|
|
if (!obj) return;
|
|
auto in = std::static_pointer_cast<AudioPort>(port(Port::Input, Port::Audio, 0));
|
|
in->pull();
|
|
size_t ts = audioEngine->curTickSize();
|
|
|
|
decltype(lv) clv; // compute on local copy to avoid artifacting on draw thread
|
|
for (size_t c = 0; c < 2; c++) {
|
|
clv[c][0] = std::numeric_limits<double>::max();
|
|
clv[c][1] = std::numeric_limits<double>::min();
|
|
}
|
|
|
|
// push entire tick; we don't need to clear anything as the buffer's maximum size is exactly how much we want
|
|
for (size_t s = 0; s < ts; s++) buf.append(static_cast<AudioFrame>((*in)[s]));
|
|
|
|
if (!buf.areIndexesValid()) buf.normalizeIndexes();
|
|
auto fst = buf.firstIndex(), lst = buf.lastIndex();
|
|
for (int i = fst; i <= lst; i++) {
|
|
auto f = buf.at(i);
|
|
clv[0][0] = std::min(clv[0][0], f.l);
|
|
clv[0][1] = std::max(clv[0][1], f.l);
|
|
clv[1][0] = std::min(clv[1][0], f.r);
|
|
clv[1][1] = std::max(clv[1][1], f.r);
|
|
}
|
|
|
|
lv = clv;
|
|
|
|
if (obj) QMetaObject::invokeMethod(obj, [obj = obj] {
|
|
obj->scene()->update(obj->sceneBoundingRect());
|
|
}, Qt::QueuedConnection);
|
|
}
|
|
|
|
// clear levels on port disconnect
|
|
void QuickLevel::onPortDisconnected(Data::Port::Type, Data::Port::DataType, uint8_t, std::weak_ptr<Data::Port>) {
|
|
lv = { };
|
|
|
|
if (obj) QMetaObject::invokeMethod(obj->scene(), "update", Qt::QueuedConnection);
|
|
}
|
|
|
|
void QuickLevel::onGadgetCreated() {
|
|
if (!obj) return;
|
|
|
|
obj->customChrome = true;
|
|
obj->autoPositionPorts = false;
|
|
obj->setGadgetSize(QPointF(43, 89));
|
|
|
|
auto r = obj->boundingRect();
|
|
auto pm = PortObject::portSize * .5 + PortObject::portSpacing;
|
|
auto offs = QPointF(r.width() / 2 + pm, 0);
|
|
obj->inputPortContainer->setPos(r.center() - offs);
|
|
obj->outputPortContainer->setPos(r.center() + offs);
|
|
}
|
|
|
|
namespace {
|
|
inline constexpr double dval(double in) {
|
|
in = std::clamp(in, -1.0, 1.0);
|
|
double n = in < 0.0 ? -1.0 : 1.0;
|
|
return std::pow(std::abs(in), QuickLevel::DISPLAY_EXPONENT) * n;
|
|
}
|
|
}
|
|
|
|
void QuickLevel::drawCustomChrome(QPainter* painter, const QStyleOptionGraphicsItem* opt) {
|
|
auto r = obj->boundingRect();
|
|
NodeObject::drawPanel(painter, opt, r, 4);
|
|
|
|
// set up bar geometry
|
|
QSizeF barSize(16, 81);
|
|
QRectF barL(QPointF(), barSize);
|
|
QRectF barR(QPointF(), barSize);
|
|
barL.moveCenter(r.center() + QPointF(-9.5, 0));
|
|
barR.moveCenter(r.center() + QPointF(9.5, 0));
|
|
|
|
double oh = barSize.height() / 2;
|
|
|
|
// draw bars
|
|
for (uint8_t i = 0; i < 2; i++) {
|
|
auto b = i == 0 ? barL : barR;
|
|
|
|
// bg
|
|
painter->setPen(Qt::NoPen);
|
|
painter->setBrush(QColor(0, 0, 0, 127));
|
|
painter->drawRect(b);
|
|
|
|
// level
|
|
QRectF bc(QPoint(), QSizeF(b.width(), 0));
|
|
bc.moveCenter(b.center());
|
|
bc.adjust(0, dval(-lv[i][1]) * oh, 0, dval(-lv[i][0]) * oh);
|
|
|
|
if (std::abs(lv[i][0]) > 1.0 || std::abs(lv[i][1]) > 1.0) painter->setBrush(QColor(255, 255, 63));
|
|
else painter->setBrush(QColor(63, 255, 63));
|
|
painter->drawRect(bc);
|
|
|
|
// center tick
|
|
QRectF ln(QPoint(), QSizeF(b.width(), 1));
|
|
ln.moveCenter(b.center());
|
|
painter->setBrush(QColor(0, 0, 0, 127));
|
|
painter->drawRect(ln.adjusted(0, -1, 0, 1));
|
|
painter->setBrush(QColor(255, 255, 255, 191));
|
|
painter->drawRect(ln);
|
|
}
|
|
}
|