diff --git a/lv2export/Makefile b/lv2export/Makefile index c5b8ce2..0946d45 100644 --- a/lv2export/Makefile +++ b/lv2export/Makefile @@ -34,16 +34,12 @@ ifeq ($(DEBUG),true) BASE_FLAGS += -UDEBUG endif -# BASE_FLAGS += -DHEADLESS -# BASE_FLAGS += -DPRIVATE= -# -# BASE_FLAGS += -fno-finite-math-only -# # BASE_FLAGS += -I../dpf/dgl -# BASE_FLAGS += -I../dpf/dgl/src/nanovg -# BASE_FLAGS += -I../dpf/distrho -# BASE_FLAGS += -I../include +BASE_FLAGS += -DHEADLESS +BASE_FLAGS += -fno-finite-math-only +BASE_FLAGS += -pthread + +# might be needed later # BASE_FLAGS += -I../include/neon-compat -# BASE_FLAGS += -I../src/Rack/include # ifeq ($(SYSDEPS),true) # BASE_FLAGS += -DCARDINAL_SYSDEPS # BASE_FLAGS += $(shell pkg-config --cflags jansson libarchive samplerate speexdsp) @@ -58,14 +54,13 @@ endif # # BASE_FLAGS += -IRack/dep/osdialog # BASE_FLAGS += -I../src/Rack/dep/oui-blendish # BASE_FLAGS += -I../src/Rack/dep/pffft -# BASE_FLAGS += -pthread -# -# ifeq ($(WINDOWS),true) -# BASE_FLAGS += -D_USE_MATH_DEFINES -# BASE_FLAGS += -DWIN32_LEAN_AND_MEAN -# BASE_FLAGS += -I../include/mingw-compat -# BASE_FLAGS += -I../include/mingw-std-threads -# endif + +ifeq ($(WINDOWS),true) +BASE_FLAGS += -D_USE_MATH_DEFINES +BASE_FLAGS += -DWIN32_LEAN_AND_MEAN +BASE_FLAGS += -I../include/mingw-compat +BASE_FLAGS += -I../include/mingw-std-threads +endif # -------------------------------------------------------------- # lots of warnings from VCV side @@ -121,10 +116,15 @@ clean: # -------------------------------------------------------------- -# $(BUILD_DIR)/valleyaudio-plateau.cpp.o: plugins/valleyaudio-plateau.cpp -# -@mkdir -p "$(shell dirname $(BUILD_DIR)/$<)" -# @echo "Compiling $<" -# $(SILENT)$(CXX) $< $(BUILD_CXX_FLAGS) -DSLUG='"$*"' -c -o $@ +$(BUILD_DIR)/aubileinstruments-%.cpp.o: plugins/aubileinstruments-%.cpp + -@mkdir -p "$(shell dirname $(BUILD_DIR)/$<)" + @echo "Compiling $<" + $(SILENT)$(CXX) $< $(BUILD_CXX_FLAGS) -DSLUG='"aubileinstruments-$*"' \ + -DTEST \ + -I../plugins/AudibleInstruments/eurorack \ + -Wno-class-memaccess \ + -Wno-unused-local-typedefs \ + -c -o $@ $(BUILD_DIR)/%.cpp.o: plugins/%.cpp -@mkdir -p "$(shell dirname $(BUILD_DIR)/$<)" diff --git a/lv2export/includes/rack.hpp b/lv2export/includes/rack.hpp index e3813de..84d993d 100644 --- a/lv2export/includes/rack.hpp +++ b/lv2export/includes/rack.hpp @@ -27,43 +27,78 @@ #pragma once +#include #include +#include #include #include #include +#include +#include #include #include #include #include -enum NVGalign {}; +#define assert(x) + +#define ENUMS(name, count) name, name ## _LAST = name + (count) - 1 + +#define VEC_ARGS(v) (v).x, (v).y +#define RECT_ARGS(r) (r).pos.x, (r).pos.y, (r).size.x, (r).size.y + +#define GLFW_MOUSE_BUTTON_LEFT 0 +#define GLFW_MOUSE_BUTTON_RIGHT 0 + +enum NVGalign { + NVG_ALIGN_LEFT, + NVG_ALIGN_RIGHT +}; struct NVGcolor { float a; }; struct NVGpaint {}; +struct NVGcontext {}; +struct NSVGimage {}; inline NVGcolor nvgRGB(int r, int g, int b) { return {}; } inline NVGcolor nvgRGBA(int r, int g, int b, int a) { return {}; } inline NVGcolor nvgRGBf(float r, float g, float b) { return {}; } inline NVGcolor nvgRGBAf(float r, float g, float b, float a) { return {}; } -inline void nvgBeginPath(void* vg) {} -inline void nvgFillColor(void* vg, NVGcolor) {} -inline void nvgFillPaint(void* vg, NVGpaint) {} -inline void nvgFill(void* vg) {} -inline void nvgStrokeColor(void* vg, NVGcolor) {} -inline void nvgStrokeWidth(void* vg, float) {} -inline void nvgStroke(void* vg) {} -inline void nvgRect(void* vg, float a, float b, float c, float d) {} -inline void nvgImageSize(void*, int, void*, void*) {} -inline NVGpaint nvgImagePattern(void*, float, float, float, float, float, int handle, float) { return {}; } +inline NVGcolor nvgTransRGBA(NVGcolor, int) { return {}; } +inline NVGcolor nvgTransRGBAf(NVGcolor, float) { return {}; } +inline void nvgBeginPath(NVGcontext* vg) {} +inline void nvgFillColor(NVGcontext* vg, NVGcolor) {} +inline void nvgFillPaint(NVGcontext* vg, NVGpaint) {} +inline void nvgFill(NVGcontext* vg) {} +inline void nvgStrokeColor(NVGcontext* vg, NVGcolor) {} +inline void nvgStrokeWidth(NVGcontext* vg, float) {} +inline void nvgStroke(NVGcontext* vg) {} +inline void nvgRect(NVGcontext* vg, float a, float b, float c, float d) {} +inline void nvgImageSize(NVGcontext*, int, void*, void*) {} +inline NVGpaint nvgImagePattern(NVGcontext*, float, float, float, float, float, int handle, float) { return {}; } +inline void nvgRoundedRect(NVGcontext* vg, float, float, float, float, float) {} +inline void nvgFontSize(NVGcontext*, int) {} +inline void nvgFontFaceId(NVGcontext*, int) {} +inline void nvgTextLetterSpacing(NVGcontext*, float) {} +inline void nvgText(NVGcontext*, float, float, const char*, const char*) {} +inline void nvgTextAlign(NVGcontext*, NVGalign) {} +inline void nvgSave(NVGcontext*) {} +inline void nvgRestore(NVGcontext*) {} +inline void nvgScale(NVGcontext*, float, float) {} struct json_t {}; -json_t* json_boolean(bool) { return NULL; } -json_t* json_integer(int) { return NULL; } -json_t* json_object() { return NULL; } -json_t* json_object_get(json_t*, const char*) { return NULL; } -bool json_boolean_value(json_t*) { return false; } -int json_integer_value(json_t*) { return 0; } -void json_object_set_new(json_t*, const char*, json_t*) {} +inline json_t* json_boolean(bool) { return NULL; } +inline json_t* json_integer(int) { return NULL; } +inline json_t* json_object(void) { return NULL; } +inline json_t* json_object_get(json_t*, const char*) { return NULL; } +inline bool json_is_true(json_t*) { return false; } +inline bool json_boolean_value(json_t*) { return false; } +inline int json_integer_value(json_t*) { return 0; } +inline float json_number_value(json_t*) { return 0.f; } +inline void json_object_set_new(json_t*, const char*, json_t*) {} +inline json_t* json_array(void) { return NULL; } +inline json_t* json_array_get(json_t*, int) { return NULL; } +inline void json_array_insert_new(json_t*, int, json_t*) {} namespace rack { @@ -103,38 +138,261 @@ namespace ui { struct Menu; } +namespace window { +struct Svg; +} + namespace math { -inline int clamp(int x, int a, int b) { - return std::max(std::min(x, b), a); +template +inline bool isEven(T x) { return x % 2 == 0; } + +template +inline bool isOdd(T x) { return x % 2 != 0; } + +inline int clamp(int x, int a, int b) { return std::max(std::min(x, b), a); } +inline int clampSafe(int x, int a, int b) { return (a <= b) ? clamp(x, a, b) : clamp(x, b, a); } + +inline float clamp(float x, float a = 0.f, float b = 1.f) { return std::fmax(std::fmin(x, b), a); } +inline float clampSafe(float x, float a = 0.f, float b = 1.f) { return (a <= b) ? clamp(x, a, b) : clamp(x, b, a); } + +inline int eucMod(int a, int b) { + int mod = a % b; + if (mod < 0) { + mod += b; + } + return mod; } -inline float clamp(float x, float a = 0.f, float b = 1.f) { - return std::fmax(std::fmin(x, b), a); +inline int eucDiv(int a, int b) { + int div = a / b; + int mod = a % b; + if (mod < 0) { + div -= 1; + } + return div; +} + +inline void eucDivMod(int a, int b, int* div, int* mod) { + *div = a / b; + *mod = a % b; + if (*mod < 0) { + *div -= 1; + *mod += b; + } +} + +inline int log2(int n) { + int i = 0; + while (n >>= 1) { + i++; + } + return i; +} + +template +bool isPow2(T n) { return n > 0 && (n & (n - 1)) == 0; } + +template +T sgn(T x) { return x > 0 ? 1 : (x < 0 ? -1 : 0); } + +#if defined __clang__ +__attribute__((optnone)) +#else +__attribute__((optimize("signed-zeros"))) +#endif +inline float normalizeZero(float x) { return x + 0.f; } + +inline float eucMod(float a, float b) { + float mod = std::fmod(a, b); + if (mod < 0.f) { + mod += b; + } + return mod; } +inline bool isNear(float a, float b, float epsilon = 1e-6f) { return std::fabs(a - b) <= epsilon; } + +inline float chop(float x, float epsilon = 1e-6f) { return std::fabs(x) <= epsilon ? 0.f : x; } + inline float rescale(float x, float xMin, float xMax, float yMin, float yMax) { return yMin + (x - xMin) / (xMax - xMin) * (yMax - yMin); } +inline float crossfade(float a, float b, float p) { return a + (b - a) * p; } + +inline float interpolateLinear(const float* p, float x) { + const int xi = x; + const float xf = x - xi; + return crossfade(p[xi], p[xi + 1], xf); +} + +inline void complexMult(float ar, float ai, float br, float bi, float* cr, float* ci) { + *cr = ar * br - ai * bi; + *ci = ar * bi + ai * br; +} + +struct Rect; + struct Vec { float x = 0.f; float y = 0.f; Vec() {} Vec(float xy) : x(xy), y(xy) {} Vec(float x, float y) : x(x), y(y) {} + float& operator[](int i) { return (i == 0) ? x : y; } + const float& operator[](int i) const { return (i == 0) ? x : y; } Vec neg() const { return Vec(-x, -y); } Vec plus(Vec b) const { return Vec(x + b.x, y + b.y); } Vec minus(Vec b) const { return Vec(x - b.x, y - b.y); } Vec mult(float s) const { return Vec(x * s, y * s); } Vec mult(Vec b) const { return Vec(x * b.x, y * b.y); } + Vec div(float s) const { return Vec(x / s, y / s); } + Vec div(Vec b) const { return Vec(x / b.x, y / b.y); } + float dot(Vec b) const { return x * b.x + y * b.y; } + float arg() const { return std::atan2(y, x); } + float norm() const { return std::hypot(x, y); } + Vec normalize() const { return div(norm()); } + float square() const { return x * x + y * y; } + float area() const { return x * y; } + Vec rotate(float angle) { + float sin = std::sin(angle); + float cos = std::cos(angle); + return Vec(x * cos - y * sin, x * sin + y * cos); + } + Vec flip() const { return Vec(y, x); } + Vec min(Vec b) const { return Vec(std::fmin(x, b.x), std::fmin(y, b.y)); } + Vec max(Vec b) const { return Vec(std::fmax(x, b.x), std::fmax(y, b.y)); } + Vec abs() const { return Vec(std::fabs(x), std::fabs(y)); } + Vec round() const { return Vec(std::round(x), std::round(y)); } + Vec floor() const { return Vec(std::floor(x), std::floor(y)); } + Vec ceil() const { return Vec(std::ceil(x), std::ceil(y)); } + bool equals(Vec b) const { return x == b.x && y == b.y; } + bool isZero() const { return x == 0.f && y == 0.f; } + bool isFinite() const { return std::isfinite(x) && std::isfinite(y); } + Vec clamp(Rect bound) const; + Vec clampSafe(Rect bound) const; + Vec crossfade(Vec b, float p) { return this->plus(b.minus(*this).mult(p)); } + bool isEqual(Vec b) const { return equals(b); } }; struct Rect { Vec pos; Vec size; + Rect() {} + Rect(Vec pos, Vec size) : pos(pos), size(size) {} + Rect(float posX, float posY, float sizeX, float sizeY) : pos(Vec(posX, posY)), size(Vec(sizeX, sizeY)) {} + static Rect fromMinMax(Vec a, Vec b) { return Rect(a, b.minus(a)); } + static Rect fromCorners(Vec a, Vec b) { return fromMinMax(a.min(b), a.max(b)); } + static Rect inf() { return Rect(Vec(-INFINITY, -INFINITY), Vec(INFINITY, INFINITY)); } + bool contains(Vec v) const { + return (pos.x <= v.x) && (size.x == INFINITY || v.x < pos.x + size.x) + && (pos.y <= v.y) && (size.y == INFINITY || v.y < pos.y + size.y); + } + bool contains(Rect r) const { + return (pos.x <= r.pos.x) && (r.pos.x - size.x <= pos.x - r.size.x) + && (pos.y <= r.pos.y) && (r.pos.y - size.y <= pos.y - r.size.y); + } + bool intersects(Rect r) const { + return (r.size.x == INFINITY || pos.x < r.pos.x + r.size.x) && (size.x == INFINITY || r.pos.x < pos.x + size.x) + && (r.size.y == INFINITY || pos.y < r.pos.y + r.size.y) && (size.y == INFINITY || r.pos.y < pos.y + size.y); + } + bool equals(Rect r) const { return pos.equals(r.pos) && size.equals(r.size); } + float getLeft() const { return pos.x; } + float getRight() const { return (size.x == INFINITY) ? INFINITY : (pos.x + size.x); } + float getTop() const { return pos.y; } + float getBottom() const { return (size.y == INFINITY) ? INFINITY : (pos.y + size.y); } + float getWidth() const { return size.x; } + float getHeight() const { return size.y; } + Vec getCenter() const { return pos.plus(size.mult(0.5f)); } + Vec getTopLeft() const { return pos; } + Vec getTopRight() const { return Vec(getRight(), getTop()); } + Vec getBottomLeft() const { return Vec(getLeft(), getBottom()); } + Vec getBottomRight() const { return Vec(getRight(), getBottom()); } + Rect clamp(Rect bound) const { + Rect r; + r.pos.x = math::clampSafe(pos.x, bound.pos.x, bound.pos.x + bound.size.x); + r.pos.y = math::clampSafe(pos.y, bound.pos.y, bound.pos.y + bound.size.y); + r.size.x = math::clamp(pos.x + size.x, bound.pos.x, bound.pos.x + bound.size.x) - r.pos.x; + r.size.y = math::clamp(pos.y + size.y, bound.pos.y, bound.pos.y + bound.size.y) - r.pos.y; + return r; + } + Rect nudge(Rect bound) const { + Rect r; + r.size = size; + r.pos.x = math::clampSafe(pos.x, bound.pos.x, bound.pos.x + bound.size.x - size.x); + r.pos.y = math::clampSafe(pos.y, bound.pos.y, bound.pos.y + bound.size.y - size.y); + return r; + } + Rect expand(Rect b) const { + Rect r; + r.pos.x = std::fmin(pos.x, b.pos.x); + r.pos.y = std::fmin(pos.y, b.pos.y); + r.size.x = std::fmax(pos.x + size.x, b.pos.x + b.size.x) - r.pos.x; + r.size.y = std::fmax(pos.y + size.y, b.pos.y + b.size.y) - r.pos.y; + return r; + } + Rect intersect(Rect b) const { + Rect r; + r.pos.x = std::fmax(pos.x, b.pos.x); + r.pos.y = std::fmax(pos.y, b.pos.y); + r.size.x = std::fmin(pos.x + size.x, b.pos.x + b.size.x) - r.pos.x; + r.size.y = std::fmin(pos.y + size.y, b.pos.y + b.size.y) - r.pos.y; + return r; + } + Rect zeroPos() const { + return Rect(Vec(), size); + } + Rect grow(Vec delta) const { + Rect r; + r.pos = pos.minus(delta); + r.size = size.plus(delta.mult(2.f)); + return r; + } + Rect shrink(Vec delta) const { + Rect r; + r.pos = pos.plus(delta); + r.size = size.minus(delta.mult(2.f)); + return r; + } + Vec interpolate(Vec p) { + return pos.plus(size.mult(p)); + } + bool isContaining(Vec v) const { return contains(v); } + bool isIntersecting(Rect r) const { return intersects(r); } + bool isEqual(Rect r) const { return equals(r); } }; +inline Vec Vec::clamp(Rect bound) const { + return Vec(math::clamp(x, bound.pos.x, bound.pos.x + bound.size.x), + math::clamp(y, bound.pos.y, bound.pos.y + bound.size.y)); +} + +inline Vec Vec::clampSafe(Rect bound) const { + return Vec(math::clampSafe(x, bound.pos.x, bound.pos.x + bound.size.x), + math::clampSafe(y, bound.pos.y, bound.pos.y + bound.size.y)); +} +inline Vec operator+(const Vec& a) { return a; } +inline Vec operator-(const Vec& a) { return a.neg(); } +inline Vec operator+(const Vec& a, const Vec& b) { return a.plus(b); } +inline Vec operator-(const Vec& a, const Vec& b) { return a.minus(b); } +inline Vec operator*(const Vec& a, const Vec& b) { return a.mult(b); } +inline Vec operator*(const Vec& a, const float& b) { return a.mult(b); } +inline Vec operator*(const float& a, const Vec& b) { return b.mult(a); } +inline Vec operator/(const Vec& a, const Vec& b) { return a.div(b); } +inline Vec operator/(const Vec& a, const float& b) { return a.div(b); } +inline Vec operator+=(Vec& a, const Vec& b) { return a = a.plus(b); } +inline Vec operator-=(Vec& a, const Vec& b) { return a = a.minus(b); } +inline Vec operator*=(Vec& a, const Vec& b) { return a = a.mult(b); } +inline Vec operator*=(Vec& a, const float& b) { return a = a.mult(b); } +inline Vec operator/=(Vec& a, const Vec& b) { return a = a.div(b); } +inline Vec operator/=(Vec& a, const float& b) { return a = a.div(b); } +inline bool operator==(const Vec& a, const Vec& b) { return a.equals(b); } +inline bool operator!=(const Vec& a, const Vec& b) { return !a.equals(b); } + +inline bool operator==(const Rect& a, const Rect& b) { return a.equals(b); } +inline bool operator!=(const Rect& a, const Rect& b) { return !a.equals(b); } + } // namespace math namespace engine { @@ -175,6 +433,16 @@ struct Light { } }; +struct LightInfo { + Module* module = NULL; + int lightId = -1; + std::string name; + std::string description; + virtual ~LightInfo() {} + virtual std::string getName() { return name; } + virtual std::string getDescription() { return description; } +}; + struct Param { float value = 0.f; float getValue() { return value; } @@ -195,35 +463,31 @@ struct Port { INPUT, OUTPUT, }; - void setVoltage(float voltage, int channel = 0) { voltages[channel] = voltage; } float getVoltage(int channel = 0) { return voltages[channel]; } float getPolyVoltage(int channel) { return isMonophonic() ? getVoltage(0) : getVoltage(channel); } - - float getNormalVoltage(float normalVoltage, int channel = 0) { return isConnected() ? getVoltage(channel) : normalVoltage; } - - float getNormalPolyVoltage(float normalVoltage, int channel) { return isConnected() ? getPolyVoltage(channel) : normalVoltage; } - + float getNormalVoltage(float normalVoltage, int channel = 0) { + return isConnected() ? getVoltage(channel) : normalVoltage; + } + float getNormalPolyVoltage(float normalVoltage, int channel) { + return isConnected() ? getPolyVoltage(channel) : normalVoltage; + } float* getVoltages(int firstChannel = 0) { return &voltages[firstChannel]; } - void readVoltages(float* v) { for (int c = 0; c < channels; c++) { v[c] = voltages[c]; } } - void writeVoltages(const float* v) { for (int c = 0; c < channels; c++) { voltages[c] = v[c]; } } - void clearVoltages() { for (int c = 0; c < channels; c++) { voltages[c] = 0.f; } } - float getVoltageSum() { float sum = 0.f; for (int c = 0; c < channels; c++) { @@ -231,7 +495,6 @@ struct Port { } return sum; } - float getVoltageRMS() { if (channels == 0) { return 0.f; @@ -247,7 +510,6 @@ struct Port { return std::sqrt(sum); } } - // template // T getVoltageSimd(int firstChannel) { // return T::load(&voltages[firstChannel]); @@ -272,7 +534,6 @@ struct Port { // void setVoltageSimd(T voltage, int firstChannel) { // voltage.store(&voltages[firstChannel]); // } - void setChannels(int channels) { if (this->channels == 0) { return; @@ -285,7 +546,6 @@ struct Port { } this->channels = channels; } - int getChannels() { return channels; } bool isConnected() { return channels > 0; } bool isMonophonic() { return channels == 1; } @@ -335,12 +595,10 @@ struct ParamQuantity : Quantity { bool randomizeEnabled = true; bool smoothEnabled = false; bool snapEnabled = false; - // Param* getParam(); // /** If smoothEnabled is true, requests to the engine to smoothly move to a target value each sample. */ // void setSmoothValue(float value); // float getSmoothValue(); - // void setValue(float value) override; // float getValue() override; float getMinValue() override { return minValue; } @@ -355,9 +613,7 @@ struct ParamQuantity : Quantity { // std::string getUnit() override; // void reset() override; // void randomize() override; - virtual std::string getDescription() { return description; } - // virtual json_t* toJson(); // virtual void fromJson(json_t* rootJ); }; @@ -376,8 +632,7 @@ struct Module { std::vector paramQuantities; std::vector inputInfos; std::vector outputInfos; -// std::vector lightInfos; - + std::vector lightInfos; virtual ~Module() { for (ParamQuantity* paramQuantity : paramQuantities) { if (paramQuantity) @@ -391,15 +646,12 @@ struct Module { if (outputInfo) delete outputInfo; } -// for (LightInfo* lightInfo : lightInfos) { -// if (lightInfo) -// delete lightInfo; -// } + for (LightInfo* lightInfo : lightInfos) { + if (lightInfo) + delete lightInfo; + } } - void config(int numParams, int numInputs, int numOutputs, int numLights = 0) { -// // This method should only be called once. -// assert(params.empty() && inputs.empty() && outputs.empty() && lights.empty() && paramQuantities.empty()); params.resize(numParams); inputs.resize(numInputs); outputs.resize(numOutputs); @@ -416,12 +668,10 @@ struct Module { for (int i = 0; i < numOutputs; i++) { configOutput(i); } -// lightInfos.resize(numLights); + lightInfos.resize(numLights); } - template TParamQuantity* configParam(int paramId, float minValue, float maxValue, float defaultValue, std::string name = "", std::string unit = "", float displayBase = 0.f, float displayMultiplier = 1.f, float displayOffset = 0.f) { -// assert(paramId < (int) params.size() && paramId < (int) paramQuantities.size()); if (paramQuantities[paramId]) delete paramQuantities[paramId]; @@ -442,24 +692,20 @@ struct Module { p->value = q->getDefaultValue(); return q; } - template TSwitchQuantity* configSwitch(int paramId, float minValue, float maxValue, float defaultValue, std::string name = "", std::vector labels = {}) { TSwitchQuantity* sq = configParam(paramId, minValue, maxValue, defaultValue, name); sq->labels = labels; return sq; } - template TSwitchQuantity* configButton(int paramId, std::string name = "") { TSwitchQuantity* sq = configParam(paramId, 0.f, 1.f, 0.f, name); sq->randomizeEnabled = false; return sq; } - template TPortInfo* configInput(int portId, std::string name = "") { -// assert(portId < (int) inputs.size() && portId < (int) inputInfos.size()); if (inputInfos[portId]) delete inputInfos[portId]; @@ -471,10 +717,8 @@ struct Module { inputInfos[portId] = info; return info; } - template TPortInfo* configOutput(int portId, std::string name = "") { -// assert(portId < (int) outputs.size() && portId < (int) outputInfos.size()); if (outputInfos[portId]) delete outputInfos[portId]; @@ -486,35 +730,28 @@ struct Module { outputInfos[portId] = info; return info; } - -// template -// TLightInfo* configLight(int lightId, std::string name = "") { -// assert(lightId < (int) lights.size() && lightId < (int) lightInfos.size()); -// if (lightInfos[lightId]) -// delete lightInfos[lightId]; -// -// TLightInfo* info = new TLightInfo; -// info->LightInfo::module = this; -// info->LightInfo::lightId = lightId; -// info->LightInfo::name = name; -// lightInfos[lightId] = info; -// return info; -// } - + template + TLightInfo* configLight(int lightId, std::string name = "") { + if (lightInfos[lightId]) + delete lightInfos[lightId]; + + TLightInfo* info = new TLightInfo; + info->LightInfo::module = this; + info->LightInfo::lightId = lightId; + info->LightInfo::name = name; + lightInfos[lightId] = info; + return info; + } void configBypass(int inputId, int outputId) { -// assert(inputId < (int) inputs.size()); -// assert(outputId < (int) outputs.size()); // // Check that output is not yet routed // for (BypassRoute& br : bypassRoutes) { // assert(br.outputId != outputId); // } -// // BypassRoute br; // br.inputId = inputId; // br.outputId = outputId; // bypassRoutes.push_back(br); } - int getNumParams() { return params.size(); } Param& getParam(int index) { return params[index]; } int getNumInputs() { return inputs.size(); } @@ -526,8 +763,7 @@ struct Module { ParamQuantity* getParamQuantity(int index) { return paramQuantities[index]; } PortInfo* getInputInfo(int index) { return inputInfos[index]; } PortInfo* getOutputInfo(int index) { return outputInfos[index]; } -// LightInfo* getLightInfo(int index) { return lightInfos[index]; } - + LightInfo* getLightInfo(int index) { return lightInfos[index]; } struct ProcessArgs { float sampleRate; float sampleTime; @@ -537,19 +773,13 @@ struct Module { step(); } virtual void step() {} - // virtual void processBypass(const ProcessArgs& args); - // virtual json_t* toJson(); // virtual void fromJson(json_t* rootJ); - - /** Serializes the "params" object. */ // virtual json_t* paramsToJson(); // virtual void paramsFromJson(json_t* rootJ); - virtual json_t* dataToJson() { return NULL; } virtual void dataFromJson(json_t* rootJ) {} - struct SampleRateChangeEvent { float sampleRate; float sampleTime; @@ -557,16 +787,13 @@ struct Module { virtual void onSampleRateChange(const SampleRateChangeEvent&) { onSampleRateChange(); } - struct ResetEvent {}; virtual void onReset(const ResetEvent&) {} // TODO - virtual void onAdd() {} virtual void onRemove() {} virtual void onReset() {} virtual void onRandomize() {} virtual void onSampleRateChange() {} - // private void doProcess(const ProcessArgs& args) { // if (!internal->bypassed) @@ -597,9 +824,54 @@ struct Widget { math::Rect box; Widget* parent = NULL; std::list children; - bool visible = true; + bool visible = false; bool requestedDelete = false; + virtual ~Widget() {} + math::Rect getBox() { return {}; } + void setBox(math::Rect box) {} + math::Vec getPosition() { return {}; } + void setPosition(math::Vec pos) {} + math::Vec getSize() { return {}; } + void setSize(math::Vec size) {} + widget::Widget* getParent() { return NULL; } + bool isVisible() { return false; } + void setVisible(bool visible) {} + void show() {} + void hide() {} + void requestDelete() {} + + virtual math::Rect getChildrenBoundingBox() { return {}; } + virtual math::Rect getVisibleChildrenBoundingBox() { return {}; } + bool isDescendantOf(Widget* ancestor) { return false; } + virtual math::Vec getRelativeOffset(math::Vec v, Widget* ancestor) { return {}; } + math::Vec getAbsoluteOffset(math::Vec v) { return {}; } + virtual float getRelativeZoom(Widget* ancestor) { return 0.f; } + float getAbsoluteZoom() { return 0.f; } + virtual math::Rect getViewport(math::Rect r) { return {}; } + + template T* getAncestorOfType() { return NULL; } + template T* getFirstDescendantOfType() { return NULL; } + + bool hasChild(Widget* child) { return false; } + void addChild(Widget* child) {} + void addChildBottom(Widget* child) {} + void addChildBelow(Widget* child, Widget* sibling) {} + void addChildAbove(Widget* child, Widget* sibling) {} + void removeChild(Widget* child) {} + void clearChildren() {} + + virtual void step() {} + struct DrawArgs { + NVGcontext* vg = NULL; + math::Rect clipBox; + void* fb = NULL; + }; + virtual void draw(const DrawArgs&) {} + // DEPRECATED virtual void draw(NVGcontext* vg) {} + virtual void drawLayer(const DrawArgs& args, int layer) {} + void drawChild(Widget* child, const DrawArgs& args, int layer = 0) {} + using BaseEvent = widget::BaseEvent; struct PositionBaseEvent { @@ -609,16 +881,14 @@ struct Widget { struct HoverEvent : BaseEvent, PositionBaseEvent { math::Vec mouseDelta; }; - virtual void onHover(const HoverEvent&) { - } + virtual void onHover(const HoverEvent&) {} struct ButtonEvent : BaseEvent, PositionBaseEvent { int button; int action; int mods; }; - virtual void onButton(const ButtonEvent&) { - } + virtual void onButton(const ButtonEvent&) {} struct DoubleClickEvent : BaseEvent {}; virtual void onDoubleClick(const DoubleClickEvent&) {} @@ -632,21 +902,18 @@ struct Widget { }; struct HoverKeyEvent : BaseEvent, PositionBaseEvent, KeyBaseEvent {}; - virtual void onHoverKey(const HoverKeyEvent&) { - } + virtual void onHoverKey(const HoverKeyEvent&) {} struct TextBaseEvent { int codepoint; }; struct HoverTextEvent : BaseEvent, PositionBaseEvent, TextBaseEvent {}; - virtual void onHoverText(const HoverTextEvent&) { - } + virtual void onHoverText(const HoverTextEvent&) {} struct HoverScrollEvent : BaseEvent, PositionBaseEvent { math::Vec scrollDelta; }; - virtual void onHoverScroll(const HoverScrollEvent&) { - } + virtual void onHoverScroll(const HoverScrollEvent&) {} struct EnterEvent : BaseEvent {}; virtual void onEnter(const EnterEvent&) {} @@ -707,8 +974,7 @@ struct Widget { PathDropEvent(const std::vector& paths) : paths(paths) {} const std::vector& paths; }; - virtual void onPathDrop(const PathDropEvent&) { - } + virtual void onPathDrop(const PathDropEvent&) {} struct ActionEvent : BaseEvent {}; virtual void onAction(const ActionEvent&) {} @@ -716,28 +982,45 @@ struct Widget { struct ChangeEvent : BaseEvent {}; virtual void onChange(const ChangeEvent&) {} - bool hasChild(Widget* child) { return false; } - void addChild(Widget* child) {} - void addChildBottom(Widget* child) {} - void addChildBelow(Widget* child, Widget* sibling) {} - void addChildAbove(Widget* child, Widget* sibling) {} - void removeChild(Widget* child) {} - void clearChildren() {} - virtual void step() {} - struct DrawArgs { - void* vg = NULL; - math::Rect clipBox; - void* fb = NULL; + struct DirtyEvent : BaseEvent {}; + virtual void onDirty(const DirtyEvent&) {} + + struct RepositionEvent : BaseEvent {}; + virtual void onReposition(const RepositionEvent&) {} + + struct ResizeEvent : BaseEvent {}; + virtual void onResize(const ResizeEvent&) {} + + struct AddEvent : BaseEvent {}; + virtual void onAdd(const AddEvent&) {} + + struct RemoveEvent : BaseEvent {}; + virtual void onRemove(const RemoveEvent&) {} + + struct ShowEvent : BaseEvent {}; + virtual void onShow(const ShowEvent&) {} + + struct HideEvent : BaseEvent {}; + virtual void onHide(const HideEvent&) {} + + struct ContextCreateEvent : BaseEvent { + NVGcontext* vg; + }; + virtual void onContextCreate(const ContextCreateEvent&) {} + + struct ContextDestroyEvent : BaseEvent { + NVGcontext* vg; }; - virtual void draw(const DrawArgs& args); + virtual void onContextDestroy(const ContextDestroyEvent&) {} }; struct OpaqueWidget : Widget { }; struct SvgWidget : Widget { + std::shared_ptr svg; void wrap() {} - void setSvg(void* svg) {} + void setSvg(std::shared_ptr) {} }; struct TransparentWidget : Widget { @@ -749,8 +1032,11 @@ namespace app { static constexpr const float RACK_GRID_WIDTH = 15; static constexpr const float RACK_GRID_HEIGHT = 380; -// static constexpr const math::Vec RACK_GRID_SIZE = math::Vec(RACK_GRID_WIDTH, RACK_GRID_HEIGHT); -// static constexpr const math::Vec RACK_OFFSET = RACK_GRID_SIZE.mult(math::Vec(2000, 100)); +static const math::Vec RACK_GRID_SIZE = math::Vec(RACK_GRID_WIDTH, RACK_GRID_HEIGHT); +static const math::Vec RACK_OFFSET = RACK_GRID_SIZE.mult(math::Vec(2000, 100)); + +struct ParamWidget; +struct PortWidget; struct CircularShadow : widget::TransparentWidget { float blurRadius; @@ -762,15 +1048,57 @@ struct LightWidget : widget::TransparentWidget { }; struct ModuleWidget : widget::OpaqueWidget { -// plugin::Model* model = NULL; + // plugin::Model* model = NULL; engine::Module* module = NULL; - void setModel(void*) {} - void setModule(void*) {} - void setPanel(void*) {} - void addParam(void*) {} - void addInput(void*) {} - void addOutput(void*) {} - virtual void appendContextMenu(ui::Menu* menu) {} + + // plugin::Model* getModel() { return NULL; } + // void setModel(plugin::Model*) {} + + engine::Module* getModule() { return NULL; } + void setModule(engine::Module*) {} + + widget::Widget* getPanel() { return NULL; } + void setPanel(widget::Widget*) {} + void setPanel(std::shared_ptr) {} + + void addParam(ParamWidget*) {} + void addInput(PortWidget*) {} + void addOutput(PortWidget*) {} + ParamWidget* getParam(int paramId) { return NULL; } + PortWidget* getInput(int portId) { return NULL; } + PortWidget* getOutput(int portId) { return NULL; } + std::vector getParams() { return {}; } + std::vector getPorts() { return {}; } + std::vector getInputs() { return {}; } + std::vector getOutputs() { return {}; } + + virtual void appendContextMenu(ui::Menu*) {} + + json_t* toJson() { return NULL; } + void fromJson(json_t*) {} + bool pasteJsonAction(json_t*) { return false; } + void copyClipboard() {} + bool pasteClipboardAction() { return false; } + void load(std::string) {} + void loadAction(std::string) {} + void loadTemplate() {} + void loadDialog() {} + void save(std::string) {} + void saveTemplate() {} + void saveTemplateDialog() {} + bool hasTemplate() { return false; } + void clearTemplate() {} + void clearTemplateDialog() {} + void saveDialog() {} + void disconnect() {} + void resetAction() {} + void randomizeAction() {} + void appendDisconnectActions(/*history::ComplexAction*/void*) {} + void disconnectAction() {} + void cloneAction(bool cc = true) {} + void bypassAction(bool) {} + void removeAction() {} + void createContextMenu() {} }; struct MultiLightWidget : LightWidget { @@ -824,37 +1152,40 @@ struct Switch : ParamWidget { }; struct SvgKnob : Knob { +// widget::FramebufferWidget* fb; CircularShadow* shadow; - void setSvg(void* svg) {} +// widget::TransformWidget* tw; + widget::SvgWidget* sw; + void setSvg(std::shared_ptr) {} }; struct SvgPanel : widget::Widget { // widget::FramebufferWidget* fb; // widget::SvgWidget* sw; // PanelBorder* panelBorder; - void setBackground(void* svg) {} + void setBackground(std::shared_ptr) {} }; struct SvgPort : PortWidget { // widget::FramebufferWidget* fb; CircularShadow* shadow; // widget::SvgWidget* sw; - void setSvg(void* svg) {} + void setSvg(std::shared_ptr) {} }; struct SvgScrew : widget::Widget { // widget::FramebufferWidget* fb; widget::SvgWidget* sw; - void setSvg(void* svg) {} + void setSvg(std::shared_ptr) {} }; struct SvgSlider : app::SliderKnob { // widget::FramebufferWidget* fb; widget::SvgWidget* background; -// widget::SvgWidget* handle; + widget::SvgWidget* handle; math::Vec minHandlePos, maxHandlePos; - void setBackgroundSvg(void* svg) {} - void setHandleSvg(void* svg) {} + void setBackgroundSvg(std::shared_ptr) {} + void setHandleSvg(std::shared_ptr) {} void setHandlePos(math::Vec minHandlePos, math::Vec maxHandlePos) {} void setHandlePosCentered(math::Vec minHandlePosCentered, math::Vec maxHandlePosCentered) {} }; @@ -865,94 +1196,271 @@ struct SvgSwitch : Switch { // widget::SvgWidget* sw; // std::vector> frames; bool latch = false; - void addFrame(void* svg) {} + void addFrame(std::shared_ptr) {} +}; + +struct Scene : widget::OpaqueWidget { +// RackScrollWidget* rackScroll; +// RackWidget* rack; + widget::Widget* menuBar; + widget::Widget* browser; + math::Vec getMousePos() { return {}; } }; } // namespace app namespace asset { -const char* plugin(void* instance, const char* path) { - return NULL; -} +inline std::string plugin(void* instance, const char* path) { return {}; } +inline std::string system(const char* path) { return {}; } } // namespace asset namespace componentlibrary { +static constexpr const NVGcolor SCHEME_BLACK_TRANSPARENT = {}; +static constexpr const NVGcolor SCHEME_BLACK = {}; +static constexpr const NVGcolor SCHEME_WHITE = {}; +static constexpr const NVGcolor SCHEME_RED = {}; +static constexpr const NVGcolor SCHEME_ORANGE = {}; +static constexpr const NVGcolor SCHEME_YELLOW = {}; +static constexpr const NVGcolor SCHEME_GREEN = {}; +static constexpr const NVGcolor SCHEME_CYAN = {}; +static constexpr const NVGcolor SCHEME_BLUE = {}; +static constexpr const NVGcolor SCHEME_PURPLE = {}; static constexpr const NVGcolor SCHEME_LIGHT_GRAY = {}; +static constexpr const NVGcolor SCHEME_DARK_GRAY = {}; template struct TSvgLight : TBase { // widget::FramebufferWidget* fb; // widget::SvgWidget* sw; - void setSvg(void* svg) {} + void setSvg(std::shared_ptr) {} }; using SvgLight = TSvgLight<>; template -struct TGrayModuleLightWidget : TBase { -}; +struct TGrayModuleLightWidget : TBase {}; using GrayModuleLightWidget = TGrayModuleLightWidget<>; template -struct TWhiteLight : TBase { -}; +struct TWhiteLight : TBase {}; using WhiteLight = TWhiteLight<>; template -struct TRedLight : TBase { -}; +struct TRedLight : TBase {}; using RedLight = TRedLight<>; template -struct TGreenLight : TBase { -}; +struct TGreenLight : TBase {}; using GreenLight = TGreenLight<>; template -struct TBlueLight : TBase { -}; +struct TBlueLight : TBase {}; using BlueLight = TBlueLight<>; template -struct TYellowLight : TBase { -}; +struct TYellowLight : TBase {}; using YellowLight = TYellowLight<>; template -struct TGreenRedLight : TBase { -}; +struct TGreenRedLight : TBase {}; using GreenRedLight = TGreenRedLight<>; template -struct TRedGreenBlueLight : TBase { -}; +struct TRedGreenBlueLight : TBase {}; using RedGreenBlueLight = TRedGreenBlueLight<>; template -struct LargeLight : TSvgLight { -}; +struct LargeLight : TSvgLight {}; template -struct MediumLight : TSvgLight { -}; +struct MediumLight : TSvgLight {}; + +template +struct SmallLight : TSvgLight {}; + +template +struct TinyLight : TSvgLight {}; + +template +struct LargeSimpleLight : TBase {}; + +template +struct MediumSimpleLight : TBase {}; +template +struct SmallSimpleLight : TBase {}; + +template +struct TinySimpleLight : TBase {}; + +template +struct RectangleLight : TBase {}; + +template +struct VCVBezelLight : TBase {}; template -struct SmallLight : TSvgLight { +using LEDBezelLight = VCVBezelLight; + +template +struct PB61303Light : TBase {}; + +struct RoundKnob : app::SvgKnob { + widget::SvgWidget* bg; +}; +struct RoundBlackKnob : RoundKnob {}; +struct RoundSmallBlackKnob : RoundKnob {}; +struct RoundLargeBlackKnob : RoundKnob {}; +struct RoundBigBlackKnob : RoundKnob {}; +struct RoundHugeBlackKnob : RoundKnob {}; +struct RoundBlackSnapKnob : RoundBlackKnob {}; +struct Davies1900hKnob : app::SvgKnob {}; +struct Davies1900hWhiteKnob : Davies1900hKnob {}; +struct Davies1900hBlackKnob : Davies1900hKnob {}; +struct Davies1900hRedKnob : Davies1900hKnob {}; +struct Davies1900hLargeWhiteKnob : Davies1900hKnob {}; +struct Davies1900hLargeBlackKnob : Davies1900hKnob {}; +struct Davies1900hLargeRedKnob : Davies1900hKnob {}; +struct Rogan : app::SvgKnob { + widget::SvgWidget* bg; + widget::SvgWidget* fg; +}; +struct Rogan6PSWhite : Rogan {}; +struct Rogan5PSGray : Rogan {}; +struct Rogan3PSBlue : Rogan {}; +struct Rogan3PSRed : Rogan {}; +struct Rogan3PSGreen : Rogan {}; +struct Rogan3PSWhite : Rogan {}; +struct Rogan3PBlue : Rogan {}; +struct Rogan3PRed : Rogan {}; +struct Rogan3PGreen : Rogan {}; +struct Rogan3PWhite : Rogan {}; +struct Rogan2SGray : Rogan {}; +struct Rogan2PSBlue : Rogan {}; +struct Rogan2PSRed : Rogan {}; +struct Rogan2PSGreen : Rogan {}; +struct Rogan2PSWhite : Rogan {}; +struct Rogan2PBlue : Rogan {}; +struct Rogan2PRed : Rogan {}; +struct Rogan2PGreen : Rogan {}; +struct Rogan2PWhite : Rogan {}; +struct Rogan1PSBlue : Rogan {}; +struct Rogan1PSRed : Rogan {}; +struct Rogan1PSGreen : Rogan {}; +struct Rogan1PSWhite : Rogan {}; +struct Rogan1PBlue : Rogan {}; +struct Rogan1PRed : Rogan {}; +struct Rogan1PGreen : Rogan {}; +struct Rogan1PWhite : Rogan {}; +struct SynthTechAlco : app::SvgKnob { + widget::SvgWidget* bg; +}; +struct Trimpot : app::SvgKnob { + widget::SvgWidget* bg; }; +struct BefacoBigKnob : app::SvgKnob { + widget::SvgWidget* bg; +}; +struct BefacoTinyKnob : app::SvgKnob { + widget::SvgWidget* bg; +}; +struct BefacoSlidePot : app::SvgSlider {}; + +struct VCVSlider : app::SvgSlider {}; +using LEDSlider = VCVSlider; + +struct VCVSliderHorizontal : app::SvgSlider {}; +using LEDSliderHorizontal = VCVSliderHorizontal; +template +struct LightSlider : TBase { + app::ModuleLightWidget* light = NULL; + app::ModuleLightWidget* getLight() { return light; } +}; + +template +struct VCVSliderLight : RectangleLight> {}; template -struct TinyLight : TSvgLight { +using LEDSliderLight = VCVSliderLight; + +template +struct VCVLightSlider : LightSlider> {}; +template +using LEDLightSlider = VCVLightSlider; + +struct LEDSliderGreen : VCVLightSlider {}; +struct LEDSliderRed : VCVLightSlider {}; +struct LEDSliderYellow : VCVLightSlider {}; +struct LEDSliderBlue : VCVLightSlider {}; +struct LEDSliderWhite : VCVLightSlider {}; + +template +struct VCVLightSliderHorizontal : LightSlider {}; +template +using LEDLightSliderHorizontal = VCVLightSliderHorizontal; + +struct PJ301MPort : app::SvgPort {}; +struct PJ3410Port : app::SvgPort {}; +struct CL1362Port : app::SvgPort {}; + +template +struct MomentarySwitch : TSwitch {}; + +struct NKK : app::SvgSwitch {}; +struct CKSS : app::SvgSwitch {}; +struct CKSSThree : app::SvgSwitch {}; +struct CKSSThreeHorizontal : app::SvgSwitch {}; +struct CKD6 : app::SvgSwitch {}; +struct TL1105 : app::SvgSwitch {}; +struct VCVButton : app::SvgSwitch {}; +using LEDButton = VCVButton; +struct VCVLatch : VCVButton {}; + +template +struct VCVLightButton : VCVButton { + app::ModuleLightWidget* light = NULL; + app::ModuleLightWidget* getLight() { return light; } }; +template +using LEDLightButton = VCVLightButton; -struct ScrewBlack : app::SvgScrew { +template +struct VCVLightLatch : VCVLightButton {}; + +struct BefacoSwitch : app::SvgSwitch {}; + +struct BefacoPush : app::SvgSwitch {}; + +struct VCVBezel : app::SvgSwitch {}; +using LEDBezel = VCVBezel; + +struct VCVBezelLatch : VCVBezel {}; + +template +struct VCVLightBezel : VCVBezel { + app::ModuleLightWidget* light = NULL; + app::ModuleLightWidget* getLight() { return light; } }; +template +using LEDLightBezel = VCVLightBezel; + +template +struct VCVLightBezelLatch : VCVLightBezel {}; + +struct PB61303 : app::SvgSwitch {}; + +struct ScrewSilver : app::SvgScrew {}; +struct ScrewBlack : app::SvgScrew {}; } // namespace componentlibrary namespace dsp { - + +static constexpr const float FREQ_C4 = 261.6256f; +static constexpr const float FREQ_A4 = 440.0000f; +static constexpr const float FREQ_SEMITONE = 1.0594630943592953f; + inline float sinc(float x) { if (x == 0.f) return 1.f; @@ -968,6 +1476,43 @@ inline float sinc(float x) { // return simd::ifelse(zeromask, 1.f, x); // } +// template +// T amplitudeToDb(T amp) { +// return simd::log10(amp) * 20; +// } + +template +T dbToAmplitude(T db) { return std::pow(10, db / 20); } + +// template +// T quadraticBipolar(T x) { +// return simd::sgn(x) * (x * x); +// } + +template +T cubic(T x) { return x * x * x; } + +// template +// T quarticBipolar(T x) { +// return simd::sgn(x) * (x * x * x * x); +// } + +template +T quintic(T x) { return x * x * x * x * x; } + +// template +// T sqrtBipolar(T x) { +// return simd::sgn(x) * simd::sqrt(x); +// } + +// template +// T exponentialBipolar(T b, T x) { +// return (simd::pow(b, x) - simd::pow(b, -x)) / (b - 1.f / b); +// } + +template +struct Frame { T samples[CHANNELS]; }; + template inline T blackmanHarris(T p) { return @@ -990,6 +1535,118 @@ inline void boxcarLowpassIR(float* out, int len, float cutoff = 0.5f) { } } +struct BooleanTrigger { + bool state = true; + void reset() { + state = true; + } + bool process(bool state) { + bool triggered = (state && !this->state); + this->state = state; + return triggered; + } +}; + +template +struct TSchmittTrigger { + T state; + TSchmittTrigger() { + reset(); + } + void reset() { + state = T::mask(); + } + T process(T in, T offThreshold = 0.f, T onThreshold = 1.f) { + T on = (in >= onThreshold); + T off = (in <= offThreshold); + T triggered = ~state & on; + state = on | (state & ~off); + return triggered; + } + T isHigh() { + return state; + } +}; + + +template <> +struct TSchmittTrigger { + bool state = true; + void reset() { + state = true; + } + bool process(float in, float offThreshold = 0.f, float onThreshold = 1.f) { + if (state) { + if (in <= offThreshold) { + state = false; + } + } + else { + if (in >= onThreshold) { + state = true; + return true; + } + } + return false; + } + bool isHigh() { + return state; + } +}; + +typedef TSchmittTrigger<> SchmittTrigger; + +struct PulseGenerator { + float remaining = 0.f; + void reset() { remaining = 0.f; } + bool process(float deltaTime) { + if (remaining > 0.f) { + remaining -= deltaTime; + return true; + } + return false; + } + void trigger(float duration = 1e-3f) { + if (duration > remaining) { + remaining = duration; + } + } +}; + +template +struct TTimer { + T time = 0.f; + void reset() { + time = 0.f; + } + T process(T deltaTime) { + time += deltaTime; + return time; + } + T getTime() { + return time; + } +}; + +typedef TTimer<> Timer; + +struct ClockDivider { + uint32_t clock = 0; + uint32_t division = 1; + void reset() { clock = 0; } + void setDivision(uint32_t division) { this->division = division; } + uint32_t getDivision() { return division; } + uint32_t getClock() { return clock; } + bool process() { + clock++; + if (clock >= division) { + clock = 0; + return true; + } + return false; + } +}; + template struct Decimator { T inBuffer[OVERSAMPLE * QUALITY]; @@ -1018,20 +1675,160 @@ struct Decimator { } }; -struct PulseGenerator { - float remaining = 0.f; - void reset() { remaining = 0.f; } - bool process(float deltaTime) { - if (remaining > 0.f) { - remaining -= deltaTime; - return true; +template +struct RingBuffer { + std::atomic start{0}; + std::atomic end{0}; + T data[S]; + void push(T t) { + size_t i = end % S; + data[i] = t; + end++; + } + void pushBuffer(const T* t, int n) { + size_t i = end % S; + size_t e1 = i + n; + size_t e2 = (e1 < S) ? e1 : S; + std::memcpy(&data[i], t, sizeof(T) * (e2 - i)); + if (e1 > S) { + std::memcpy(data, &t[S - i], sizeof(T) * (e1 - S)); } - return false; + end += n; } - void trigger(float duration = 1e-3f) { - if (duration > remaining) { - remaining = duration; + T shift() { + size_t i = start % S; + T t = data[i]; + start++; + return t; + } + void shiftBuffer(T* t, size_t n) { + size_t i = start % S; + size_t s1 = i + n; + size_t s2 = (s1 < S) ? s1 : S; + std::memcpy(t, &data[i], sizeof(T) * (s2 - i)); + if (s1 > S) { + std::memcpy(&t[S - i], data, sizeof(T) * (s1 - S)); + } + start += n; + } + void clear() { + start = end.load(); + } + bool empty() const { + return start >= end; + } + bool full() const { + return end - start >= S; + } + size_t size() const { + return end - start; + } + size_t capacity() const { + return S - size(); + } +}; + +template +struct DoubleRingBuffer { + std::atomic start{0}; + std::atomic end{0}; + T data[2 * S]; + void push(T t) { + size_t i = end % S; + data[i] = t; + data[i + S] = t; + end++; + } + T shift() { + size_t i = start % S; + T t = data[i]; + start++; + return t; + } + void clear() { + start = end.load(); + } + bool empty() const { + return start >= end; + } + bool full() const { + return end - start >= S; + } + size_t size() const { + return end - start; + } + size_t capacity() const { + return S - size(); + } + T* endData() { + size_t i = end % S; + return &data[i]; + } + void endIncr(size_t n) { + size_t i = end % S; + size_t e1 = i + n; + size_t e2 = (e1 < S) ? e1 : S; + std::memcpy(&data[S + i], &data[i], sizeof(T) * (e2 - i)); + if (e1 > S) { + std::memcpy(data, &data[S], sizeof(T) * (e1 - S)); } + end += n; + } + const T* startData() const { + size_t i = start % S; + return &data[i]; + } + void startIncr(size_t n) { + start += n; + } +}; + +template +struct AppleRingBuffer { + size_t start = 0; + size_t end = 0; + T data[N]; + void returnBuffer() { + size_t s = size(); + std::memmove(data, &data[start], sizeof(T) * s); + start = 0; + end = s; + } + void push(T t) { + if (end + 1 > N) { + returnBuffer(); + } + data[end++] = t; + } + T shift() { + return data[start++]; + } + bool empty() const { + return start == end; + } + bool full() const { + return end - start == S; + } + size_t size() const { + return end - start; + } + size_t capacity() const { + return S - size(); + } + T* endData(size_t n) { + if (end + n > N) { + returnBuffer(); + } + return &data[end]; + } + void endIncr(size_t n) { + end += n; + } + const T* startData() const { + return &data[start]; + } + void startIncr(size_t n) { + start += n; } }; @@ -1065,13 +1862,13 @@ using DragDrop = widget::Widget::DragDropEvent; using PathDrop = widget::Widget::PathDropEvent; using Action = widget::Widget::ActionEvent; using Change = widget::Widget::ChangeEvent; -// using Dirty = widget::Widget::DirtyEvent; -// using Reposition = widget::Widget::RepositionEvent; -// using Resize = widget::Widget::ResizeEvent; -// using Add = widget::Widget::AddEvent; -// using Remove = widget::Widget::RemoveEvent; -// using Show = widget::Widget::ShowEvent; -// using Hide = widget::Widget::HideEvent; +using Dirty = widget::Widget::DirtyEvent; +using Reposition = widget::Widget::RepositionEvent; +using Resize = widget::Widget::ResizeEvent; +using Add = widget::Widget::AddEvent; +using Remove = widget::Widget::RemoveEvent; +using Show = widget::Widget::ShowEvent; +using Hide = widget::Widget::HideEvent; } // namespace event namespace plugin { @@ -1086,6 +1883,119 @@ struct Plugin { } // namespace plugin +namespace random { + +struct Xoroshiro128Plus { + uint64_t state[2] = {}; + void seed(uint64_t s0, uint64_t s1) { + state[0] = s0; + state[1] = s1; + operator()(); + } + bool isSeeded() { return state[0] || state[1]; } + static uint64_t rotl(uint64_t x, int k) { return (x << k) | (x >> (64 - k)); } + uint64_t operator()() { + uint64_t s0 = state[0]; + uint64_t s1 = state[1]; + uint64_t result = s0 + s1; + s1 ^= s0; + state[0] = rotl(s0, 55) ^ s1 ^ (s1 << 14); + state[1] = rotl(s1, 36); + return result; + } + constexpr uint64_t min() const { return 0; } + constexpr uint64_t max() const { return UINT64_MAX; } +}; + +Xoroshiro128Plus& local(); + +template +T get() { return local()(); } + +template <> +inline uint32_t get() { return get() >> 32; } + +template <> +inline uint16_t get() { return get() >> 48; } + +template <> +inline uint8_t get() { return get() >> 56; } + +template <> +inline bool get() { return get() >> 63; } + +template <> +inline float get() { return get() * 2.32830629e-10f; } + +template <> +inline double get() { return get() * 5.421010862427522e-20; } + +inline uint64_t u64() { return get(); } +inline uint32_t u32() { return get(); } +inline float uniform() { return get(); } + +inline float normal() { + const float radius = std::sqrt(-2.f * std::log(1.f - get())); + const float theta = 2.f * M_PI * get(); + return radius * std::sin(theta); +} + +inline void buffer(uint8_t* out, size_t len) { + Xoroshiro128Plus& rng = local(); + for (size_t i = 0; i < len; i += 4) { + uint64_t r = rng(); + out[i] = r; + if (i + 1 < len) + out[i + 1] = r >> 8; + if (i + 2 < len) + out[i + 2] = r >> 16; + if (i + 3 < len) + out[i + 3] = r >> 24; + } +} + +inline std::vector vector(size_t len) { + std::vector v(len); + buffer(v.data(), len); + return v; +} + +} // namespace random + +namespace settings { + +const bool tooltips = false; + +} // namespace settings + +namespace string { + +inline std::string fV(const char* format, va_list args) { + // va_lists cannot be reused but we need it twice, so clone args. + va_list args2; + va_copy(args2, args); + // Compute size of required buffer + int size = vsnprintf(NULL, 0, format, args); + if (size < 0) + return ""; + // Create buffer + std::string s; + s.resize(size); + vsnprintf(&s[0], size + 1, format, args2); + return s; +} + +__attribute__((format(printf, 1, 2))) +inline std::string f(const char* format, ...) { + va_list args; + va_start(args, format); + std::string s = fV(format, args); + va_end(args); + return s; +} + +} // namespace string + namespace ui { struct Button : widget::OpaqueWidget { @@ -1111,12 +2021,20 @@ struct MenuItem : MenuEntry { std::string text; std::string rightText; bool disabled = false; + virtual Menu* createChildMenu() { return NULL; } }; struct MenuLabel : MenuEntry { std::string text; }; +struct MenuSeparator : MenuEntry { +}; + +struct Tooltip : widget::Widget { + std::string text; +}; + } // namespace ui namespace window { @@ -1129,13 +2047,34 @@ inline math::Vec in2px(math::Vec in) { return in.mult(SVG_DPI); } inline float mm2px(float mm) { return mm * (SVG_DPI / MM_PER_IN); } inline math::Vec mm2px(math::Vec mm) { return mm.mult(SVG_DPI / MM_PER_IN); } +struct Svg { +// NSVGimage* handle = NULL; +// ~Svg(); +// /** Don't call this directly. Use `Svg::load()` for caching. */ +// void loadFile(const std::string& filename); +// void loadString(const std::string& str); +// math::Vec getSize(); +// int getNumShapes(); +// int getNumPaths(); +// int getNumPoints(); +// void draw(NVGcontext* vg); + static std::shared_ptr load(const std::string&) { return {}; } +}; + +inline void svgDraw(NVGcontext*, NSVGimage*) {} + +struct Font { + int handle; +}; + struct Image { int handle; }; struct Window { - std::shared_ptr loadImage(const std::string&) { return {}; } - void* loadSvg(const void*) { return NULL; } + inline std::shared_ptr loadFont(const std::string&) { return {}; } + inline std::shared_ptr loadImage(const std::string&) { return {}; } + inline std::shared_ptr loadSvg(const std::string&) { return {}; } }; }; @@ -1151,7 +2090,7 @@ using plugin::Plugin; using plugin::Model; template -plugin::Model* createModel(std::string slug) { +plugin::Model* createModel(std::string) { struct TModel : plugin::Model { engine::Module* createModule() override { return new TModule; @@ -1167,54 +2106,125 @@ template T* construct(F f, V v, Args... args) { return NULL; } template -TWidget* createWidget(math::Vec pos) { +inline TWidget* createWidget(math::Vec pos) { + return NULL; +} + +template +inline TWidget* createWidgetCentered(math::Vec) { + return NULL; +} + +inline app::SvgPanel* createPanel(std::string) { return NULL; } template -TParamWidget* createParam(math::Vec pos, engine::Module* module, int paramId) { +inline TParamWidget* createParam(math::Vec, engine::Module*, int) { return NULL; } template -TParamWidget* createParamCentered(math::Vec pos, engine::Module* module, int paramId) { +inline TParamWidget* createParamCentered(math::Vec, engine::Module*, int) { return NULL; } template -TPortWidget* createInput(math::Vec pos, engine::Module* module, int inputId) { +inline TPortWidget* createInput(math::Vec, engine::Module*, int) { return NULL; } template -TPortWidget* createInputCentered(math::Vec pos, engine::Module* module, int inputId) { +inline TPortWidget* createInputCentered(math::Vec, engine::Module*, int) { return NULL; } template -TPortWidget* createOutput(math::Vec pos, engine::Module* module, int outputId) { +inline TPortWidget* createOutput(math::Vec pos, engine::Module*, int) { return NULL; } template -TPortWidget* createOutputCentered(math::Vec pos, engine::Module* module, int outputId) { +inline TPortWidget* createOutputCentered(math::Vec pos, engine::Module*, int) { return NULL; } template -TModuleLightWidget* createLight(math::Vec pos, engine::Module* module, int firstLightId) { +inline TModuleLightWidget* createLight(math::Vec, engine::Module*, int) { return NULL; } template -TModuleLightWidget* createLightCentered(math::Vec pos, engine::Module* module, int firstLightId) { +inline TModuleLightWidget* createLightCentered(math::Vec, engine::Module*, int) { + return NULL; +} + +template +inline TParamWidget* createLightParam(math::Vec, engine::Module*, int, int) { + return NULL; +} + +template +inline TParamWidget* createLightParamCentered(math::Vec, engine::Module*, int, int) { + return NULL; +} + +template +inline TMenu* createMenu() { + return NULL; +} + +template +inline TMenuLabel* createMenuLabel(std::string) { + return NULL; +} + +template +inline TMenuItem* createMenuItem(std::string, std::string rt = "") { + return NULL; +} + +template +inline TMenuItem* createMenuItem(std::string, std::string rightText, std::function, bool d = false, bool ac = false) { + return NULL; +} + +template +inline ui::MenuItem* createCheckMenuItem(std::string, std::string, std::function, std::function, bool d = false, bool ac = false) { + return NULL; +} + +template +inline ui::MenuItem* createBoolMenuItem(std::string, std::string, std::function, std::function, bool d = false, bool ac = false) { + return NULL; +} + +template +inline ui::MenuItem* createBoolPtrMenuItem(std::string, std::string, T*) { + return NULL; +} + +template +inline ui::MenuItem* createSubmenuItem(std::string, std::string, std::function, bool d = false) { + return NULL; +} + +template +inline ui::MenuItem* createIndexSubmenuItem(std::string, std::vector, std::function, std::function, bool d = false, bool ac = false) { + return NULL; +} + +template +inline ui::MenuItem* createIndexPtrSubmenuItem(std::string, std::vector, T*) { return NULL; } struct Context { + app::Scene _scene; engine::Engine _engine; window::Window _window; engine::Engine* engine = &_engine; + app::Scene* scene = &_scene; window::Window* window = &_window; }; diff --git a/lv2export/lv2plugin.cpp b/lv2export/lv2plugin.cpp index 45e5676..102d9f4 100644 --- a/lv2export/lv2plugin.cpp +++ b/lv2export/lv2plugin.cpp @@ -35,6 +35,9 @@ static constexpr const bool kCvOutputs[] = PLUGIN_CV_OUTPUTS; #include "DistrhoUtils.hpp" +#include +#include + namespace rack { static thread_local Context* threadContext = nullptr; @@ -51,6 +54,15 @@ void contextSet(Context* context) { threadContext = context; } +namespace random { + +Xoroshiro128Plus& local() { + static Xoroshiro128Plus rng; + return rng; +} + +} // namespace random + } struct PluginLv2 { @@ -62,6 +74,21 @@ struct PluginLv2 { PluginLv2(double sr) { + rack::random::Xoroshiro128Plus& rng(rack::random::local()); + + if (! rng.isSeeded()) + { + struct timeval tv; + gettimeofday(&tv, NULL); + uint64_t usec = uint64_t(tv.tv_sec) * 1000 * 1000 + tv.tv_usec; + + static uint64_t globalCounter = 1; + rng.seed(usec, globalCounter++); + + for (int i = 0; i < 4; i++) + rng(); + } + context._engine.sampleRate = sr; contextSet(&context); module = PLUGIN_MODEL->createModule(); diff --git a/lv2export/plugins/aubileinstruments-macro-osc-2.cpp b/lv2export/plugins/aubileinstruments-macro-osc-2.cpp new file mode 100644 index 0000000..b626f40 --- /dev/null +++ b/lv2export/plugins/aubileinstruments-macro-osc-2.cpp @@ -0,0 +1,60 @@ +/* + * DISTRHO Cardinal Plugin + * Copyright (C) 2021-2022 Filipe Coelho + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation; either version 3 of + * the License, or any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * For a full copy of the GNU General Public License see the LICENSE file. + */ + +#define PLAITS_ALWAYS_LOW_CPU_MODE +#include "AudibleInstruments/src/Plaits.cpp" +#include "AudibleInstruments/eurorack/stmlib/utils/random.cc" +#include "AudibleInstruments/eurorack/stmlib/dsp/units.cc" + +#include "AudibleInstruments/eurorack/plaits/dsp/voice.cc" +#include "AudibleInstruments/eurorack/plaits/dsp/engine/additive_engine.cc" +#include "AudibleInstruments/eurorack/plaits/dsp/engine/bass_drum_engine.cc" +#include "AudibleInstruments/eurorack/plaits/dsp/engine/chord_engine.cc" +#include "AudibleInstruments/eurorack/plaits/dsp/engine/fm_engine.cc" +#include "AudibleInstruments/eurorack/plaits/dsp/engine/grain_engine.cc" +#include "AudibleInstruments/eurorack/plaits/dsp/engine/hi_hat_engine.cc" +#include "AudibleInstruments/eurorack/plaits/dsp/engine/modal_engine.cc" +#include "AudibleInstruments/eurorack/plaits/dsp/engine/noise_engine.cc" +#include "AudibleInstruments/eurorack/plaits/dsp/engine/particle_engine.cc" +#include "AudibleInstruments/eurorack/plaits/dsp/engine/snare_drum_engine.cc" +#include "AudibleInstruments/eurorack/plaits/dsp/engine/speech_engine.cc" +#include "AudibleInstruments/eurorack/plaits/dsp/engine/string_engine.cc" +#include "AudibleInstruments/eurorack/plaits/dsp/engine/swarm_engine.cc" +#include "AudibleInstruments/eurorack/plaits/dsp/engine/virtual_analog_engine.cc" +#include "AudibleInstruments/eurorack/plaits/dsp/engine/waveshaping_engine.cc" +#include "AudibleInstruments/eurorack/plaits/dsp/engine/wavetable_engine.cc" + +#include "AudibleInstruments/eurorack/plaits/dsp/physical_modelling/modal_voice.cc" +#include "AudibleInstruments/eurorack/plaits/dsp/physical_modelling/resonator.cc" +#include "AudibleInstruments/eurorack/plaits/dsp/physical_modelling/string.cc" +#include "AudibleInstruments/eurorack/plaits/dsp/physical_modelling/string_voice.cc" + +#include "AudibleInstruments/eurorack/plaits/dsp/speech/lpc_speech_synth.cc" +#include "AudibleInstruments/eurorack/plaits/dsp/speech/lpc_speech_synth_controller.cc" +#include "AudibleInstruments/eurorack/plaits/dsp/speech/lpc_speech_synth_phonemes.cc" +#include "AudibleInstruments/eurorack/plaits/dsp/speech/lpc_speech_synth_words.cc" +#include "AudibleInstruments/eurorack/plaits/dsp/speech/naive_speech_synth.cc" +#include "AudibleInstruments/eurorack/plaits/dsp/speech/sam_speech_synth.cc" + +#include "AudibleInstruments/eurorack/plaits/resources.cc" + +#define PLUGIN_MODEL modelPlaits +#define PLUGIN_CV_INPUTS {1,1,1,1,1,1,1,1} +#define PLUGIN_CV_OUTPUTS {0,0} + +#include "lv2plugin.cpp" +#include "export.cpp" diff --git a/lv2export/plugins/aubileinstruments-macro-osc.cpp b/lv2export/plugins/aubileinstruments-macro-osc.cpp new file mode 100644 index 0000000..d9c4db7 --- /dev/null +++ b/lv2export/plugins/aubileinstruments-macro-osc.cpp @@ -0,0 +1,40 @@ +/* + * DISTRHO Cardinal Plugin + * Copyright (C) 2021-2022 Filipe Coelho + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation; either version 3 of + * the License, or any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * For a full copy of the GNU General Public License see the LICENSE file. + */ + +#define BRAIDS_ALWAYS_LOW_CPU_MODE +#include "AudibleInstruments/src/Braids.cpp" +#include "AudibleInstruments/eurorack/stmlib/utils/random.cc" + +#define kHighestNote kHighestNoteAnalog +#define kPitchTableStart kPitchTableStartAnalog +#define kOctave kOctaveAnalog +#include "AudibleInstruments/eurorack/braids/analog_oscillator.cc" +#undef kHighestNote +#undef kPitchTableStart +#undef kOctave + +#include "AudibleInstruments/eurorack/braids/digital_oscillator.cc" +#include "AudibleInstruments/eurorack/braids/macro_oscillator.cc" +// #include "AudibleInstruments/eurorack/braids/quantizer.cc" +#include "AudibleInstruments/eurorack/braids/resources.cc" + +#define PLUGIN_MODEL modelBraids +#define PLUGIN_CV_INPUTS {1,1,1,1,1} +#define PLUGIN_CV_OUTPUTS {0} + +#include "lv2plugin.cpp" +#include "export.cpp" diff --git a/lv2export/plugins/fundamental-quantizer.cpp b/lv2export/plugins/fundamental-quantizer.cpp new file mode 100644 index 0000000..e2131bf --- /dev/null +++ b/lv2export/plugins/fundamental-quantizer.cpp @@ -0,0 +1,25 @@ +/* + * DISTRHO Cardinal Plugin + * Copyright (C) 2021-2022 Filipe Coelho + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation; either version 3 of + * the License, or any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * For a full copy of the GNU General Public License see the LICENSE file. + */ + +#include "Fundamental/src/Quantizer.cpp" + +#define PLUGIN_MODEL modelQuantizer +#define PLUGIN_CV_INPUTS {1} +#define PLUGIN_CV_OUTPUTS {1} + +#include "lv2plugin.cpp" +#include "export.cpp" diff --git a/lv2export/plugins/fundamental-seq3.cpp b/lv2export/plugins/fundamental-seq3.cpp new file mode 100644 index 0000000..e2dde55 --- /dev/null +++ b/lv2export/plugins/fundamental-seq3.cpp @@ -0,0 +1,25 @@ +/* + * DISTRHO Cardinal Plugin + * Copyright (C) 2021-2022 Filipe Coelho + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation; either version 3 of + * the License, or any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * For a full copy of the GNU General Public License see the LICENSE file. + */ + +#include "Fundamental/src/SEQ3.cpp" + +#define PLUGIN_MODEL modelSEQ3 +#define PLUGIN_CV_INPUTS {1,1,1,1,1} +#define PLUGIN_CV_OUTPUTS {1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1} + +#include "lv2plugin.cpp" +#include "export.cpp" diff --git a/plugins/AudibleInstruments b/plugins/AudibleInstruments index 6833ade..2a19bb2 160000 --- a/plugins/AudibleInstruments +++ b/plugins/AudibleInstruments @@ -1 +1 @@ -Subproject commit 6833adeb7141a06151c9ee7059cc43d3c854c96d +Subproject commit 2a19bb25c0da725756390ad96dca55632800c74d diff --git a/plugins/Fundamental b/plugins/Fundamental index 612b0db..5ad6f48 160000 --- a/plugins/Fundamental +++ b/plugins/Fundamental @@ -1 +1 @@ -Subproject commit 612b0dba4634c365b98f96da2459ba58124bbcf0 +Subproject commit 5ad6f48a006beedda1465208aab8f9296d39c48a