xybrid/xybrid/nodes/instrument/xriek.cpp

151 lines
3.8 KiB
C++

#include "xriek.h"
using Xybrid::Instruments::Xriek;
using namespace Xybrid::NodeLib;
using Note = InstrumentCore::Note;
using namespace Xybrid::Data;
#include "nodelib/commandreader.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/knobgadget.h"
using namespace Xybrid::UI;
#include "util/strings.h"
#include <cmath>
#include <QDebug>
#include <QCborMap>
#include <QCborValue>
#include <QCborArray>
// clazy:excludeall=non-pod-global-static
RegisterPlugin(Xriek, {
i->id = "plug:xriek";
i->displayName = "Xriek";
i->category = "Instrument";
})
namespace {
[[maybe_unused]] inline double wrap(double d) {
while (true) {
if (d > 1.0) d = (d - 2.0) * -1; //d-=2.0;
else if (d < -1.0) d = (d + 2.0) * -1;
else return d;
}
}
[[maybe_unused]] inline double lerp(double p, double a, double b) {
return b * p + a * (1.0 - p);
}
}
Xriek::Xriek() { }
void Xriek::init() {
addPort(Port::Input, Port::Command, 0);
addPort(Port::Output, Port::Audio, 0);
core.onNoteOn = [this](Note& note) {
//qDebug() << "note on";
note.adsr = adsr.normalized();
};
core.globalParam['Q'] = [](const ParamReader& pr) {
qDebug() << "global recieved" << pr.param() << pr.val();
return true;
};
core.processNote = [this](Note& note, AudioPort* p) {
double freq;
double smpTime = core.sampleTime();
size_t ts = p->size;
for (size_t i = 0; i < ts; i++) {
core.advanceNote(note);
double n = note.effectiveNote();
freq = 440.0 * std::pow(SEMI, n - (45+12*2));
auto si = note.scratch[0];
note.scratch[0] += smpTime * freq;
double o = 0;
double op = 0;
double drv = 1.0+drive;
for (int i = 1; i <= 5; i++) {
double p = 1.0/i;
o += wrap(std::sin(si*PI*i) * drv) * p;
op += p;
}
o = wrap((o/op) * drv);
o = std::pow(o, 1.0/(1.0+saturation));
//o = note.scratch[1] + (smpTime / 1.0/20000) * (note.scratch[1] - o); // simple low pass
note.scratch[3] += /*(smpTime / (1.0/20000.0))*/0.5 * (o - note.scratch[3]); // simple low pass;
if (note.scratch[3] != note.scratch[3]) note.scratch[3] = 0; // nan!? WHY
//qDebug() << note.scratch[3];
//if (note.id % 2 != 0) o *= -1;
AudioFrame out = note.scratch[3];
(*p)[i] += out.gainBalance(0, note.pan) * note.ampMult();
}
};
}
void Xriek::reset() { core.reset(); }
void Xriek::release() { core.release(); }
void Xriek::process() { core.process(this); }
void Xriek::saveData(QCborMap& m) const {
m[qs("adsr")] = adsr;
m[qs("drive")] = drive;
m[qs("saturation")] = saturation;
//
}
void Xriek::loadData(const QCborMap& m) {
adsr = m.value("adsr");
drive = m.value("drive").toDouble(drive);
saturation = m.value("saturation").toDouble(saturation);
//
}
void Xriek::onGadgetCreated() {
auto l = new LayoutGadget(obj);
{
auto k = new KnobGadget(l);
k->min = 0.0;
k->max = 5.0;
k->step = .01;
k->bind(drive);
k->setLabel("Drive");
k->setTextFunc(KnobGadget::textPercent);
}
{
auto k = new KnobGadget(l);
k->min = 0.0;
k->max = 5.0;
k->step = .01;
k->bind(saturation);
k->setLabel("Saturate");
k->setTextFunc(KnobGadget::textPercent);
}
l->addSpacer();
KnobGadget::autoCreate(l, adsr);
}