Common STFX storage class
This commit is contained in:
parent
97efa774aa
commit
4e646da32b
5
crunch.h
5
crunch.h
@ -28,13 +28,14 @@ struct CrunchSTFX : public BaseEffect {
|
||||
static constexpr int oversampleHalfLatency = 16;
|
||||
static constexpr Sample autoGainLevel = 0.1;
|
||||
|
||||
ParamRange drive{4};
|
||||
const bool autoGain;
|
||||
|
||||
ParamRange drive{stfx::units::dbToGain(autoGain ? 24 : 12)};
|
||||
ParamRange fuzz{0};
|
||||
ParamRange toneHz{2000};
|
||||
ParamRange cutHz{50};
|
||||
ParamRange outGain{1};
|
||||
|
||||
const bool autoGain;
|
||||
CrunchSTFX(bool autoGain=true) : autoGain(autoGain) {}
|
||||
|
||||
template<class Storage>
|
||||
|
||||
@ -8,7 +8,7 @@
|
||||
#include <optional>
|
||||
|
||||
#include "../param-info.h"
|
||||
#include "../storage/storage.h"
|
||||
#include "../storage/stfx-storage.h"
|
||||
#include "../ui/web-ui.h"
|
||||
|
||||
namespace stfx { namespace clap {
|
||||
@ -357,27 +357,12 @@ plugin.host->request_callback(plugin.host);
|
||||
}
|
||||
};
|
||||
std::vector<Param> params;
|
||||
struct ParamScanner {
|
||||
struct ParamScanner : public storage::STFXStorageScanner<ParamScanner> {
|
||||
std::vector<Param> ¶ms;
|
||||
Crc32 crc;
|
||||
|
||||
// Do nothing for most types
|
||||
template<class V>
|
||||
void operator()(const char *key, V &v) {
|
||||
LOG_EXPR(key);
|
||||
if constexpr (std::is_void_v<decltype(v.state(*this))>) {
|
||||
LOG_EXPR("recursing");
|
||||
ParamScanner recurse{*this};
|
||||
recurse.crc.addString(key, true);
|
||||
v.state(recurse);
|
||||
}
|
||||
}
|
||||
ParamScanner(std::vector<Param> ¶ms) : params(params) {}
|
||||
|
||||
// Extra methods we add for STFX storage
|
||||
void info(const char *, const char *) {}
|
||||
int version(int v) {
|
||||
return v;
|
||||
}
|
||||
template<class PR>
|
||||
RangeParamInfo & range(const char *key, PR ¶m) {
|
||||
param.context.index = params.size();
|
||||
@ -396,12 +381,6 @@ plugin.host->request_callback(plugin.host);
|
||||
entry.steppedParam = ¶m;
|
||||
return entry.steppedInfo.emplace(param);
|
||||
}
|
||||
template<class V>
|
||||
bool changed(const char *key, V &v) {
|
||||
(*this)(key, v);
|
||||
return false;
|
||||
}
|
||||
void invalidate(const char *) {}
|
||||
};
|
||||
void scanParams() {
|
||||
params.resize(0);
|
||||
@ -558,14 +537,9 @@ plugin.host->request_callback(plugin.host);
|
||||
}
|
||||
}
|
||||
|
||||
struct StateWriter : public stfx::storage::StorageCborWriter<true, StateWriter> {
|
||||
using stfx::storage::StorageCborWriter<true, StateWriter>::StorageCborWriter;
|
||||
struct StateWriter : public stfx::storage::STFXStorageWriter<StateWriter> {
|
||||
using stfx::storage::STFXStorageWriter<StateWriter>::STFXStorageWriter;
|
||||
|
||||
void info(const char *, const char *) {}
|
||||
int version(int v) {
|
||||
(*this)("version", v);
|
||||
return v;
|
||||
}
|
||||
template<class RP>
|
||||
RangeParamIgnore range(const char *key, RP ¶m) {
|
||||
double v = param;
|
||||
@ -578,21 +552,10 @@ plugin.host->request_callback(plugin.host);
|
||||
(*this)(key, v);
|
||||
return {};
|
||||
}
|
||||
template<class V>
|
||||
bool changed(const char *key, V &v) {
|
||||
(*this)(key, v);
|
||||
return false;
|
||||
}
|
||||
void invalidate(const char *) {}
|
||||
};
|
||||
struct StateReader : public stfx::storage::StorageCborReader<true, StateReader> {
|
||||
using stfx::storage::StorageCborReader<true, StateReader>::StorageCborReader;
|
||||
struct StateReader : public stfx::storage::STFXStorageReader<StateReader> {
|
||||
using stfx::storage::STFXStorageReader<StateReader>::STFXStorageReader;
|
||||
|
||||
void info(const char *, const char *) {}
|
||||
int version(int v) {
|
||||
(*this)("version", v);
|
||||
return v;
|
||||
}
|
||||
template<class RP>
|
||||
RangeParamIgnore range(const char *key, RP ¶m) {
|
||||
double v = param;
|
||||
@ -607,13 +570,9 @@ plugin.host->request_callback(plugin.host);
|
||||
param = v;
|
||||
return {};
|
||||
}
|
||||
template<class V>
|
||||
bool changed(const char *key, V &v) {
|
||||
V prev = v;
|
||||
(*this)(key, v);
|
||||
return v == prev;
|
||||
void invalidate(const char *) {
|
||||
LOG_EXPR("invalidate() called on state load - this should mark the entire state as dirty for reload");
|
||||
}
|
||||
void invalidate(const char *) {}
|
||||
};
|
||||
|
||||
std::vector<unsigned char> stateBuffer;
|
||||
|
||||
136
stfx/storage/stfx-storage.h
Normal file
136
stfx/storage/stfx-storage.h
Normal file
@ -0,0 +1,136 @@
|
||||
#pragma once
|
||||
|
||||
#include "./storage.h"
|
||||
|
||||
namespace stfx { namespace storage {
|
||||
|
||||
template<class SubClassCRTP>
|
||||
struct STFXStorageScanner : public signalsmith::storage::StorageScanner<SubClassCRTP> {
|
||||
void info(const char *, const char *) {}
|
||||
int version(int v) {
|
||||
return v;
|
||||
}
|
||||
|
||||
template<class V>
|
||||
bool changed(const char *key, V &v) {
|
||||
auto &sub = *(SubClassCRTP *)this;
|
||||
sub(key, v);
|
||||
return false;
|
||||
}
|
||||
void invalidate(const char *) {}
|
||||
|
||||
bool extra() {
|
||||
return false;
|
||||
}
|
||||
template<class V>
|
||||
void extra(const char *, V) {}
|
||||
|
||||
template<class PR>
|
||||
RangeParamIgnore range(const char *key, PR ¶m) {
|
||||
return {};
|
||||
}
|
||||
template<class PS>
|
||||
SteppedParamIgnore stepped(const char *key, PS ¶m) {
|
||||
return {};
|
||||
}
|
||||
};
|
||||
|
||||
template<class SubClassCRTP=void>
|
||||
struct STFXStorageWriter : public signalsmith::storage::StorageCborWriter<SubClassCRTP> {
|
||||
using signalsmith::storage::StorageCborWriter<SubClassCRTP>::StorageCborWriter;
|
||||
|
||||
void info(const char *name, const char *desc) {
|
||||
sub().extra("name", name);
|
||||
sub().extra("desc", desc);
|
||||
}
|
||||
|
||||
template<class V>
|
||||
bool changed(const char *key, V &v) {
|
||||
sub()(key, v);
|
||||
return false;
|
||||
}
|
||||
void invalidate(const char *) {}
|
||||
|
||||
int version(int v) {
|
||||
sub()("version", v);
|
||||
return v;
|
||||
}
|
||||
bool extra() {
|
||||
return false;
|
||||
}
|
||||
template<class V>
|
||||
void extra(const char *key, V v) {}
|
||||
|
||||
template<class PR>
|
||||
RangeParamIgnore range(const char *key, PR ¶m) {
|
||||
sub()(key, param);
|
||||
return {};
|
||||
}
|
||||
template<class PS>
|
||||
SteppedParamIgnore stepped(const char *key, PS ¶m) {
|
||||
sub()(key, param);
|
||||
return {};
|
||||
}
|
||||
|
||||
private:
|
||||
SubClassCRTP & sub() {
|
||||
return *(SubClassCRTP *)this;
|
||||
}
|
||||
};
|
||||
|
||||
template<class SubClassCRTP=void>
|
||||
struct STFXStorageReader : public signalsmith::storage::StorageCborReader<SubClassCRTP> {
|
||||
using signalsmith::storage::StorageCborReader<SubClassCRTP>::StorageCborReader;
|
||||
|
||||
// This is supplemental
|
||||
void info(const char *, const char *) {}
|
||||
|
||||
int version(int v) {
|
||||
sub()("version", v);
|
||||
return v;
|
||||
}
|
||||
bool extra() {
|
||||
return false;
|
||||
}
|
||||
template<class V>
|
||||
void extra(const char *key, V v) {}
|
||||
|
||||
template<class V>
|
||||
bool changed(const char *key, V &v) {
|
||||
V prev = v;
|
||||
sub()(key, v);
|
||||
return v != prev;
|
||||
}
|
||||
void invalidate(const char *invalidatedKey) {
|
||||
LOG_EXPR(invalidatedKey);
|
||||
abort();
|
||||
}
|
||||
|
||||
template<class PR>
|
||||
RangeParamIgnore range(const char *key, PR ¶m) {
|
||||
sub()(key, param);
|
||||
return {};
|
||||
}
|
||||
template<class PS>
|
||||
SteppedParamIgnore stepped(const char *key, PS ¶m) {
|
||||
sub()(key, param);
|
||||
return {};
|
||||
}
|
||||
|
||||
private:
|
||||
SubClassCRTP & sub() {
|
||||
return *(SubClassCRTP *)this;
|
||||
}
|
||||
};
|
||||
|
||||
// If void, use itself for CRTP
|
||||
template<>
|
||||
struct STFXStorageWriter<void> : public STFXStorageWriter<STFXStorageWriter<void>> {
|
||||
using STFXStorageWriter<STFXStorageWriter<void>>::STFXStorageWriter;
|
||||
};
|
||||
template<>
|
||||
struct STFXStorageReader<void> : public STFXStorageReader<STFXStorageReader<void>> {
|
||||
using STFXStorageReader<STFXStorageReader<void>>::STFXStorageReader;
|
||||
};
|
||||
|
||||
}} // namespace
|
||||
@ -2,14 +2,18 @@
|
||||
|
||||
#include "./cbor-walker.h"
|
||||
|
||||
namespace stfx { namespace storage {
|
||||
#include <string>
|
||||
#include <vector>
|
||||
|
||||
namespace signalsmith { namespace storage {
|
||||
|
||||
struct StorageDummy {
|
||||
template<class V>
|
||||
void operator()(const char *, V &) {}
|
||||
};
|
||||
|
||||
struct StorageExample {
|
||||
template<class SubClassCRTP>
|
||||
struct StorageScanner {
|
||||
template<class V>
|
||||
void operator()(const char */*key*/, V &v) {
|
||||
value(v);
|
||||
@ -27,6 +31,7 @@ private:
|
||||
void value(float &v) {};
|
||||
void value(double &v) {};
|
||||
void value(bool &v) {};
|
||||
void value(std::string &str) {};
|
||||
|
||||
template<class Item>
|
||||
void value(std::vector<Item> &array) {
|
||||
@ -37,32 +42,16 @@ private:
|
||||
|
||||
template<class V>
|
||||
void value(V &v) {
|
||||
v.state(*this);
|
||||
v.state(*(SubClassCRTP *)this);
|
||||
}
|
||||
};
|
||||
|
||||
namespace _impl {
|
||||
template<class Fallback, class MaybeVoid=void>
|
||||
struct VoidFallback {
|
||||
using T = MaybeVoid;
|
||||
};
|
||||
template<class Fallback>
|
||||
struct VoidFallback<Fallback, void> {
|
||||
using T = Fallback;
|
||||
};
|
||||
}
|
||||
|
||||
template<bool compact=false, class SubClassCRTP=void>
|
||||
template<class SubClassCRTP=void>
|
||||
struct StorageCborWriter {
|
||||
StorageCborWriter(const signalsmith::cbor::CborWriter &writer, std::vector<unsigned char> *buffer=nullptr) : cbor(writer) {
|
||||
if (buffer) buffer->resize(0);
|
||||
|
||||
if (compact) {
|
||||
cbor.addTag(0xCB68ADED); // turns into "2store..." when base64-encoded
|
||||
cbor.openArray();
|
||||
} else {
|
||||
cbor.openMap();
|
||||
}
|
||||
|
||||
cbor.openMap();
|
||||
}
|
||||
StorageCborWriter(std::vector<unsigned char> &cborBuffer) : StorageCborWriter(signalsmith::cbor::CborWriter(cborBuffer), &cborBuffer) {}
|
||||
~StorageCborWriter() {
|
||||
@ -71,7 +60,7 @@ struct StorageCborWriter {
|
||||
|
||||
template<class V>
|
||||
void operator()(const char *key, V &value) {
|
||||
if (!compact) cbor.addUtf8(key);
|
||||
cbor.addUtf8(key);
|
||||
writeValue(value);
|
||||
}
|
||||
|
||||
@ -102,6 +91,7 @@ private:
|
||||
cbor.addBool(value);
|
||||
}
|
||||
|
||||
// Support writing C strings (but not reading them), so inherited writers can add supplemental hints/info without allocating
|
||||
void writeValue(const char *cStr) {
|
||||
cbor.addUtf8(cStr);
|
||||
}
|
||||
@ -133,41 +123,42 @@ private:
|
||||
STORAGE_TYPED_ARRAY(double)
|
||||
#undef STORAGE_TYPED_ARRAY
|
||||
|
||||
using SubStorage = typename _impl::VoidFallback<StorageCborWriter, SubClassCRTP>::T;
|
||||
|
||||
template<class Obj>
|
||||
void writeValue(Obj &obj) {
|
||||
cbor.openMap();
|
||||
obj.state(*(SubStorage *)this);
|
||||
obj.state(*(SubClassCRTP *)this);
|
||||
cbor.close();
|
||||
}
|
||||
};
|
||||
|
||||
template<bool compact=false, class SubClassCRTP=void>
|
||||
template<class SubClassCRTP=void>
|
||||
struct StorageCborReader {
|
||||
using Cbor = signalsmith::cbor::TaggedCborWalker;
|
||||
|
||||
StorageCborReader(Cbor c) : cbor(c) {
|
||||
if (compact) {
|
||||
if (cbor.isArray()) cbor = cbor.enter();
|
||||
} else {
|
||||
if (cbor.isMap()) cbor = cbor.enter();
|
||||
}
|
||||
if (cbor.isMap()) cbor = cbor.enter();
|
||||
}
|
||||
StorageCborReader(const std::vector<unsigned char> &v) : StorageCborReader(Cbor(v)) {}
|
||||
|
||||
template<class V>
|
||||
void operator()(const char *key, V &v) {
|
||||
LOG_EXPR(key);
|
||||
LOG_EXPR(filterKeyLength);
|
||||
if (filterKeyBytes != nullptr) {
|
||||
if (!keyMatch(key, filterKeyBytes, filterKeyLength)) return;
|
||||
} else if (!compact) {
|
||||
if (!cbor.isUtf8()) return; // We expect a string key
|
||||
if (!keyMatch(key, (const char *)cbor.bytes(), cbor.length())) {
|
||||
return; // key doesn't match
|
||||
}
|
||||
cbor++;
|
||||
}
|
||||
LOG_EXPR(cbor.isUtf8());
|
||||
if (!cbor.isUtf8()) return; // We expect a string key
|
||||
LOG_EXPR(cbor.utf8());
|
||||
// If we have a filter, we *should* be just in front of the appropriate key, but check anyway
|
||||
if (!keyMatch(key, (const char *)cbor.bytes(), cbor.length())) {
|
||||
LOG_EXPR(key);
|
||||
LOG_EXPR(cbor.utf8());
|
||||
return; // key doesn't match
|
||||
}
|
||||
cbor++;
|
||||
readValue(v);
|
||||
LOG_EXPR(v);
|
||||
}
|
||||
|
||||
private:
|
||||
@ -177,11 +168,6 @@ private:
|
||||
|
||||
template<class Obj>
|
||||
void readValue(Obj &obj) {
|
||||
if (compact) {
|
||||
// No keys, no filters
|
||||
return obj.state(*(SubClass *)this);
|
||||
}
|
||||
|
||||
if (!cbor.isMap()) return;
|
||||
|
||||
cbor = cbor.forEachPair([&](Cbor key, Cbor value){
|
||||
@ -193,8 +179,8 @@ private:
|
||||
// Temporarily set key, and scan the object for that property
|
||||
filterKeyBytes = (const char *)key.bytes();
|
||||
filterKeyLength = key.length();
|
||||
cbor = value;
|
||||
obj.state(*(SubClass *)this);
|
||||
cbor = key;
|
||||
obj.state(*(SubClassCRTP *)this);
|
||||
|
||||
filterKeyBytes = fkb;
|
||||
filterKeyLength = fkl;
|
||||
@ -219,7 +205,9 @@ private:
|
||||
#undef STORAGE_BASIC_TYPE
|
||||
|
||||
void readValue(std::string &v) {
|
||||
v = (cbor++).utf8();
|
||||
if (!cbor.isUtf8()) v.clear();
|
||||
v.assign((const char *)cbor.bytes(), cbor.length());
|
||||
++cbor;
|
||||
}
|
||||
|
||||
template<class Item>
|
||||
@ -263,8 +251,16 @@ private:
|
||||
}
|
||||
return key[filterKeyLength] == 0;
|
||||
}
|
||||
|
||||
using SubClass = typename _impl::VoidFallback<StorageCborReader, SubClassCRTP>::T;
|
||||
};
|
||||
|
||||
// If void, use itself for CRTP
|
||||
template<>
|
||||
struct StorageCborWriter<void> : public StorageCborWriter<StorageCborWriter<void>> {
|
||||
using StorageCborWriter<StorageCborWriter<void>>::StorageCborWriter;
|
||||
};
|
||||
template<>
|
||||
struct StorageCborReader<void> : public StorageCborReader<StorageCborReader<void>> {
|
||||
using StorageCborReader<StorageCborReader<void>>::StorageCborReader;
|
||||
};
|
||||
|
||||
}} // namespace
|
||||
|
||||
@ -113,7 +113,6 @@
|
||||
data.gesture = true;
|
||||
data._gestureUnit = data.rangeUnit;
|
||||
if (count == 2) {
|
||||
console.log("data.gesture", data.gesture);
|
||||
data.value = data.defaultValue;
|
||||
data.gesture = false;
|
||||
}
|
||||
|
||||
@ -21,80 +21,40 @@ struct ParamContext {
|
||||
ParamContext() : index(0) {}
|
||||
};
|
||||
|
||||
struct WebStateWriter : public storage::StorageCborWriter<false, WebStateWriter> {
|
||||
using Super = storage::StorageCborWriter<false, WebStateWriter>;
|
||||
struct WebStateWriter : public storage::STFXStorageWriter<WebStateWriter> {
|
||||
using Super = storage::STFXStorageWriter<WebStateWriter>;
|
||||
|
||||
using Super::Super;
|
||||
|
||||
void info(const char *name, const char *desc) {
|
||||
extra("name", name);
|
||||
extra("desc", desc);
|
||||
}
|
||||
|
||||
int version(int v) {
|
||||
return v; // we don't use old states, so not worth tracking
|
||||
}
|
||||
|
||||
|
||||
// There should never be a version mismatch, ignore this
|
||||
int version(int v) {return v;}
|
||||
|
||||
// Include extra info
|
||||
bool extra() {
|
||||
return true;
|
||||
}
|
||||
|
||||
template<class V>
|
||||
void extra(const char *key, V v) {
|
||||
(*this)(key, v);
|
||||
}
|
||||
|
||||
template<class PR>
|
||||
RangeParamIgnore range(const char *key, PR ¶m) {
|
||||
(*this)(key, param);
|
||||
return {};
|
||||
}
|
||||
template<class PS>
|
||||
SteppedParamIgnore stepped(const char *key, PS ¶m) {
|
||||
(*this)(key, param);
|
||||
return {};
|
||||
}
|
||||
template<class V>
|
||||
bool changed(const char *key, V &v) {
|
||||
(*this)(key, v);
|
||||
return false;
|
||||
}
|
||||
void invalidate(const char *) {}
|
||||
};
|
||||
|
||||
struct WebStateReader : public storage::StorageCborReader<false, WebStateReader> {
|
||||
using Super = storage::StorageCborReader<false, WebStateReader>;
|
||||
struct WebStateReader : public storage::STFXStorageReader<WebStateReader> {
|
||||
using Super = storage::STFXStorageReader<WebStateReader>;
|
||||
|
||||
using Super::Super;
|
||||
|
||||
void info(const char *, const char *) {}
|
||||
|
||||
// There should never be a version mismatch, ignore this
|
||||
int version(int v) {return v;}
|
||||
|
||||
// We're interested in an extended set of values being sent back
|
||||
bool extra() {
|
||||
// We're interested in an extended set of values being sent back, even if we ignore actual "extra" things
|
||||
return true;
|
||||
}
|
||||
|
||||
// But anything read-only gets skipped
|
||||
template<class V>
|
||||
void extra(const char *key, V v) {}
|
||||
|
||||
template<class PR>
|
||||
RangeParamIgnore range(const char *key, PR ¶m) {
|
||||
(*this)(key, param);
|
||||
return {};
|
||||
}
|
||||
template<class PS>
|
||||
SteppedParamIgnore stepped(const char *key, PS ¶m) {
|
||||
(*this)(key, param);
|
||||
return {};
|
||||
}
|
||||
template<class V>
|
||||
bool changed(const char *key, V &v) {
|
||||
V prev = v;
|
||||
(*this)(key, v);
|
||||
return v != prev;
|
||||
}
|
||||
void invalidate(const char *invalidatedKey) {
|
||||
LOG_EXPR(invalidatedKey);
|
||||
}
|
||||
@ -199,6 +159,7 @@ struct WebUIHelper {
|
||||
SuperRange::state(storage);
|
||||
|
||||
constexpr bool isWeb = storageIsWeb<Storage>();
|
||||
LOG_EXPR(isWeb);
|
||||
if (isWeb) { // a bunch of extra state
|
||||
storage.extra("$type", "ParamRange");
|
||||
storage.extra("name", info->name);
|
||||
@ -210,7 +171,9 @@ struct WebUIHelper {
|
||||
storage("gesture", gesture);
|
||||
|
||||
double unit = info->toUnit(*this);
|
||||
LOG_EXPR(unit);
|
||||
if (storage.changed("rangeUnit", unit)) {
|
||||
LOG_EXPR(unit);
|
||||
SuperRange::operator=(info->fromUnit(unit));
|
||||
storage.invalidate("value");
|
||||
}
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user