quicklevel polish

master
zetaPRIME 2021-11-11 14:26:48 -05:00
parent b86452b1af
commit 25408ba776
3 changed files with 90 additions and 18 deletions

3
notes
View File

@ -32,7 +32,8 @@ parameters {
TODO {
immediate frontburner {
???
standardized "draw panel" function in nodeobject
^ use in both default draw and quicklevel
distortion effect
single-selection sampler

View File

@ -2,6 +2,9 @@
using Xybrid::Gadgets::QuickLevel;
using namespace Xybrid::Data;
#include <cmath>
#include <iostream>
#include <QPainter>
#include <QGraphicsScene>
#include <QStyleOptionGraphicsItem>
@ -28,7 +31,6 @@ namespace {
i->category = "Gadget";
i->createInstance = []{ return std::make_shared<QuickLevel>(); };
PluginRegistry::registerPlugin(i);
//inf = i;
});
}
@ -40,6 +42,29 @@ void QuickLevel::init() {
auto in = addPort(Port::Input, Port::Audio, 0);
auto out = addPort(Port::Output, Port::Audio, 0);
out->passthroughTo = in;
lv[0][0] = 0;
lv[0][1] = 0;
lv[1][0] = 0;
lv[1][1] = 0;
}
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[0][0] = 0;
lv[0][1] = 0;
lv[1][0] = 0;
lv[1][1] = 0;
if (obj) obj->scene()->update();
}
void QuickLevel::process() {
@ -52,15 +77,29 @@ void QuickLevel::process() {
lv[c][1] = std::numeric_limits<double>::min();
}
for (size_t s = 0; s < ts; s++) {
AudioFrame f = (*in)[s];
// 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);
lv[0][0] = std::min(lv[0][0], f.l);
lv[0][1] = std::max(lv[0][1], f.l);
lv[1][0] = std::min(lv[1][0], f.r);
lv[1][1] = std::max(lv[1][1], f.r);
}
//if (obj) obj->update(obj->boundingRect());
if (obj) obj->scene()->update();
}
// clear levels on port disconnect
void QuickLevel::onPortDisconnected(Data::Port::Type, Data::Port::DataType, uint8_t, std::weak_ptr<Data::Port>) {
lv[0][0] = 0;
lv[0][1] = 0;
lv[1][0] = 0;
lv[1][1] = 0;
if (obj) obj->scene()->update();
}
@ -68,17 +107,30 @@ void QuickLevel::onGadgetCreated() {
if (!obj) return;
obj->customChrome = true;
//obj->autoPositionPorts = false;
//qreal ps = (PortObject::portSize + PortObject::portSpacing) * 2;
obj->setGadgetSize(QPointF(48, 95));
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) {
// first, draw backing panel
QColor outline = QColor(31, 31, 31);
if (opt->state & QStyle::State_Selected) outline = QColor(127, 127, 255);
auto r = obj->boundingRect();
//auto rf = r - QMarginsF(1, 1, 1, 1);
QLinearGradient fill(QPointF(0, 0), QPointF(0, r.height()));
fill.setColorAt(0, QColor(95, 95, 95));
@ -89,36 +141,41 @@ void QuickLevel::drawCustomChrome(QPainter* painter, const QStyleOptionGraphicsI
painter->setRenderHint(QPainter::RenderHint::Antialiasing);
painter->setBrush(QBrush(fill));
painter->setPen(QPen(QBrush(outline), 2));
painter->drawRoundedRect(r, 8, 8);
painter->drawRoundedRect(r, 4, 4);
// set up bar geometry
QSizeF barSize(16, 81);
//QRectF barL(r.topLeft() + QPointF(1, 1), barSize);
QRectF barL(QPointF(), barSize);
QRectF barR(QPointF(), barSize);
barL.moveCenter(r.center() + QPointF(-10, 0));
barR.moveCenter(r.center() + QPointF(10, 0));
barL.moveCenter(r.center() + QPointF(-9.5, 0));
barR.moveCenter(r.center() + QPointF(9.5, 0));
double oh = barSize.height() / 2;
painter->setPen(Qt::NoPen);
// 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, std::clamp(lv[i][1], -1.0, 1.0) * oh, 0, std::clamp(lv[i][0], -1.0, 1.0) * oh);
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));
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);
}
}

View File

@ -1,18 +1,32 @@
#pragma once
#include <QContiguousCache>
#include "data/node.h"
#include "data/audioframe.h"
namespace Xybrid::Gadgets {
class QuickLevel : public Data::Node {
QContiguousCache<Data::AudioFrame> buf;
std::array<std::array<double, 2>, 2> lv;
public:
// time across which the displayed levels are calculated
static const constexpr double SPAN_TIME = 1.0/30;
static const constexpr double DISPLAY_EXPONENT = 1;
QuickLevel();
~QuickLevel() = default;
~QuickLevel() override = default;
void init() override;
void reset() override;
void release() override;
void process() override;
void onPortDisconnected(Data::Port::Type, Data::Port::DataType, uint8_t, std::weak_ptr<Data::Port>) override;
void onGadgetCreated() override;
void drawCustomChrome(QPainter*, const QStyleOptionGraphicsItem*) override;
};
}