1
0

Update STFX library

This commit is contained in:
Geraint 2024-02-29 14:08:17 +00:00
parent ba22c38a61
commit b79063c97b

View File

@ -10,8 +10,86 @@
#include <vector>
#include <string>
#include <cmath>
namespace stfx {
// Convenient units for range parameters - not really part of the main STFX API
namespace units {
static inline double dbToGain(double db) {
return std::pow(10, db*0.05);
}
static inline double gainToDb(double gain) {
return std::log10(std::max<double>(gain, 1e-10))*20;
}
static inline double dbToEnergy(double db) {
return std::pow(10, db*0.1);
}
static inline double energyToDb(double gain) {
return std::log10(std::max<double>(gain, 1e-10))*10;
}
static inline double pcToRatio(double percent) {
return percent*0.01;
}
static inline double ratioToPc(double linear) {
return linear*100;
}
static inline double kHzToHz(double kHz) {
return kHz*1000;
}
static inline double hzToKHz(double hz) {
return hz*0.001;
}
static inline double sToMs(double sec) {
return sec*1000;
}
static inline double msToS(double ms) {
return ms*0.001;
}
template<class RangeParam>
RangeParam & rangeGain(RangeParam &param) {
return param
.unit("dB", 1, dbToGain, gainToDb) // default display is dB
.unit("%", 0, pcToRatio, ratioToPc)
.exact(0, "off")
.unit("x"); // Allow things like "2x" (~6dB) for text-input
}
template<class RangeParam>
RangeParam & rangeHz(RangeParam &param) {
return param
.unit("Hz", 0, 10, 1000)
.unit("Hz", 1, 1, 10)
.unit("Hz", 2, 0, 1)
.unit("kHz", 1, kHzToHz, hzToKHz, 10000, 1e100)
.unit("kHz", 2, kHzToHz, hzToKHz, 1000, 10000);
}
template<class RangeParam>
RangeParam & rangePercent(RangeParam &param) {
return param
.unit("%", 0, pcToRatio, ratioToPc)
.unit("x");
}
template<class RangeParam>
RangeParam & rangeMs(RangeParam &param) {
return param
.unit("ms", 2, 0, 1)
.unit("ms", 1, 1, 10)
.unit("ms", 0, 10, 1000)
.unit("seconds", 2, sToMs, msToS, 0, 1)
.unit("seconds", 1, sToMs, msToS, 1, 10)
.unit("seconds", 0, sToMs, msToS, 10, 1e100);
}
template<class RangeParam>
RangeParam & rangeSec(RangeParam &param) {
return param
.unit("seconds", 2, 0, 1)
.unit("seconds", 1, 1, 10)
.unit("seconds", 0, 10, 1e100)
.unit("ms", 2, msToS, sToMs, 0, 0.001)
.unit("ms", 1, msToS, sToMs, 0.001, 0.01)
.unit("ms", 0, msToS, sToMs, 0.01, 1);
}
}
namespace {
// A value which changes linearly within a given index range (e.g. a block)
@ -20,7 +98,7 @@ namespace stfx {
public:
LinearSegment(double offset, double gradient) : offset(offset), gradient(gradient) {}
double at(int i) {
double at(double i) {
return offset + i*gradient;
}
bool changing() {
@ -119,7 +197,7 @@ namespace stfx {
}
struct DoNothingEvent {
int offset = 0;
void operator ()() {}
void operator()() {}
};
DoNothingEvent operator [](int) {
return DoNothingEvent();
@ -142,7 +220,7 @@ namespace stfx {
}
template<class EventList, class ...Others>
const Block & split(EventList &&list, Others &&...others) const {
return split(list).split(std::forward<Others>(others)...);
return split(list, list.size()).split(std::forward<Others>(others)...);
}
/// Base-case for templated recursion
const Block & split() const {
@ -170,7 +248,7 @@ namespace stfx {
current = v;
return *this;
}
// Return the
// Return the current fade
Value from() const {
return _from;
}
@ -178,6 +256,11 @@ namespace stfx {
return _to;
}
template<class Storage>
void state(Storage &storage) {
storage("value", current);
}
// Shuffle the internal values along to start a new fade, return whether it's actually changing
bool _libStartFade() {
_from = _to;
@ -220,7 +303,7 @@ namespace stfx {
return *this;
}
template<typename ...Args>
PInfoPlaceholder & names(Args ...) {
PInfoPlaceholder & label(Args ...) {
return *this;
}
};
@ -228,11 +311,14 @@ namespace stfx {
}
/// Base class for our effect to inherit from. Provides parameter classes and some default config.
template<typename SampleType>
class LibraryEffectBase {
protected:
using ParamRange = LibraryParam<double>;
using ParamSteps = LibraryParam<int>;
using ParamStepped = LibraryParam<int>;
public:
using Sample = SampleType;
ParamRange bpm{120};
double paramFadeMs() {
@ -250,10 +336,10 @@ namespace stfx {
};
/// Creates an effect class from an effect template, with optional extra config.
/// The effect template takes `EffectTemplate<Sample, BaseClass, ...ExtraConfig>`
/// The effect template takes `EffectTemplate<BaseClass, ...ExtraConfig>`
template<typename Sample, template <class, class...> class EffectTemplate, class ...ExtraConfig>
class LibraryEffect : public EffectTemplate<Sample, stfx::LibraryEffectBase, ExtraConfig...> {
using EffectClass = EffectTemplate<Sample, stfx::LibraryEffectBase, ExtraConfig...>;
class LibraryEffect : public EffectTemplate<stfx::LibraryEffectBase<Sample>, ExtraConfig...> {
using EffectClass = EffectTemplate<stfx::LibraryEffectBase<Sample>, ExtraConfig...>;
// This is passed to the effect's `.state()` method during initialisation, and collects pointers to the effect's parameters
class CollectParams {
@ -262,12 +348,12 @@ namespace stfx {
std::vector<LibraryParam<int> *> stepParams;
// Add registered parameters to the list
PInfoPlaceholder param(const char *, LibraryParam<double> &param, const char *codeExpr=nullptr) {
PInfoPlaceholder range(const char *, LibraryParam<double> &param, const char *codeExpr=nullptr) {
(void)codeExpr;
rangeParams.push_back(&param);
return {};
}
PInfoPlaceholder param(const char *, LibraryParam<int> &param, const char *codeExpr=nullptr) {
PInfoPlaceholder stepped(const char *, LibraryParam<int> &param, const char *codeExpr=nullptr) {
(void)codeExpr;
stepParams.push_back(&param);
return {};
@ -275,23 +361,28 @@ namespace stfx {
// The effect might ask us to store/fetch the serialisation version, we just echo it back
static int version(int v) {return v;}
// Ignore the UI/synchronisation stuff
static bool extra() {return false;}
static void invalidate(const char *) {}
// We ignore any basic type
void operator ()(bool) {}
void operator ()(int) {}
void operator ()(long) {}
void operator ()(double) {}
void operator ()(float) {}
void operator()(const char *, bool) {}
void operator()(const char *, int) {}
void operator()(const char *, long) {}
void operator()(const char *, double) {}
void operator()(const char *, float) {}
// And strings
void operator()(const char *, std::string &) {}
// Iterate through vectors
template<class Item>
void operator()(const char *label, std::vector<Item> &vector) {
for (auto &item : vector) (*this)(label, item);
}
// Assume all other arguments have a `.state()`, and recurse into it
template<class OtherObject>
void operator ()(OtherObject &obj) {
void operator()(const char *, OtherObject &obj) {
obj.state(*this);
}
// Drop all names/labels we're given
template<class V>
void operator ()(const char *, V &v) {
(*this)(v);
}
template<class Fn>
void group(const char *, Fn fn) {
fn(*this);
@ -317,7 +408,7 @@ namespace stfx {
std::vector<int> auxInputs, auxOutputs;
int maxBlockSize = 256;
bool operator ==(const Config &other) {
bool operator ==(const Config &other) const {
return sampleRate == other.sampleRate
&& inputChannels == other.inputChannels
&& outputChannels == other.outputChannels
@ -373,7 +464,7 @@ namespace stfx {
/// Wraps the common `process(float** inputs, float** outputs, int length)` call into the `.process(io, config, block)`.
/// It actually accepts any objects which support `inputs[channel][index]`, so you could write adapters for interleaved buffers etc.
template<class Inputs, class Outputs>
void process(Inputs inputs, Outputs outputs, int blockLength) {
void process(Inputs &&inputs, Outputs &&outputs, int blockLength) {
// How long should the parameter fade take?
double fadeSamples = EffectClass::paramFadeMs()*0.001*config.sampleRate;
// Fade position at the end of the block