new synth: Xriek; UI fixups

portability/boost
zetaPRIME 2019-06-24 03:36:25 -04:00
parent 61db0ebd85
commit 3a054ff1a1
6 changed files with 195 additions and 13 deletions

View File

@ -16,6 +16,8 @@ using namespace Xybrid::Audio;
#include "ui/gadgets/knobgadget.h"
using namespace Xybrid::UI;
#include "util/strings.h"
#include <cmath>
#include <QDebug>
@ -23,8 +25,6 @@ using namespace Xybrid::UI;
#include <QCborValue>
#include <QCborArray>
#define qs QStringLiteral
namespace {
bool _ = PluginRegistry::enqueueRegistration([] {
auto i = std::make_shared<PluginInfo>();

View File

@ -0,0 +1,150 @@
#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/knobgadget.h"
using namespace Xybrid::UI;
#include "util/strings.h"
#include <cmath>
#include <QDebug>
#include <QCborMap>
#include <QCborValue>
#include <QCborArray>
namespace {
bool _ = PluginRegistry::enqueueRegistration([] {
auto i = std::make_shared<PluginInfo>();
i->id = "plug:xriek";
i->displayName = "Xriek";
i->category = "Instrument";
i->createInstance = []{ return std::make_shared<Xriek>(); };
PluginRegistry::registerPlugin(i);
});
[[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() {
qreal xfirst = 6;
qreal line = 38;
obj->setGadgetSize((xfirst*2)+(line*1)+32, 32+32);
{
auto k = new KnobGadget(obj->contents);
k->setPos(xfirst, 16);
k->min = 0.0;
k->max = 5.0;
k->step = .01;
k->bind(drive);
k->setLabel("Drive");
}
{
auto k = new KnobGadget(obj->contents);
k->setPos(xfirst+line*1, 16);
k->min = 0.0;
k->max = 5.0;
k->step = .01;
k->bind(saturation);
k->setLabel("Saturate");
}
}

View File

@ -0,0 +1,28 @@
#pragma once
#include "nodelib/instrumentcore.h"
namespace Xybrid::Instruments {
class Xriek : public Data::Node {
NodeLib::InstrumentCore core;
NodeLib::ADSR adsr;
double drive = 0.0;
double saturation = 0.0;
public:
Xriek();
~Xriek() override = default;
void init() override;
void reset() override;
void release() override;
void process() override;
void saveData(QCborMap&) const override;
void loadData(const QCborMap&) override;
void onGadgetCreated() override;
};
}

View File

@ -178,8 +178,7 @@ void BeatPad::initUI(NodeUIScene* scene) {
//UIState() { qDebug() << "constructing ui"; }
//~UIState() { qDebug() << "deconstructing ui"; }
};
auto state = std::make_shared<UIState>();
auto _state = state.get(); // non-owning pointer for use inside state
auto state = scene->makeStateObject<UIState>();
// set up gadgets
auto noteSelector = new SelectorGadget();
@ -222,18 +221,18 @@ void BeatPad::initUI(NodeUIScene* scene) {
// create functions now that all UI elements exist to be referenced
state->selectNote = [=](int16_t n) {
if (auto f = cfg.find(n); f != cfg.end()) _state->cfg = f->second;
else _state->cfg = std::make_shared<NoteConfig>();
_state->note = n;
auto smp = _state->cfg->smp.lock();
if (auto f = cfg.find(n); f != cfg.end()) state->cfg = f->second;
else state->cfg = std::make_shared<NoteConfig>();
state->note = n;
auto smp = state->cfg->smp.lock();
sampleSelector->setSample(smp);
noteSelector->setEntry({n, qs("%1 %2").arg(Util::noteName(n)).arg(smp ? smp->name : "")}, false);
};
state->setSample = [=](std::shared_ptr<Sample> smp) {
_state->cfg->smp = smp;
auto n = _state->note;
if (smp) cfg[n] = _state->cfg;
state->cfg->smp = smp;
auto n = state->note;
if (smp) cfg[n] = state->cfg;
else if (auto f = cfg.find(n); f != cfg.end()) cfg.erase(f);
noteSelector->setEntry({n, qs("%1 %2").arg(Util::noteName(n)).arg(smp ? smp->name : "")}, false);

View File

@ -26,6 +26,8 @@ namespace Xybrid::UI {
//QShortcut* shortcut(QKeySequence);
std::shared_ptr<void> stateObject;
public:
bool autoCenter = true;
@ -41,6 +43,9 @@ namespace Xybrid::UI {
void keyPressEvent(QKeyEvent*) override;
void keyReleaseEvent(QKeyEvent*) override;
// default constructs an object of given type and manages its lifetime
template<typename T> inline T* makeStateObject() { auto o = std::make_shared<T>(); stateObject = o; return o.get(); }
signals:
void notePreview(int16_t);

View File

@ -67,7 +67,7 @@ PatternEditorModel::PatternEditorModel(QObject *parent)
int PatternEditorModel::rowCount(const QModelIndex & /*parent*/) const {
//if (pattern->channels.size() == 0) return 1;
return pattern->rows + 2; // not sure why two are required here, but...?
return pattern->rows + 1;
}
int PatternEditorModel::columnCount(const QModelIndex & /*parent*/) const {
@ -225,7 +225,7 @@ void PatternEditorModel::updateFold() {
int rows = this->rowCount();
for (int i = 0; i < rows; i++) {
view->setRowHidden(i, false); // dispel any "phantoms" we might end up having
view->setRowHidden(i, (i % ifold != 0));
view->setRowHidden(i, (i < pattern->rows && i % ifold != 0));
}
view->setUpdatesEnabled(true);
}