177 lines
5.4 KiB
C++
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)); });
|
|
}
|