new synth: Xriek; UI fixups
parent
61db0ebd85
commit
3a054ff1a1
|
@ -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>();
|
||||
|
|
|
@ -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");
|
||||
}
|
||||
}
|
|
@ -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;
|
||||
};
|
||||
}
|
||||
|
|
@ -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);
|
||||
|
|
|
@ -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);
|
||||
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue