xybrid/xybrid/nodes/gadget/mixboard.cpp

177 lines
5.4 KiB
C++

#include "mixboard.h"
using Xybrid::Gadgets::MixBoard;
using namespace Xybrid::Data;
#include "util/strings.h"
#include "nodelib/basics.h"
using namespace Xybrid::NodeLib;
#include "data/audioframe.h"
#include "data/porttypes.h"
#include "config/pluginregistry.h"
using namespace Xybrid::Config;
#include "audio/audioengine.h"
using namespace Xybrid::Audio;
#include "ui/patchboard/nodeobject.h"
#include "ui/gadgets/layoutgadget.h"
#include "ui/gadgets/buttongadget.h"
#include "ui/gadgets/togglegadget.h"
#include "ui/gadgets/knobgadget.h"
using namespace Xybrid::UI;
#include <cmath>
#include <QPen>
#include <QCborMap>
#include <QCborArray>
// clazy:excludeall=non-pod-global-static
RegisterPlugin(MixBoard, {
i->id = "gadget:mixboard";
i->displayName = "Mixer Board";
i->category = "Gadget";
})
MixBoard::MixBoard() {
}
void MixBoard::init() {
addPort(Port::Output, Port::Audio, 0);
if (sections.empty()) insertSection(0);
}
void MixBoard::process() {
auto out = std::static_pointer_cast<AudioPort>(port(Port::Output, Port::Audio, 0));
out->pull();
bool solo = false;
for (auto& s : sections) if (s.solo) { solo = true; break; }
size_t ts = audioEngine->curTickSize();
for (size_t i = 0; i < sections.size(); i++) {
auto& s = sections[i];
if (s.mute || (solo && !s.solo)) continue; // muted
auto m = AudioFrame::gainBalanceMult(s.gain);
auto in = std::static_pointer_cast<AudioPort>(port(Port::Input, Port::Audio, static_cast<uint8_t>(i)));
in->pull();
for (size_t f = 0; f < ts; f++) (*out)[f] += (*in)[f] * m;
}
}
void MixBoard::saveData(QCborMap& m) const {
QCborArray sa;
for (auto& sec : sections) {
QCborMap s;
s[qs("gain")] = QCborValue(sec.gain);
s[qs("mute")] = sec.mute;
s[qs("solo")] = sec.solo;
sa << s;
}
m[qs("sections")] = sa;
}
void MixBoard::loadData(const QCborMap& m) {
auto sa = m.value("sections").toArray();
sections.clear();
sections.resize(static_cast<size_t>(sa.size()));
for (size_t i = 0; i < sections.size(); i++) {
addPort(Port::Input, Port::Audio, static_cast<uint8_t>(i), false);
auto& s = sections[i];
auto sm = sa[static_cast<int>(i)].toMap();
s.gain = sm.value("gain").toDouble(s.gain);
s.mute = sm.value("mute").toBool(s.mute);
s.solo = sm.value("solo").toBool(s.solo);
}
if (obj) {
obj->updatePorts();
onGadgetCreated();
emit obj->finalized();
}
}
void MixBoard::insertSection(uint8_t p) {
p = std::min(p, static_cast<uint8_t>(sections.size()));
sections.emplace(sections.begin()+p);
for (int16_t ii = 254; ii >= p; ii--) {
auto i = static_cast<uint8_t>(ii);
movePort(Port::Input, Port::Audio, i, i+1, false);
}
addPort(Port::Input, Port::Audio, p, false);
if (obj) {
obj->updatePorts();
onGadgetCreated();
emit obj->finalized();
}
}
void MixBoard::removeSection(uint8_t p) {
if (p >= sections.size()) return;
sections.erase(sections.begin()+p);
removePort(Port::Input, Port::Audio, p, false);
for (int16_t ii = p+1; ii < 256; ii++) {
auto i = static_cast<uint8_t>(ii);
movePort(Port::Input, Port::Audio, i, i-1, false);
}
if (obj) {
obj->updatePorts();
onGadgetCreated();
emit obj->finalized();
}
}
void MixBoard::onGadgetCreated() {
if (!obj) return;
{
auto k = new Gadget();
auto ch = obj->contents->childItems(); // avoid detach warnings
for (auto c : ch) c->setParentItem(k);
k->deleteLater();
}
qDeleteAll(obj->contents->childItems()); // clear out anything already there
auto l = new LayoutGadget(obj, true);
QObject::connect(obj, &NodeObject::postGeometryUpdate, l, [this] {
obj->inputPortContainer->setY(obj->contents->y());
auto c = static_cast<QGraphicsLineItem*>(obj->inputPortContainer.get());
c->setPen(Qt::NoPen); // hide line
});
auto c = obj->inputPortContainer->childItems();
auto count = sections.size();
for (size_t i = 0; i < count; i++) {
constexpr qreal spc = 7;
auto ln = (new LayoutGadget(l))->setMetrics(3);
auto tl = (new LayoutGadget(ln, true))->setMetrics(-1, spc);
/*auto mute = */(new ToggleGadget(tl))->bind(sections[i].mute)->setColor({255, 0, 0})->setToolTip("Mute", {-1, 0});
/*auto solo = */(new ToggleGadget(tl))->bind(sections[i].solo)->setColor({191, 191, 0})->setToolTip("Solo", {-1, 0});
/*auto gain = */KnobGadget::autoGain(ln, sections[i].gain);
auto end = (new LayoutGadget(ln, true))->setMetrics(-1, spc);
auto bIns = (new ButtonGadget(end))->setSize(16, 16)->setText("+");
auto bDel = (new ButtonGadget(end))->setSize(16, 16)->setText("-");
QObject::connect(bIns, &ButtonGadget::clicked, obj, [this, i] { insertSection(static_cast<uint8_t>(i)); });
QObject::connect(bDel, &ButtonGadget::clicked, obj, [this, i] { removeSection(static_cast<uint8_t>(i)); });
if (count <= 1) delete bDel; // no dropping to zero
}
l->updateGeometry();
auto lc = l->childItems();
for (auto i = 0; i < static_cast<int>(count); i++) c[i]->setY(lc[i]->y() + lc[i]->boundingRect().center().y());
auto btn = (new ButtonGadget(l))->setSize(16, 16)->setText("+");
QObject::connect(btn, &ButtonGadget::clicked, obj, [this, count] { insertSection(static_cast<uint8_t>(count)); });
}