automatable param stuff

master
zetaPRIME 2022-03-24 02:27:04 -04:00
parent 853ba8a901
commit 779af6bdab
3 changed files with 122 additions and 0 deletions

22
xybrid/nodelib/param.cpp Normal file
View File

@ -0,0 +1,22 @@
#include "param.h"
using Xybrid::NodeLib::Param;
using namespace Xybrid::Data;
ParameterPort* Param::makePort(Data::Node* node, const QString& name) {
if (port) return port;
node->inputs.try_emplace(Port::Parameter);
auto& inp = node->inputs.find(Port::Parameter)->second;
uint8_t id = 0;
auto it = inp.begin();
while (it != inp.end() && it->first == id) { ++id; ++it; } // scan for first unused id
auto n = name;
if (n.isEmpty()) n = this->name.toLower();
port = static_cast<ParameterPort*>(node->addPort(Port::Input, Port::Parameter, id).get());
port->name = n;
return port;
}

96
xybrid/nodelib/param.h Normal file
View File

@ -0,0 +1,96 @@
#pragma once
#include <cmath>
#include <limits>
#include <QCborMap>
#include "data/node.h"
#include "data/porttypes.h"
//namespace Xybrid::Data { class ParameterPort; }
namespace Xybrid::NodeLib {
/// Plugin parameter with associated metadata and automation faculties
class Param {
//
Param() = default;
public:
class Reader {
friend class Param;
Param* p;
size_t pos = 0;
bool r;
Reader(Param* p, bool r) : p(p), r(r) { }
public:
double next() {
if (r) {
auto v = p->port->data[pos++];
if (!std::isnan(v)) {
if (p->min == -p->max) // "signed" logic if symmetrical around zero
v = std::clamp(v, -1.0, 1.0) * p->max;
else // extents 0.0 .. 1.0 scaled across parameter range
v = std::clamp(p->min + v * (p->max - p->min), p->min, p->max);
p->vt = v;
}
}
if (!std::isnan(p->vt)) return p->vt;
return p->value;
}
};
enum Flags : uint8_t {
ResetOnTick = 0b00000001,
};
QString name;
double min, max, def;
double value;
double vt = std::numeric_limits<double>::quiet_NaN();
Flags flags;
Data::ParameterPort* port = nullptr;
Param(const QString& name, double min, double max, double def, Flags flags = {}) : Param() {
this->name = name;
this->min = min;
this->max = max;
this->def = def;
this->value = def;
this->flags = flags;
}
Data::ParameterPort* makePort(Data::Node*, const QString& name = { });
Reader start() {
bool r = port && port->isConnected();
if (r) port->pull();
if (flags & ResetOnTick) vt = std::numeric_limits<double>::quiet_NaN();
return Reader(this, r);
}
inline QString saveName() {
return name.toLower().remove(' ');
}
inline void save(QCborMap& m, QString id = { }) {
if (id.isEmpty()) id = saveName();
m[id] = value;
}
inline void load(QCborMap& m, QString id = { }) {
if (id.isEmpty()) id = saveName();
value = m.value(id).toDouble(value);
vt = std::numeric_limits<double>::quiet_NaN();
}
// - give it a Reader abstraction (grab from a "startTick" thing, call its next() function for tracking and values)
// - functions to automatically save/load from QCborMap
// binding a knobgadget automatically populates range+defaults(+label)
// - figure out how to do range stuff
// - "signed" extents only if min == -max
// meta info for parameter ports?
};
}

View File

@ -43,6 +43,10 @@ namespace Xybrid::NodeLib {
};
// explicit instantiation declarations to eliminate warnings
//template<> void Xybrid::NodeLib::GenericSVFilter<Data::AudioFrame>::process(Data::AudioFrame, double, double, int);
//template<> void Xybrid::NodeLib::GenericSVFilter<double>::process(double, double, double, int);
/// 12db Chamberlin State Variable Filter
typedef GenericSVFilter<Data::AudioFrame> SVFilter;
/// 12db Chamberlin State Variable Filter (mono version)